Programming 版 (精华区)
发信人: Roe (Roe), 信区: Programming
标 题: 引用到底是不是指针?
发信站: 哈工大紫丁香 (2002年02月22日20:43:49 星期五), 站内信件
拿以下的程序来说,swap函数定义了两个整形引用,当调用swap函数时,&用于传递参数
之前,指示编译程序将参数的地址传递给swap的两个整形引用,那这里的a,b是否与i,
j共用了同一块儿内存?
#include <stdio.h>
main()
{
extern void swap(int &a, int &b);
int i = 7, j = -3;
swap(i, j);
printf("The value of i = %d j = %d", i, j);
}
void swap(int &a, int &b)
{
int temp = b;
b = a;
a = temp;
}
--回复得分 5 --
在C中,所有函数调用都是传值调用,传引用是通过模拟实现的,即传递指向对象的
指针并在函数调用时复引用该指针访问这个对象。传递的数组名也是指针,C++通过
提供引用参数纠正C的这种不足。INT &count :COUNT 是对一个整数变量的引用。只
要传递该变量名,变量会自动以传引用方式调用。
--回复得分 20 --
有一千个读者,就有一千个哈姆雷特。
对于luckybat的咬文嚼字似乎并不利于我们理解和解决问题。就把引用看成是常指针
吧,我不觉得有什么不妥的。C++的一大优点在于:她首先是C,然后是C++。
多研究些问题,少谈些“主义”。
--回复得分 0 --
References is an alias (an alternate name) for an object.
References are frequently used for pass-by-reference:
void swap(int& i, int& j)
{
int tmp = i;
i = j;
j = tmp;
}
int main()
{
int x, y;
// ...
swap(x,y);
}
Here i and j are aliases for main's x and y respectively. In other words,
i is x — not a pointer to x, nor a copy of x, but x itself. Anything you
do to i gets done to x, and vice versa.
OK. That's how you should think of references as a programmer. Now, at the
risk of confusing you by giving you a different perspective, here's how
references are implemented. Underneath it all, a reference i to object x
is typically the machine address of the object x. But when the programmer
says i++, the compiler generates code that increments x. In particular,
the address bits that the compiler uses to find x are not changed. A C programmer
will think of this as if you used the C style pass-by-pointer, with the
syntactic variant of (1) moving the & from the caller into the callee, and
(2) eliminating the *s. In other words, a C programmer will think of i as
a macro for (*p), where p is a pointer to x (e.g., the compiler automatically
dereferences the underlying pointer; i++ is changed to (*p)++; i = 7 is
automatically changed to *p = 7).
Important note: Even though a reference is often implemented using an address
in the underlying assembly language, please do not think of a reference
as a funny looking pointer to an object. A reference is the object. It is
not a pointer to the object, nor a copy of the object. It is the object.
--回复得分 0 --
gun2,象你这样的“批评”,至少说明你根本不懂得研究的乐趣。再者,你可以对"
咬文嚼字"不以为然,但对你没用的不等于对别人没用,我承认,总编一些应用软件
的话,当然不必考虑这么多。
--回复得分 30 --
如果你做过编译程序,你就会知道: 引用当然不是指针!
事实上,引用属于“传地址”的一种参数传递方式,类似于Pascal语言里的VAR型参
数,而指针却是属于“传拷贝”的一种方式,二者是有本质差别的。一般用户经常混
淆它们,是因为它们能够用来实现相似的功能,但在高级用法里面,引用是受推荐的
,因为它能够确保传入的地址确实是参数声明类型(或与之兼容类型)的对象的地址,
相反,指针虽然灵活性较强,但安全性却很差,它不能确保传入地址的合法性。C++
引入引用的概念,正是为了弥补C的这一缺陷,所以说C++的参数传递是更为多样的,
而C却很十分单调。
补充一下,地址的概念是非常复杂的,但指针的概念却是非常简单的(仅仅是一种基
本类型而已),除了指针,编译程序在其他许多地方都要花大量工夫处理地址,比如
引用,表面上是传递了一个地址,和传递一个指针(地址值的一个拷贝)差不多,但
前者的处理比后者复杂多了,如果你了解template或cast的概念你就会明白这些。
runninglapin、sunsetyang两位朋友虽然结论是错的,但列举的“现象”都是对的。
哎,说了这么多也不一定能说清,有问题再讨论吧。
--回复得分 0 --
luckybat,得请教您:
1)地址和地址值的拷贝有什么不一样的地方吗?
2)你所言的传地址和传拷贝之间有什么差别?
3)指针和地址之间有什么复杂的关系?
4)地址概念复杂?从何而言呢?
--回复得分 0 --
chyang说得很好,我想有很多人都会对以上概念产生混乱.
--回复得分 0 --
这个问题的提出和回答很有代表性,分数不多,但回答的同志,很多,很详细!值得
大家学习!
--回复得分 0 --
可以这么说:引用是经过编译器对象验证的“安全的指针”。
我的一条经验是:引用只能作参数,返回值尽量用指针。
我在上面的帖子中从编译方法的角度作了些阐述,仅仅是因为对此较有兴趣而已,其
实经过大家讨论应该都清楚怎么个用法了,这就够了。
--回复得分 0 --
a
--回复得分 0 --
分数是低了点,但我总有很多问题要问,因此有些保留,大家不介意吧!
--回复得分 5 --
引用是指针.不信的话可以在VC汇编窗口下调是,看看他们的指令有和不同.
--回复得分 0 --
chyang,
谢谢你提出的问题,下面是我的意见供参考,注意参照我上一帖的上下文。
1)地址值的拷贝可能在保存、传递的过程中被无意被篡改或者由于该地址失效(例
如该地址指向的临时对象被释放)而引发灾难,糟糕的是编译程序无法对此提供有效
的检测手段,但编译程序对于引用对象的合法性则能够进行有效的检测,以确保引用
参数对应的实参是合法的,当然此时传递的地址也将是合法的(注意你还是有办法绕
过这道屏障的,这叫“编译欺诈”,不过后果自负);
2)传地址和传拷贝的区别是很明显的,请回忆一下Pascal语言里的VAR型参数的用法
。指针确实是属于传拷贝的一种,不信,可以试着在函数体里对指针型参数赋值,你
会发现这和对整型参数赋值没有任何区别(如果不是用作临时变量,一样的毫无意义
);
3)指针和地址之间并没有什么复杂的关系,指针作为一种标量类型,类似于整型,
区别仅在于其值为地址,可以用来访问其他对象(有点象废话),但指针操作却极易引
起隐患,我想很多C程序的错误都是由指针操作错误引起的;
4)这里说地址概念的复杂主要是指编译系统中对地址的处理非常复杂。
本人水平有限,对此问题不能给出一个理想的结论,还望高手指教。
--回复得分 10 --
我也说两句:
在c++里面的引用概念同DELPHI及JAVA中的引用概念不大相同。
在C++ 里引用只用于参数传递上。在这儿引用同指针当然不同。
而DELPHI和JAVA中, 引用概念被推广了,
在这时,说引用是指针的另一种形式并没有什么错:)
众所周知: DELPHI,BCB两者相通之处就是这两个概念的共性。
luckybat所说的地址概念复杂是从机器指令角度来说的吧。
一个指针,仅一个变量,用它来确定物理内存中的那一个地方,因为,
多数时候,我们仅仅需要知道这个信息,而不必去考虑什么段地址页地址之类的东东
...
--回复得分 5 --
从引用是变量的一种别名来看,引用是相同(至少兼容)数据类型的变量的地址,没有强
制类型转换的引用吧(我没见过)。这相当于一种约束,指针就灵活多了。在同一种类
型的参数传递中,两者用起来好象差不多。
--回复得分 10 --
当然是指针,不过他有自己的特殊性。在c语言里,引用变量和被引用的变量共享同
一块内存。两者之一的变化都会影响到对方!!!
--回复得分 10 --
你的理解是正确的。引用在实现上是常量指针,但是具体实现不用人为干预什么。C
++中堆引用的说明可以说是一个变量的别名,即这个变量和它的引用共享数据的存储
单元,而实际上的操作是传递了指针。在进行访问时进行了转换。作左值时,用地址
;左右值时,取内容。和普通的变量处理方式一致。并且引用中加入了可存取性控制
,可以通过对其引用类型作const型保护来防止引用对它进行修改。具体的有关引用
的内容,你可以找一本c++方面的书看一下。
--回复得分 5 --
给大家贴一段E文看看,这是MSDN里面的说法
A reference is a 16-bit or 32-bit quantity that holds the address of an object
but behaves syntactically like that object. A reference declaration consists
of an (optional) list of specifiers followed by a reference declarator.
In C++, any object whose address can be converted to a given pointer type
can also be converted to the analogous reference type. For example, any
object whose address can be converted to type char * can also be converted
to type char &. No constructors or class conversion functions are called
to make a conversion to a reference type.
--回复得分 5 --
相对于编译器来说,引用是指针
但相对于程序员来说不同
--回复得分 0 --
你的意思是不是和luckybat的差不多,你是否能更加详细一些?
--回复得分 5 --
凑凑热闹:
“引用就是指针”说法也许不太准确,但数值上: i的引用 = i的指针。
f(int& refi){ return refi } 和 f(int* pi){ return *pi; }生成的汇编代码
一模一样!
--回复得分 0 --
gun2高手你在吗?希望你加入进来.
--回复得分 0 --
应该是 i的引用 == i所指向的变量.
--回复得分 0 --
always:
的确存在引用的强制类型转换.
cybersailor:
这段话与我说的并不矛盾,因为从最前端(机器指令)的角度来讲,任何类型的概
念都不存在了,目前的问题既然是关于类型的,还是让我们多从编译时刻谈论吧.
qxp:
你思考的角度令我佩服,但在你的例子中,i的左值是它所在的地址单元,i的右值
是它所代表的地址值;j的左值是它所代表的对象,j的右值是它所代表的对象的值.
sunsetyang:
常量指针的参数传递方式仍是传值,并非我所说的“传地址”,以char型为例,有
两种形式:
1) char * const p; ----毫无意义,类似于const int i
2) const char * p; ----可限制对p指向的内存单元的修改
这两种方式都是传值
amanne:
谢谢!不过您说的第一条有误,对于指针的传递来讲,并没有做任何特殊的工作,
分配内存和函数结束时的释放内存都必须由程序员自己显式地完成;至于我说最好不
要以引用作为返回值,原因仅仅是便于错误处理:若函数处理过程中出错,便不能传
回一个有效的对象了,这点在BIDS类库体系中很明显,BIDS专门定义了一个静态全局
对象作为错误对象供返回,多麻烦!若返回指针,可以NULL作为出错标志。当然,目
前比较流行的已经是使用异常类作为出错处理手段了.
wuyunzhou:
正确!
--回复得分 10 --
任何一个变量都有其语义含义,即他的左值和右值.现在我们来比较一下引用和指针的
左值和右值有和差别. int * i; int & j;
i 的左值是什么?是分配给指针i的内存单元.i 的右值是里面存放的内容:指针值
j 的左值是所引用对象的地址,右值是所引用对象的值.
很显然,指针和引用是有明显不同的,在概念上或语义上.
当然,在实现上,现有的系统可能会用指针来实现引用.
以上是我的理解,请大家指教
--回复得分 0 --
常量指针传递的不就是不能篡改的指针?也就是luckybat所说的传地址!传拷贝
的方法在具体实现上就少了写保护这一点。应该说在实现上常量指针和引用是相同的
。luckybat所言是针对强类型语言而言的。
--回复得分 10 --
可以说luckybat对引用的理解非常的透澈,也讲解的特别的详细!我总结一下三点说明
:
1,引用和一般指针(为什麽叫一般指针,我认为它们之间还是有不少共同点)的最大区
别是,是否在程序运行过程中进行了值拷贝.(当然这些工作我们不可见,有编译程序具
体处理)虽然指针只传递了一个地址给函数,但函数在编译后实际的执行还是把该指针
指向的值进行了拷贝(我这样认为,不知对不对),当然也就多了开始时的分配内存和函
数结束时的释放内存(因此增加了开销!)而引用就没有进行这项工作.
2,我们常常把那些在函数中不需更改的参数用常量引用来代替.告诉函数不能修改引
用参数的值.如:
int sum(const &a,const &b,const &c)
{return a+b+c;}
当然返回时用引用也是很常用的,特别是在对象被修改时,返回一个对象的引用是一个
不错的建议.因为函数的返回值不用在调用环境中分配额外的空间来保存.
3,在不用常量引用时,对我们编程人员来说,"好象"是一样的(表象上),但实际的实现
上很不一样,当然这个不一样是体现在编译程序的处理上,第一点是很好的说明,不过
对编程序员透明.
[注]如有不对的地方,请luckybat批评指正.
--回复得分 5 --
amanne的第一条好象还是有点问题,指针应用拷贝的是指针值,而不是指向的值的拷贝
,试想,当你用指向一个巨大的结构或类的指针作函数参数时,拷贝到形参的肯定是机
构的地址,而非结构本身.
另LUCKYBAT说的对,引用,乃一变量之别名,说白了是对它所引用的变量来操作,当然
会有安全检查保证你确实引用到了这个变量,而指针,把所要用的变量的地址记下来,
函数的形参值是这个地址值的拷贝,...
--
※ 来源:·哈工大紫丁香 bbs.hit.edu.cn·[FROM: dianqi.hit.edu.cn]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:3.308毫秒