Shao Miller said:
If the following program strictly conforming?
#include<stddef.h>
#include<stdarg.h>
static void test(int, ...);
int main(void) {
test(0, 42);
return 0;
}
static void test(int x, ...) {
typedef unsigned char one_char[1];
/* Valid declaration on the next line */
one_char * unused;
va_list varargs;
unsigned char * byte;
va_start(varargs, x);
if (x)
byte = va_arg(varargs, one_char);
va_end(varargs);
return;
}
It looks fine to me. Does some implementation reject it?
Yes. At least two of them, and in different ways that I didn't quite
expect.
GCC 4.3.3 says:
test.c: In function 'test':
test.c:21: error: invalid use of non-lvalue array
Where 'va_arg' expands to '__builtin_va_arg'.
gcc 4.6.1 is happy with it...
Aha. Thanks a lot for checking that!
clang 2.9 says:
test.c:21:16: error: assigning to 'unsigned char *' from
incompatible type 'one_char' (aka 'unsigned char [1]')
byte = va_arg(varargs, one_char);
^ ~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
... and my clang version 2.9 (tags/RELEASE_29/final) is also happy with
it.
That's odd. I was also using "tags/RELEASE_29/final". I was using
'-ansi -pedantic' as well, so maybe there's a C90 vs. C99 difference in
treatment?
It seems OK to me. va_arg(varargs, one_char) must be an expression of
type one_char. An expression with type "array of T" gets converted to
one of type "pointer to T" so the assignment should type-check. (You
can't execute the line without UB, but presumably that's why you put
the if (x) check there.)
I had the same reasoning, so thank you very much for that. Yes, I
believe the undefined behaviour would be from the 'va_arg' result not
being addressable, so the address of the first element would be
undefined and hence the would-be-resulting pointer value.
Beyond all of that business, I don't know of a way to pass an array to
any function, so the 'one_char' type could never be compatible with any
parameter. But that wasn't the point of the original question, of course.
I was briefly concerned that maybe the array-to-pointer conversion might
only apply to lvalue expressions, but it does not -- it applies to any
expression with an array type (other than a string literal or one that
is the operand of sizeof or unary&).
I had some moments where I shared this same concern and drew the same
conclusion. This seems to be related to something like:
typedef unsigned char one_char[1];
register one_char foo;
where there are now restrictions on the use of 'foo'; 'sizeof' and
'_AlignOf' only, presumably.