I have the following function which writes passed arguments to a binary file.
void writeFile(FILE *fp, const int numOfChars, ...)
{
va_list ap;
va_start(ap, numOfChars);
for(int i = 0; i < numOfChars; i++)
{
const char c = va_arg(ap, char);
putc(c, fp);
}
va_end(ap);
}
Upon compiling, I get the following warning from the compiler
warning: second argument to 'va_arg' is of promotable type 'char'; this va_arg
has undefined behavior because arguments will be promoted to 'int' [- Wvarargs]
Now as I understand it, C wants to promote char type to int. Why does C want to do this? Second, is the best solution to cast the int back to a char?
In the C Programming Language, the va_arg function fetches an argument in a variable argument list. The va_arg function updates ap so that the next call to the va_arg function fetches the next argument. You must call the va_start function to initialize ap before using the va_arg function.
The va_start macro enables access to the variable arguments following the named argument parm_n . va_start should be invoked with an instance to a valid va_list object ap before any calls to va_arg.
va_arg returns the value of the next argument in the list. The type is always the same as the second argument to va_arg . CAUTION. The results of va_start are unpredictable if the argument values are not appropriate. In certain cases, arguments are converted when they are passed to another type.
va_list is a complete object type suitable for holding the information needed by the macros va_start, va_copy, va_arg, and va_end. If a va_list instance is created, passed to another function, and used via va_arg in that function, then any subsequent use in the calling function should be preceded by a call to va_end.
But given the existence of prototypes, there's really no reason to write code that depends on such promotions -- except for the special case of variadic functions.) Because of that, there's no point in having va_arg () accept char as the type argument.
The C library macro type va_arg (va_list ap, type) retrieves the next argument in the parameter list of the function with type. This does not determine whether the retrieved argument is the last argument passed to the function.
This object should be initialized by an initial call to va_start before the first call to va_arg. type − This is a type name. This type name is used as the type of the expression, this macro expands to.
A type name. This type name is used as the type of the expression this macro expands to (i.e., its return type). For a type expression to be suitable for its use with va_arg, it must be such that type* produces a pointer to type.
Now as I understand it, C wants to promote char type to int. Why does C want to do this?
Because that's what the standard says. If you pass an integral value with conversion rank smaller than that of int
(e. g. char
, bool
or short
) to a function taking a variable number of arguments, it will be converted to int
. Presumably the reason for this has its roots in performance, where it was (and in fact, often it still is nowadays) better to pass values aligned to a machine word boundary.
Second, is the best solution to cast the int back to a char?
Yes, but you don't really need a cast even, an implicit conversion will do:
char ch = va_arg(ap, int);
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