C语言 如何使用 printf 系列可移植地打印 size_t 变量?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2524611/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 04:57:41  来源:igfitidea点击:

How can one print a size_t variable portably using the printf family?

cprintf

提问by Arun

I have a variable of type size_t, and I want to print it using printf(). What format specifier do I use to print it portably?

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

In 32-bit machine, %useems right. I compiled with g++ -g -W -Wall -Werror -ansi -pedantic, and there was no warning. But when I compile that code in 64-bit machine, it produces warning.

在 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'

The warning goes away, as expected, if I change that to %lu.

正如预期的那样,如果我将其更改为%lu.

The question is, how can I write the code, so that it compiles warning free on both 32- and 64- bit machines?

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

Edit: As a workaround, I guess one answer might be to "cast" the variable into an integer that is big enough, say unsigned long, and print using %lu. That would work in both cases. I am looking if there is any other idea.

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

回答by Adam Rosenfield

Use the zmodifier:

使用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

回答by T.J. Crowder

Looks like it varies depending on what compiler you're using (blech):

看起来它因您使用的编译器而异(blech):

  • gnu 说%zu(或%zx,或%zd但显示它好像已签名,等等)
  • 微软称%Iu(或%Ix,或者%Id不过再次证明的签署等) -但作为CL V19的(在Visual Studio 2015年),微软支持%zu(见此回复这条评论

...and of course, if you're using C++, you can use coutinstead as suggested by AraK.

...当然,如果您使用的是 C++,则可以cout按照AraK 的建议使用。

回答by John Bode

For C89, use %luand cast the value to unsigned long:

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

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

For C99 and later, use %zu:

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

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

回答by vulcan raven

Extending on Adam Rosenfield's answer for Windows.

扩展 Adam Rosenfield 对 Windows 的回答。

I tested this code with on both VS2013 Update 4 and VS2015 preview:

我在 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 generated binary outputs:

VS2015 生成的二进制输出:

1
1
2

1
1
2

while the one generated by VS2013 says:

而 VS2013 生成的那个说:

zu
zx
zd

zu
zx
zd

Note: ssize_tis a POSIX extension and SSIZE_Tis similar thing in Windows Data Types, hence I added <BaseTsd.h>reference.

注意:ssize_t是 POSIX 扩展并且SSIZE_TWindows Data Types 中是类似的东西,因此我添加了<BaseTsd.h>参考。

Additionally, except for the follow C99/C11 headers, all C99 headers are available in VS2015 preview:

此外,除了以下 C99/C11 头文件,所有 C99 头文件都在 VS2015 预览版中可用:

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

Also, C11's <uchar.h>is now included in latest preview.

此外,C11<uchar.h>现在包含在最新预览中。

For more details, see this oldand the newlist for standard conformance.

有关详细信息,请参阅本旧的新的标准符合性列表。

回答by swestrup

For those talking about doing this in C++ which doesn't necessarily support the C99 extensions, then I heartily recommend boost::format. This makes the size_t type size question moot:

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

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

Since you don't need size specifiers in boost::format, you can just worry about how you want to display the value.

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

回答by AraK

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

回答by nategoose

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

回答by Rick Berge

As AraK said, the c++ streams interface will always work portably.

正如 AraK 所说,c++ 流接口将始终可移植地工作。

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

std::size_t s = 1024; std::cout << s; // 或任何其他类型的流,如 stringstream!

If you want C stdio, there is no portable answer to this for certain cases of "portable." And it gets ugly since as you've seen, picking the wrong format flags may yield a compiler warning or give incorrect output.

如果你想要 C stdio,对于某些“便携”的情况,没有便携的答案。并且它变得丑陋,因为正如您所见,选择错误的格式标志可能会产生编译器警告或给出不正确的输出。

C99 tried to solve this problem with inttypes.h formats like "%"PRIdMAX"\n". But just as with "%zu", not everyone supports c99 (like MSVS prior to 2013). There are "msinttypes.h" files floating around to deal with this.

C99 试图用像 "%"PRIdMAX"\n" 这样的 inttypes.h 格式来解决这个问题。但与“%zu”一样,并非所有人都支持 c99(如 2013 年之前的 MSVS)。有“msinttypes.h”文件可以处理这个问题。

If you cast to a different type, depending on flags you may get a compiler warning for truncation or a change of sign. If you go this route pick a larger relevant fixed size type. One of unsigned long long and "%llu" or unsigned long "%lu" should work, but llu may also slow things down in a 32bit world as excessively large. (Edit - my mac issues a warning in 64 bit for %llu not matching size_t, even though %lu, %llu, and size_t are all the same size. And %lu and %llu are not the same size on my MSVS2012. So you may need to cast + use a format that matches.)

如果您转换为不同的类型,根据标志,您可能会收到有关截断或符号更改的编译器警告。如果您走这条路线,请选择更大的相关固定尺寸类型。unsigned long long 和 "%llu" 或 unsigned long "%lu" 之一应该可以工作,但 llu 也可能会在 32 位世界中减慢速度,因为它太大了。(编辑 - 我的 mac 在 64 位中发出警告 %llu 与 size_t 不匹配,即使 %lu、%llu 和 size_t 的大小都相同。我的 MSVS2012 上的 %lu 和 %llu 大小不同。所以您可能需要强制转换 + 使用匹配的格式。)

For that matter, you can go with fixed size types, such as int64_t. But wait! Now we're back to c99/c++11, and older MSVS fails again. Plus you also have casts (e.g. map.size() is not a fixed size type)!

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

You can use a 3rd party header or library such as boost. If you're not already using one, you may not want to inflate your project that way. If you're willing to add one just for this issue, why not use c++ streams, or conditional compilation?

您可以使用 3rd 方标头或库,例如 boost。如果您还没有使用它,您可能不想以这种方式膨胀您的项目。如果您愿意为此问题添加一个,为什么不使用 C++ 流或条件编译?

So you're down to c++ streams, conditional compilation, 3rd party frameworks, or something sort of portable that happens to work for you.

因此,您只能使用 C++ 流、条件编译、第 3 方框架或碰巧适合您的某种可移植的东西。

回答by Kylotan

Will it warn you if you pass a 32-bit unsigned integer to a %lu format? It should be fine since the conversion is well-defined and doesn't lose any information.

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

I've heard that some platforms define macros in <inttypes.h>that you can insert into the format string literal but I don't see that header on my Windows C++ compiler, which implies it may not be cross-platform.

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

回答by peterchen

C99 defines "%zd" etc. for that. (thanks to the commenters) There is no portable format specifier for that in C++ - you coulduse %p, which woulkd word in these two scenarios, but isn't a portable choice either, and gives the value in hex.

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

Alternatively, use some streaming (e.g. stringstream) or a safe printf replacement such as Boost Format. I understand that this advice is only of limited use (and does require C++). (We've used a similar approach fitted for our needs when implementing unicode support.)

或者,使用一些流(例如 stringstream)或安全的 printf 替代品,例如Boost Format。我知道这个建议的用途有限(并且确实需要 C++)。(在实现 unicode 支持时,我们使用了一种适合我们需要的类似方法。)

The fundamental problem for C is that printf using an ellipsis is unsafe by design - it needs to determine the additional argument's size from the known arguments, so it can't be fixed to support "whatever you got". So unless your compiler implement some proprietary extensions, you are out of luck.

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