未返回递归调用结果的recfind函数为何能返回正确值?
为什么这个没返回递归结果的C函数能输出正确值?
这是个典型的C语言未定义行为却“碰巧”工作的案例,咱们来拆解背后的细节:
核心问题:函数存在未定义行为
先看代码里的recfind函数:在最后一个分支(执行递归调用的地方),没有写return语句。根据C语言标准,当一个非void类型的函数执行到函数末尾却没有返回值时,属于未定义行为——也就是说,C标准完全不规定程序会出现什么结果,一切都依赖编译器的具体实现,换个环境就可能彻底失效。
GCC 7.3.1下“碰巧正确”的原因
在GCC(以及大多数遵循x86/x86-64调用约定的编译器)中,函数的返回值是通过寄存器传递的:
- 对于
int类型的返回值,会使用eax(32位环境)或rax(64位环境)寄存器。
咱们看递归调用的执行流程:
- 当执行
recfind(value, ++index)时,这个递归调用的函数会把自己的返回值写入eax寄存器。 - 当前的上层函数执行完这个递归调用后,没有任何代码去修改
eax寄存器,直接就结束了。 - 由于C语言对于未定义行为的宽松性,GCC在这里没有额外处理,直接让上层函数把
eax寄存器里残留的值(也就是递归调用的返回值)当成了自己的返回值,传递给更上层的调用者。
具体场景验证
- 找6的情况:递归到
index=3时,触发arr[index]==value,返回1,这个1被写入eax。之后所有上层的递归调用都没有修改eax,最终main拿到的就是这个1。 - 找9的情况:递归到
index=6时,触发index >= arraySize,返回0,这个0被写入eax。同样,上层调用没有修改寄存器,最终main拿到0。
绝对不要这么写!
这完全是依赖编译器实现的巧合,风险极大:
- 如果在递归调用之后添加任何可能修改
eax的代码(比如一个简单的printf),返回值就会出错。 - 换用其他编译器(比如Clang)、开启更高优化等级(比如
-O2),或者换不同的硬件架构,结果可能变成随机值、程序崩溃,或者完全不符合预期的输出。
正确的写法应该把递归调用的结果返回:
return recfind(value, ++index);
内容的提问来源于stack exchange,提问作者gandalf3
相关产品推荐
相关产品推荐

