[C++]如果传入函数的参数会经过复制的话,那么函数的返回值会不会也经过复制

1 函数参数传递

从swap函数讲起,如果我是一个新手,我不加思索写下

void swap(int a,int b) {
    int c = a;
    a = b;
    b = c;
}

很显然,这个函数是无效的,正确的写法是

void swap(int * a, int * b) {
    int c = *a;
    *a = *b;
    *b = c;
}

这里我的解释是,函数传参时传入的参数会被复制给形式参数,不管这个传入的参数是整数还是指针(先不考虑union类型)。
在这种解释下,第一个函数无效的原因也找到了,假设一下,在主函数中的代码可以替换成这种形式

//原代码  
int main() {
    int a = 1, b = 2;
    swap(a,b);
    return 0;
}  
//替换后
int main() {
    int a = 1, b = 2;
    // swap
    int __a = a;
    int __b = b;
    int c = __a;
    __a = __b;
    __b = c;
    return 0;
}  

2.1 函数返回值传递

按照上面的想法,我认为函数返回值也会经过传递,那么这段错误代码可以这样解释

int * foo() {
  int a = 1;
  return &a;
}

首先调用int * ptr = foo()时创建一个临时变量 int * __a = &a;调用结束后,ptr = __a,此时foo内部变量a被回收,所以ptr == NULL

但是我发现一个问题,这个问题反驳了我的猜测

2.2 函数返回值传递 问题

这是一个有问题的代码

// question: a 应该被删除了
int foo() {
  int a = 1;
  cout<< &a << endl;
  return a;
}
int main() {
  int a = foo();
  cout<< &a << endl;
  return 0;
}

问题出在运行结果,本来foo内部的a应该被回收了,但是打出来的两个地址竟然是一样的

(base) ➜  test ./a.out 
0x7ffe9fccabc4
0x7ffe9fccabe4

不太怎么懂编译器是怎么运作的,我在想会不会我的想法有问题,请各位帮我解答

这段话是有问题的。指针指向的数据是临时变量,你的例子里一定不会是NULL,返回后如果使用这个指针指向的数据的话是undefined behavior.

至于你2.2的实验,这是十分正常的。编译器一般对返回值都会有优化的。在你的例子里,临时变量a被返回了,编译器通常都是会直接在存放返回值的地方放这个a变量。可以参考一下相关内容 (RVO

注:想必你也发现了,我的用词极其随意。表明我并不真懂,只是想参与一下讨论 :dog:

1赞

好吧,我改一下,首先调用int * ptr = foo()时创建一个临时变量 int * __a = &a;调用结束后,ptr = __a,此时foo内部变量a被回收,所以ptr == NULL

这也是不对的。比如临时变量a存放在地址0x1234,你的 return &a 就会返回 0x1234。然后你的 ptr 就会是 0x1234,不会是NULL。a被回收了,不会改变你返回的值。只不过你现在不应该dereference这个ptr指针,否则是undefined behavior。

懂了一点,我还是乖乖的用智能指针吧

但是我把2.2的实验改动一下,变成这样

int * foo() {
  int a = 1;
  printf("%p\n",&a);
  return &a;
}

int main() {
  int * a = foo();
  printf("%p\n",&a);
  printf("%d\n",*a);
  return 0;
}

现在我有点搞不懂了,因为运行结果是

(base) ➜  test ./a.out 
0x7ffdd514aa44
0x7ffdd514aa60
[1]    13048 segmentation fault (core dumped)  ./a.out

指针的地址不再是一样的了,另外引用*a会发生段错误

一个一个说。

foo里, a是一个int。临时变量,位于0x7ffdd514aa44,存放了数据1。你将这个地址打印并返回。

main里,a是一个int指针,位于0x7ffdd514aa60(注:指针本身也是变量,也在内存里,所以指针也有地址),存放的数据是刚刚返回的地址的值0x7ffdd514aa44。

main里第一行printf,打印了指针a的地址(即&a)。 main里第二行printf,dereference了指针a。但a指向的地址是曾经foo里的临时变量,使用内容是undefined behavior。在你的例子里就是崩溃

看来我的问题解决了,如果没有编译器优化的我的想法是正确的
ps: 刚刚我发现printf("%p\n",&a)只是我打错了,把a看作int 了

主要是编译器优化了。更深入的可以看一下c++的左值引用和右值引用。