MinGW GCC:“未知的转换类型字符'h'”(snprintf)

时间:2012-05-20 23:39:05

标签: c gcc mingw printf

好的,我在Windows 7上使用MinGW(GCC 4.6.2)编译C文件时遇到了一个奇怪的问题。该文件包含以下C代码:

#include <stdio.h>

int main(int argc, char *argv[]) {
    printf("%2hhX\n", 250);
    char c[80];
    snprintf(c, sizeof(c), "%2hhX", 250);
    printf("%s\n", c);
    return 0;
}

编译结果如下:

$ gcc.exe -std=c99 -pedantic -Wall test.c
test.c: In function 'main':
test.c:6:2: warning: unknown conversion type character 'h' in format [-Wformat]
test.c:6:2: warning: too many arguments for format [-Wformat-extra-args]

现在,对我来说很奇怪的是它抱怨第6行的snprintf电话,而不是第4行的printf电话。我错过了什么或是警告只是不正​​确?另外,格式字符串"%2hhX"可能有更好的等价物吗? (我试图将char变量打印为十六进制值。)

2 个答案:

答案 0 :(得分:19)

从历史上看,MinGW在一些奇怪的情况下,特别是在C99支持下。 MinGW主要依赖于随Windows一起发布的msvcrt.dll运行时,并且该运行时不支持C99。

因此,对于旧版本的MinGW,使用C99特定的格式说明符时,可能会在C99模式下遇到问题。同样在历史上,GCC没有为msvcrt.dll缺乏对C99说明符的支持做任何特殊的调整。因此,您会遇到-Wformat不会警告无法使用的格式的情况。

双方情况都有所改善 - GCC在与MS运行时一起使用时特别支持-Wformat,例如:

  • -Wpedantic-ms-format因此,GCC不会抱怨"I32""I64"(即使有记录,我仍然会抱怨它即使在4.7.0中也无法识别 - 也许它是全新的)
  • ms_printf
  • __attribute__((__format__))选项

另一方面,MinGW提供了自己的snprintf()一段时间,因为MSVC的变体_snprintf()的行为完全不同。但是,MinGW在msvcrt.dll中的printf()上依赖了很长时间,因此printf()的C99格式说明符不起作用。在某些时候,MinGW开始提供它自己的版本printf()和朋友,这样你就可以获得适当的C99(和GNU?)支持。但是,似乎在保守的一面,这些并没有最初取代msvcrt.dll版本。它们的名称类似于__mingw_printf()

看起来在4.6.1和4.7.0之间的某个时刻,MinGW标头开始使用MinGW提供的版本作为msvcrt.dll函数的替换(至少如果你已经指定了C99)。

然而,似乎在较新的版本中,GCC和MinGW仍然有点不同步。如果之前GCC不会警告那些实际上不会在MinGW上工作的说明者,那么就不会抱怨那些会产生spcifiers。

您可能需要尝试以下snipet代码,以了解您的MinGW版本支持"hhX"的效果如何:

printf("%hhX\n", 0x11223344);
__mingw_printf("%hhX\n", 0x11223344);

我不确定要解决您遇到的问题的建议 - 我认为您可以修补MinGW stdio.h标题,以便它具有__attribute__((__format__ (gnu_printf, ...)))属性printf函数(它们不在较新的stdio.h中,因此GCC将使用它默认的格式支持)。

答案 1 :(得分:3)

除了另一个答案,这里有一些关于GCC中printf格式检查的更多信息:

当您说__attribute__((__format__ (FORMAT, ...)))时,FORMAT的值可以是(就printf而言)以下之一:printfgnu_printf,{{1 }}

ms_printf使GCC认为该函数采用了用于Microsoft Visual Studio CRT printf系列函数的格式字符串。这意味着GCC会抱怨ms_printfzhh,但会在没有任何警告的情况下通过ll

I64让GCC假设下面是GNU libc printf实现(或者只是一个符合POSIX / C99标准的printf实现,我不确定)。因此,GCC会抱怨gnu_printf和其他Microsoft扩展程序,但会接受I64zhh

ll是编译Windows时printf的别名,否则是ms_printf的别名。

请注意,此检查与正在使用的实际printf实现完全正交。如果您编写类似printf的函数并将gnu_printf放在其上,这很容易看出 - GCC会根据__attribute__((__format__ (FORMAT, ...)))抱怨不同的东西,但您可以在函数内部执行任何操作。< / p>

我知道的可用printf实现:

  • MinGW ANSI STDIO(使用FORMAT编译)在MinGW.org和MinGW-w64工具链中。符合-D__USE_MINGW_ANSI_STDIO=1(完全?)和ms_printf格式(部分 - 不支持位置参数)。
  • MSVCRT(不使用gnu_printf编译)。符合-D__USE_MINGW_ANSI_STDIO=1(duh ...),对ms_printf的合规性非常低并且取决于运行时版本(旧版本不支持gnu_printf,新版本支持ll; z到目前为止,任何版本都不支持hh; GCC幸福地没有意识到这些发展,并假设最坏的情况,VC 6.0时代的msvcrt,似乎)。
  • gnulib。完全符合ms_printfgnu_printf(或接近完全)。

MinGW.org中的stdio.h标题不使用attribute format

MinGW-w64中的stdio.h标头使用attribute format gnu_printf进行MinGW ANSI STDIO实现,但不对MSVCRT实现使用任何内容。 已修复:在较新版本的MinGW-w64标头stdio.h中,attribute format ms_printf将用于MSVCRT实施。

gnulib充分意识到printfgnu_printf之间的区别,并且会根据一些复杂的宏来选择其中一个(大概是伴随着一个支持格式化的正确实现)它确实)。

目前已知(目前)GCC格式检查有问题的软件:

  • glib - 使用printf格式,但实现来自gnulib;将其更改为gnu_printf
  • 有一个突出的错误
  • CPython - 代码充满z格式,但官方二进制文件是针对MSVCRT构建的;它在其扩展标头中也使用printf格式,即使扩展程序通常也使用z