I want to know what is the right printf format specifier for custom integer types such as time_t, socklen_t, etc.
For example,
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
int main()
{
struct addrinfo *ai;
if (getaddrinfo("localhost", "http", NULL, &ai) != 0) {
printf("error\n");
return EXIT_FAILURE;
}
printf("%d\n", ai->ai_addrlen);
}
Although this program compiles and runs fine, I am not happy about using %d to print ai_addrlen which is defined to be of type socklen_t in struct addrinfo because there is no guarantee that socklen_t is type int.
How can we correctly print such integers that are defined as socklen_t, time_t, etc.? I am concerned about portability here. I should not have to modify the format specifier when the program is compiled on a different implementation with different definitions for socklen_t.
Use an intermediate cast to intmax_t and the %jd specifier:
printf("%jd\n", (intmax_t) ai->ai_addrlen);
The cast enlarges the integer to an integer of the largest possible size which is able to represent the values of any other signed integer type. There is a slight caveat here: in case sizeof(intmax_t) == sizeof ai->addrlen and ai->addrlen is unsigned, large values which do not fit into a signed integer (intmax_t) will be truncated.
In case you are sure that the printed type is unsigned, use uintmax_t and %ju instead.
The j character is a "length sub-specifier" especially suited for handling the size of intmax_t/uintmax_t and it can go together with the d or i specifier characters (for intmax_t) or the u, o, X and x characters (for uintmax_t).
@Blagovest Buyukliev answer is the best approach when the sign-ness of the type is known.
// if some unsigned type
printf("%ju\n", (uintmax_t) ux);
// if some signed type
printf("%jd\n", (intmax_t) x);
A challenge occurs when that sign-ness is not known. Macro preprocessing does not account for types. The following accounts for the sign-ness issue. This is useful if the value is representable in only one of intmax_t and uintmax_t .
int main(void) {
mystery_integer_type x = rand() - RAND_MAX/2;
// Compiler can easily optimized one of the 2 paths out
if (x * 0 - 1 > 0) {
printf("Unigned %ju\n", (uintmax_t) x);
} else {
printf("Signed %jd\n", (intmax_t) x);
}
}
Note that if the type is narrower than int/unsigned, the path taken is well defined either way per "... one promoted type is a signed integer type, the other promoted type is the corresponding unsigned integer type, and the value is representable in both types; ..." C11dr §6.5.2.2 6
A special issue occurs when trying to print time_t, which the C spec does not define sign-ness nor if it is an integer or floating-point, just that it is a real type.
For the rare case where time_t needs to be portable printed, some ideas, ranging from pedantic to casual.
// pedantic
printf("%La\n", (long double) time());
printf("%.*Le\n", LDBL_DECIMAL_DIG - 1, (long double) time());
printf("%.*e\n", DBL_DECIMAL_DIG - 1, (double) time());
printf("%jd\n", (intmax_t) time());
printf("%lld\n", (long long) time());
printf("%ld\n", (long) time());
// casual
Note: time() may return (time_t)(-1).
(u)intmax_t and some others of the above rely on C99/C11.
Portability to pre-C99 adds additional issues not discussed typically resulting in using (long) or (double).
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