32

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

4

10 回答 10

20

PRIuPTR宏(来自 <inttypes.h>)为 定义了十进制格式,uintptr_t它应该总是足够大,以至于您可以size_t在不截断的情况下对其进行转换,例如

fprintf(stream, "Your size_t var has value %" PRIuPTR ".", (uintptr_t) your_var);
于 2008-10-06T15:17:47.240 回答
13

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

Windows上,使用“ %Iu”。

LinuxOSX上,使用“ %zu”。

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

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

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

于 2009-08-24T20:19:26.247 回答
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);
于 2008-10-06T15:07:12.940 回答
4

Dan Saks 在 Embedded Systems Design 中写了一篇文章来讨论这个问题。根据 Dan 的说法, %zu 是标准方式,但很少有编译器支持这一点。作为替代方案,他建议使用 %lu 以及将参数显式转换为 unsigned long:

size_t n;
...
printf("%lu", (unsigned long)n);
于 2008-10-06T17:42:36.903 回答
2

Use boost::format. It's typesafe, so it'll print size_t correctly with %d, also you don't need to remember to put c_str() on std::strings when using it, and even if you pass a number to %s or vice versa, it'll work.

于 2008-10-06T19:25:42.717 回答
1

我不知道任何令人满意的解决方案,但您可能会考虑使用专门的函数将 size_t 项目格式化为字符串,然后打印该字符串。

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

于 2008-10-06T15:01:35.323 回答
1

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

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

另见

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

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

于 2009-08-25T03:29:54.237 回答
0

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

不管怎样,你的琴弦永远不会尴尬。

于 2011-03-16T19:01:55.593 回答
0

选项1:

由于在大多数(如果不是全部?)系统上,PRIuPTR printf 格式字符串来自也足够长来容纳一个类型,我建议对printf 格式字符串size_t使用以下定义。size_t

但是,重要的是您要验证这是否适用于您的特定架构(编译器、硬件等),因为标准并未强制执行此操作。

#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”长度说明符,如此处所示,用于size_t类型:

在此处输入图像描述

示例用法:

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

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

选项 3:

但是,在您需要它绝对保证工作的地方,或者在您不确定您的特定架构的地方,只需将其转换为 a 并打印即可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/
于 2019-05-01T22:32:21.990 回答
-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 年左右之前,它应该处理所有古老和新平台的效率有点低,那时double可能缺乏精确的结果。

printf("%.0f\n", (double) some_size_t_object);
于 2017-07-31T17:58:07.720 回答