如何使用printf系列便携地打印size_t变量?

时间:2010-03-26 15:52:25

标签: c printf

我有size_t类型的变量,我想使用printf()打印它。我使用什么格式说明符来便携地打印它?

在32位机器中,%u似乎是正确的。我用g++ -g -W -Wall -Werror -ansi -pedantic编译,没有任何警告。但是当我在64位机器中编译该代码时,它会产生警告。

size_t x = <something>;
printf( "size = %u\n", x );

warning: format '%u' expects type 'unsigned int', 
    but argument 2 has type 'long unsigned int'

如果我将其更改为%lu,警告就会消失。

问题是,如何编写代码,以便在32位和64位机器上编译警告?

编辑:作为一种解决方法,我想一个答案可能是将变量“强制转换”为足够大的整数,比如unsigned long,并使用%lu进行打印。这在两种情况下都有效。我在寻找是否还有其他想法。

14 个答案:

答案 0 :(得分:416)

使用z修饰符:

size_t x = ...;
ssize_t y = ...;
printf("%zu\n", x);  // prints as unsigned decimal
printf("%zx\n", x);  // prints as hex
printf("%zd\n", y);  // prints as signed decimal

答案 1 :(得分:80)

看起来它取决于您正在使用的编译器(blech):

...当然,如果您使用的是C ++,则可以使用cout代替suggested by AraK

答案 2 :(得分:57)

对于C89,请使用%lu并将值转换为unsigned long

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

对于C99及更高版本,请使用%zu

size_t foo;
...
printf("foo = %zu\n", foo);

答案 3 :(得分:9)

扩展Adam Rosenfield对Windows的回答。

我在VS2013 Update 4和VS2015预览中测试了这段代码:

// test.c

#include <stdio.h>
#include <BaseTsd.h> // see the note below

int main()
{
    size_t x = 1;
    SSIZE_T y = 2;
    printf("%zu\n", x);  // prints as unsigned decimal
    printf("%zx\n", x);  // prints as hex
    printf("%zd\n", y);  // prints as signed decimal
    return 0;
}

VS2015生成二进制输出:

  

1
  1
  2

而VS2013生成的那个说:

  


  ZX
  ZD

注意:ssize_t是POSIX扩展,而SSIZE_TWindows Data Types中类似,因此我添加了<BaseTsd.h>引用。

此外,除了以下C99 / C11标题外,VS2015预览版中还提供了所有C99标题:

C11 - <stdalign.h>
C11 - <stdatomic.h>
C11 - <stdnoreturn.h>
C99 - <tgmath.h>
C11 - <threads.h>

此外,C11&#39; <uchar.h>现已包含在最新预览中。

有关详细信息,请参阅此oldnew列表,了解标准一致性。

答案 4 :(得分:5)

printf("size = %zu\n", sizeof(thing) );

答案 5 :(得分:5)

std::size_t s = 1024;
std::cout << s; // or any other kind of stream like stringstream!

答案 6 :(得分:5)

对于那些在C ++中执行此操作而不一定支持C99扩展的人,我衷心推荐boost :: format。这使得size_t类型大小问题没有问题:

std::cout << boost::format("Sizeof(Var) is %d\n") % sizeof(Var);

由于您不需要boost :: format中的大小说明符,您可以担心如何显示该值。

答案 7 :(得分:2)

正如AraK所说,c ++流接口总是可以移植。

  

std :: size_t s = 1024;   std :: cout&lt;&lt; S; //或者像stringstream一样的任何其他类型的流!

如果你想要C stdio,对于某些便携式的病例,没有便携式答案。&#34;它变得很难看,因为你已经看到,选择错误的格式标志可能会产生编译器警告或输出错误。

C99试图用inttypes.h格式来解决这个问题,例如&#34;%&#34; PRIdMAX&#34; \ n&#34;。但就像&#34;%zu&#34;一样,并非所有人都支持c99(如2013年之前的MSVS)。有&#34; msinttypes.h&#34;浮动的文件来处理这个。

如果您转换为其他类型,根据标记,您可能会收到编译器警告,要求截断或更改符号。如果你走这条路线,选择一个更大的相关固定尺寸类型。一个unsigned long long和&#34;%llu&#34;或未签名的长期&#34;%lu&#34;应该工作,但llu也可能减慢32位世界的速度,因为它过大。 (编辑 - 我的mac发出64位警告,因为%llu不匹配size_t,即使%lu,%llu和size_t都是相同的大小。%lu和%llu在我的MSVS2012上的大小不同。所以你可能需要施放+使用匹配的格式。)

就此而言,您可以使用固定大小的类型,例如int64_t。可是等等!现在我们回到c99 / c ++ 11,旧的MSVS再次失败。另外你还有演员表(例如map.size()不是固定大小的类型)!

您可以使用第三方标题或库,例如boost。如果您还没有使用过,那么您可能不希望以这种方式给项目充气。如果您愿意为此问题添加一个,为什么不使用c ++流或条件编译?

因此,您需要了解c ++流,条件编译,第三方框架或适合您的便携式设备。

答案 8 :(得分:1)

在任何合理的现代 C 实现中,"%zu" 是打印 size_t 类型值的正确方法:

printf("sizeof (int) = %zu\n", sizeof (int));

"%zu" 格式说明符已添加到 1999 年 ISO C 标准中(并被 2011 年 ISO C++ 标准采用)。如果您不需要担心更早的实现,现在可以停止阅读。

如果您的代码需要可移植到 C99 之前的实现,您可以将该值转换为 unsigned long 并使用 "%lu"

printf("sizeof (int) = %lu\n", (unsigned long)sizeof (int));

这不能移植到 C99 或更高版本,因为 C99 引入了 long longunsigned long long,因此 size_t 可能比 unsigned long 更宽。

