当 va_arg 接收到指针参数时,const mismatch 是否调用 UB?

Does const mismatch invoke UB when pointer argument is received by va_arg?

我注意到 va_arg 宏存在一些潜在问题,该宏用于从可变参数函数接收未命名参数。考虑以下简化示例:

#include <stdio.h>
#include <stdarg.h>

void foo(int n, ...)
{
    va_list ap;  
    const char *s;

    va_start(ap, n);
    for (s = va_arg(ap, const char *); *s != '[=11=]'; s++)
        putchar(*s);
    va_end(ap);
}

int main(void)
{
    char str[] = "xyz";
    foo(1, str);
    return 0;
}

va_arg 宏的参考指出(强调我的):

If va_arg is called when there are no more arguments in ap, or if the type of the next argument in ap (after promotions) is not compatible with T, the behavior is undefined (...)

我的理解是 const char *char * 类型 兼容。当 char 数组以 char 指针的形式传递给 foo 时(默认参数提升未更改),则表达式假定为 const 限定指针:

s = va_arg(ap, const char *)

可以调用 "technical" UB。同样的情况会出现,当 arr 被定义为 const 数组,并且参数被接收为 char *,以及其他类型,例如int *const int *.

n1570/6.2.5p28 似乎表明这在实践中应该没问题:

...Similarly, pointers to qualified or unqualified versions of compatible types shall have the same representation and alignment requirements...

由于 char 与自身兼容,指向 char 的指针与指向 const char 的指针具有相同的表示形式。由于可变参数函数本质上假定了每个后续参数的表示,因此这首先看起来定义明确。

反之,如果您使用指向 char 的指针来修改 const char,那确实是 UB n1570/6.7.3p6:

If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined.


要从您在评论中链接到的答案继续分析,n1570/6.7.6.1p2

For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to compatible types.

由于这两种指针类型本身都是非 const,因此它们具有相同的限定条件,剩下的唯一问题是 const char 是否与 char 兼容以达到我们的目的。

而且看起来它们并不像 nickie 指出的那样。所以一方面,这可以解释为UB。

C11 language standard 的第 6.2.7 节定义了 类型兼容性 ,第 6.7.6.1 节进一步指定了指针声明符,第 6.7.3 节进一步指定了类型限定符。后者(约束 10)表示:

For two qualified types to be compatible, both shall have the identically qualified version of a compatible type; the order of type qualifiers within a list of specifiers or qualifiers does not affect the specified type.

此外,第 6.7.6.1 节说:

For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to compatible types.

根据这两个,我理解为const char*char*不兼容(因为const charchar不兼容)。根据需要类型兼容性的 7.16.1.1 节中 va_arg 的定义(有几个例外不适用于此处),我相信在这种情况下您确实有未定义的行为。

现在,如果我们停止扮演律师角色,我看不出将 char* 视为 const char* 有何危害 --- 它只会限制合法运作。因此,我认为 va_arg 的规格过于保守。

是的,它们不兼容,当实际类型为 char* 时使用 va_arg 和 const char* 类型,反之亦然,是未定义的行为。

类型只与自身兼容1.

此外,关于限定符的部分,const 是一个,支持这个2


(引自ISO/IEC 9899:201x)

1(6.2.7兼容类型与复合类型1)
如果两种类型相同,则它们具有兼容类型

2(6.7.3 类型限定符 10)
为了使两个合格的类型兼容,两者都应具有相同的合格版本 兼容类型;说明符或限定符列表中类型限定符的顺序 不影响指定类型。