引用不能等同于指针,他是某个变量的别名。他并不新分配内存,但指针仍旧分配4字节内存,此地址里的内容是其所指向变量的地址。 可作如下测试: int a = 1; int &b = a; int *c = &a; printf("0x%lx\n", &a); printf("0x%lx\n", &b); printf("0x%lx\n", &c);
1. 引用 cannot be null in C++. 2. 引用 does not have pointer arithmetic. with pointer, int* p=&i; p++; but with 引用, no way to do that.3. 引用 may use memory. void f(int& ri){......}
to duiduiblues(duiduiblues): >他并不新分配内存,...可是用VC可看到: 2: int a=1; 0040B478 mov dword ptr [ebp-4],1 3: int &b=a; 0040B47F lea eax,[ebp-4] 0040B482 mov dword ptr [ebp-8],eax ;还是给这个引用分配了内存吧?//====================to fibbery (飞) : 小弟比较愚笨,引用看不懂,就看汇编代码,还是觉得汇编明白些(也许因为这样我才一直没什么进步).发现用引用和指针编译后的代码是一样的,都是一个指向变量的地址而已,但是在C++里面就有许多规定,老是记不住,不爽.下面是俺D学习笔记,供参考.有错误别怪我,我实在是很菜,郁闷 :( //======================================C++个人学习笔记C++中有些概念很难理解,但是从汇编级看,不管由什么语言编译的都没区别,所以我觉得难懂的地方就看它生成的汇编程序,借以了解其实现细节.但这对了解"面向对象"的思想没有帮助,而"面向对象"才是C++的精髓啊,我是舍本求末了.请前辈指教. 汇编代码是用BC3.1编译来的学习笔记-看汇编学引用(by furydied,2002)要点: 1.引用和指针都是取地址 2.指向指针的引用(指向引用的指针是错误的!!!!!)int *&r=p 3.指向常量的引用int &r=const int MAX 4.动态分配的引用(new/delete &) 5.引用作为函数参数:int func(int &x) 6.返回引用的函数(比较难哟)int &func()1.引用和指针都是取地址 ; int i=100,j=100; mov word ptr [bp-2],100 ;定义的2个变量存放位置:[bp-2],[bp-4] mov word ptr [bp-4],100 ; int &refi=i; lea ax,word ptr [bp-2] ;所谓引用:就是取该变量的地址 mov si,ax ; int *p=&i; lea ax,word ptr [bp-2] ;所谓指针,也是地址,在汇编代码的层次, mov di,ax ;和引用没两样 ; int &refj=j; lea ax,word ptr [bp-4] ;取地址 mov word ptr [bp-6],ax ;寄存器不够用,就保存到内存中[bp-6] ; int *q=&j; lea ax,word ptr [bp-4] ;同上 mov word ptr [bp-8],ax ; refi+=100; add word ptr [si],100 ; *p-=100; sub word ptr [di],100 ; cout<<endl<<"refi="<<refi; push word ptr [si] ;使用"引用"时也和使用"指针"一样 ... ;从右至左将<<右边的内容入栈 call near ptr ostream ;然后调用若干次输出库函数 add sp,4 ;调整堆栈指针 ... call near ptr ostream add sp,4 ; cout<<endl<<"(*p)="<<*p; push word ptr [di] ;对指针的处理完全一样 ... call near ptr @@ostream@$blsh$qpqr7ostream$r7ostream add sp,4 ... ;到目前为止,从汇编代码看来,引用跟指针没什么区别啊! ;注意:写程序的时候, ;引用是直接将变量赋给引用(int &refi=i), ; ~~~~ ~~ ;而指针是将变量的地址赋给指针(int *p=&i,多了一个&) ; ~~~~~~~~~~ ~~~ ;继续:2.指向指针的引用(指向引用的指针是错误的!!!!!) ; int i=100,j=100; mov word ptr [bp-2],100 mov word ptr [bp-4],100 ; int &refi=i; //declare a reference to an integer lea ax,word ptr [bp-2] mov si,ax ; int *p=&i; //declare a pointer to an integer lea ax,word ptr [bp-2] mov word ptr [bp-8],ax //[bp-8]中存放一个指针 ; int *&refp=p; //declare a reference to a pointer ; ~~ 注意这里的写法:*&表示一个引用!!不能写作&*!! lea ax,word ptr [bp-8] //将bp-8(就是指针变量p所在的地址) mov word ptr [bp-10],ax //送到一个引用[bp-10]3.指向常量的引用 ; const int k=64; //常量 mov word ptr [bp-6],64 ; int &refk=k; //指向常量的引用 mov word ptr [bp-12],64 //生成了一个临时变量[bp-12] lea ax,word ptr [bp-12] //并将这个变量的值设为常量的值 mov di,ax //di->该临时变量 ; refk+=64; //改变该引用的值 add word ptr [di],64 //改变的是临时变量的值,而不是原来定义的常量的值4.动态分配的引用 float &reff=* new float(66.6); //动态分配引用的话,new 前面要加 * !! //new float返回一个指针,所以用*取该指针指向的变量. //如果不加*的话,编译错误:Can't convert (float *) to float //意即引用&reff被定义为float类型,和float f没区别, delete &reff; //如何删除:前面加&5.引用作为函数参数 #include <iostream.h> void swap(int &a,int &b){ //形参为整型引用 int temp=a; a=b; b=temp; return; } 汇编代码: enter 2,0 push si push di mov si,word ptr [bp+4] ;x的地址=>si mov di,word ptr [bp+6] ;y的地址=>di ; int temp=a; mov ax,word ptr [si] mov word ptr [bp-2],ax ;暂存x([bp-2]为temp) ; a=b; mov ax,word ptr [di] mov word ptr [si],ax ;y=>[x] ; b=temp; mov ax,word ptr [bp-2] ;暂存的x=>[y] mov word ptr [di],ax pop di pop si leave ret int main() { int x=10,y=20; /* 汇编代码: * mov word ptr [bp-2],10 ;x * mov word ptr [bp-4],20 ;y */ cout << endl << "x=" << x << "\ty=" << y; swap(x,y); //实参为整型变量 /* 汇编代码: * lea ax,word ptr [bp-4] ;y的地址(而不是变量值!!!) * push ax ;push &y(先) * lea ax,word ptr [bp-2] ;x的地址(而不是变量值!!!) * push ax ;push &x(后) * call near ptr @swap$qrit1 * add sp,4 */ cout << endl << "After swapping:" << endl << "x=" << x << "\ty=" << y; return 0; } 从上面可以看到,以引用作为函数参数时,调用函数直接使用整型变量.编译程序找到函数原型,发现这里须要的是一个引用,就将实参的地址(而不是变量值)入栈传给函数.被调用函数中是用传过来的地址运算的.下面用指针实现相同的功能: #include <iostream.h> void swap(int *a,int *b){ int temp=*a; *a=*b; *b=temp; return; } /*这一段的汇编代码: ; void swap(int *a,int *b){ @swap$qpit1 proc near enter 2,0 push si push di mov si,word ptr [bp+4] mov di,word ptr [bp+6] ; int temp=*a; mov ax,word ptr [si] mov word ptr [bp-2],ax ; *a=*b; mov ax,word ptr [di] mov word ptr [si],ax ; *b=temp; mov ax,word ptr [bp-2] mov word ptr [di],ax ; return; jmp short @1@58 ; } pop di pop si leave ret */ int main() { int x=10,y=20; cout << endl << "x=" << x << "\ty=" << y; swap(&x,&y); /*汇编代码: * lea ax,word ptr [bp-4] * push ax * lea ax,word ptr [bp-2] * push ax * call near ptr @swap$qpit1 * add sp,4 */ cout << endl << "After swapping:" << endl << "x=" << x << "\ty=" << y; return 0; } 可以看到,使用指针与使用引用,生成的汇编代码完全一样,没有分别;有分别的地方只在于C++中声明/调用的形式不同.6.返回引用的函数(比较难哟) //返回引用的函数示例 #include <iostream.h> int &func(void){ //在函数名前加一个&,则此函数返回一个引用 //此引用的类型就是函数名前面声明的类型 static int count=0; return (++count); } 汇编代码: inc word ptr DGROUP:d@w+0 ;将变量count加1 mov ax,offset DGROUP:d@w+0 ;返回变量count的地址 pop bp ret int main(){ int i; cout << endl; for ( i=0; i<5; i++) //输出1 2 3 4 5 cout << func() << '\t'; //奇怪:这里func()返回一个地址,cout却输出一个变量值? //要输出这个地址,还得cout << &func() (前面加&取地址!) /*这一句的代码: * push 9 ;\t * call near ptr @func$qv ;调用自己的函数 * mov bx,ax ;返回AX为静态变量count的地址 * push word ptr [bx] ;将count的值入栈 * 编译程序发现这个函数返回引用,就以返回的值为地址取得变量值 * push offset DGROUP:_cout * call near ptr @@ostream@$blsh$qi ;输出? * add sp,4 * push ax * call near ptr @ostream@$blsh$qzc * add sp,4 */ cout <<endl; func()=100; //可以对函数赋值!! /* call near ptr @func$qv * mov bx,ax * mov word ptr [bx],100 * 先调用函数,返回ax=变量地址,再对变量赋值100 * 在call完之后,func()中的静态变量值为6 */ for (i=0; i<5; i++) cout << func() << '\t'; //输出101 102 103 104 105 return 0; } 再看:将该函数的&去掉,令它不再返回引用 #include <iostream.h> int func(void){ static int count=0; return (++count); } /* inc word ptr DGROUP:d@w+0 * mov ax,word ptr DGROUP:d@w+0 ;直接返回变量值 * pop bp * ret */int main() { int i; cout << endl; for ( i=0; i<5; i++) cout << func() << '\t'; /* push 9 * call near ptr @func$qv ;由于函数定义为整型 * push ax ;所以返回值(变量值)不再是地址 * push offset DGROUP:_cout * call near ptr @@ostream@$blsh$qi * add sp,4 * push ax * call near ptr @ostream@$blsh$qzc * add sp,4 */ cout <<endl; //func()=100; //这一句现在不行啦 for (i=0; i<5; i++) cout << func() << '\t'; return 0; } 再看:改成指针类型: #include <iostream.h> int *func(void){ //在函数名前加一个&,则此函数返回一个引用
可作如下测试:
int a = 1;
int &b = a;
int *c = &a; printf("0x%lx\n", &a);
printf("0x%lx\n", &b);
printf("0x%lx\n", &c);
2. 引用 does not have pointer arithmetic.
with pointer, int* p=&i; p++;
but with 引用, no way to do that.3. 引用 may use memory.
void f(int& ri){......}
>他并不新分配内存,...可是用VC可看到:
2: int a=1;
0040B478 mov dword ptr [ebp-4],1
3: int &b=a;
0040B47F lea eax,[ebp-4]
0040B482 mov dword ptr [ebp-8],eax ;还是给这个引用分配了内存吧?//====================to fibbery (飞) :
小弟比较愚笨,引用看不懂,就看汇编代码,还是觉得汇编明白些(也许因为这样我才一直没什么进步).发现用引用和指针编译后的代码是一样的,都是一个指向变量的地址而已,但是在C++里面就有许多规定,老是记不住,不爽.下面是俺D学习笔记,供参考.有错误别怪我,我实在是很菜,郁闷 :(
//======================================C++个人学习笔记C++中有些概念很难理解,但是从汇编级看,不管由什么语言编译的都没区别,所以我觉得难懂的地方就看它生成的汇编程序,借以了解其实现细节.但这对了解"面向对象"的思想没有帮助,而"面向对象"才是C++的精髓啊,我是舍本求末了.请前辈指教.
汇编代码是用BC3.1编译来的学习笔记-看汇编学引用(by furydied,2002)要点:
1.引用和指针都是取地址
2.指向指针的引用(指向引用的指针是错误的!!!!!)int *&r=p
3.指向常量的引用int &r=const int MAX
4.动态分配的引用(new/delete &)
5.引用作为函数参数:int func(int &x)
6.返回引用的函数(比较难哟)int &func()1.引用和指针都是取地址
; int i=100,j=100;
mov word ptr [bp-2],100 ;定义的2个变量存放位置:[bp-2],[bp-4]
mov word ptr [bp-4],100
; int &refi=i;
lea ax,word ptr [bp-2] ;所谓引用:就是取该变量的地址
mov si,ax
; int *p=&i;
lea ax,word ptr [bp-2] ;所谓指针,也是地址,在汇编代码的层次,
mov di,ax ;和引用没两样
; int &refj=j;
lea ax,word ptr [bp-4] ;取地址
mov word ptr [bp-6],ax ;寄存器不够用,就保存到内存中[bp-6]
; int *q=&j;
lea ax,word ptr [bp-4] ;同上
mov word ptr [bp-8],ax
; refi+=100;
add word ptr [si],100
; *p-=100;
sub word ptr [di],100
; cout<<endl<<"refi="<<refi;
push word ptr [si] ;使用"引用"时也和使用"指针"一样
... ;从右至左将<<右边的内容入栈
call near ptr ostream ;然后调用若干次输出库函数
add sp,4 ;调整堆栈指针
...
call near ptr ostream
add sp,4
; cout<<endl<<"(*p)="<<*p;
push word ptr [di] ;对指针的处理完全一样
...
call near ptr @@ostream@$blsh$qpqr7ostream$r7ostream
add sp,4
... ;到目前为止,从汇编代码看来,引用跟指针没什么区别啊!
;注意:写程序的时候,
;引用是直接将变量赋给引用(int &refi=i),
; ~~~~ ~~
;而指针是将变量的地址赋给指针(int *p=&i,多了一个&)
; ~~~~~~~~~~ ~~~
;继续:2.指向指针的引用(指向引用的指针是错误的!!!!!)
; int i=100,j=100;
mov word ptr [bp-2],100
mov word ptr [bp-4],100
; int &refi=i; //declare a reference to an integer
lea ax,word ptr [bp-2]
mov si,ax
; int *p=&i; //declare a pointer to an integer
lea ax,word ptr [bp-2]
mov word ptr [bp-8],ax //[bp-8]中存放一个指针
; int *&refp=p; //declare a reference to a pointer
; ~~ 注意这里的写法:*&表示一个引用!!不能写作&*!!
lea ax,word ptr [bp-8] //将bp-8(就是指针变量p所在的地址)
mov word ptr [bp-10],ax //送到一个引用[bp-10]3.指向常量的引用
; const int k=64; //常量
mov word ptr [bp-6],64
; int &refk=k; //指向常量的引用
mov word ptr [bp-12],64 //生成了一个临时变量[bp-12]
lea ax,word ptr [bp-12] //并将这个变量的值设为常量的值
mov di,ax //di->该临时变量
; refk+=64; //改变该引用的值
add word ptr [di],64 //改变的是临时变量的值,而不是原来定义的常量的值4.动态分配的引用
float &reff=* new float(66.6); //动态分配引用的话,new 前面要加 * !!
//new float返回一个指针,所以用*取该指针指向的变量.
//如果不加*的话,编译错误:Can't convert (float *) to float
//意即引用&reff被定义为float类型,和float f没区别,
delete &reff; //如何删除:前面加&5.引用作为函数参数
#include <iostream.h>
void swap(int &a,int &b){ //形参为整型引用
int temp=a;
a=b;
b=temp;
return;
}
汇编代码:
enter 2,0
push si
push di
mov si,word ptr [bp+4] ;x的地址=>si
mov di,word ptr [bp+6] ;y的地址=>di
; int temp=a;
mov ax,word ptr [si]
mov word ptr [bp-2],ax ;暂存x([bp-2]为temp)
; a=b;
mov ax,word ptr [di]
mov word ptr [si],ax ;y=>[x]
; b=temp;
mov ax,word ptr [bp-2] ;暂存的x=>[y]
mov word ptr [di],ax pop di
pop si
leave
ret int main()
{
int x=10,y=20;
/* 汇编代码:
* mov word ptr [bp-2],10 ;x
* mov word ptr [bp-4],20 ;y
*/
cout << endl << "x=" << x
<< "\ty=" << y;
swap(x,y); //实参为整型变量
/* 汇编代码:
* lea ax,word ptr [bp-4] ;y的地址(而不是变量值!!!)
* push ax ;push &y(先)
* lea ax,word ptr [bp-2] ;x的地址(而不是变量值!!!)
* push ax ;push &x(后)
* call near ptr @swap$qrit1
* add sp,4
*/
cout << endl << "After swapping:"
<< endl << "x=" << x
<< "\ty=" << y;
return 0;
}
从上面可以看到,以引用作为函数参数时,调用函数直接使用整型变量.编译程序找到函数原型,发现这里须要的是一个引用,就将实参的地址(而不是变量值)入栈传给函数.被调用函数中是用传过来的地址运算的.下面用指针实现相同的功能:
#include <iostream.h>
void swap(int *a,int *b){
int temp=*a;
*a=*b;
*b=temp;
return;
}
/*这一段的汇编代码:
; void swap(int *a,int *b){
@swap$qpit1 proc near
enter 2,0
push si
push di
mov si,word ptr [bp+4]
mov di,word ptr [bp+6]
; int temp=*a;
mov ax,word ptr [si]
mov word ptr [bp-2],ax
; *a=*b;
mov ax,word ptr [di]
mov word ptr [si],ax
; *b=temp;
mov ax,word ptr [bp-2]
mov word ptr [di],ax
; return;
jmp short @1@58
; }
pop di
pop si
leave
ret
*/
int main()
{
int x=10,y=20;
cout << endl << "x=" << x
<< "\ty=" << y;
swap(&x,&y);
/*汇编代码:
* lea ax,word ptr [bp-4]
* push ax
* lea ax,word ptr [bp-2]
* push ax
* call near ptr @swap$qpit1
* add sp,4
*/ cout << endl << "After swapping:"
<< endl << "x=" << x
<< "\ty=" << y;
return 0;
}
可以看到,使用指针与使用引用,生成的汇编代码完全一样,没有分别;有分别的地方只在于C++中声明/调用的形式不同.6.返回引用的函数(比较难哟)
//返回引用的函数示例
#include <iostream.h>
int &func(void){ //在函数名前加一个&,则此函数返回一个引用
//此引用的类型就是函数名前面声明的类型
static int count=0;
return (++count);
}
汇编代码:
inc word ptr DGROUP:d@w+0 ;将变量count加1
mov ax,offset DGROUP:d@w+0 ;返回变量count的地址
pop bp
ret int main(){
int i;
cout << endl;
for ( i=0; i<5; i++) //输出1 2 3 4 5
cout << func() << '\t'; //奇怪:这里func()返回一个地址,cout却输出一个变量值?
//要输出这个地址,还得cout << &func() (前面加&取地址!)
/*这一句的代码:
* push 9 ;\t
* call near ptr @func$qv ;调用自己的函数
* mov bx,ax ;返回AX为静态变量count的地址
* push word ptr [bx] ;将count的值入栈
* 编译程序发现这个函数返回引用,就以返回的值为地址取得变量值
* push offset DGROUP:_cout
* call near ptr @@ostream@$blsh$qi ;输出?
* add sp,4
* push ax
* call near ptr @ostream@$blsh$qzc
* add sp,4
*/
cout <<endl;
func()=100; //可以对函数赋值!!
/* call near ptr @func$qv
* mov bx,ax
* mov word ptr [bx],100
* 先调用函数,返回ax=变量地址,再对变量赋值100
* 在call完之后,func()中的静态变量值为6
*/
for (i=0; i<5; i++)
cout << func() << '\t'; //输出101 102 103 104 105
return 0;
}
再看:将该函数的&去掉,令它不再返回引用
#include <iostream.h>
int func(void){
static int count=0;
return (++count);
}
/* inc word ptr DGROUP:d@w+0
* mov ax,word ptr DGROUP:d@w+0 ;直接返回变量值
* pop bp
* ret
*/int main()
{
int i; cout << endl;
for ( i=0; i<5; i++)
cout << func() << '\t';
/* push 9
* call near ptr @func$qv ;由于函数定义为整型
* push ax ;所以返回值(变量值)不再是地址
* push offset DGROUP:_cout
* call near ptr @@ostream@$blsh$qi
* add sp,4
* push ax
* call near ptr @ostream@$blsh$qzc
* add sp,4
*/
cout <<endl; //func()=100; //这一句现在不行啦 for (i=0; i<5; i++)
cout << func() << '\t'; return 0;
}
再看:改成指针类型:
#include <iostream.h>
int *func(void){ //在函数名前加一个&,则此函数返回一个引用
按书上的说法,引用是为了支持运算符的重载而引入的:)