先看一段代码:
int& Add(int a, int b) {
int c = a + b;
return c;
}
int main() {
int& ret = Add(1, 2);
cout << ret << endl;
return 0;
}
运行出来打印的值是没问题的。但这段代码没问题吗?
这里引用做了函数返回值,
首先将 Add(1, 2) 的返回值赋给 ret,
ret 是个引用,
并没有实际空间,
而这段代码是能编过的,
所以 ret 会指向一块空间,
这块空间给谁了呢?
很显然,这块空间是 Add 函数开辟给 c 的。
出了函数的作用域,
再访问那块空间实际上就越界访问了,
此时 ret 是界外的那块空间的别名,这样会有什么副作用呢?
看下面一段代码的运行结果:
分析一下:
首先解释一下 ret1 = 7 的原因,
因为 ret1 指向的是 Add 函数开辟给变量 c 的那块空间,
当出了函数的作用域 c 就销毁了,
此时 ret 还是那块空间的别名,
销毁 c 的同时那块空间储存的值并没有被重置为随机值,
所以一开始给的那段代码打印出来的结果没问题。
但是,后来又调用了一次 Add 函数,
此时并没有将返回值赋给其他变量。
因为 ret1 只是一个别名,
它改变了说明它指向的那块空间储存的值改变了,
所以这两次调用 Add 函数所开辟的函数栈帧是没有变的,
创建局部变量 c 的位置也没有变。
第一次打印 ret1 的值是 7 没问题了,
但为什么第二次打印就成了随机值?
这是调用第一次打印函数时的汇编代码,
当运行完黄标指向的那行之前时 ret1 的值就已经变了,
但还不是打印出来的那个随机值。
刚刚运行完的那一行调用了 call 指令,
此时去调用的是运算符重载函数 operator<< 。
函数调用要开辟栈帧,这里也不例外,
ret1 的值改变说明它指向的那块空间成了函数栈帧的一部分,
函数在执行过程中改变了那一块空间的值,
至于为什么 ret1 第一次打印出来的值是正确的,
这是因为在第一次改变 ret1 指向的那块空间之前就已经将 ret1 输出到屏幕上来了。
而我们可以看到后面又调用了几次 call 语句,
在后面开辟函数栈帧的过程中 ret1 指向的那块空间的值被多次改变,
第二次输出的随机值只是被改变之后的一个值,
ret1 之后可能还会再改变。
所以,如果函数返回时,出了函数作用域,如果返回对象还未还给系统,则可以使用引用返回,如果已经还给系统了,则必须使用传值返回
。