1.复合类型
复合类型是指基于其他类型定义的类型,指针和引用就是复合类型
2.指针
2.1概念
指针是一种复合类型,它指向某个对象,它本身也是一个对象,指针的值是它指向对象的地址,同时指针也有它自己的地址
void func(int* p) { int m_value = 1; p = &m_value; } int main() { int n = 2; int *pn = &n; cout << *pn << endl; func(pn); cout << *pn << endl; return 0; }
2.2sizeof指针
sizeof指针得到是指针本身的大小(字节数),sizeof引用得到的是所引用对象的大小
2.3指针的大小
指针大小是由CPU实际运行的寻址位数决定,与所指向的对象的类型无关,寻址位数为16位则为2字节,32位为4字节(通常情况下),64位为8字节
2.4空指针
不指向任何对象的指针,它的值为NULL
2.5跟指针有关的操作符
1)取地址符&
2)解引用符*
2.6指针的比较
同类型的指针可以比较,不同类型的指针比较会报错
3.引用
3.1概念
引用仅仅是给对象起了一个别名,实质上跟原来的对象是同一个东西;定义引用时,必须初始化
3.2引用的底层实现
int main() { int x=1; int &b=x; return 0; } //汇编语言:vs编译环境在调试模式下,右键鼠标菜单->反汇编 9: int x = 1; //源代码 00401048 mov dword ptr [ebp-4],1 //汇编代码 10: int &b = x; //源代码 0040104F lea eax,[ebp-4] //汇编代码 00401052 mov dword ptr [ebp-8],eax//汇编代码
mov dword ptr [ebp-4],1 //把1赋值给ebp(栈底指针)-4的地址
lea eax,[ebp-4] //把ebp-4的地址赋值给寄存器eax
mov dword ptr [ebp-8],eax //把寄存器eax里的值赋值给ebp-8的这块地址
上述三行代码的作用就是将1赋值给x,然后将x的地址赋值给了引用b,所以,在底层,引用存放的是被引用对象的地址
而在内存中,它是这样的:
所以有这么一种说法:引用的底层是通过类似指针的方式实现的
3.3如何获得引用的地址和其真正的值
int main() { int a = 1; int& b = a; cout << "a:address->" << &a << endl; cout << "b:address->" << &b << endl; getchar(); return 0; } 运行结果: a:address->0031FD54 b:address->0031FD54
引用b的地址和变量a的地址一样,这种方法不能得到引用b的地址
int main() { int x = 1; int y = 2; int &b = x; printf("&x=%x, &y=%x, &b=%x, b=%x ",&x,&y,&y-1,*(&y-1)); return 0; } 运行结果: &x=12ff7c, &y=12ff78, &b=12ff74, b=12ff7c
引用b的地址可以间接通过&y-1来得到b的地址;*(&y-1) 就是b的值,从结果可以知道,b的值即x的地址,从而可以知道,从底层实现来看,引用的确存放的是被引用对象的地址,只不过,这些对于高级程序员来说是透明的,编译器屏蔽了这些细节
4.函数的值传递、指针传递、引用传递
1)值传递:发生拷贝,形参是实参的拷贝,改变形参的值并不会影响实参的值
2)指针传参时,函数会创建一个副本指针(局部变量),这个副本指针的值和实参的值相同,副本指针的地址和实参的地址不同,函数可以通过副本指针对实参指针所指向的对象进行操作
3)引用传参时,从底层看,形参其实也是作为局部变量在栈中开辟了空间,这个局部变量真实存放的其实是实参的地址(可从汇编层看到),在函数体内任何对形参的操作都会通过间接寻址的方式(即通过这个存放的地址去访问主函数中的实参)影响到主函数的实参;但是编译器隐藏了这些细节,通过编译器查看,形参和实参的地址相同,形参和实参的值也相同
4)效率上讲,指针传递和引用传递比值传递效率高,编程过程中想要传递大的类对象时,值传递由于要拷贝整个对象将耗费更多的时间和空间
参考资料: