引用作为变量别名而存在,因此在一些场合可以代替指针
引用相对于指针来说具有更好的可读性和实用性
swap函数的实现对比如下:
注意:
函数中的引用形参不需要进行初始化。
示例程序如下:
形参没有初始化,而是在第15行调用的时候对引用形参进行初始化。
const引用:
当使用常量对const引用进行初始化时,C++编译器会为常量值分配空间,并将引用名作为这段空间的别名。
例:
结论:
使用常量对const引用初始化后将生成一个只读变量。普通引用不能使用常量值进行初始化,但是const引用可以。
我们不能直接改变const引用的值,但是可以通过取地址的方式间接的改变这块内存的值。
示例程序:
1 #include <stdio.h> 2 3 void Example() 4 { 5 printf("Example:\n"); 6 7 int a = 4; 8 const int& b = a; 9 int* p = (int*)&b; 10 11 //b = 5; 12 13 *p = 5; 14 15 printf("a = %d\n", a); 16 printf("b = %d\n", b); 17 } 18 19 void Demo() 20 { 21 printf("Demo:\n"); 22 23 const int& c = 1; 24 int* p = (int*)&c; 25 26 //c = 5; 27 28 *p = 5; 29 30 printf("c = %d\n", c); 31 } 32 33 int main(int argc, char *argv[]) 34 { 35 Example(); 36 37 printf("\n"); 38 39 Demo(); 40 41 return 0; 42 }
运行结果如下:
可以看到Example函数中通过指针p可以改变内存空间的值,最终影响到了a和b的值,其实它们指的是同一块内存空间,在这个函数中我们也可以直接改变a的值。因为a并不是const的。
引用有自己的存储空间吗?
示例程序如下:
1 #include <stdio.h> 2 3 struct TRef 4 { 5 char& r; 6 }; 7 8 int main(int argc, char *argv[]) 9 { 10 char c = 'c'; 11 char& rc = c; 12 TRef ref = { c }; 13 14 printf("sizeof(char&) = %d\n", sizeof(char&)); 15 printf("sizeof(rc) = %d\n", sizeof(rc)); 16 17 printf("sizeof(TRef) = %d\n", sizeof(TRef)); 18 printf("sizeof(ref.r) = %d\n", sizeof(ref.r)); 19 20 return 0; 21 }
执行结果:
char&代表char的引用,所以占用一个字节,rc也是char的引用,占用一个字节,ref.r是char的引用,也占用一个字节。
sizeof(TRef)占用四个字节,这让我们联想到了指针。
引用的内部实现:引用在C++中的内部实现是一个指针常量,具体如下。
引用的存储空间:
示例程序:
1 #include <stdio.h> 2 3 struct TRef 4 { 5 char* before; 6 char& ref; 7 char* after; 8 }; 9 10 int main(int argc, char* argv[]) 11 { 12 char a = 'a'; 13 char& b = a; 14 char c = 'c'; 15 16 TRef r = {&a, b, &c}; 17 18 printf("sizeof(r) = %d\n", sizeof(r)); 19 printf("sizeof(r.before) = %d\n", sizeof(r.before)); 20 printf("sizeof(r.after) = %d\n", sizeof(r.after)); 21 printf("&r.before = %p\n", &r.before); 22 printf("&r.after = %p\n", &r.after); 23 24 return 0; 25 }
执行结果如下:
根据计算,可以得到结构体中的ref占用了4个字节。从地址的判断也可以得出ref占用了4个字节。
上述程序在windows下的反汇编如下:
引用的意义:
函数返回引用示例程序如下:
1 #include <stdio.h> 2 3 int& demo() 4 { 5 int d = 0; 6 7 printf("demo: d = %d\n", d); 8 9 return d; 10 } 11 12 int& func() 13 { 14 static int s = 0; 15 16 printf("func: s = %d\n", s); 17 18 return s; 19 } 20 21 int main(int argc, char* argv[]) 22 { 23 int& rd = demo(); 24 int& rs = func(); 25 26 printf("\n"); 27 printf("main: rd = %d\n", rd); 28 printf("main: rs = %d\n", rs); 29 printf("\n"); 30 31 rd = 10; 32 rs = 11; 33 34 demo(); 35 func(); 36 37 printf("\n"); 38 printf("main: rd = %d\n", rd); 39 printf("main: rs = %d\n", rs); 40 printf("\n"); 41 42 return 0; 43 }
demo函数中相当于返回了局部变量的“指针”,这是有问题的。返回局部变量的引用是要禁止的。func函数中返回的是静态变量的引用。静态变量的存储空间不会由于函数调用的返回而被摧毁,因此,这样做是没有问题的。
执行结果如下:
第一次打印rd的值发生了很大的变化,这是因为26行函数的调用,改变了栈空间中的数据。第二次打印rd同理。
小结:
引用作为变量别名而存在旨在代替指针
const引用可以使得变量具有只读属性
引用在编译器内部使用指针常量实现
引用的最终本质为指针
引用可以尽可能的避开内存错误