I am a bit confused about how default argument promotions effect wchar_t
.
I understand that char
is promoted to int
, and therefore I have to supply int
as the second parameter of va_arg
, otherwise I may (GCC) or may not (MSVC) get an error, as demonstrated by the "%c"
example below.
So, I thought that - analogically - I should take into account some similar promotion in case of wchar_t
, and read the definitions of the relevant types in the C99 standard:
7.17
wchar_t
... is an integer type whose range of values can represent distinct codes for all members of the largest extended character set specified among the supported locales; the null character shall have the code value zero. Each member of the basic character set shall have a code value equal to its value when used as the lone character in an integer character constant if an implementation does not define__STDC_MB_MIGHT_NEQ_WC__
.7.24.1
wint_t
... is an integer type unchanged by default argument promotions that can hold any value corresponding to members of the extended character set, as well as at least one value that does not correspond to any member of the extended character set (seeWEOF
below).
It is clear to me that wint_t
is not promoted to anything, and I suspect but do not know for sure that wchar_t
is not promoted either.
I tried fetching arguments with va_arg
as wchar_t
, wint_t
and int
, and all of these worked, however, this may have happened because of luck:
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <wchar.h>
void print( char const* format, ... );
int main()
{
printf( "char == %zu, int == %zu, wchar_t == %zu, wint_t == %zu.\n",
sizeof( char ), sizeof( int ), sizeof( wchar_t ), sizeof( wint_t ) );
// MSVC x86: char == 1, int == 4, wchar_t == 2, wint_t == 2.
// MSVC x64: char == 1, int == 4, wchar_t == 2, wint_t == 2.
// GCC x64: char == 1, int == 4, wchar_t == 4, wint_t == 4.
char charA = 'A';
print( "%c", charA );
wchar_t wchar_tA = L'A';
print( "%lc", wchar_tA );
printf( "\n" );
}
void print( char const* format, ... )
{
va_list arguments;
va_start( arguments, format );
if( strcmp( format, "%c" ) == 0 ) {
// char c = va_arg( arguments, char ); // -> Bad (1)
char c = va_arg( arguments, int ); // -> Good
putchar( ( int ) c );
} else if( strcmp( format, "%lc" ) == 0 ) {
wchar_t w = va_arg( arguments, wchar_t ); // -> Good
// wint_t w = va_arg( arguments, wint_t ); // -> Good
// int w = va_arg( arguments, int ); // -> Good
putwchar( ( wchar_t ) w );
}
va_end( arguments );
}
// (1) GCC prints:
// warning: 'char' is promoted to 'int' when passed through '...'
// note: (so you should pass 'int' not 'char' to 'va_arg')
// Running the program prints:
// Illegal instruction
The question: Which of the three lines containing va_arg
in the else if
block is the correct, standard-compliant one?