当一个具有警告“控制到达非空函数结束”的函数被调用时,实际发生了什么?

我知道这个消息意味着什么,我只是想知道为什么它不是错误信息,而只是一个警告?

在这种情况下会发生什么? 例如,假设我有一个function

int f() { } 

当我打电话时会发生什么?
在这种情况下,编译器是否添加“非初始化” int返回?
或者丢失的返回可能导致堆栈损坏?
或者(绝对) 未定义的行为

用gcc 4.1.2和4.4.3测试


编辑:阅读答案我理解一件事,阅读评论 – 另一个..

好吧,让我们总结一下:它是未定义的行为 。 那么,这意味着可能导致堆栈损坏,对吧? (甚至意味着,我的电脑可能会开始通过麦克风插孔把腐烂的西红柿扔到我身上,尖叫 – “你做了什么?”)。

但如果是这样,那么为什么这里的最佳答案说,堆栈损坏不会发生,同时,行为是未定义的?

并且未定义? 调用者,尝试使用“未返回的值”,或只是函数的结尾是未定义的,如果它必须返回值,但它不是?

或者它不是未定义的行为,只是试图使用该值的用户(未返回,但是哦!)将“接收”未定义的 ? 换句话说 – 只是一些垃圾价值,没有更多的事情可以发生?

答:不,缺少的回报不会导致堆栈损坏

答:是的,如果调用者试图读取和/或使用(未定义的!)返回值,则行为将是“未定义的”。

PS:

这是C ++的引用:

C ++03§6.6.3/ 2:

流出函数末尾相当于没有值的返回; 这会导致值返回函数中的未定义行为。

你问过C和C ++。 这两种语言的规则不同。

在C中,仅当调用者尝试使用函数返回的值时,行为才是未定义的。 如果你有:

 int func(void) { /* no return statement */ } ... func(); 

然后行为很明确。

在C ++中,行为是未定义的(如果函数完全被调用),调用者是否尝试使用结果。 (这是出于历史原因;前ANSI C没有void关键字,并且通常定义(隐式)返回int函数不返回int 。)

John Bode的回答已经引用了2011年ISO C标准的N1570草案 ,6.9.1p12:

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

paulsm4引用了C ++标准; 引用最新的2011版本,6.6.3p2:

流出函数末尾相当于没有值的return ; 这会导致值返回函数中的未定义行为。

导致C允许值返回函数无法返回值的历史原因(只要调用者未使用该值)不适用于C ++,其设计不受需要避免的强烈影响打破旧的(ANSI C之前的)代码。

在C(从C99开始)和C ++中, mainfunction是特殊情况; 在不执行返回的情况下到达main的closing }相当于return 0; 。 (C允许main返回除int之外的实现定义类型;在那种(罕见)情况下,从结尾处掉落会向主机环境返回未指定的终止状态。)

当然,从值返回函数中省略return语句,或者有一条未达到return语句的可能执行路径,这是一个坏主意。 仅在使用int作为void的替身的古代遗留C代码中才有意义,并且在main (尽管即使对于main ,我个人也希望有一个显式的return 0; )。

该标准认为它未定义。

实际上,将读取为返回值保留的存储器或寄存器。 无论那里有什么东西。

C 2011年草案N1570

6.9.1函数定义


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

“未定义”仅仅意味着语言标准不要求编译器以任何特定方式处理这种情况; 任何行动都被认为是“正确的”。 编译器可以自由发出诊断和暂停转换,或发出诊断和完整翻译(这是您所看到的),或者完全忽略该问题。

至于实际的运行时行为,这取决于以下内容:

  • 调用者如何使用返回值?
  • 使用的调用约定是什么?
  • 底层架构如何表现?

等等

编译器不需要诊断它,因为在某些情况下它很难 。 因此规则是行为未定义。

我在Linux 64位上执行了一个简单的测试,GCC 4.63让我们在实践中看到GCC如何组装这样的东西..

我创建了一个简单的例子

这是test.c,具有正常的返回值

 int main() { return 0; } 

这是test.c的GCC汇编程序输出:

 main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl $0, %eax popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc 

这是test2.c,没有返回值

 int main() { } 

这是test2.c的GCC汇编程序输出:

 main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc 

基本上我们可以看到缺少以下行。

  movl $0, %eax 

该行将值移动到eax寄存器,该寄存器是函数的返回值。 如果在现实生活中使用该函数,那么可能包含垃圾值的eax寄存器将代表main()的返回值…

我用g ++做了测试。 看起来你得到一个随机内容的对象,即它不会调用任何构造函数。 当我只处理整数时,这意味着我得到一个随机数。 当我处理字符串时,我会遇到分段违规。 我不知道这是不是你的腐败意味着什么,但这很糟糕。

至于你的第一个问题,我会更进一步。 我想知道为什么这是一个默认禁用的警告! 这对我来说似乎非常重要。

 #include  #include  class X { private: int _x; public: X(int x) : _x(x) { } // No default construcor! int get() const { return _x; } }; X test1(int x) { if (x > 0) return X(x); // warning: control reaches end of non-void function } class Y { private: std::string _y; public: Y(std::string y) : _y(y) { } // No default construcor! std::string get() const { return _y; } }; Y test2(std::string y) { if (y.length() > 3) return Y(y); // warning: control reaches end of non-void function } int main(int, char**) { std::cout<<"4 -> "< "< "< "<