C语言中自定义整数类型的正确格式说明符,以确保可移植性。

5
我想知道针对自定义整数类型(例如time_t、socklen_t等),正确的printf格式说明符是什么。
例如,
#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);
}

尽管程序可以正常编译和运行,但我不喜欢使用%d来打印struct addrinfo中定义为socklen_t类型的ai_addrlen,因为不能保证socklen_tint类型。

我们如何正确打印像socklen_ttime_t等所定义的整数呢?我关注的是移植性。当在不同实现上编译程序时,不应该修改格式说明符。

2
除了 size_t 之外,对于其他标准(C、POSIX或其他)类型没有特殊的格式。您必须实际上知道基础类型是什么。幸运的是,GCC和Clang非常擅长知道它,并且如果您使用错误的格式,它们会发出警告。不幸的是,如果您的代码需要在具有不同类型大小的不同平台上构建,这并不能真正帮助到您。 - Some programmer dude
如果不确定,就转换为最大可用值。例如 printf("%llu", (unsigned long long)ai->ai_addrlen); - Weather Vane
2
@WeatherVane:long long并不能保证是最大的可用整数,尽管在大多数当前的实现中它是。C99明确定义了intmax_t来满足这个目的。 - Blagovest Buyukliev
1
@BlagovestBuyukliev 对不起,intmax_tstdint.h 中,谢谢。 - Weather Vane
1
关于"除了size_t之外,没有任何其他标准..类型的特殊格式"。对于ptrdiff_t,有t,如"td",以及(u)intmax_tj。此外,还有包含适用于这些类型的某些字符串的SCN...PRI...宏。 - chux - Reinstate Monica
显示剩余3条评论
2个回答

6

使用中间转换变量 intmax_t%jd 格式符:

printf("%jd\n", (intmax_t) ai->ai_addrlen);

cast将整数扩大为能够表示任何其他有符号整数类型值的最大可能大小的整数。这里有一个小警告:如果sizeof(intmax_t) == sizeof ai->addrlenai->addrlen是无符号的,那么不适合于有符号整数(intmax_t)的大值将被截断。

如果您确定打印的类型是无符号的,请使用uintmax_t%ju

j字符是一个“长度子说明符”,特别适用于处理intmax_t/uintmax_t的大小,并且它可以与di说明符字符(用于intmax_t)或uoXx字符(用于uintmax_t)一起使用。


2

@Blagovest Buyukliev的回答是最佳方法,当类型的符号性已知时。

// if some unsigned type
printf("%ju\n", (uintmax_t) ux);

// if some signed type
printf("%jd\n", (intmax_t) x);

当符号未知时,就会出现挑战。宏预处理不考虑类型。下面解决了符号问题。如果值只能在 intmax_tuintmax_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);
  }

}

请注意,如果类型比 int/unsigned 更窄,则无论哪种方式都符合“... 一个晋升的类型是有符号整数类型,另一个晋升的类型是相应的无符号整数类型,并且该值可以用两种类型表示; ...”C11dr §6.5.2.2 6。
尝试打印 time_t 时会出现特殊问题,C规范没有定义它的符号性,也没有指定它是整数还是浮点数,只是说它是一个“实数类型”。
对于需要可移植打印 time_t 的罕见情况,有一些想法,从严谨到随意不等。
    // 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

注意:time() 可能会返回 (time_t)(-1)。
(u)intmax_t 和上述某些类型依赖于 C99/C11。 在向前兼容到 C99 之前需要考虑额外的问题,通常使用 (long) 或 (double)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接