好的,我在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变量打印为十六进制值。)
答案 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而言)以下之一:printf
,gnu_printf
,{{1 }}
ms_printf
使GCC认为该函数采用了用于Microsoft Visual Studio CRT printf系列函数的格式字符串。这意味着GCC会抱怨ms_printf
,z
和hh
,但会在没有任何警告的情况下通过ll
。
I64
让GCC假设下面是GNU libc printf实现(或者只是一个符合POSIX / C99标准的printf实现,我不确定)。因此,GCC会抱怨gnu_printf
和其他Microsoft扩展程序,但会接受I64
,z
和hh
。
ll
是编译Windows时printf
的别名,否则是ms_printf
的别名。
请注意,此检查与正在使用的实际printf实现完全正交。如果您编写类似printf的函数并将gnu_printf
放在其上,这很容易看出 - GCC会根据__attribute__((__format__ (FORMAT, ...)))
抱怨不同的东西,但您可以在函数内部执行任何操作。< / p>
我知道的可用printf实现:
FORMAT
编译)在MinGW.org和MinGW-w64工具链中。符合-D__USE_MINGW_ANSI_STDIO=1
(完全?)和ms_printf
格式(部分 - 不支持位置参数)。gnu_printf
编译)。符合-D__USE_MINGW_ANSI_STDIO=1
(duh ...),对ms_printf
的合规性非常低并且取决于运行时版本(旧版本不支持gnu_printf
,新版本支持ll
; z
到目前为止,任何版本都不支持hh
; GCC幸福地没有意识到这些发展,并假设最坏的情况,VC 6.0时代的msvcrt,似乎)。ms_printf
和gnu_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充分意识到printf
和gnu_printf
之间的区别,并且会根据一些复杂的宏来选择其中一个(大概是伴随着一个支持格式化的正确实现)它确实)。
目前已知(目前)GCC格式检查有问题的软件:
printf
格式,但实现来自gnulib;将其更改为gnu_printf
z
格式,但官方二进制文件是针对MSVCRT构建的;它在其扩展标头中也使用printf
格式,即使扩展程序通常也使用z