我正在处理一些遗留的 C 代码。原始代码是在 90 年代中期编写的,针对那个时代的 Solaris 和 Sun 的 C 编译器。当前版本在 GCC 4 下编译(尽管有很多警告),它似乎可以工作,但我正在努力整理它——我想尽可能多地排除潜在的错误,因为我确定可能需要什么使它适应 64 位平台,以及它所针对的编译器以外的编译器。
我在这方面的主要事件之一是确保所有函数都有完整的原型(prototype)(许多函数没有),在这种情况下,我发现了一些调用函数(以前没有原型(prototype))的代码,参数更少比函数定义声明的要多。函数实现确实使用了缺失参数的值。
例子:
实现.c:
int foo(int one, int two) {
if (two) {
return one;
} else {
return one + 1;
}
}
client1.c:
extern foo();
int bar() {
/* only one argument(!): */
return foo(42);
}
client2.c:
extern int foo();
int (*foop)() = foo;
int baz() {
/* calls the same function as does bar(), but with two arguments: */
return (*foop)(17, 23);
}
问题:是否定义了缺少参数的函数调用的结果?如果是这样,该函数将为未指定的参数接收什么值?否则,ca 的 Sun C 编译器会不会。 1996(对于 Solaris,而不是 VMS)已经展示了一种可预测的特定于实现的行为,我可以通过向受影响的调用添加特定的参数值来模拟这种行为?
最佳答案
编辑:我找到了一个堆栈线程 C function with no parameters behavior 给出了非常简洁、具体、准确的答案。 PMG 在答案末尾的评论是关于 UB 的。以下是我最初的想法,我认为它们是相同的,并解释了为什么行为是 UB..
Questions: is the result of a function call with missing arguments defined?
我会说不......原因是我认为该函数将像它具有第二个参数一样运行,但如下所述,第二个参数可能只是垃圾。
If so, what value will the function receive for the unspecified argument?
我认为收到的值是未定义的。这就是您可以拥有 UB 的原因。
我知道有两种通用的参数传递方式...(维基百科在 calling conventions 上有一个很好的页面)
- 通过注册。即,平台的 ABI(应用程序二进制接口(interface))会说例如寄存器 x 和 y 用于传递参数,以及以上通过堆栈传递的任何其他...
- 一切都通过堆栈传递...
因此,当你给一个模块一个函数的定义时,“...未指定(但不是可变的)参数数量......”(extern def),它不会放置与你给它一样多的参数(在这种情况下 1) 在实际函数将查找以获取参数值的寄存器或堆栈位置中。因此,遗漏的第二个参数的第二个区域基本上包含随机垃圾。
编辑:基于我发现的另一个堆栈线程,我将上面的内容修改为 extern 声明了一个没有参数的函数到声明了一个具有“未指定(但不是可变的)参数数量”的函数。
当程序跳转到函数时,该函数假设参数传递机制已被正确遵守,因此要么查看寄存器或堆栈并使用它找到的任何值...假设它们是正确的。
Otherwise, would the Sun C compiler of ca. 1996 (for Solaris, not VMS) have exhibited a >> predictable implementation-specific behavior
您必须检查您的编译器文档。我对此表示怀疑...外部定义是否会被完全信任,因此我怀疑寄存器或堆栈是否会根据参数传递机制正确初始化...
关于参数太少的 C 函数调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17604189/