【问题标题】:What is the result of calling return in a non-void function after calling a function returning int?调用返回int的函数后在非void函数中调用return的结果是什么?
【发布时间】:2017-04-30 03:22:23
【问题描述】:

2018 年更新:这是我写的一个老问题,对 C 的理解有所降低。请不要投反对票。


当我使用以下代码时:

int mytest(void);
int main(void)
{
   mytest();
   return;
}
int mytest(void)
{
   return 3;
}

main 的返回值是多少?我明白这是

  • 未定义的行为
  • 可能会产生错误

编辑:大注释:我知道这是未定义的行为。逻辑上,会产生什么返回值?

编辑 2:示例:http://ideone.com/fAxnNn

【问题讨论】:

  • return wirhout 在返回非 void 的函数中不允许使用表达式。
  • @abelenky IDEONE 永远不应该用于代码检查。如果您有代码,请自行检查,如果您确定您的代码没问题,请使用 IDEONE 显示。但永远不要使用 IDEONE 来检查你的代码。 Check this simple example。您确实意识到初始化元素不是恒定的。此外,据我所知,IDEONE 只使用 32 位编译器(也许我错了)。
  • 简短回答:这取决于编译器/设置。这就是未定义行为的含义。

标签: c return return-value


【解决方案1】:

这里的问题是你似乎不明白undefined behavior 的意思。

当您调用未定义的行为时,任何事情都可能发生。您的程序可能会崩溃,可能会产生意想不到的结果,或者看起来可以正常工作。进行看似无关的更改,例如添加未使用的变量或额外调用 printf,可能会改变未定义行为的表现方式。

这也意味着两个不同的编译器可以为相同的代码生成不同的结果,或者一个具有两种不同优化设置的编译器可以生成不同的结果。

我给你举个例子。我在带有 gcc 4.1.2 的 CentOS 5.10 上编译了您的原始代码。首先我编译没有任何优化:

gcc -Wall -Wextra -g -o /tmp/x1 /tmp/x1.c

我运行了生成的代码,然后运行了echo $?。后者打印前一个进程的退出代码(即main的返回值)。结果:

[dbush@db-centos tmp]$ /tmp/x1
[dbush@db-centos tmp]$ echo $?
3
[dbush@db-centos tmp]$ 

现在我将在 Windows 7 机器上的 Visual Studio 2010 下编译相同的代码:

cl x1.c

如果我运行这个然后echo %ERRORLEVEL% 打印返回码,我会得到这个:

C:\Users\dbush\Documents>x1

C:\Users\dbush\Documents>echo %ERRORLEVEL%
0

C:\Users\dbush\Documents>

如您所见,gcc 和 MSVC 为相同的代码生成不同的结果。在一种情况下它返回 3,而在另一种情况下它返回 0。

现在让我们开始优化。我在同一台 CentOS 机器上用相同版本的 gcc 编译了相同的代码,但开启了优化:

gcc -Wall -Wextra -g -o /tmp/x1 /tmp/x1.c -O3

然后我运行了两次。然后我得到以下结果:

[dbush@db-centos tmp]$ /tmp/x1
[dbush@db-centos tmp]$ echo $?
68
[dbush@db-centos tmp]$ /tmp/x1
[dbush@db-centos tmp]$ echo $?
36
[dbush@db-centos tmp]$ 

因此,我不仅在使用不同的优化设置运行时会得到不同的结果,而且在运行同一程序多次时会得到不同的结果。这就是未定义行为可能发生的情况。

所以要回答您的问题“将产生什么返回值”,答案取决于您调用了未定义的行为。你无法可靠地预测会发生什么。

编辑:

更新代码的更多示例。

在没有优化的 gcc 上:

[dbush@db-centos tmp]$ /tmp/x1
mytest2 = 3
[dbush@db-centos tmp]$ echo $?
12

在 gcc 上优化 -O3:

[dbush@db-centos tmp]$ /tmp/x1
mytest2 = -1078711820
[dbush@db-centos tmp]$ echo $?
22
[dbush@db-centos tmp]$ /tmp/x1
mytest2 = -1077511916
[dbush@db-centos tmp]$ echo $?
22

在没有优化的 MSVC 上:

C:\Users\dbush\Documents>x1
mytest2 = 3

C:\Users\dbush\Documents>echo %ERRORLEVEL%
0

C:\Users\dbush\Documents>

在 MSVC 上进行优化 /Ox:

C:\Users\dbush\Documents>x1
mytest2 = 1

C:\Users\dbush\Documents>echo %ERRORLEVEL%
0

C:\Users\dbush\Documents>

同样,没有保证。

【讨论】:

  • 我会奖励你最好的答案。
【解决方案2】:

