7.16.1.1 2 describes va_arg
as following (emphasis mine):
If there is no actual next argument, or if type is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), the behavior is undefined, except for the following cases:
- one type is a signed integer type, the other type is the corresponding unsigned integer type, and the value is representable in both types;
- one type is pointer to void and the other is a pointer to a character type.
Now to my understanding and it seems that 6.5.2.2 (function calls) does not contradict me, though I might be wrong, the default promotions are:
char
to either int
or unsigned
(implementation specified)signed char
to int
unsigned char
to unsigned
short
to int
unsigned short
to unsigned
float
to double
This is all fine and dandy when you know the exact underlying types passed to the va_list
(except for char
, which AFAIK is impossible to retrieve portably because its signedness is implementation specified).
It gets more complicated when you're expecting types from <stdint.h>
to be passed to your va_list
.
int8_t
and int16_t
, deducting through logical limit observations, are guaranteed to be promoted or already be of type int
. However it's very dubious to rely on my original "logical" limit observations, so I'm seeking your (and the standard's) confirmation on this deduction (I may be missing some corner cases I'm not even aware of).uint8_t
and uint16_t
, except the underlying type is unsigned
int32_t
may or may not be promoted to int
. It may be larger than , smaller than or exactly the same as int
. Same holds for uint32_t
but for unsigned
. How to portably retrieve int32_t
and uint32_t
passed to va_list
? In other words, how to determine if int32_t
(uint32_t
) has been promoted to int
(unsigned
)? In yet other words, how to determine whether I should use va_arg(va, int)
or va_arg(va, int32_t)
to retrieve int32_t
passed to the variadic function without invoking undefined behaviour on any platform?
int64_t
and uint64_t
.This is a theoretical (standard-only concerned) question, with a presumption that all exact-width types in <stdint.h>
are present. I'm not interested in "what's true in practice" type of answers, because I believe I already know them.
EDIT
One idea that I have in mind is to use _Generic
to determine the underlying type of int32_t
. I'm not sure how exactly would you use it though. I'm looking for better (easier) solutions.
#define IS_INT_OR_PROMOTED(X) _Generic((X)0 + (X)0, int: 1, default: 0)
Usage:
int32_t x = IS_INT_OR_PROMOTED(int32_t) ?
(int32_t)va_arg(list, int) :
va_arg(list, int32_t);
With gcc on my PC the macro returns 1 for int8_t
, int16_t
and int32_t
, and 0 for int64_t
.
With gcc-avr (a 16-bit target) the macro returns 1 for int8_t
and int16_t
, and 0 for int32_t
and int64_t
.
For long
the macro returns 0 regardless of whether sizeof(int)==sizeof(long)
.
I don't have any targets with 64-bit int
s but I don't see why it wouldn't work on such a target.
I'm not sure this will work with truly pathological implementations though Actually I'm pretty sure now it will work with any conforming implementation.
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