在调用函数returning int 后,在非void 函数中调用return 的结果是什么?
What is the result of calling return in a non-void function after calling a function returning int?
2018 年更新:这是我写的一个老问题,对 C 的理解有所减少。请不要投票。
当我使用以下代码时:
int mytest(void);
int main(void)
{
mytest();
return;
}
int mytest(void)
{
return 3;
}
main
的 return 值是多少?我明白这是
- 未定义的行为
- 可能会产生错误
Edit: large comment: I know this is undefined behavior. Logically, what return value will be produced?
Edit 2: Sample: http://ideone.com/fAxnNn
这不是期望的行为,尽管没有明确提及为未定义的行为。这是一个"Constraints violation".
引用 C11
,第 6.8.6.4 章
[...] A return
statement without an expression shall only appear in a function
whose return type is void
.
并且,从第 §5.1.1.3 章开始,诊断,
A conforming implementation shall produce at least one diagnostic message (identified in
an implementation-defined manner) if a preprocessing translation unit or translation unit
contains a violation of any syntax rule or constraint, even if the behavior is also explicitly
specified as undefined or implementation-defined. [....]
在这种情况下,编译器继续生成可执行代码,因此,是的,执行此二进制文件会调用 undefined behavior。
底线,对于预期 return 的 main()
,int
不应该有没有表达式的 return
语句。避免编写这样的代码。
[注意:As per this discussion,有一种强烈的观念认为这是未定义的行为,但仍然没有显式在标准中提到。]
显示的程序不是合法的 C。在这样的非空函数中使用 return;
是 约束违规 ,并且需要您的编译器抱怨。通常,这将是一个错误,但允许编译器生成程序——但标准显然没有对此类程序的行为提出任何要求。因此它的行为未定义(从技术上讲,这与未定义行为略有不同,因为它没有在标准中明确指定为“undefined”)。
问这样的问题是没有意义的,“当我导致未定义的行为时会发生什么?”,因为答案总是,“任何事情 ."
顺便说一句,您可以 省略 一个 return 语句(即 运行 关闭函数的末尾) 只要您的程序不使用 return 值 。如果程序使用 return 值,则行为是未定义的(不仅仅是未指定)。特别是,运行关闭 main()
的末尾是合法的。虽然没有理由允许没有 return
的函数,但不允许有显式 void return
的函数,我认为这是由于函数的结束是否可到达的逻辑不确定性(这是暂停问题的一个例子) ).
在这种情况下特别感兴趣的是所讨论的函数是 main()
。编译器可用的选项(除了发出所需的诊断信息之外)包括(但不限于):
- 终止编译
- 将
return;
优化为函数中的最后一条语句,并将其视为与 main()
相同,没有 return 语句
- Return 一个未初始化的值(可能是支持它们的体系结构上的信号值,或者它可能是最近使用的堆栈值,或者其他的东西)
从评论中(不是从问题中),我了解到主要目的是通过 return;
找到 main 返回的内容。我希望我正朝着正确的方向前进。
第一件事 很少有编译器允许只写 return;
并且有些编译器会给出警告并且一些编译器会隐式添加 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;
给出相同的输出(返回的隐式值也可能是垃圾)。尽管如此,我们仍不能保证这一点,但现在您拥有查找所需的所有工具。
这里的问题是你似乎不明白 undefined behavior 的意思。
当你调用未定义的行为时,任何事情都可能发生。您的程序可能会崩溃,可能会产生意想不到的结果,或者看起来工作正常。进行看似无关的更改,例如添加未使用的变量或额外调用 printf
,可以改变未定义行为的表现方式。
这也意味着两个不同的编译器可以为相同的代码生成不同的结果,或者具有两个不同优化设置的一个编译器可以生成不同的结果。
我举个例子。我使用 gcc 4.1.2 在 CentOS 5.10 上编译了您的原始代码。首先我在没有任何优化的情况下编译:
gcc -Wall -Wextra -g -o /tmp/x1 /tmp/x1.c
我运行结果代码然后运行echo $?
。后者打印前一个进程的退出代码(即 main
的 return 值)。结果:
[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%
打印 return 代码,我得到这个:
C:\Users\dbush\Documents>x1
C:\Users\dbush\Documents>echo %ERRORLEVEL%
0
C:\Users\dbush\Documents>
如您所见,gcc 和 MSVC 为相同的代码生成不同的结果。在一种情况下它 returns 3 而在另一种情况下它 return 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]$
因此,当 运行 不同的优化设置时,我不仅会得到不同的结果,而且 运行 多次 相同的程序 也会得到不同的结果。这就是未定义行为可能发生的情况。
所以要回答您的问题 "what return value will be produced",答案取决于您调用了未定义的行为。你无法可靠地预测会发生什么。
编辑:
更新代码的更多示例。
在没有优化的 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>
再说一次,没有 gua运行T 恤。
2018 年更新:这是我写的一个老问题,对 C 的理解有所减少。请不要投票。
当我使用以下代码时:
int mytest(void);
int main(void)
{
mytest();
return;
}
int mytest(void)
{
return 3;
}
main
的 return 值是多少?我明白这是
- 未定义的行为
- 可能会产生错误
Edit: large comment: I know this is undefined behavior. Logically, what return value will be produced?
Edit 2: Sample: http://ideone.com/fAxnNn
这不是期望的行为,尽管没有明确提及为未定义的行为。这是一个"Constraints violation".
引用 C11
,第 6.8.6.4 章
[...] A
return
statement without an expression shall only appear in a function whose return type isvoid
.
并且,从第 §5.1.1.3 章开始,诊断,
A conforming implementation shall produce at least one diagnostic message (identified in an implementation-defined manner) if a preprocessing translation unit or translation unit contains a violation of any syntax rule or constraint, even if the behavior is also explicitly specified as undefined or implementation-defined. [....]
在这种情况下,编译器继续生成可执行代码,因此,是的,执行此二进制文件会调用 undefined behavior。
底线,对于预期 return 的 main()
,int
不应该有没有表达式的 return
语句。避免编写这样的代码。
[注意:As per this discussion,有一种强烈的观念认为这是未定义的行为,但仍然没有显式在标准中提到。]
显示的程序不是合法的 C。在这样的非空函数中使用 return;
是 约束违规 ,并且需要您的编译器抱怨。通常,这将是一个错误,但允许编译器生成程序——但标准显然没有对此类程序的行为提出任何要求。因此它的行为未定义(从技术上讲,这与未定义行为略有不同,因为它没有在标准中明确指定为“undefined”)。
问这样的问题是没有意义的,“当我导致未定义的行为时会发生什么?”,因为答案总是,“任何事情 ."
顺便说一句,您可以 省略 一个 return 语句(即 运行 关闭函数的末尾) 只要您的程序不使用 return 值 。如果程序使用 return 值,则行为是未定义的(不仅仅是未指定)。特别是,运行关闭 main()
的末尾是合法的。虽然没有理由允许没有 return
的函数,但不允许有显式 void return
的函数,我认为这是由于函数的结束是否可到达的逻辑不确定性(这是暂停问题的一个例子) ).
在这种情况下特别感兴趣的是所讨论的函数是 main()
。编译器可用的选项(除了发出所需的诊断信息之外)包括(但不限于):
- 终止编译
- 将
return;
优化为函数中的最后一条语句,并将其视为与main()
相同,没有 return 语句 - Return 一个未初始化的值(可能是支持它们的体系结构上的信号值,或者它可能是最近使用的堆栈值,或者其他的东西)
从评论中(不是从问题中),我了解到主要目的是通过 return;
找到 main 返回的内容。我希望我正朝着正确的方向前进。
第一件事 很少有编译器允许只写 return;
并且有些编译器会给出警告并且一些编译器会隐式添加 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;
给出相同的输出(返回的隐式值也可能是垃圾)。尽管如此,我们仍不能保证这一点,但现在您拥有查找所需的所有工具。
这里的问题是你似乎不明白 undefined behavior 的意思。
当你调用未定义的行为时,任何事情都可能发生。您的程序可能会崩溃,可能会产生意想不到的结果,或者看起来工作正常。进行看似无关的更改,例如添加未使用的变量或额外调用 printf
,可以改变未定义行为的表现方式。
这也意味着两个不同的编译器可以为相同的代码生成不同的结果,或者具有两个不同优化设置的一个编译器可以生成不同的结果。
我举个例子。我使用 gcc 4.1.2 在 CentOS 5.10 上编译了您的原始代码。首先我在没有任何优化的情况下编译:
gcc -Wall -Wextra -g -o /tmp/x1 /tmp/x1.c
我运行结果代码然后运行echo $?
。后者打印前一个进程的退出代码(即 main
的 return 值)。结果:
[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%
打印 return 代码,我得到这个:
C:\Users\dbush\Documents>x1
C:\Users\dbush\Documents>echo %ERRORLEVEL%
0
C:\Users\dbush\Documents>
如您所见,gcc 和 MSVC 为相同的代码生成不同的结果。在一种情况下它 returns 3 而在另一种情况下它 return 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]$
因此,当 运行 不同的优化设置时,我不仅会得到不同的结果,而且 运行 多次 相同的程序 也会得到不同的结果。这就是未定义行为可能发生的情况。
所以要回答您的问题 "what return value will be produced",答案取决于您调用了未定义的行为。你无法可靠地预测会发生什么。
编辑:
更新代码的更多示例。
在没有优化的 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>
再说一次,没有 gua运行T 恤。