代码之家  ›  专栏  ›  技术社区  ›  twk Mark Adler

类型大小的变量的跨平台格式字符串?

  •  30
  • twk Mark Adler  · 技术社区  · 16 年前

    在一个跨平台的c/c++项目(Win32、Linux、OSX)上,我需要使用*printf函数来打印一些size类型的变量。在某些环境中,size是8字节,在其他环境中是4字节。在glibc上我有%zd,在Win32上我可以使用 %Id . 有没有一种优雅的方法来处理这个问题?

    9 回复  |  直到 16 年前
        1
  •  20
  •   tzot    16 年前

    这个 PRIuPTR 宏(from<inttypes.h>)为 uintptr_t ,它应该总是足够大,以便您可以 size_t 不截断,例如。

    fprintf(stream, "Your size_t var has value %" PRIuPTR ".", (uintptr_t) your_var);
    
        2
  •  13
  •   ipmcc    12 年前

    这里有两个问题。第一个问题是这三个平台的正确printf说明符字符串是什么。请注意 size_t 是无符号类型。

    打开 Windows ,使用“ %Iu ".

    打开 Linux OSX ,使用“ %zu ".

    第二个问题是如何支持多个平台,因为每个平台上的格式字符串可能不同。正如其他人指出的,使用 #ifdef 很快就变丑了。

    相反,为每个目标平台分别编写一个makefile或项目文件。然后通过源文件中的某个宏名引用说明符,在每个makefile中适当地定义宏。特别是,GCC和Visual Studio都接受“D”开关来在命令行上定义宏。

    如果您的生成系统非常复杂(多个生成选项、生成的源等),维护3个单独的makefile可能会变得很困难,您将不得不使用某种高级生成系统,如CMake或GNU autotools。但基本原理是相同的——使用构建系统来定义特定于平台的宏,而不是将平台检测逻辑放在源文件中。

        3
  •  8
  •   tzot    16 年前

    我唯一能想到的是典型的:

    #ifdef __WIN32__ // or whatever
    #define SSIZET_FMT "%ld"
    #else
    #define SSIZET_FMT "%zd"
    #endif
    

    然后利用不断的折叠:

    fprintf(stream, "Your size_t var has value " SSIZET_FMT ".", your_var);
    
        4
  •  4
  •   FredericoFrederico    16 年前

    丹萨克斯在《嵌入式系统设计》上写了一篇文章 covered 这件事。丹认为,%zu是标准的方法,但是很少有编译器支持这种方法。作为替代方案,他建议使用%lu并将参数显式转换为unsigned long:

    size_t n;
    ...
    printf("%lu", (unsigned long)n);
    
        5
  •  2
  •   Lev    16 年前

    使用 boost::format . 它是打字安全的,所以可以打印 size_t 正确使用 %d ,也不需要记住 c_str() std::string 使用它时,即使您将一个数字传递给 %s 反之亦然,它会起作用的。

        6
  •  1
  •   Head Geek    16 年前

    我不知道有什么令人满意的解决方案,但是您可以考虑使用一个专门的函数将大小项格式化为字符串,并打印字符串。

    (或者,如果您可以摆脱它,boost::format可以轻松地处理这类事情。)

        7
  •  1
  •   youcantmakemeregisteryoucantmakemeregister    15 年前

    您只需找到一个具有最大存储类的整数类型,将值强制转换为该类型,然后对较大的类型使用适当的格式字符串。注意,此解决方案适用于任何类型(ptrdiff_t等),而不仅仅是尺寸。

    您要使用的是uintmax和format宏PRIuMAX。对于Visual C++,你需要下载C99兼容的Stdit.h和IpType h标题,因为微软不提供它们。

    另见

    http://www.embedded.com/columns/technicalinsights/204700432

    这篇文章纠正了弗雷德里科引用的文章中的错误。

        8
  •  0
  •   paniq    14 年前

    对于这个问题,我的选择是简单地将size_t参数转换为unsigned long,并在任何地方使用%lu—当然,只有在值不超过2^32-1的情况下。如果这对您来说太短,您可以将其转换为unsigned long long,并将其格式化为%llu。

    不管怎样,你的琴弦永远不会笨拙。

        9
  •  0
  •   Gabriel Staples    6 年前

    选项1:

    从那以后大部分(如果不是全部?)系统 PRIuPTR printf format string 也足够长来保持 size_t 类型,我建议使用以下定义 尺寸 printf格式字符串。

    但是,重要的是您要验证这是否适用于您的特定体系结构(编译器、硬件等),因为标准没有强制执行这一点。

    #include <inttypes.h>
    
    // Printf format strings for `size_t` variable types.
    #define PRIdSZT PRIdPTR
    #define PRIiSZT PRIiPTR
    #define PRIoSZT PRIoPTR
    #define PRIuSZT PRIuPTR
    #define PRIxSZT PRIxPTR
    #define PRIXSZT PRIXPTR
    

    示例用法:

    size_t my_variable;
    printf("%" PRIuSZT "\n", my_variable);
    

    选择2:

    不过,在可能的情况下,只需使用 %zu “z”长度说明符, as shown here ,用于 尺寸

    enter image description here

    示例用法:

    size_t my_variable;
    printf("%zu\n", my_variable);
    

    但是,在一些系统上,例如使用gcc作为编译器的STM32微控制器上 %z 长度说明符不一定是实现的,它的作用是 printf("%zu\n", my_size_t_num); 可能只会打印出一个文本“%zu”(我亲自测试并发现它是真的),而不是您的 尺寸 变量。

    选择3:

    在你需要的地方 绝对保证工作 但是,如果您不确定自己的特定体系结构,可以将 uint64_t 做吧,因为这是保证工作,但需要额外的步骤铸造。

    示例用法:

    #include <stdint.h>    // for uint64_t
    #include <inttypes.h>  // for PRIu64
    
    size_t my_variable;
    printf("%" PRIu64 "\n", (uint64_t)my_variable);
    

    引用的消息来源:

    1. http://www.cplusplus.com/reference/cstdio/printf/
    2. http://www.cplusplus.com/reference/cinttypes/
    3. http://www.cplusplus.com/reference/cstdint/
        10
  •  -1
  •   chux    7 年前

    size_t 是一个 未签名 至少16位的类型。宽度通常为32和64。

    printf("%zu\n", some_size_t_object); // Standard since C99
    

    上面的方法是最好的方法,但是如果代码还需要移植到C99之前的平台,请将值转换为一些宽类型。 unsigned long 是合理的候选人,但可能缺乏。

    // OK, yet insufficient with large sizes > ULONG_MAX
    printf("%lu\n", (unsigned long) some_size_t_object); 
    

    或使用条件代码

    #ifdef ULLONG_MAX
      printf("%llu\n", (unsigned long long) some_size_t_object); 
    #else
      printf("%lu\n", (unsigned long) some_size_t_object); 
    #endif
    

    最后考虑 double . 它的效率有点低,但应该在2030-2040年之前处理所有的旧平台和新平台 Moore's law 什么时候 双重的 可能缺乏精确的结果。

    printf("%.0f\n", (double) some_size_t_object);