x64平台GCC对可变参数函数类型与大小的处理机制问询
在x64架构GCC中处理可变参数函数的类型与大小机制
你观察到的这个现象,其实是x64平台的标准调用约定(System V AMD64 ABI)加上C语言本身的默认参数提升规则共同作用的结果,咱们来具体拆解:
1. x64 System V调用约定的参数传递规则
在Linux x64平台遵循的System V AMD64调用约定里,所有整数类、指针类的参数,只要大小不超过64位,都会被以64位值的形式传递——不管你在调用时传入的是int、short还是long。
比如你代码里的f(1,2,3,...),这些1、2都是32位的int字面量,但在调用函数时,会被自动零扩展成64位的long值,要么放到对应的通用寄存器里,要么(当寄存器不够用时)压到栈上。这是x64调用约定强制要求的,和你有没有显式转成long无关。
2. C语言的默认参数提升规则
除了调用约定的作用,C语言本身对可变参数函数的实参有默认提升规则:
- 小于int的整数类型(char、short等)会被提升为int;
- float类型会被提升为double;
而对于int类型,在x64平台下,虽然int是32位,但调用约定会进一步把它扩展成64位,所以当你用va_arg(ap, long)去读取参数时,刚好和传递的64位值大小匹配,自然能正确拿到数据。
3. 结合你的测试代码看为什么没问题
你的函数f用va_arg(ap, long)来读取可变参数,而调用时传入的所有整数(包括1到18,还有最后的0)都被调用约定自动扩展成了64位,和你读取的类型大小完全一致,所以不会出现错位或者值截断的问题。哪怕是1L<<63这个明确的64位long值,传递和读取的过程也完全匹配。
4. 要警惕的例外情况
虽然这个例子里一切正常,但不是所有场景都这么省心:
- 如果你传递的是浮点类型,比如传
float,会被提升为double传递,这时候如果用va_arg(ap, float)去读,就会因为大小不匹配导致错误; - 对于自定义结构体、枚举这类非基础类型,可变参数传递时不会有任何自动提升,必须保证
va_arg的类型和传递的类型完全一致,否则会破坏栈/寄存器的读取位置,导致程序崩溃或者数据错误;
内容的提问来源于stack exchange,提问作者effbiae
相关产品推荐
相关产品推荐

