请考虑:
#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 函数中,返回类型为 char,但在 toUpper() 中没有任何“return”,并使用gcc(GCC)4.5.1 20100924(Red Hat 4.5.1-4),Fedora 14编译源代码。
当然,警告是发出了:“警告:控件到达非void函数的末尾”,但是,运行良好。
在用gcc编译的过程中,代码中发生了什么?
8条答案
按热度按时间ylamdve61#
当C程序被编译成汇编语言时,你的toUpper函数的结果可能是这样的:
32的减法是在%eax寄存器中执行的。而在x86调用约定中,这就是返回值应该在的寄存器!所以...你很幸运。
但请**注意警告。**他们在那里是有原因的!
lp0sw83n2#
它取决于Application Binary Interface以及用于计算的寄存器。
例如,在x86上,第一个函数参数和返回值存储在
EAX
中,因此gcc很可能也使用它来存储计算结果。dz6r00yl3#
本质上,
c
被推入稍后应该用返回值填充的位置;因为它不会被return
覆盖,所以它将作为返回值结束。请注意,依赖于此(在C语言或任何其他语言中,这不是一个显式的语言特性,如Perl)是一个坏主意™。
r1zhe5dt4#
有一点需要了解,那就是省略return语句很少是一个可诊断的错误。
只要你不使用参数42调用它,那么包含这个函数的程序就是完全有效的C,不会调用任何未定义的行为,尽管如果你调用
f(42)
并随后试图使用返回值,它 * 会 * 调用UB。因此,虽然编译器可以为缺少的返回语句提供警告启发式方法,但在没有误报或漏报的情况下是不可能做到的。这是不可能解决停机问题的结果。
dauxcl2d5#
我不能告诉你你的平台的细节,因为我不知道它,但有一个一般的答案,你看到的行为。
当某个函数被编译时,编译器会使用一个约定来返回该数据。它可以是一个机器寄存器,或者是一个定义的内存位置,比如通过堆栈或其他方式(尽管通常使用机器寄存器)。编译后的代码在执行函数的工作时也会使用该位置(寄存器或其他)。
如果函数没有返回任何值,那么编译器就不会生成用返回值显式填充该位置的代码。但是,就像我前面所说的,它可能会在执行函数时使用该位置。当您编写读取返回值
(ch2 = toUpper(ch);)
的代码时,编译器将编写使用其关于如何从常规位置检索返回值约定的代码。就调用程序代码而言,它只会从该位置读取该值,即使没有显式写入任何内容,也会得到一个值。现在看看Ray的例子。编译器使用EAX寄存器来存储大写操作的结果。碰巧的是,这可能是返回值被写入的位置。在调用端,ch 2被加载了EAX中的值--因此是一个幻像返回。这只适用于x86范围的处理器。如在其它体系结构上一样,编译器可以使用完全不同的方案来决定应当如何组织约定。
然而,好的编译器会根据一组局部条件、代码知识、规则和启发式来进行优化。因此,需要注意的一点是,这只是运气的作用。编译器可以进行优化,但不做这件事或其他任何事情--你不应该回答这种行为。
gv8xihay6#
您应该记住,这类代码可能会崩溃,具体取决于编译器。例如,Clang在此类函数的末尾生成一条 ud2 指令,您的应用将在运行时崩溃。
anhgbhbe7#
这里没有局部变量,所以函数结束时栈顶的值是参数c,函数退出时栈顶的值是返回值,所以不管c是什么,都是返回值.
lndjwyie8#
我试过一个小程序:
结果:
测试:<1>
测试:<10>
测试:<11>
测试:<11>
测试:<11>
我使用的是MinGW-GCC编译器,因此可能会有差异。
你可以试一下,比如说,一个char函数,只要你不使用结果值,它仍然可以正常工作。
但是我仍然建议设置void函数或者给予一些返回值。
您的函数似乎需要返回: