C++ return返回值小问题


    同事修改代码,有个函数是返回string的一个引用,函数中有个分支,有一个分支没有调用return返回值,函数类似如下:

#include<iostream>
std::string str("hello");
std::string str1("hello1");
const std::string& get_str()
{
  return str;
}

const std::string& get_str1()
{
  return str1;
}

const std::string& func(int i)
{
  if (i==0){
    return get_str();
  }else{
    get_str1();//没有返回值
  }
}

int main()
{
  std::cout<<func(0)<<std::endl;
  std::cout<<func(1)<<std::endl;
  return 0;
}
    但是他自己编译、测试没有问题,我自己去验证了一下也没有问题,难道C++这么智能,自动编译优化?不过仔细想下,就应该知道是不可能的,不会做这种多余的事情。于是就研究了一下。

    首先上面的代码确实执行后结果符合预期:

pc-csapp06:~/test> ./test
hello
hello1

    用gdb翻了一下编译后的汇编代码,又在网上补了一下return的知识,就豁然开朗了。

    我是要Suse 11 64位刀片机上测试的。网上知识:函数返回值是使用eax寄存器返回,如果是内置变量,直接在eax中存放值,如果是复杂变量,存放对象地址。不过64位机上使用的是rax,eax是rax的低32位。

    汇编:

(gdb) disas /m func
Dump of assembler code for function func(int):
14      const std::string& func(int i)
0x0000000000400aa2 <func(int)+0>:       push   %rbp   保护rbp——一般都会用rbp来做为栈临时指针,保护上层调用函数的值
0x0000000000400aa3 <func(int)+1>:       mov    %rsp,%rbp   将栈顶rsp指针给rbp——rsp是栈顶指针,总是指向栈顶。linux,gcc使用的是AT&T格式,和windows的intel汇编不同,mov src dst。
0x0000000000400aa6 <func(int)+4>:       sub    $0x10,%rsp
0x0000000000400aaa <func(int)+8>:       mov    %edi,-0x4(%rbp)  i放入栈中,不同编译器,使用方式不同,这里用edi传递
15      {
16        if (i==0){
0x0000000000400aad <func(int)+11>:      cmpl   $0x0,-0x4(%rbp)
0x0000000000400ab1 <func(int)+15>:      jne    0x400ac2 <func(int)+32>
17          return get_str();
0x0000000000400ab3 <func(int)+17>:      callq  0x400a8c <get_str()>
0x0000000000400ab8 <func(int)+22>:      mov    %rax,-0x10(%rbp)  将get_str返回值放入栈中
18        }else{
19          get_str1();
0x0000000000400ac2 <func(int)+32>:      callq  0x400a97 <get_str1()>
20        }
21      }
0x0000000000400abc <func(int)+26>:      mov    -0x10(%rbp),%rax  将栈中返回值放入rax中,返回
0x0000000000400ac0 <func(int)+30>:      jmp    0x400ac7 <func(int)+37>
0x0000000000400ac7 <func(int)+37>:      leaveq 
0x0000000000400ac8 <func(int)+38>:      retq   

    可以发现,没有使用return返回的函数,少了将返回值放入栈中的操作,最后返回的直接是rax寄存器的值。但是巧合的是,这个rax,刚好是get_str1函数调用后,用来存放返回值的,所以,最终是没有问题的。

    但是,这种写法是不合格的,这次没有问题只是个巧合,如果get_str1后调用了任何一个函数,都会导致结果错误。如:

const std::string& func(int i)
{
  if (i==0){
    return get_str();
  }else{
    get_str1();//没有返回值
  }
  get_str();
}

写成这个样子,结果就有问题了。


还有,现在是返回的一个常量,也就是保证在上层调用的函数使用时,变量是有效的,如果返回的是临时变量,就会有问题了。如:

const std::string get_str()
{
  std::string str("hello");
  return str;
}

const std::string get_str1()
{
  std::string str1("hello1");
  return str1;
}

const std::string func(int i)
{
  if (i==0){
    return get_str();
  }else{
    get_str1();//没有返回值
  }
}

修改成这个样子,则调用get_str1()返回值,是在func函数的作用栈中,在main函数调用func函数时,返回值还是用的这个值,但这个值已经销毁掉了,使用时会segment fault,core掉。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值