【发布时间】:2011-09-14 04:25:56
【问题描述】:
我的 C99 标准草案的 6.7.2.1 第 14 段对联合和指针有这样的说法(强调,一如既往,添加):
联合的规模足以容纳其最大的成员。的值 大多数成员可以随时存储在联合对象中。 指向 a 的指针 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
7.15.1.1 第 2 段有这样的说法:
va_arg宏扩展为具有指定类型和值的表达式 调用中的下一个参数。参数ap应已由va_start或va_copy宏(没有对相同ap 的va_end宏的介入调用)。每次调用va_arg宏都会修改ap,以便依次返回连续参数的值。参数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 或我们将其视为union 的存储空间)并且联合的所有元素的大小相同,这是否允许使用可变参数?还是只允许使用指针?
在实践中,我希望这段代码几乎永远不会失败,但我想知道它是否是已定义的行为。我目前的猜测是它似乎没有被定义,但这似乎非常愚蠢。
【问题讨论】:
-
现在想想,我想我更感兴趣的是
func(0, -1); func(0, UINT_MAX);是否合法。func(0, 1)可能是合法的,仅仅是因为 1 适合int和unsigned。 -
unsigned k = is.u;在 C99 中是合法的。 -
@Dietrich - 由于已签名/未签名的事情是否存在异常?
-
这些调用都没有通过联合。您可能需要一个复合文字:
func(1, (union ints) { .i = 0 }); func(2, (union ints) { .u = UINT_MAX });等。或者联合变量。 -
@Chris Lutz:正如 Pascal Cuoq 所指出的,在 C99 TC3 中通过联合进行任意类型双关语是合法的。结果值未定义。
标签: c variadic-functions language-lawyer unions