我们可以将va_arg用于工会吗?

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_startva_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 ?或者只允许使用指针?

在实践中,我希望这段代码几乎不会失败,但我想知道它是否定义了行为.我目前的猜测似乎是没有定义,但这看起来非常愚蠢.

R..*_*R.. 0

我不明白为什么你认为代码在实践中永远不应该失败。在任何整数类型通过寄存器传递但聚合类型(即使很小)在堆栈上传递的实现上都会失败,并且我在标准中没有看到任何禁止此类实现的内容。包含 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 以有符号和无符号类型表示,因此这应该是合法的,除非作为特殊情况明确禁止。

如果这不能回答您的问题,也许您可​​以详细说明您要解决的问题(尽管是理论上的)。