抵制在没有演员表的情况下使用 "%lu""%llu" 的诱惑。用于实现 size_t 的类型是实现定义的,如果类型不匹配,则行为未定义。像 printf("%lu\n", sizeof (int)); 这样的东西可能“有效”,但它根本不便携。

原则上,以下内容应该涵盖所有可能的情况:

#if __STDC_VERSION__ < 199901L
    printf("sizeof (int) = %lu\n", (unsigned long)sizeof (int));
#else
    printf("sizeof (int) = %zu\n", sizeof (int));
#endif

在实践中,它可能并不总是正常工作。 __STD_VERSION__ >= 199901L 应该保证支持 "%zu",但并非所有实现都必须正确,特别是因为 __STD_VERSION__ 是由编译器设置的,而 "%zu" 是由运行时库实现。例如,具有部分 C99 支持的实现可能会实现 long long 并使 size_t 成为 unsigned long long 的 typedef,但不支持 "%zu"。 (这样的实现可能不会定义 __STDC_VERSION__。)

有人指出,Microsoft 的实现可以有 32 位 unsigned long 和 64 位 size_t。 Microsoft 确实支持 "%zu",但该支持添加的相对较晚。另一方面,只有当特定的 unsigned long 值碰巧超过 size_t 时,转换为 ULONG_MAX 才会有问题,这在实践中不太可能发生。

如果您能够假设合理的现代实现,只需使用 "%zu"。如果您需要允许较旧的实现,这里有一个适用于各种配置的可移植的荒谬程序:

#include <stdio.h>
#include <limits.h>
int main(void) {
    const size_t size = -1; /* largest value of type size_t */
#if __STDC_VERSION__ < 199901L
    if (size > ULONG_MAX) {
        printf("size is too big to print\n");
    }
    else {
        printf("old: size = %lu\n", (unsigned long)size);
    }
#else
    printf("new: size = %zu\n", size);
#endif
    return 0;
}

打印“尺寸太大而无法打印”的一种实现(Windows/Cygwin 上的 x86_64-w64-mingw32-gcc.exe -std=c90)实际上支持 unsigned long long 作为 C90 之上的扩展,因此您可以利用那 - 但我可以想象一个支持 unsigned long long 但不支持 "%llu" 的 C99 之前的实现。而且该实现无论如何都支持 "%zu"

根据我的经验,当我探索实现时,我只想在快速一次性代码中打印 size_t 值,而不是在生产代码中。在这种情况下,做任何有效的事情可能就足够了。

(问题是关于 C,但我会提到在 C++ 中,std::cout << sizeof (int) 将在任何版本的语言中正常工作。)

答案 9 :(得分:0)

如果您将32位无符号整数传递给%lu格式,它会警告您吗?它应该没问题,因为转换是明确定义的,不会丢失任何信息。

我听说有些平台在<inttypes.h>中定义了你可以插入到格式字符串文字中的宏但我在Windows C ++编译器上看不到那个标题,这意味着它可能不是跨平台的

答案 10 :(得分:0)

在程序员想要输出 size_t 的大多数情况下,程序员会对输出的数值有一个合理的上限。如果程序员是例如输出一条消息,说明 int 有多大,使用:

printf("int is %u bytes", (unsigned)sizeof (int) );

出于所有实际目的,与以下内容一样便携,但可能比以下内容更快更小:

printf("int is %zu bytes", sizeof (int) );

这种构造可能失败的唯一情况是在一个平台上,int 上的填充字节数相对于 unsigned int 可以达到的最大值的幅度大得离谱表示(sizeof (int) 可能大于 65535 有点令人难以置信,但更令人难以置信的是,如果 unsigned 没有足够的值位来表示大于 sizeof (int) 的数字,它可能会那么大。

答案 11 :(得分:-1)

C99为此定义了“%zd”等。 (感谢评论者)在C ++中没有可移植的格式说明符 - 你可以使用%p,这两个场景中的单词是woulkd,但也不是便携式选择,并以十六进制给出值。

或者,使用一些流式传输(例如stringstream)或安全的printf替换,例如Boost Format。据我所知,这个建议仅限于使用(并且需要C ++)。 (在实现unicode支持时,我们使用了类似的方法来满足我们的需求。)

C的基本问题是使用省略号的printf在设计上是不安全的 - 它需要根据已知参数确定附加参数的大小,因此无法修复它以支持“无论你得到什么”。因此,除非你的编译器实现一些专有扩展,否则你运气不好。

答案 12 :(得分:-3)

在某些平台上,对于某些类型,有特定的printf转换说明符可用,但有时候必须使用强制转换为更大的类型。

我在这里记录了这个棘手的问题,示例代码: http://www.pixelbeat.org/programming/gcc/int_types/ 并定期更新新平台和类型的信息。

答案 13 :(得分:-5)

如果要将size_t的值打印为字符串,可以执行以下操作:

char text[] = "Lets go fishing in stead of sitting on our but !!";
size_t line = 2337200120702199116;

/* on windows I64x or I64d others %lld or %llx if it works %zd or %zx */
printf("number: %I64d\n",*(size_t*)&text);
printf("text: %s\n",*(char(*)[])&line);

结果是:

号码:2337200120702199116

text:让我们去钓鱼,而不是坐在我们身边!

编辑:重新阅读问题因为投票结果我注意到他的问题不是%llu或%I64d但是不同机器上的size_t类型看到了这个问题https://stackoverflow.com/a/918909/1755797
http://www.cplusplus.com/reference/cstdio/printf/

size_t在32位机器上是unsigned int,在64位机器上是unsigned long long int 但是%ll总是需要一个unsigned long long int。

size_t在不同操作系统上的长度不同,而%llu是相同的