c++引用详细解说3

电子说

1.3w人已加入

描述

但是临时变量在哪?

  • 如果 c 比较小(4/8 byte),一般是寄存器充当临时变量,例如eax
  • 如果 c 比较大,临时变量放在调用 add 函数的栈帧中,

最后将临时变量中的值赋值给ret

图:

C语言

所有的传值返回都会生成一个拷贝

便于理解,看一下汇编:

C语言

看第四句话,这里是说,把 eax 中的值,拷贝到 ret 中。

而再函数调用返回时:

C语言

这里是将 c 的值放到 eax 中的。

这也就印证了返回时,是以临时拷贝形式返回的,由于返回值是 int ,所以是直接用的 eax 寄存器。

而不论这个函数结束后,返回的那个值会不会被销毁,都会创建临时变量返回,例如这段代码 :

int fun(){            static int n = 0;        n++;        return n;}
int main(){ int ret = fun(); cout << ret << endl; return 0;}

对于该函数,编译器仍然是创建临时变量返回;因为编译器不会对其进行特殊处理。

看一下汇编:

C语言

仍然是放到 eax 寄存器中返回的。

埋个伏笔:你觉不觉的这个临时变量创建的很冤枉,明明这块空间一直存在,我却依然创建临时变量返回了?能不能帮它洗刷冤屈。

如果我改成引用返回会发生什么情况吗?

int& add(int a, int b){            int c = a + b;            return c;}int main(){            int ret = add(1, 2);            cout << ret << endl;            return 0;}

引用返回就是不生成临时变量,直接返回 c 的引用。而这里产生的问题就是 非法访问 。

造成的问题:

  • 存在非法访问,因为 add 的返回值是 c 的引用,所以 add 栈帧销毁后,会访问 c 位置空间,而这是读操作,不一定检查出来,但是本质是错的。
  • 如果 add 函数栈帧销毁,空间被清理,那么取 c 值时取到的就是随机值,取决于编译器的决策。

ps:虽然vs销毁栈帧没有清理空间数据,但是会二次覆盖

来看个有意思的:

C语言

例如这里,当调用 add 函数之后,返回 c 的引用,接收返回值是用的ret相当于是 c 的引用,这时由于没有清理栈帧数据,所以打印3;

但是第二次调用,重新建立栈帧,由于栈帧大小相同,第二次建立栈帧可能还是在原位置,之前空间的数据被覆盖,继续运算,但是此时,ret 那块空间的值就被修改了,而这时没有接收返回值,但是原先的那块 c 的值被修改,所以打印出来 ret 是 30 。

所以使用引用返回时,一旦返回后,返回值的空间被修改,那么都可能会造成错误,使用要小心!

引用返回有一个原则:如果函数返回时,出了函数作用域,如果返回对象还在(还没还给系统),则可以使用引用返回,如果已经还给系统了,则必须使用传值返回。

它俩的区别就是一个生成拷贝,一个不生成拷贝。

而这时 static 修饰的静态变量不委屈了:

int& fun(){            static int n = 0;        n++;        return n;}

因为 static 修饰的变量在静态区,出了作用域也存在,这时就可以引用返回。

我们可以理解引用返回也有一个返回值,但是这个返回值的类型是 int& ,中间并不产生拷贝,因为返回的是别名。这就相当于返回的就是它本身。

有时引用返回可以发挥出意想不到的结果:

#include#define N 10typedef struct Array{            int a[N];            int size;}AY;
int& PostAt(AY& ay, int i){ assert(i < N); return ay.a[i];}int main(){ AY ay; PostAt(ay, 1); // 修改返回值 for (int i = 0; i < N; i++) { PostAt(ay, i) = i * 3; } for (int i = 0; i < N; i++) { cout << PostAt(ay, i) << ' '; } return 0;}

由于PostAt 的形参 ay 为 main 中 局部变量 ay的别名,所以 ay 一直存在;这时可以使用引用返回。

引用返回 减少了值拷贝 ,不比将其拷贝到临时变量中返回;并且由于是引用返回,我们也可以 修改返回对象 。

C语言

总结提炼:如果出了作用域,返回变量(静态,全局,上一层栈帧,malloc等)仍然存在,则可以使用引用返回。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分