View this question from the perspective of someone implementing printf.
Since the arguments of printf are passed through an ellipsis (...), they get integer promoted. I know that char, short and int get promoted to int while long long does not get promoted. Similarly for their unsigned counterparts.
This implies that when reading the varargs, va_arg(args, int) should be used for char, short and int while va_arg(args, long long) should be used for long long.
My question is, do long and size_t get promoted, and if they do, to what? There are many sources on the internet on integer promotion, but I haven’t seen any that talk about these types.
P.S. I would appreciate a reference to the standard.
The integer conversion rank of
longis required to be greater than the rank ofint(6.3.1.1p1), sova_arg(args, long)is required even iflonghas the same representation (and precision) asint. Note that on most 64-bit platforms,longis 64-bit; Windows (an LLP64 platform) is an exception.size_tis required to be an unsigned integer type (6.5.3.4p5, 7.19p2) and is recommended to have an integer conversion rank no greater than that oflong int(7.19p4); it is required to have a precision of at least 16 bits (7.20.3p2, minimum value ofSIZE_MAX). It is not required to be a (typedef to a) standard integer type, although it is allowed to be.There are then three possibilities for the integer conversion rank of
size_t:int, so asize_targument will be promoted to eitherint(if the precision ofsize_tis less than that ofint) orunsigned int(if the two types have the same precision). In either case you would need to writeva_arg(args, unsigned int)(even if thesize_targument is promoted toint, using the equivalent unsigned type is allowed by 7.16.1.1p2).int, i.e.size_tis the same type asunsigned int. In this case eitherva_arg(args, unsigned int)orva_arg(args, size_t)are allowed.int. In this caseva_arg(args, size_t)must be used.Note that either of 1 and 3 can obtain even if the precision of
size_tis the same as that ofint.This means that to extract a
size_tparameter usingva_arg, it is necessary to know or infer the integer conversion rank ofsize_t. This can be done using a type-generic macro (6.5.1.1):If
size_tis promoted tointby the unary plus operator as used above, then we extract anunsigned int; ifsize_tis promoted tounsigned int, or is a typedef tounsigned int, then we extract anunsigned int; if it is not promoted and is a distinct type fromunsigned int, then we hit thedefaultblock. We can’t supplysize_titself as an option, as that would conflict ifsize_twere a typedef forunsigned int.Note that this is a problem not restricted to
size_t,ptrdiff_tandwchar_thave the same issue (for the latter,wint_tcan hold anywchar_tvalue and is not subject to promotion, but there is no guarantee thatwchar_tis promoted towint_t, unlike the guarantee thatcharis promoted toint). I’d suggest that the standard needs to introduce new typesspromo_t,ppromo_tandwpromo_t, and similarly for the types instdint.h. (Sure, you can use_Genericas above, but it’s a pain in the neck.)