从 cmets(不是从问题中),我了解到主要目的是通过执行 return; 找到 main 返回的内容。我希望我朝着正确的方向前进。

第一件事是很少有编译器允许只写return;,有些会给出警告,有些编译器会在main没有返回任何内容时隐式添加return 0;。

第二件事返回 0 表示程序成功执行,如果返回任何其他内容将意味着一些运行时错误(至少您的环境是这样认为的)。

现在你的疑问是:main 通过写 return; 隐式返回的内容。我检查了您使用的工具 ideone.com 和 C (gcc-5.1)。

return; 它给了

Runtime error   time: 0 memory: 2164 signal:-1

return 0; 它给了

Success time: 0 memory: 2164 signal:0

return 1; 又给了

Runtime error   time: 0 memory: 2164 signal:-1

在这个信号中表示来自main的返回值

如果使用终端运行程序,可以使用echo $?获取返回值。

所以现在您可以观察到return; 和return 1; 给出相同的输出(返回的隐式值也可能是垃圾)。尽管如此,我们仍不能保证这一点,但现在您拥有所有需要的工具。

【讨论】:

  • 感谢您的帮助。你能用新的更新代码试试吗
  • @MarkYisri 抱歉,这不是论坛。欢迎您提出新问题,但回答您的人改变您的问题是不公平的。
【解决方案3】:

所示的程序是不合法的 C。在像这样的非 void 函数中使用return; 是违反约束,你的编译器需要投诉。通常,这将是一个错误,但允许编译器生成程序 - 但标准显然对此类程序的行为没有任何要求。因此它的行为没有定义(从技术上讲,这与未定义行为略有不同,因为它不是标准中明确指定为“undefined”的东西)。

没有必要问“当我导致未定义的行为时会发生什么?”之类的问题,因为答案总是“任何事情。”

顺便说一句,只要您的程序不使用返回值,您就可以省略返回语句(即在函数末尾运行) >。如果程序使用返回值,那么行为是未定义的(不仅仅是未指定的)。特别是,跑完main() 是合法的。虽然没有理由允许没有 return 的函数但不允许那些具有显式 void return 的函数,但我认为这是由于函数结尾是否可达的逻辑不确定性(这是停止问题的情况)。

在这种情况下特别感兴趣的是所讨论的函数是main()。编译器可用的选项(除了发出所需的诊断)包括(但不限于):

  • 终止编译
  • 优化出return;作为函数中的最后一条语句,并将其视为main(),不带return语句
  • 返回一个未初始化的值(这可能是支持它们的架构上的信号值,也可能是最近使用的堆栈值,或者完全是其他东西)

【讨论】:

  • 如果您能引用与此相关的内容,我会很感兴趣。谢谢。
  • 该标准对格式不正确的程序没有任何要求 - 编译器通常应该拒绝此类输入并提供适当的错误消息。 “不带表达式的 return 语句只能出现在返回类型为 void 的函数中 - “应”使编译器无法在以下位置生成任何程序全部。
  • 也就是说,您可以从main()(并且只能从main())中省略return,因此编译器可能会选择将void 返回视为等效。但除非它记录了这一点,否则我们无法在这里推理!
  • 好的,我快速浏览了标准草案,你说得对。我已经修改了我的答案以包括参考链。 :)
  • @TobySpeight 您的 cmets 比您提供的整个答案更有帮助。这意味着您的评论实际上是一个答案,而您的答案只不过是一个评论。
【解决方案4】:

这不是期望的行为,尽管没有明确提到未定义的行为。这是“违反约束”。

引用C11,第 6.8.6.4 章

[...] 没有表达式的return 语句只能出现在函数中 其返回类型为void。

并且,从第 §5.1.1.3 章,诊断,

一个符合要求的实现应产生至少一个诊断消息(在 实现定义的方式)如果是预处理翻译单元或翻译单元 包含违反任何语法规则或约束的行为,即使该行为也是显式的 指定为未定义或实现定义。 [....]

在这种情况下,编译器会继续生成可执行代码,因此,是的,执行此二进制文件会调用 undefined behavior。

对于main(),它应该返回int 的底线不应该有没有表达式的return 语句。不要写这样的代码。


[注意:As per this discussion,强烈认为这是未定义的行为,但标准中仍然没有明确提及。]

【讨论】:

  • 我的问题可能措辞有误,但main 的返回值是什么可能在这种情况下?
  • @MarkYisri 这里的问题是,这个程序的执行(如果编译器生成二进制文件的话)会调用 UB,所以事情是有保证的。
猜你喜欢
  • 2020-01-18
  • 1970-01-01
  • 1970-01-01
  • 2016-04-22
  • 2018-08-10
  • 2017-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多