但是临时变量在哪?
最后将临时变量中的值赋值给ret
图:
所有的传值返回都会生成一个拷贝
便于理解,看一下汇编:
看第四句话,这里是说,把 eax 中的值,拷贝到 ret 中。
而再函数调用返回时:
这里是将 c 的值放到 eax 中的。
这也就印证了返回时,是以临时拷贝形式返回的,由于返回值是 int ,所以是直接用的 eax 寄存器。
而不论这个函数结束后,返回的那个值会不会被销毁,都会创建临时变量返回,例如这段代码 :
int fun()
{
static int n = 0;
n++;
return n;
}
int main()
{
int ret = fun();
cout << ret << endl;
return 0;
}
对于该函数,编译器仍然是创建临时变量返回;因为编译器不会对其进行特殊处理。
看一下汇编:
仍然是放到 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销毁栈帧没有清理空间数据,但是会二次覆盖
来看个有意思的:
例如这里,当调用 add 函数之后,返回 c 的引用,接收返回值是用的ret相当于是 c 的引用,这时由于没有清理栈帧数据,所以打印3;
但是第二次调用,重新建立栈帧,由于栈帧大小相同,第二次建立栈帧可能还是在原位置,之前空间的数据被覆盖,继续运算,但是此时,ret 那块空间的值就被修改了,而这时没有接收返回值,但是原先的那块 c 的值被修改,所以打印出来 ret 是 30 。
所以使用引用返回时,一旦返回后,返回值的空间被修改,那么都可能会造成错误,使用要小心!
引用返回有一个原则:如果函数返回时,出了函数作用域,如果返回对象还在(还没还给系统),则可以使用引用返回,如果已经还给系统了,则必须使用传值返回。
它俩的区别就是一个生成拷贝,一个不生成拷贝。
而这时 static 修饰的静态变量不委屈了:
int& fun()
{
static int n = 0;
n++;
return n;
}
因为 static 修饰的变量在静态区,出了作用域也存在,这时就可以引用返回。
我们可以理解引用返回也有一个返回值,但是这个返回值的类型是 int& ,中间并不产生拷贝,因为返回的是别名。这就相当于返回的就是它本身。
有时引用返回可以发挥出意想不到的结果:
typedef 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 一直存在;这时可以使用引用返回。
引用返回 减少了值拷贝 ,不比将其拷贝到临时变量中返回;并且由于是引用返回,我们也可以 修改返回对象 。
总结提炼:如果出了作用域,返回变量(静态,全局,上一层栈帧,malloc等)仍然存在,则可以使用引用返回。
-
C语言
+关注
关注
180文章
7597浏览量
136133 -
C++
+关注
关注
22文章
2104浏览量
73482 -
面向对象
+关注
关注
0文章
64浏览量
9978
发布评论请先 登录
相关推荐
评论