据我所知,当将值传递给包含有C风格省略号的函数参数时,这些值可能会被提升。
signed
/unsigned
char
和short
分别晋升为signed
/unsigned
int
float
升级为double
维基百科的可变参数页面也符合以下条件:
bool
、char
、short
和无范围枚举转换为int
或更宽的整数类型,如整数提升
(强调矿(
我不太清楚或更宽的整数类型的实际含义。如果long
的长度大于int
,那么它就等于long
,或者这只是说明这是编译器上实现定义的行为?
没有说明的是其他更大的类型会发生什么,例如long long
和long double
,如果它比编译器上的int
长,可能还会发生long
。我假设它们是按原样通过的,这是有道理的,但对此事没有明确的声明。
假设较大的类型按原样传递,那么对于较大的类型,大小规范是有意义的。但是,较小的类型呢?维基百科的长度字段页面声明:
hh-对于整数类型,导致printf期望一个
int
大小的整数参数,该参数是从char
升级而来的。h-对于整数类型,导致printf期望从
short
升级而来的int
大小的整数参数。
我认为整数类型应该替换为积分类型TBH,但忽略了这一点,如果编译器已经进行了任何适用的升级,并且函数已经丢失了对这些类型的跟踪,那么从XXX升级的预期int
侧整数参数实际上意味着可能升级的int
侧整数参数,不管它是从什么升级的,并且它会将它截断为指定的大小。
这是什么意思吗?
尽管具体得令人尴尬,但在每个有用的执行模型中,它们都是可以推送到堆栈上的最小大小的项。在每个重要的情况下,它都对应于机器的本地字大小:32位CPU:32位堆栈元素;64位CPU:64位堆栈元素。
可变C函数自变量由单词大小和对齐的对象组成:
- 较小的项目会扩展到文字大小
- 较大的项目与文字大小对齐
所以,如果我调用了一个带参数的变元函数:
(short a, char b, long c, struct { int a; long b; } d)
在单词大小与sizeof(int(相同的机器上它将被传递为:
(int a, int b, struct { int lo,hi; } c, union { struct { int a, long b }; int x[3] } d)
和一台单词大小与长度相同的机器:
(long a, long b, long c, union { struct { int a; long b; }; long x[2]; } d)