为什么C标准允许您不从函数返回值?

时间:2018-06-15 12:32:52

标签: c gcc

我已经在gcc中成功编译并执行了以下代码:

#include <stdio.h>

int foo()
{

}

int main()
{
    int i = 12345;
    i = foo();
    printf("i is: %d", i);
}

输出结果为:

i is: 0

因此,gcc允许我不从函数foo()返回并使foo()返回0

此行为是否仅适用于gcc或其他C标准是否也有此行为(根据我的理解,gcc不符合任何C标准)?

5 个答案:

答案 0 :(得分:9)

如果函数缺少return语句¹,C标准都不要求编译器产生错误或警告。

在所有C标准中,如果控制流在没有return的情况下到达非void函数的末尾,则行为是未定义的,然后使用函数的返回值(如在代码中)。

因此,如果“允许”意味着“将其指定为合法的,明确定义的行为”,那么C标准都不允许您的代码。如果你的意思是“不需要实现来产生错误”,那么所有这些都可以。

请注意,如果更改i的定义(不添加foo),示例程序中return的值可以轻松更改。你不能依赖它为0.没有规则说“如果没有回报,则返回0” - 既不是标准也不是GCC的实施。根据标准,它是未定义的,在GCC中它只是当时在返回寄存器中发生的任何事情。

¹在一般情况下,这将是不可判定的。人们可以做什么,例如Java执行并定义规则,以便拒绝一些其他有效的函数定义,但没有一个C标准执行此操作。

答案 1 :(得分:8)

gcc会发出警告:

  

警告:控制到达非空函数的末尾[-Wreturn-type] |

(免责声明:我从不同版本的gcc获得不同的结果。确保使用-Wall -Wextra -pedantic-errors进行编译。)

在这种情况下发生的事情不在C标准范围内。标准只是说(C11 6.9.1 / 12):

  

如果到达了终止函数的},并且调用者使用了函数调用的值,则行为是未定义的。

在您的情况下,调用者通过将其存储在i中来确实使用返回值。所以这是未定义的行为 - C标准不包含该行为,编译器没有义务通知您。什么事情都可能发生。因此,在大多数情况下,这应该被视为一个错误。

答案 2 :(得分:5)

嗯,标准中没有任何内容阻止您编写非法代码,但是,它确实提到这是未定义的行为。

引用C11,章节§6.9.1

  

如果到达了终止函数的},则使用函数调用的值   调用者,行为未定义。

答案 3 :(得分:2)

C89陈述(至少在草案中):

If a return statement without an expression is 
executed, and the value of the function call 
is used by the caller, the behavior is undefined.

这意味着您可以像编写代码一样合法。这不是错误。 它是未定义的行为,但它在语法上是合法的。因为它并非特定&#34;非法&#34;并且语法上允许一个遵循C89的编译器可能只是编译它并且结果将是未定义的...但就编译器而言它是一个合法的程序。

这就是为什么即使使用&#34; -pedantic&#34;它可能不会被拒绝。未定义的行为意味着它所说的内容:它未定义会发生什么。请注意,编译器可能会完全优化未定义的行为(这是好的,因为它无论如何都是未定义的行为,因此编译器可以做任何想做的事情)。

答案 4 :(得分:0)

我认为在这种情况下,未定义的行为在最流行的编译器之间很常见。如果您反汇编foo函数,几乎每个版本的gcc都会找到这些说明:

    push    rbp
    mov     rbp, rsp
    nop
    pop     rbp
    ret

此代码无所事事,正是您想要的。但是,每当期望函数的结果时,它甚至不遵循调用约定规则。结果应该由eax/rax寄存器传递给调用者。在foo中,此寄存器甚至未被修改,这就是您获得未定义行为的原因。

省略回归就像是在说:“我不想遵循你给我的规则”。 clang生成类似的代码,我猜其他编译器(除非应用了更多选项或优化)以相同的方式推理。没有什么可以阻止你这样做,既不是编译器。未定义的行为是这种自由的代价。

编译器对编写的代码充满信心的证明是eax/rax寄存器实际上被认为是函数结果的存储。在您的情况下,eax之前从未在该代码中使用,并且其最后一个值为零。但试试这个:

int i = 12345;
int m = 12345;
m *= 3;
i = foo();
printf("i is: %d", i);

输出可能将是37035,即使这是m的值。