废话少说,直接上代码:
#include <iostream>int main()
{int a = 5;int* ptr = &a;int& ref = a;std::cin.get(); //设置断点
}
为了避免混淆,我建议在定义指针时写int* ptr
而不是int *ptr
,同理,定义引用写成int& ref
而不是int &ref
。
设置断点,然后Debug程序,看看源程序的汇编程序是什么:
精彩的地方来了,指针和引用实际上干的是同样的事,都是把变量a所在的偏移地址传给ptr指针,只不过指针的标号名为ptr,引用的标号名为ref而已。
再看看赋值操作:
#include <iostream>int main()
{int a = 5;int* ptr = &a;int& ref = a;*ptr = 6; //赋值ref = 7; //赋值std::cin.get();
}
在汇编语言中还是一样的!
最后看一下函数参数传递的三种方式:
#include <iostream>void Increment1(int value) //传值
{value++;
}void Increment2(int* value) //指针
{(*value)++;
}void Increment3(int& value) //引用
{value++;
}int main()
{int a = 5;Increment1(a);Increment2(&a);Increment3(a);std::cin.get();
}
Increment1函数
Increment2函数
Increment3函数
可以看到指针和引用还是一样的,结合调用的代码看:
首先,把5送到内存地址为a的双字大小的空间中;
对于Increment1,从内存地址为a的双字大小的空间中取出内容(整数5),送到eax寄存器中,然后call函数Increment1,把eax加一,最后送到内存地址为value的空间中,而不是内存地址为a的空间,如图:
对于Increment2,取内存地址为a的偏移地址,送到eax寄存器中,然后call函数Increment2,如图:
先是寻址参数value,从中取出偏移地址送到eax,再用eax寻址,找到数值5送到ecx,然后ecx加一,再将结果ecx送回到偏移地址为value空间中记录的那个地址,即内存地址a,这样就实现了对a的加一操作。
顺便看下此时的value空间,记录的是内存地址a的偏移地址:
也就是说,如果使用传值调用,内存地址为参数value的空间中记录的就是数值,运算的结果也会记录在这里,但是如果函数退出,这一块空间就会清除,所以结果并没有保存。如果使用指针和引用调用,内存地址为参数value的空间中记录的就是偏移地址,然后根据这个偏移地址取值,作运算,结果也是根据这个偏移地址送回去,而这块空间即使函数退出也不会消失。