1

考虑以下 C 程序:

int main()
{
    waitpid(1337);
}

正如预期的那样,gcc 和 clang 都警告我关于 waitpid 的隐式声明,但生成的二进制文件可以正常工作。当ltrace-ing 它时,我看到了

waitpid(1337, 0x7fff86950ec8, -2037051688)

叫做。现在三个问题:

  1. 为什么上面的 C 代码甚至可以工作?我应该必须包括sys/wait.h才能调用waitpid。printf调用而不包括时也会发生同样的事情stdio.h。输出有效。
  2. 所以 gcc 仍然可以调用 waitpid,但为什么即使它有 3 个参数,也可以只用一个参数调用它?ltrace多次调用暗示其他两个参数是随机值?Gcc 在单参数 waitpid 和三参数 waitpid 之间进行了一些转换,但是如何实现呢?
  3. 如何使用 gdb 找出最后两个参数背后的值?其他两个值没有显示在源代码中,那么如何在 gcc 中访问它们?

我收集到的一些线索:我查看了 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)?

4

2 回答 2

3

为什么上面的 C 代码甚至可以工作?

它是偶然“起作用”的。C 允许您在不指定原型的情况下调用函数,即不包括其标题。当您这样做时,它会假设参数的数量和类型并尝试调用它。显然,使用错误数量的参数调用函数是未定义的行为。

6.5.2.2-3

如果表示被调用函数的表达式的类型不包含原型,则对每个参数执行整数提升,而浮点类型的参数将提升为双精度。这些称为默认参数提升。如果参数的数量不等于参数的数量,则行为未定义。

至于你的第三个问题,我怀疑你看到的垃圾只是堆栈上的剩菜。要确定这是否属实,请在 the 之前放置一个断点waitpid,然后进入它,看看传递了什么。

于 2013-02-25T08:07:31.020 回答
0

通过在 ac 程序中包含头文件,您基本上给出了函数的原型。如果没有给出,那么它假定参数的数量和参数的类型由 cnicutar 告知。如果未定义原型,它还假定函数的返回类型为 int。

于 2013-02-25T08:19:34.137 回答