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

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

#include <stdio.h> char toupper(char); int main(void) { char ch,ch2; printf("lowercase input : "); ch = getchar(); ch2 = toupper(ch); printf("%c ==> %cn",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进行编译时,代码中发生了什么? 我想在这种情况下得到一个坚实的答案。 谢谢 :)

__declspec(dllimport / dllexport)和inheritance

如何切换/切换Windows任务栏从“显示”到“自动隐藏”(反之亦然)?

如何:检查不可用networking共享时防止超时 – C#

在linux下编译mingw,如何得到毫秒级的分辨率

包含Linux GCC链接

如何删除input的字母/string或任何input

Home-Brew服务器场和远程debugging

应用程序可以依赖于两个不同版本的libstdc ++吗?

有没有比parsing/ proc / self / maps找出内存保护更好的方法

我的服务器退出代码137

发生了什么事情是,当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()); }

结果:

测试:<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; }

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

相关推荐