2

如果我有这样的功能:

int addNumbers(int x, int y)
{
    return x + y;
}

如果我这样使用它:

cout << addNumbers(4, 5) << endl;

它将返回并打印9. 使用上面的同一cout行,如果我注释掉或删除 return in addNumbers,它将返回并 print 1。如果我这样做:

int addNumbers(int x, int y)
{
    int answer = x + y;
    //return x + y;
}

它会自动返回并打印9,无需我使用返回。同样,我可以写int answer = x;它会返回4。我也可以这样写:

int addNumbers(int x, int y)
{
    int answer = x;
    answer = 1;
    //return x + y;
}

它仍然会返回 4。

究竟返回了什么,为什么?当我使用参数变量时,它只返回 1 以外的值,但它没有返回变量 answer ,如上一个示例所示,因为我将其更改为 1 并且它仍然返回x (4).

4

4 回答 4

3

§6.6.3 [stmt.return]/p2:

从函数的末尾流出相当于return没有值的 a;这会导致值返回函数中的未定义行为。

(main()是一个特殊的例外。从末尾流出main()相当于 a return 0;)

允许的 UB 包括:

  • 返回您“想要”返回的内容
  • 而是返回垃圾值
  • 崩溃
  • 将您的密码发送给黑客
  • 格式化硬盘
  • 让你的电脑爆炸并炸掉你的腿
  • 召唤鼻恶魔
  • 时光倒流并将您的程序修复到正确的位置
  • 创建黑洞
  • ……

但说真的,UB可以以各种方式表现出来。例如,给定以下代码:

#include <iostream>
bool foo = false;
int addNumbers(int x, int y)
{
    int answer = x;
    answer = 1;
    //return x + y;
}

int main(){
  if(!foo) {
    addNumbers(10, 20);
    std::cout << 1 << std::endl;
  }
  else {
    std::cout << 2 << std::endl;
  }
}

-O2 处的 clang++打印 2.

为什么?因为它推断出addNumbers(10, 20);具有未定义的行为,这允许它假设第一个分支从未被采用并且foo总是true,即使显然情况并非如此。

于 2014-09-06T08:48:21.397 回答
1

您依赖于“未定义的行为”。对于简单类型,返回值通常存储在寄存器中,也可以用于形成计算结果。但它也可能不会被使用,并且您会得到一些任意的“随机”结果,并且是“未定义的行为”,您还可能会得到您的计算机可能执行的任何其他可能的操作 - 例如崩溃或执行一些您没有执行的代码想执行...

于 2014-09-06T08:44:53.633 回答
0

您正在观察未定义的行为。没有充分的理由“为什么”该程序会这样做,因为它不是一个格式良好的程序。它可以做任何事情,包括在运行时从磁盘中删除自己。启用编译器警告和错误(例如g++ -Wall -Wextra -Werror),您将被自动阻止编写此类代码(您应该这样做)。

于 2014-09-06T08:43:41.780 回答
0

因此这是未定义的行为,反汇编您的二进制文件可以解释为什么返回这些值。

objdump -d example.bin

由于返回值与 rax 注册表相关联,因此如果编译器使用 rax 处理函数,则返回值是保留在 rax 中的值。

无论如何,您不应该这样做,因为在您编写此类代码时不知道编译器优化和注册表的使用。

于 2014-09-07T03:11:16.377 回答