连接:https://zhuanlan.zhihu.com/p/38399566
本文主要提一下如下三个区别:程序员
咱们在定义一个引用的时候必须为其指定一个初始值,可是指针却不须要。安全
int &r; //不合法,没有初始化引用 int *p; //合法,但p为野指针,使用须要当心
2. 引用不能为空,而指针能够为空。函数
因为引用不能为空,因此咱们在使用引用的时候不须要测试其合法性,而在使用指针的时候须要首先判断指针是否为空指针,不然可能会引发程序崩溃。性能
void test_p(int* p) { if(p != nullptr) //对p所指对象赋值时需先判断p是否为空指针 *p = 3; return; } void test_r(int& r) { r = 3; //因为引用不能为空,因此此处无需判断r的有效性就能够对r直接赋值 return; }
3. 引用不能更换目标测试
指针能够随时改变指向,可是引用只能指向初始化时指向的对象,没法改变。spa
int a = 1; int b = 2; int &r = a; //初始化引用r指向变量a int *p = &a; //初始化指针p指向变量a p = &b; //指针p指向了变量b r = b; //引用r依然指向a,但a的值变成了b
只看二者区别的话,咱们发现引用能够完成的任务均可以使用指针完成,而且在使用引用时限制条件更多,那么C++为何要引入“引用”呢?指针
限制条件多不必定是缺点,C++的引用在减小了程序员自由度的同时提高了内存操做的安全性和语义的优美性。好比引用强制要求必须初始化,可让咱们在使用引用的时候不用再去判断引用是否为空,让代码更加简洁优美,避免了指针满天飞的情形。除了这种场景以外引用还用于以下两个场景:code
通常咱们使用const reference参数做为只读形参,这种状况下既能够避免参数拷贝还能够得到与传值参数同样的调用方式。对象
void test(const vector<int> &data) { //... } int main() { vector<int> data{1,2,3,4,5,6,7,8}; test(data); }
2. 引用型返回值内存
C++提供了重载运算符的功能,咱们在重载某些操做符的时候,使用引用型返回值能够得到跟该操做符原来语法相同的调用方式,保持了操做符语义的一致性。一个例子就是operator []操做符,这个操做符通常须要返回一个引用对象,才能正确的被修改。
vector<int> v(10); v[5] = 10; //[]操做符返回引用,而后vector对应元素才能被修改 //若是[]操做符不返回引用而是指针的话,赋值语句则须要这样写 *v[5] = 10; //这种书写方式,彻底不符合咱们对[]调用的认知,容易产生误解
指针与引用之间有没有性能差距呢?这种问题就须要进入汇编层面去看一下。咱们先写一个test1函数,参数传递使用指针:
void test1(int* p) { *p = 3; //此处应该首先判断p是否为空,为了测试的须要,此处咱们没加。 return; }
该代码段对应的汇编代码以下:
pushq %rbp movq %rsp, %rbp movq %rdi, -8(%rbp) movq -8(%rbp), %rax movl $3, (%rax) nop popq %rbp ret
上述代码一、2行是参数调用保存现场操做;第3行是参数传递,函数调用第一个参数通常放在rdi寄存器,此行代码把rdi寄存器值(指针p的值)写入栈中;第4行是把栈中p的值写入rax寄存器;第5行是把当即数3写入到rax寄存器值所指向的内存中,此处要注意(%rax)两边的括号,这个括号并并非无关紧要的,(%rax)和%rax彻底是两种意义,(%rax)表明rax寄存器中值所表明地址部分的内存,即至关于C++代码中的*p,而%rax表明rax寄存器,至关于C++代码中的p值,因此汇编这里使用了(%rax)而不是%rax。
咱们再写出参数传递使用引用的C++代码段test2:
void test2(int& r) { r = 3; //赋值前无需判断reference是否为空 return; }
这段代码对应的汇编代码以下:
pushq %rbp movq %rsp, %rbp movq %rdi, -8(%rbp) movq -8(%rbp), %rax movl $3, (%rax) nop popq %rbp ret
咱们发现test2对应的汇编代码和test1对应的汇编代码彻底相同,这说明C++编译器在编译程序的时候将指针和引用编译成了彻底同样的机器码。因此C++中的引用只是C++对指针操做的一个“语法糖”,在底层实现时C++编译器实现这两种操做的方法彻底相同。
C++中引入了引用操做,在对引用的使用加了更多限制条件的状况下,保证了引用使用的安全性和便捷性,还能够保持代码的优雅性。在适合的状况使用适合的操做,引用的使用能够必定程度避免“指针满天飞”的状况,对于提高程序鲁棒性也有必定的积极意义。最后,指针与引用底层在实现大部分状况下都是同样的,不用担忧二者的性能差距。