调用返回int的函数后,在非void函数中调用return的结果是什么?

时间:2016-12-14 17:29:01

标签: c return return-value

更新2018年:这是我写的一个老问题,对C的理解有所减少。请不要贬低。

当我使用以下代码时:

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

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

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

编辑:大评论:我知道这是未定义的行为。逻辑上,会产生什么样的回报值?

     

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

4 个答案:

答案 0 :(得分:8)

这不是所需的行为,但显式未提及未定义的行为。这是“违反约束条件”。

引用C11,第6.8.6.4章

  

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

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

  

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

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

预计返回main()的{​​{1}}的底线不应该没有表达式的int语句。不要写那样的代码。

[注意:As per this discussion,有一个强烈的概念,即这是未定义的行为,但仍然,标准中没有显式提及。]

答案 1 :(得分:0)

显示的程序不合法C.在非空函数中使用return;就是约束违规,并且您的编译器需要进行投诉。通常,这将是一个错误,但允许编译器生成程序 - 但标准显然不会对此类程序的行为提出任何要求。因此,它的行为没有定义(从技术上讲,这与Undefined Behavior略有不同,因为标准中没有明确指定为“ undefined ”。

没有必要问诸如“当我导致未定义的行为时会发生什么?”这样的问题,因为答案总是“ Anything 。”

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

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

  • 终止编译
  • 优化return;作为函数中的最后一个语句,并将其视为与main()相同且没有返回语句
  • 返回一个未初始化的值(可能是支持它们的体系结构的信号值,或者它可能是最近使用的堆栈值,或者完全不同的其他内容)

答案 2 :(得分:0)

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

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

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

现在出现疑问:主要通过撰写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;给出相同的输出(返回的隐式值也可以是垃圾)。尽管如此,我们无法保证这一点,但现在您拥有了所需的所有工具。

答案 3 :(得分:0)

这里的问题是你似乎不明白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]$ 

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

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.

现在让我们来玩优化吧。我使用相同版本的gcc在相同的CentOS机器上编译相同的代码,但启用了优化:

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>

在具有优化/Ox的MSVC上:

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

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

C:\Users\dbush\Documents>

所以再一次,没有保证。