我草拟的C99标准的第6.7.2.1段第14句有关共用体和指针的规定如下(重点始终加粗):
一个共用体的大小足以容纳其最大成员。最多只能在共用体对象中存储一个成员的值。合适转换的共用体对象指针指向其每个成员(如果成员是位域,则指向其所在单位),反之亦然。
这意味着做类似以下操作是合法的,可以将有符号或无符号int复制到共用体中,假设我们只想将它复制到相同类型的数据中:
union ints { int i; unsigned u; };
int i = 4;
union ints is = *(union ints *)&i;
int j = is.i; // legal
unsigned k = is.u; // not so much
7.15.1.1第2段有这样的描述:
va_arg宏扩展成一个表达式,该表达式具有指定类型和调用中下一个参数的值。参数ap必须已由va_start或va_copy宏(没有在同一ap上调用va_end宏)初始化。每次调用va_arg宏都会修改ap,以便按顺序返回连续参数的值。参数type应是指定的类型名称,以便可以通过将*后缀到type来获取指向具有指定类型的对象的指针的类型。如果没有实际的下一个参数,或者 type与实际下一个参数的类型(根据默认参数提升进行升级)不兼容,则行为是未定义的,但以下情况除外:
- 一个类型是有符号整数类型,另一个类型是相应的无符号整数类型,并且该值可以在两种类型中表示; - 一个类型是指向void的指针,另一个类型是指向字符类型的指针。
我不打算引用默认参数提升的部分。我的问题是:这是定义好的行为吗?
void func(int i, ...)
{
va_list arg;
va_start(arg, i);
union ints is = va_arg(arg, union ints);
va_end(arg);
}
int main(void)
{
func(0, 1);
return 0;
}
如果是这样的话,似乎可以通过一种相当困难且法律上无法做任何处理的方式来克服有符号/无符号整数转换的“值与两种类型兼容”的要求。如果不是这种情况,看起来在这种情况下只使用
unsigned
是安全的,但如果联合体中有更多元素具有不兼容的类型怎么办呢?如果我们可以保证不通过元素访问联合体(即我们只将其复制到另一个 union
或我们视为 union
的存储空间中),并且联合体的所有元素都具有相同的大小,那么varargs是否允许这样做?还是只允许使用指针?实际上,我认为这段代码几乎永远不会失败,但我想知道它是否被定义为行为。我目前的猜测是它似乎没有被定义,但这似乎非常愚蠢。
func(0, -1); func(0, UINT_MAX);
是否合法。func(0, 1)
可能仅仅因为 1 可以适应于int
和unsigned
而合法。 - Chris Lutzunsigned k = is.u;
在 C99 中是合法的。 - Dietrich Eppfunc(1, (union ints) { .i = 0 }); func(2, (union ints) { .u = UINT_MAX });
等等。或者使用联合变量。 - Jonathan Leffler