Chr*_*utz 8 c variadic-functions unions language-lawyer
6.7.2.1我的C99标准草案第14段有关于工会和指针的说法(强调,一如既往地增加):
联合的大小足以容纳其中最大的成员.最多一个成员的值可以随时存储在union对象中.指向适当转换的联合对象的指针指向其每个成员(或者如果成员是位字段,则指向它所在的单元),反之亦然.
一切都很好,这意味着执行类似下面的操作将有符号或无符号的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
Run Code Online (Sandbox Code Playgroud)
7.15.1.1第2段有这样的说法:
的
va_arg
宏扩展到具有SPECI音响版类型和在呼叫中的下一个参数的值的表达式.该参数ap
应该由va_start
或va_copy
宏初始化 (没有va_end
为sameap 中间调用宏).每次调用va_arg
宏都会修改,ap
以便依次返回连续参数的值.参数type
应该是一个特定的类型名称,这样只需通过后缀a*
来获得指向具有指定类型的对象的指针类型type
.如果没有实际的下一个参数,或者type与实际的下一个参数的类型不兼容(根据默认参数提升而提升),则行为是未定义的,除了以下情况:-one type是有符号整数类型,另一种类型是相应的无符号整数类型,并且该值可在两种类型中表示;
-one类型是指向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;
}
Run Code Online (Sandbox Code Playgroud)
如果是这样,它似乎是一个巧妙的技巧来克服"并且值与两种类型兼容"的有符号/无符号整数转换要求(虽然以某种方式很难合法地做任何事情).如果没有,unsigned
在这种情况下使用它似乎是安全的,但是如果有union
更多不兼容的类型中有更多的元素呢?如果我们可以保证我们不会按元素访问union(即我们只是将它复制到union
我们正在处理的另一个或存储空间中union
)并且union的所有元素都是相同的大小,这是否允许使用varargs ?或者只允许使用指针?
在实践中,我希望这段代码几乎不会失败,但我想知道它是否定义了行为.我目前的猜测似乎是没有定义,但这看起来非常愚蠢.
我不明白为什么你认为代码在实践中永远不应该失败。在任何整数类型通过寄存器传递但聚合类型(即使很小)在堆栈上传递的实现上都会失败,并且我在标准中没有看到任何禁止此类实现的内容。包含 an 的联合int
不是与 兼容的类型int
,即使它们的大小相同。
回到你的第一个代码片段,它也有一个问题:
union ints is = *(union ints *)&i;
Run Code Online (Sandbox Code Playgroud)
这是一种别名冲突并会调用未定义的行为。你可以通过使用来避免它memcpy
,我想那么它就是合法的..
我对你在这里的评论也有点困惑:
unsigned k = is.u; // not so much
Run Code Online (Sandbox Code Playgroud)
由于值 4 以有符号和无符号类型表示,因此这应该是合法的,除非作为特殊情况明确禁止。
如果这不能回答您的问题,也许您可以详细说明您要解决的问题(尽管是理论上的)。