如何在 mingw-w64 gcc 7.1 中毫无征兆地打印size_t?

How to printf a size_t without warning in mingw-w64 gcc 7.1?

提问人:Scooter 提问时间:6/6/2017 最后编辑:Scooter 更新时间:7/5/2020 访问量:7761

问:

我正在使用 nuwen.net 上准备的 minGW 的 mingw-w64 (x64) fork。这是来自 gcc 的 7.1 版本:

gcc --version
gcc (GCC) 7.1.0

我正在编译这个程序:

#include <stdio.h>

int main(void)
{
    size_t a = 100;
    printf("a=%lu\n",a);
    printf("a=%llu\n",a);
    printf("a=%zu\n",a);
    printf("a=%I64u\n",a);
}

带警告和 C11 标准:

gcc -Wall -Wextra -Wpedantic -std=c11 test_size_t.c

我收到这些警告:

   test_size_t.c: In function 'main':
    test_size_t.c:6:14: warning: format '%lu' expects argument of type 'long unsigned int', but argument 2 has type 'size_t {aka long long unsigned int}' [-Wformat=]
      printf("a=%lu\n",a);
                ~~^
                %I64u
    test_size_t.c:6:14: warning: format '%lu' expects argument of type 'long unsigned int', but argument 2 has type 'size_t {aka long long unsigned int}' [-Wformat=]
      printf("a=%lu\n",a);
                ~~^
                %I64u
    test_size_t.c:7:14: warning: unknown conversion type character 'l' in format [-Wformat=]
      printf("a=%llu\n",a);
                  ^
    test_size_t.c:7:9: warning: too many arguments for format [-Wformat-extra-args]
      printf("a=%llu\n",a);
             ^~~~~~~~~~
    test_size_t.c:7:14: warning: unknown conversion type character 'l' in format [-Wformat=]
      printf("a=%llu\n",a);
                  ^
    test_size_t.c:7:9: warning: too many arguments for format [-Wformat-extra-args]
      printf("a=%llu\n",a);
             ^~~~~~~~~~
    test_size_t.c:8:13: warning: unknown conversion type character 'z' in format [-Wformat=]
      printf("a=%zu\n",a);
                 ^
    test_size_t.c:8:9: warning: too many arguments for format [-Wformat-extra-args]
      printf("a=%zu\n",a);
             ^~~~~~~~~
    test_size_t.c:8:13: warning: unknown conversion type character 'z' in format [-Wformat=]
      printf("a=%zu\n",a);
                 ^
    test_size_t.c:8:9: warning: too many arguments for format [-Wformat-extra-args]
      printf("a=%zu\n",a);
             ^~~~~~~~~
    test_size_t.c:9:9: warning: ISO C does not support the 'I64' ms_printf length modifier [-Wformat=]
      printf("a=%I64u\n",a);
         ^~~~~~~~~~~
    test_size_t.c:9:9: warning: ISO C does not support the 'I64' ms_printf length modifier [-Wformat=]

我想在没有警告的情况下打印size_t,但在这种情况下不知道正确的格式说明符。

C printf 尺寸-T C89

评论

5赞 StoryTeller - Unslander Monica 6/6/2017
zu是 C99 的添加,而且是正确的方法。确保编译为 C99(或更高版本)。
5赞 Stargateur 6/6/2017
@StoryTeller OP 使用 .-std=c11
7赞 6/6/2017
MinGW 使用微软的 c 库——这个库只符合 c89 并且不理解 -->使用条件编译,在 windows() 上使用 .msvcrtz#ifdef _WIN32%I64umsvcrt
1赞 msc 6/6/2017
我的 GCC 编译器除了 %llu 之外不会给出任何错误或警告。
5赞 M.M 6/6/2017
开始源文件#define __USE_MINGW_ANSI_STDIO 1

答:

28赞 user2371524 6/6/2017 #1

问题不在于编译器,而在于 C 库。MinGW使用Microsoft的“Visual C Runtime”(),它只符合,不支持格式说明符。msvcrtz

以下是使用 MinGW 时可以安全打印的方法:size_t

#include <inttypes.h>
#include <stdio.h>

#ifdef _WIN32
#  ifdef _WIN64
#    define PRI_SIZET PRIu64
#  else
#    define PRI_SIZET PRIu32
#  endif
#else
#  define PRI_SIZET "zu"
#endif

int main(void)
{
    size_t mySize = 24;

    printf("%" PRI_SIZET "\n", mySize);
}

