【问题标题】:Correct format specifiers for custom integer types in C to ensure portability为 C 中的自定义整数类型更正格式说明符以确保可移植性
【发布时间】:2017-01-17 22:59:24
【问题描述】:

我想知道time_tsocklen_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 打印ai_addrlen,它在struct addrinfo 中定义为socklen_t 类型,因为不能保证socklen_t 是类型int.

我们如何才能正确打印定义为socklen_ttime_t 等的整数?我担心这里的可移植性。当程序在具有不同socklen_t 定义的不同实现上编译时,我不必修改格式说明符。

【问题讨论】:

  • 除了size_t 之外,没有任何其他标准(C、POSIX 或其他)类型的特殊格式。你必须真正知道底层类型是什么。幸运的是 GCC 和 Clang 非常擅长了解它,如果您使用错误的格式会给出警告。不幸的是,如果您的代码必须构建在具有不同类型大小的不同平台上,这并没有真正的帮助。
  • 您没有显示struct,但对于size_t,您使用printf("%zu", ...);
  • @WeatherVane: long long 不能保证是最大的可用整数,尽管在大多数当前实现中它是。 C99 为此明确定义了intmax_t
  • @BlagovestBuyukliev 我很抱歉,intmax_tstdint.h 谢谢。
  • @Joachim Pileborg 关于“除了size_t 之外,没有任何其他标准......类型的特殊格式”。有t 对应ptrdiff_t,例如"td"j 对应(u)intmax_t。还有 SCN...PRI... 宏,其中包含适用于这些类型的 some 字符串。

标签: c cross-platform portability


【解决方案1】:

使用intmax_t%jd 说明符的中间转换:

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

强制转换将整数放大为最大可能大小的整数,该整数能够表示任何其他有符号整数类型的值。这里有一个小提示:如果sizeof(intmax_t) == sizeof ai-&gt;addrlenai-&gt;addrlen 是无符号的,不适合有符号整数(intmax_t)的大值将被截断。

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

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

【讨论】:

    【解决方案2】:

    @Blagovest Buyukliev 答案是当类型的 sign-ness 已知时的最佳方法。

    // 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)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-01-05
      • 1970-01-01
      • 1970-01-01
      • 2014-01-24
      • 1970-01-01
      • 1970-01-01
      • 2022-01-14
      • 1970-01-01
      相关资源
      最近更新 更多