考虑以下C程序:
int main()
{
waitpid(1337);
}
正如预期的那样,gcc和clang都警告我有关waitpid的隐式声明但是生成的二进制文件工作正常。当我ltrace
时,我看到了
waitpid(1337, 0x7fff86950ec8, -2037051688)
被调用。现在有三个问题:
sys/wait.h
以便能够调用waitpid。调用printf
而不包括stdio.h
时会发生同样的事情。输出有效。ltrace
暗示其他两个参数是随机值? Gcc在单参数waitpid和三参数waitpid之间做了一些翻译,但是怎么做?我收集了一些线索:我查看了main函数的汇编输出:
0x000000000040050c <+0>: push %rbp
0x000000000040050d <+1>: mov %rsp,%rbp
0x0000000000400510 <+4>: mov $0x539,%edi
0x0000000000400515 <+9>: mov $0x0,%eax
0x000000000040051a <+14>: callq 0x4003f0 <waitpid@plt>
0x000000000040051f <+19>: pop %rbp
0x0000000000400520 <+20>: retq
所以我猜这不是gcc做的事情,因为确实有一个waitpid函数只需要一个参数(1337)?
答案 0 :(得分:3)
为什么上面的C代码甚至可以工作?
它“偶然”起作用。 C允许您在不指定原型的情况下调用函数,即不包括其标题。当你这样做时,对参数的数量和类型做出假设并试图调用它。显然,使用错误数量的参数调用函数是未定义的行为。
6.5.2.2-3
如果表示被调用函数的表达式具有类型 不包括原型,执行整数促销 每个参数和类型为float的参数都被提升为 双。这些被称为默认参数促销。 如果 参数个数不等于参数个数, 行为未定义。
至于你的第三个问题,我怀疑你看到的垃圾只是堆栈上的残羹剩饭。要查明是否属实,请在waitpid
之前设置一个断点,然后单步执行它,看看会传递什么。
答案 1 :(得分:0)
通过在c程序中包含头文件,您基本上可以提供该函数的原型。如果没有给出,那么它假设cnicutar告诉的参数数量和参数类型。如果未定义原型,它还假定函数的返回类型为int。