更新2018年:这是我写的一个老问题,对C的理解有所减少。请不要贬低。
当我使用以下代码时:
int mytest(void);
int main(void)
{
mytest();
return;
}
int mytest(void)
{
return 3;
}
main
的返回值是多少?我明白这是
编辑:大评论:我知道这是未定义的行为。逻辑上,会产生什么样的回报值?
编辑2:示例:http://ideone.com/fAxnNn
答案 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>
所以再一次,没有保证。