I noticed some potential issue with va_arg
macro, that is used to receive an unnamed parameter from variadic function. Consider following, simplified example:
#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 != '\0'; s++)
putchar(*s);
va_end(ap);
}
int main(void)
{
char str[] = "xyz";
foo(1, str);
return 0;
}
The reference for va_arg
macro states that (emphasis mine):
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 (...)
My understanding is that const char *
and char *
types are not compatible. When char
array is passed to the foo
, in form of char
pointer (that is unchanged by default argument promotions), then the expression, that assumes const-qualified pointer:
s = va_arg(ap, const char *)
could invoke "technical" UB. The same situation would arise, when arr
is defined as const
array, and the argument is received as char *
, as well as for other types e.g. int *
and const int *
.
Section 6.2.7 of the C11 language standard defines type compatibility, section 6.7.6.1 further specifies it for pointer declarators and section 6.7.3 for type qualifiers. The latter (constraint 10) says:
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.
Also, section 6.7.6.1 says:
For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to compatible types.
According to these two, I understand that const char*
and char*
are not compatible (because const char
and char
are not compatible). According to the definition of va_arg
in section 7.16.1.1 which requires type compatibility (with a couple of exceptions that do not apply here), I believe that indeed you have undefined behavior in this case.
Now, if we stop playing the lawyer, I don't see how it could be harmful to treat a char*
as a const char*
--- it would only limit the legal operations. Therefore, I believe that the specification of va_arg
is too conservative.
n1570/6.2.5p28 seems to suggest this should be okay in practice:
...Similarly, pointers to qualified or unqualified versions of compatible types shall have the same representation and alignment requirements...
Since char
is compatible with itself, a pointer to char will have the same representation as a pointer to a const char. Since a variable argument function essentially assumes a representation of each subsequent parameter, this appears at first well defined.
As for the other way around, if you use a pointer to char so as to modify a const char, that is indeed UB per 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.
To continue the analysis from the answer you linked to in the comments, 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.
Since both pointer types are themselves non const, they are identically qualified, the only question that remains is if const char
is compatible with char
for our purposes.
And it appears that they are not... as nickie pointed out. So on the one hand, this could be construed as UB.
Yes, they are not compatible and using va_arg with type const char* when the actual type is char*, and vice-versa, is undefined behavior.
A type is only compatible with itself1.
Additionally, the section on qualifiers, const being one, supports this2.
(Quoted from ISO/IEC 9899:201x)
1 (6.2.7 Compatible type and composite type 1)
Two types have compatible type if their types are the same
2 (6.7.3 Type qualifiers 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With