The most important difference between pointers in C and those in C++ is that C++ is a more strongly typed language.This stands out where void * is concerne.
代码:
bird * b;
rock * r;
void * v;
v=r;
b=v;//这两个赋值在C编译器通过,在C++编译器不通过
void * 在C中表示指向任意的类型. 在C++中表示指向void类型.
A reference (&) is like a const pointer that is automatically dereferenced.There are certain rules when using references.
1.A reference must be initialized when it is created.(Pointers can be intialized at any time).
2.Once a reference is initialized to an object,it can not be changed to refer to another object.
(Pointers can be pointed to another object at any time).
3.you can not have NULL reference.
把引用认为是一个对象的别名,上面规则就好理解了.
下面看一段代码的汇编来理解
#include <iostream>
using namespace std;
int* f1(int * px)
{
(*px)++;
return px;
}
int& g1(int & rx)
{
rx++;
return rx;
}
int main()
{
int a=1;
f1(&a);
g1(a);
return 0;
}
- 6:
- 17: int main()
- 18: {
- 004012E0 push ebp
- 004012E1 mov ebp,esp
- 004012E3 sub esp,44h
- 004012E6 push ebx
- 004012E7 push esi
- 004012E8 push edi
- 004012E9 lea edi,[ebp-44h]
- 004012EC mov ecx,11h
- 004012F1 mov eax,0CCCCCCCCh
- 004012F6 rep stos dword ptr [edi]
- 19: int a=1;
- 004012F8 mov dword ptr [ebp-4],1//SS:[ebp-4]存放的是a变量的值
- 20: f1(&a);
- 004012FF lea eax,[ebp-4]//eax存的是a变量的地址
- 00401302 push eax//函数的参数入栈
- 00401303 call @ILT+10(f1) (0040100f)//跟进去
- /*
- 00401308 add esp,4
- 21: g1(a);
- 0040130B lea ecx,[ebp-4]
- 0040130E push ecx
- 0040130F call @ILT+20(g1) (00401019)
- 00401314 add esp,4
- 22: return 0;
- 00401317 xor eax,eax
- 23: }
- 00401319 pop edi
- 0040131A pop esi
- 0040131B pop ebx
- 0040131C add esp,44h
- 0040131F cmp ebp,esp
- 00401321 call __chkesp (004081f0)
- 00401326 mov esp,ebp
- 00401328 pop ebp
- 00401329 ret
- */
- @ILT+10(?f1@@YAPAHPAH@Z):
- 0040100F jmp f1 (00401260)
- 4: int* f1(int * px)
- 5: {
- 00401260 push ebp
- 00401261 mov ebp,esp
- 00401263 sub esp,40h
- 00401266 push ebx
- 00401267 push esi
- 00401268 push edi
- 00401269 lea edi,[ebp-40h]
- 0040126C mov ecx,10h
- 00401271 mov eax,0CCCCCCCCh
- 00401276 rep stos dword ptr [edi]
- 6: (*px)++;
- 00401278 mov eax,dword ptr [ebp+8]
- //注意这句 ebp+8指的内存里存的是先前eax的值,即a的地址.
- //所以操作后 eax是a的地址.
- 0040127B mov ecx,dword ptr [eax]//ecx=*px
- 0040127D add ecx,1
- 00401280 mov edx,dword ptr [ebp+8]//edx=&px注意
- 00401283 mov dword ptr [edx],ecx//这句改变了a的值,
- 7: return px;
- 00401285 mov eax,dword ptr [ebp+8]
- 8:
- 9: }
- 00401288 pop edi
- 00401289 pop esi
- 0040128A pop ebx
- 0040128B mov esp,ebp
- 0040128D pop ebp
- 0040128E ret
- 返回到main函数
- 00401308 add esp,4 //移除函数参数
- 21: g1(a);
- 0040130B lea ecx,[ebp-4]//注意和前面的指针调用一模一样
- 0040130E push ecx//函数参数入栈
- 0040130F call @ILT+20(g1) (00401019)跟进去
- @ILT+20(?g1@@YAAAHAAH@Z):
- 00401019 jmp g1 (004012a0)
- int& g1(int & rx)
- 12: {
- 004012A0 push ebp
- 004012A1 mov ebp,esp
- 004012A3 sub esp,40h
- 004012A6 push ebx
- 004012A7 push esi
- 004012A8 push edi
- 004012A9 lea edi,[ebp-40h]
- 004012AC mov ecx,10h
- 004012B1 mov eax,0CCCCCCCCh
- 004012B6 rep stos dword ptr [edi]
- 13: rx++;
- //我们来和指针做个比较,代码一模一样
- /*
- 6: (*px)++;
- 00401278 mov eax,dword ptr [ebp+8]
- //注意这句 ebp+8指的内存里存的是先前eax的值,即a的地址.
- //所以操作后 eax是a的地址.
- 0040127B mov ecx,dword ptr [eax]//ecx=*px
- 0040127D add ecx,1
- 00401280 mov edx,dword ptr [ebp+8]//edx=&px注意
- 00401283 mov dword ptr [edx],ecx//这句改变了a的值,
- 7: return px;
- 00401285 mov eax,dword ptr [ebp+8]
- */
- 004012B8 mov eax,dword ptr [ebp+8]
- 004012BB mov ecx,dword ptr [eax]
- 004012BD add ecx,1
- 004012C0 mov edx,dword ptr [ebp+8]
- 004012C3 mov dword ptr [edx],ecx
- 14: return rx;
- 004012C5 mov eax,dword ptr [ebp+8]
- 15: }
- 004012C8 pop edi
- 004012C9 pop esi
- 004012CA pop ebx
- 004012CB mov esp,ebp
- 004012CD pop ebp
- 004012CE ret
- 返回到main函数
- 00401314 add esp,4//移除函数参数
- 22: return 0;
- 00401317 xor eax,eax
- 23: }
- 00401319 pop edi
- 0040131A pop esi
- 0040131B pop ebx
- 0040131C add esp,44h
- 0040131F cmp ebp,esp
- 00401321 call __chkesp (004081f0)也跟进去看看
- 00401326 mov esp,ebp
- 00401328 pop ebp
- 00401329 ret
- _chkesp:
- 004081F0 jne __chkesp+3 (004081f3)//不相等就转去处理异常了
- 004081F2 ret
- 004081F3 push ebp
- 004081F4 mov ebp,esp
- 004081F6 sub esp,0
- 004081F9 push eax
- 004081FA push edx
- 004081FB push ebx
- 004081FC push esi
- 004081FD push edi
- 004081FE push offset string "The value of ESP was not properl"... (004312b4)
- 00408203 push offset string "" (004311b4)
- 00408208 push 2Ah
- 0040820A push offset string "i386//chkesp.c" (004312a4)
- 0040820F push 1
- 00408211 call _CrtDbgReport (0040ca70)
- 00408216 add esp,14h
- 00408219 cmp eax,1
- 0040821C jne __chkesp+2Fh (0040821f)
- 0040821E int 3
- 0040821F pop edi
- 00408220 pop esi
- 00408221 pop ebx
- 00408222 pop edx
- 00408223 pop eax
- 00408224 mov esp,ebp
- 00408226 pop ebp
- 00408227 ret
总结:从此例汇编层来看,引用和指针是没有区别的.
那我就有一个地方想不明白了
#include <iostream>
using namespace std;
int main()
{
int a=3;
int *pa=&a;
int & ra=a;
cout<<&pa<<endl;
cout<<&ra<<endl;
cout<<&a<<endl;
return 0;
}
结果:
0012FF78
0012FF7C
0012FF7C
这两个输出为什么不同?
继续跟踪汇编
1: #include <iostream>
2:
3: using namespace std;
4:
5: int main()
6:
7: {
00401250 push ebp
00401251 mov ebp,esp
00401253 sub esp,54h//(54h-40h)/04h=20/4=5
00401256 push ebx
00401257 push esi
00401258 push edi
00401259 lea edi,[ebp-54h]
0040125C mov ecx,15h
00401261 mov eax,0CCCCCCCCh
00401266 rep stos dword ptr [edi]
8:
9: int a=3;
00401268 mov dword ptr [ebp-4],3//第一个局部变量a
10:
11: int *pa=&a;
0040126F lea eax,[ebp-4]
00401272 mov dword ptr [ebp-8],eax //第二个局部变量pa
12:
13: int & ra=a;
00401275 lea ecx,[ebp-4]
00401278 mov dword ptr [ebp-0Ch],ecx // 第三个局部变量ra
14: int * pb=&a;
0040127B lea edx,[ebp-4]
0040127E mov dword ptr [ebp-10h],edx//第四个局部变量pb
15: pb=&ra;
00401281 mov eax,dword ptr [ebp-0Ch]
00401284 mov dword ptr [ebp-10h],eax
16: int ** pc=&pa;
00401287 lea ecx,[ebp-8]//第五个局部变量
0040128A mov dword ptr [ebp-14h],ecx
17:
18:
19: return 0;
0040128D xor eax,eax
20:
21: }
0040128F pop edi
00401290 pop esi
00401291 pop ebx
00401292 mov esp,ebp
00401294 pop ebp
00401295 ret
总结:
搞明白了,引用其实就是一个指针,只是编译器会自动对它解析,并进行代码优化.
也就是 A reference (&) is like a const pointer that is automatically dereferenced.
所以上面的代码
cout<<&ra<<endl;编译器对它做了转化 cout<< &(*ra)<<endl;