GCC为什么以及如何编译一个缺lessreturn语句的函数?

#include <stdio.h> char toUpper(char); int main(void) { char ch, ch2; printf("lowercase input : "); ch = getchar(); ch2 = toUpper(ch); printf("%c ==> %c\n", ch, ch2); return 0; } char toUpper(char c) { if(c>='a'&&c<='z') c = c - 32; } 

在toUpper函数中,返回types是char,但toUpper()中没有“return”。 并用gcc(GCC)4.5.1 20100924(Red Hat 4.5.1-4),fedora-14编译源代码。

当然,发出警告:“警告:控制达到非无效function结束”,但运行良好。

在使用gcc进行编译时,代码中发生了什么? 我想在这种情况下得到一个坚实的答案。 谢谢 :)

Solutions Collecting From Web of "GCC为什么以及如何编译一个缺lessreturn语句的函数?"

发生了什么事情是,当C程序被编译成汇编语言,你的toUpper函数结束了,也许:

 _toUpper: LFB4: pushq %rbp LCFI3: movq %rsp, %rbp LCFI4: movb %dil, -4(%rbp) cmpb $96, -4(%rbp) jle L8 cmpb $122, -4(%rbp) jg L8 movzbl -4(%rbp), %eax subl $32, %eax movb %al, -4(%rbp) L8: leave ret 

在%eax寄存器中进行32的减法运算。 而在x86调用约定中,这是返回值预期的寄存器! 所以…你很幸运

但请注意警告。 他们在那里是有原因的!

它取决于应用程序的二进制接口和哪些寄存器用于计算。

例如,在x86上,第一个函数参数和返回值存储在EAX ,因此gcc最有可能使用它来存储计算结果。

本质上, c被推入到应该被返回值填充的位置。 因为它不会被使用return覆盖,所以它返回的值就结束了。

注意,依赖于这个(在C语言中,或者其他语言中,这不是一个明确的语言特性,比如Perl)是一个坏主意。 在极端。

一个重要的理解是缺少一个返回语句,这是很少有可诊断的错误。 考虑这个功能:

 int f(int x) { if (x!=42) return x*x; } 

只要你从来没有用42的参数调用它,包含这个函数的程序是完全有效的C,并且不会调用任何未定义的行为,尽管如果你调用f(42)并且随后试图使用UB返回值。

因此,尽管编译器可能提供缺少返回语句的警告启发式方法,但不可能在没有误报或漏报的情况下这样做。 这是无法解决暂停问题的结果。

我不能告诉你具体的平台,因为我不知道它,但是对于你所看到的行为有一个普遍的答案。

当有一个返回的函数被编译时,编译器将使用一个关于如何返回这个数据的约定。 它可能是一个机器寄存器,或者是一个定义好的内存位置,例如通过堆栈或其他(尽管通常使用机器寄存器)。 编译后的代码也可以在执行该功能的同时使用该位置(注册或以其他方式)。

如果函数没有返回任何东西,那么编译器将不会生成显式地使用返回值填充该位置的代码。 然而就像我上面说过的,它可能会在该函数中使用该位置。 当您编写读取返回值(ch2 = toUpper(ch);)的代码时,编译器将编写使用其约定的代码,以检索从传统位置返回的代码。 就调用者代码而言,即使没有明确写入,也只是从该位置读取该值。 因此你得到一个价值。

现在看一下@ Ray的例子,编译器使用EAX寄存器来存储上壳操作的结果。 它恰好如此,这可能是返回值写入的位置。 在调用端,ch2加载了EAX中的值 – 因此是一个幻像返回。 这只适用于x86系列处理器,因为在其他架构上,编译器可以使用完全不同的方案来决定应该如何组织

不过好的编译器会根据当地的条件,代码,规则和启发式知识来尝试优化。 所以重要的一点是,这只是运气,它的工作原理。 编译器可以优化,而不是这样或那样做 – 你不应该回应行为。

没有局部变量,所以在函数结尾的堆栈顶部的值将是参数c。 退出时堆栈顶部的值是返回值。 所以无论持有多少,这就是回报价值。

您应该记住,这样的代码可能会崩溃取决于编译器。 例如,clang在这个函数的末尾生成ud2指令,你的应用程序将在运行时崩溃。

我试过了一个小程序:

 #include <stdio.h> int f1() { } int main() { printf("TEST: <%d>\n", f1()); printf("TEST: <%d>\n", f1()); printf("TEST: <%d>\n", f1()); printf("TEST: <%d>\n", f1()); printf("TEST: <%d>\n", f1()); } 

结果:

测试:<1>

测试:<10>

测试:<11>

测试:<11>

测试:<11>

我已经使用了mingw32-gcc编译器,所以可能会有差异。

你可以玩耍,尝试一个字符函数。 只要你不使用结果值,它将仍然工作正常。

 #include <stdio.h> char f1() { } int main() { f1(); } 

但我仍然会建议设置void函数或给一些返回值。

你的功能似乎需要一个回报:

 char toUpper(char c) { if(c>='a'&&c<='z') c = c - 32; return c; }