小编典典

为什么在不返回值的情况下从非 void 函数的末尾流出不会产生编译器错误?

all

自从我多年前意识到,默认情况下这不会产生错误(至少在 GCC 中),我一直想知道为什么?

我知道您可以发出编译器标志来产生警告,但它不应该总是错误吗?为什么非 void 函数不返回值是有效的?

评论中要求的示例:

#include <stdio.h>
int stringSize()
{
}

int main()
{
    char cstring[5];
    printf( "the last char is: %c\n", cstring[stringSize()-1] ); 
    return 0;
}

…编译。


阅读 55

收藏
2022-08-19

共1个答案

小编典典

C99 和 C 标准要求非void函数返回值,除了main. 将定义缺少的 return 语句main(以 return 0)。在
C
中,如果执行实际上到达void除 之外的非函数的末尾,则它是未定义的行为main,而在 C 中,如果调用者 使用 返回值,则只有 UB。

这意味着函数看起来可能会在没有返回值的情况下到达结尾,但实际上无法到达结尾}。 John 的回答展示了一些示例,例如从if.
如果执行实际上没有到达return更早的时间,那么它只是未定义的行为。基本原理包括检查每个真实的代码路径是否返回一个值是非常困难的(不知道哪些函数永远不会返回),所以像你的例子那样
编译 一个函数并不违法,只是像你的那样实​​际调用它main

作为扩展,至少有一个编译器 (MSVC)允许使用 inline assembly设置返回值,但大多数其他编译器仍然需要在使用 inline 的函数中使用 return 语句asm

从函数的末尾流出 […] 会导致返回值的函数出现未定义的行为。

搂搂3.6.1/5

如果控制到达末尾main没有遇到return 语句,效果就是执行

return 0;

请注意,C++ 6.6.3/2 中描述的行为在 C 中是不同的。


如果你用 -Wreturn-type 选项调用它,gcc 会给你一个警告。

-Wreturn-type 每当使用默认为 int 的返回类型定义函数时发出警告。还要警告在返回类型不是 void
的函数中任何没有返回值的返回语句(从函数体的末尾落下被认为返回没有值),以及在函数中带有表达式的返回语句返回类型是无效的。

此警告由 -Wall 启用。


出于好奇,看看这段代码的作用:

#include <iostream>

int foo() {
   int a = 5;
   int b = a + 1;
}

int main() { std::cout << foo() << std::endl; } // may print 6

此代码具有形式上未定义的行为,实际上它依赖于调用约定体系结构。eax在具有特定编译器的特定系统上,如果禁用优化,则返回值是最后一次表达式评估的结果,存储在该系统处理器的寄存器中。

这似乎是 GCC
内部禁用优化的结果,因为在这种情况下,如果它需要任何返回值寄存器来实现语句,它就会选择返回值寄存器。在 C++
模式下启用优化后,GCC 和 clang 假定此执行路径不可访问,因为它包含未定义的行为。它们甚至不发出ret指令,因此执行落入 .text
部分中的下一个函数。当然,未定义的行为意味着任何事情都可能发生。

2022-08-19