简体   繁体   English

gcc (x64) 如何处理可变函数中的类型/大小?

[英]How does gcc (x64) deal with types/sizes in variadic functions?

A variadic function and main()可变参数 function 和 main()

#include <stdio.h>
#include <stdarg.h>
int f(long x,...)
{ va_list ap;
  int i=0;
  va_start(ap,x);
  while(x)
  { i++;
    printf("%ld ", x);
    x=va_arg(ap,long);
  }
  va_end(ap);
  printf("\n");
  return i;
}

int main()
{ return f(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,1L<<63,0);
}

On gcc, linux and x64: even though f()'s arguments are not cast to a 64bit long, gcc seems to get it right.在 gcc、linux 和 x64 上:即使 f() 的 arguments 没有转换为 64 位长,gcc 似乎也正确。

$ gcc t.c && ./a.out
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 -9223372036854775808 

How?如何?

在Linux x64上,可变参数的参数被“提升”为64位值,因此无需在此平台上显式转换为64位值。

Three things are happening here:这里发生了三件事:

1. Default argument promotion 1. 默认参数提升

The C standard defines something called “ default argument promotion ” for variadic arguments. That means : C 标准为可变参数 arguments 定义了称为“ 默认参数提升”的东西。这 意味着

  • If the actual argument is of type float , it is promoted to type double prior to the function call.如果实际参数是float类型,它会在 function 调用之前提升为double类型。
  • Any signed char or unsigned char , signed short or unsigned short , enumerated type, or bit field is converted to either a signed int or an unsigned int using integral promotion.任何signed charunsigned charsigned shortunsigned short 、枚举类型或位字段都使用整数提升转换为 signed int 或 unsigned int。

But beware: This is int , which is 32bit on x64 linux. There is more happening:但要注意:这是int ,它在 x64 linux 上是 32 位的。还有更多的事情发生:

2. The System V x64 calling convention 2. System V x64调用约定

The SysV AMD64 ABI defines how the arguments are then passed to the function in section 3.2.3. SysV AMD64 ABI在第 3.2.3 节中定义了 arguments 如何传递给 function。 What matters here is: The first 6 integers are passed in registers, the remaining ones are pushed to the stack.这里重要的是:前 6 个整数在寄存器中传递,其余的被压入堆栈。 Also: “The size of each argument gets rounded up to eightbytes.另外:“每个参数的大小四舍五入到八字节。 [...] Therefore the stack will always be eightbyte aligned”. [...] 因此堆栈将始终是八字节对齐的”。 Note that floats are passed in the floating point registers, not the normal ones.请注意,浮点数是在浮点寄存器中传递的,而不是普通寄存器。

3. The va_... macros 3. va_... 宏

Functions that contain va_start get a special prologue that also pushes all the argument registers to the stack.包含va_start的函数有一个特殊的序言,该序言还将所有参数寄存器压入堆栈。 How this is done is not part of the calling convention.这是如何完成的不是调用约定的一部分。 Since the compiler doesn't know the actual argument sizes at this point, you can expect it to push the whole registers making these values also 64bit aligned.由于此时编译器不知道实际参数大小,您可以预期它会压入整个寄存器,使这些值也是 64 位对齐的。 va_arg then first iterates through these, then the rest of the arguments which were passed on the stack. va_arg然后首先遍历这些,然后是传递到堆栈的 arguments 中的 rest。

What this means for your code example这对您的代码示例意味着什么

All of this means that all the arguments are 64bit aligned.所有这些意味着所有 arguments 都是 64 位对齐的。 But they are not actually 64bit wide.但它们实际上并不是 64 位宽的。 All integers are at least 32bit wide ( _Bool / bool is 8bit wide), but nothing more.所有整数至少为 32 位宽( _Bool / bool为 8 位宽),仅此而已。 The value of the unused bits is unspecified.未使用的位的值未指定。 The caller is free to leave these bits uninitialized.调用者可以自由地保留这些位未初始化。 Thus:因此:

  • The code is broken for negative values ( godbold )代码因负值而损坏( godbold
  • The spec doesn't guarantee that garbage data won't just pop up in the higher bits at some point in the future.该规范不保证垃圾数据不会在将来的某个时候以更高的位弹出。 But this seems rather unlikely since a lot of code probably depends on this behavior (see comments).但这似乎不太可能,因为很多代码可能都依赖于这种行为(见评论)。

The essential bit of code which makes it work is 使之起作用的基本代码是

x = va_arg(ap, long);

You could shoot yourself in the foot quite well by changing it to any other type. 通过将其更改为任何其他类型,您可以很好地射击自己的脚。

char ch = va_arg(ap, char);

Depending on the rules of the target architecture, this might increment ap by one, two, four, or eight after each access. 根据目标体系结构的规则,这可能在每次访问后将ap增加ap或8。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM