用于size_t类型变量的跨平台格式字符串?

问题描述 投票:31回答:10

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

c++ c cross-platform printf size-t
10个回答
20
投票

PRIuPTR宏(来自 )为。定义十进制格式 uintptr_t,它应该总是足够大,你可以在没有截断的情况下向它施放size_t,例如:

fprintf(stream, "Your size_t var has value %" PRIuPTR ".", (uintptr_t) your_var);

-1
投票

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时,double可能缺乏精确的结果。

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

13
投票

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

Windows,使用“%Iu”。

LinuxOSX上,使用“%zu”。

第二个问题是如何支持多个平台,因为格式字符串之类的东西可能在每个平台上都不同。正如其他人所指出的那样,使用#ifdef会很快变得难看。

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

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


8
投票

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

#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
投票

Dan Saks在嵌入式系统设计中写了一篇文章,covered这件事。根据Dan的说法,%zu是标准方式,但很少有编译器支持这一点。作为替代方案,他建议使用%lu以及对unsigned long的显式转换:

size_t n;
...
printf("%lu", (unsigned long)n);

2
投票

使用boost::format。它是类型安全的,所以它会用size_t正确打印%d,你也不需要记得在使用它时将c_str()放在std::strings上,即使你将数字传递给%s,反之亦然,它也会起作用。


1
投票

我不知道任何令人满意的解决方案,但你可能会考虑一个专门的函数来将size_t项格式化为字符串,并打印字符串。

(或者,如果你能逃脱它,boost :: format可以轻松处理这类事情。)


1
投票

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

您要使用的是uintmax_t和格式宏PRIuMAX。对于Visual C ++,您将需要下载与c99兼容的stdint.h和inttypes.h头文件,因为Microsoft不提供它们。

另见

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

本文纠正了Frederico引用的文章中的错误。


0
投票

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

无论哪种方式,你的字符串永远不会尴尬。


0
投票

Option 1:

由于在大多数(如果不是全部?)系统中,来自PRIuPTRprintf format string 也足以容纳size_t类型,我建议使用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);

Option 2:

但是,如果可能,只需使用%zu“z”长度说明符as shown here,用于size_t类型:

enter image description here

用法示例:

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

然而,在某些系统上,例如使用gcc作为编译器的STM32微控制器,%z长度说明符不一定实现,并且像printf("%zu\n", my_size_t_num);这样的操作可能最终打印出文字“%zu”(我亲自测试了这个并找到了这是真的)而不是你的size_t变量的值。

Option 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);

Sources Cited:

  1. http://www.cplusplus.com/reference/cstdio/printf/
  2. http://www.cplusplus.com/reference/cinttypes/
  3. http://www.cplusplus.com/reference/cstdint/
© www.soinside.com 2019 - 2024. All rights reserved.