引用和指针的区别
引用和指针有如下三种区别:
1 引用必须在声明时初始化,而指针不用;
2 对于NULL不能引用,而指针可以指向NULL;
3 引用一旦声明,引用的对象不能改变(但对象的值可以改变);而指针可以随时改变指向的对象。
引用能做到的,指针也可以,但指针更危险;
(1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。
(2)不能有NULL引用,引用必须与合法的存储单元关联(指针则可以是NULL)。
(3)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。
从上面的区别看,引用比指针安全。从编译器的角度看,引用仅仅是一个更为安全的指针。正是因为指针非常灵活,导致指针在编程时出错的可能性也很大,可以这样说指针是C++中最危险的。为了在不牺牲性能的情况下,更安全的利用指针特性,C++引入了引用。
实例分析:
#include <iostream> void swapint1(int *a,int *b) { int temp; temp=*a; *a=*b; *b=temp; } void swapint2(int &a,int &b) { int temp; temp=a; a=b; b=temp; } int main() { int a = 3; int b = 4; int &c = a; c = 12; swapint1(&a,&b); swapint2(a,b); return 0; }
swapint1和swapint2对应的汇编代码:
swapint1: pushl %ebp movl %esp, %ebp subl $16, %esp movl 8(%ebp), %eax movl (%eax), %eax movl %eax, -4(%ebp) movl 12(%ebp), %eax movl (%eax), %edx movl 8(%ebp), %eax movl %edx, (%eax) movl 12(%ebp), %eax movl -4(%ebp), %edx movl %edx, (%eax) leave ret swapint2: pushl %ebp movl %esp, %ebp subl $16, %esp movl 8(%ebp), %eax movl (%eax), %eax movl %eax, -4(%ebp) movl 12(%ebp), %eax movl (%eax), %edx movl 8(%ebp), %eax movl %edx, (%eax) movl 12(%ebp), %eax movl -4(%ebp), %edx movl %edx, (%eax) leave ret
从上面的代码可以看出,两种函数的实现在汇编层面是完全一样的。然后再从调用的层面进行分析。
// int a = 3 movl $3, 24(%esp) // int b = 4 movl $4, 20(%esp) // int &c = a; leal 24(%esp), %eax movl %eax, 28(%esp) // c = 12 movl 28(%esp), %eax movl $12, (%eax) // swapint1(a,b) leal 20(%esp), %eax movl %eax, 4(%esp) leal 24(%esp), %eax movl %eax, (%esp) call swapint1 // swapint2(a,b) leal 20(%esp), %eax movl %eax, 4(%esp) leal 24(%esp), %eax movl %eax, (%esp) call swapint2
可见两种实现方式在调用的角度也是一样的,均是采用传地址的方式来实现的。
另外,从 int & c = a; c = 12; 对应的汇编代码中可以看出对于引用类型也是分配了空间的,空间中保存的是被引用量的地址,这和指针的方式也是一致的。