微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

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

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 会给你一个警告。

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

此警告由 启用。


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

#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 部分中的下一个函数。当然,未定义的行为意味着任何事情都可能发生。

解决方法

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

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

评论中要求的示例:

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

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

…编译。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。