在 win64 上,您将收到此代码的警告,因为扩展为特定于 -的格式说明符。但是你可以用 GCC 标志 .PRIu64msvcrtI64u-Wno-pedantic-ms-format


请注意,您需要类似的技巧(此处在 32 位和 64 位窗口上使用),因为两者都不知道。long longPRIu64msvcrtll


编辑:正如@M.M在评论中指出的那样,您可以将MinGW提供的支持C11的替代函数与.如果我能绕过 的特殊性,我宁愿不链接额外的代码,但这当然是一个品味问题。stdio#define __USE_MINGW_ANSI_STDIO 1msvcrt

评论

3赞 Cody Gray - on strike 6/6/2017
%zu在 Visual Studio 2013 及更高版本上受支持。使用 检查它。你声称 MSVC 不支持是完全错误的;这种支持已经存在了很多版本,很多我不记得什么时候不支持它。_MSC_VER >= 1800ll
3赞 6/6/2017
@CodyGray我刚刚检查过:我的 Windows 7.0.10586.0 系统上的版本 10 不支持。所以我猜 MSVC 做了类似 MinGW 的事情,并链接了一些额外的代码来支持这一点?%zumsvcrt.dll__USE_MINGW_ANSI_STDIO
2赞 Cody Gray - on strike 6/6/2017
嗯,不,不同之处在于 MSVC 已经有 20 年没有链接了。最后一个使用的版本是 VC++ 6。我记得我知道这一点,但我不知何故忘记了 MinGW 正在链接到 Microsoft 的私有运行时库。我猜它是使用 DDK 链接的。未记录更新,因为 DLL 不供公众使用。我想它的支持还没有扩大,因为 Microsoft 不使用它。好吧,并不是说有人需要更多理由不使用 MinGW!msvcrt.dllmsvcrt.dllprintf
1赞 6/6/2017
这当然是非常固执己见的,即使 Microsoft 不希望您使用这个“原始”,它也被 Windows 附带的应用程序使用,当然它将与旧的“vc6”版本保持兼容,因为它不能破坏旧的应用程序。因此,如果你坚持使用这个功能,就不会有太多问题,你也不需要用完整的运行时来“膨胀”你的产品。现有的许多运行时版本对我来说并不是一个很好的解决方案,但当然,这也只是一种意见。msvcrt
0赞 S.S. Anne 9/1/2019
您也可以使用 s 禁用警告。#pragma
11赞 Lundin 9/18/2018 #2

注释中提到的替代解决方案是抛入编译器开关:__USE_MINGW_ANSI_STDIO

#define __USE_MINGW_ANSI_STDIO 1

#include <stdio.h>

int main(void)
{
    size_t a = 100;
    printf("a=%lu\n",a);
    printf("a=%llu\n",a);
    printf("a=%zu\n",a);
    printf("a=%I64u\n",a);
}

这使得代码按预期编译,并且 gcc 现在给出相应的警告:

warning: format '%lu' expects argument of type 'long unsigned int', but argument 2 has type 'size_t' [-Wformat=]  
warning: ISO C does not support the 'I' printf flag [-Wformat=]  
warning: format '%u' expects argument of type 'unsigned int', but argument 2 has type 'size_t' [-Wformat=]  

或者,您可以使用以下命令在命令行上定义宏-D__USE_MINGW_ANSI_STDIO=1

评论

1赞 kowalski5233 9/21/2021
需要注意的重要事项 - 在实际包含 stdio.h 之前先做 #include。我喜欢先做我的包含,然后是我的宏,这让我有点困惑,因为在命令行中定义宏是有效的,但在代码中不是......直到我注意到显而易见的事情。
3赞 infval 7/5/2020 #3

PRIuPTRtrick works(size_t?类型变量的跨平台格式字符串):

#include <stdio.h>
#include <inttypes.h>
int main()
{
    size_t a = (size_t)(1024ULL * 1024 * 1024 * 1024 + 13);
    printf("a                 = %" PRIuPTR "\n", a);
    printf("sizeof(size_t)    = %" PRIuPTR "\n", sizeof(size_t));
    printf("sizeof(uintptr_t) = %" PRIuPTR "\n", sizeof(uintptr_t));
    return 0;
}

输出 x86:

a                 = 13
sizeof(size_t)    = 4
sizeof(uintptr_t) = 4

输出 x64:

a                 = 1099511627789
sizeof(size_t)    = 8
sizeof(uintptr_t) = 8