【问题标题】:MinGW GCC: "Unknown conversion type character 'h'" (snprintf)MinGW GCC:“未知转换类型字符'h'”(snprintf)
【发布时间】:2012-05-20 23:39:05
【问题描述】:

好的,我在 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 变量打印为十六进制值。)

【问题讨论】:

  • 有趣的是,它适用于 GCC 4.3.4:ideone.com/LAPP9。我也尝试过 4.1.2,它也很好。
  • 使用 MinGW GCC 4.6.1 我在 printf()snprintf() 上都收到警告 - 你使用的是什么 MinGW 发行版?我目前正在使用 TDM 发行版。
  • @MichaelBurr:呃,我什至没有意识到有几个 MinGW 发行版。我想我正在使用“标准”版本(mingw.org,与sourceforge.net/projects/mingw/files/Installer/mingw-get-inst 一起安装)。不过,这会有所不同吗?
  • 另外,我现在更加困惑,因为我们在 4 个版本的 GCC 上有 3 种不同的行为。格式字符串 is 有效,不是吗?
  • @Socob:我通常使用 TDM 的 MinGW (tdm-gcc.tdragon.net) 和/或 nuwen 的 (nuwen.net/mingw.html)。我发现它们比官方的 MinGW 更容易安装(nuwen 的字面意思是解压缩档案),它们打包了其他有用的库(例如,TDM 中的 pthreads-win32 和 nuwen 中的 Boost)并且 TDM 有一个选项64 位目标编译器。

标签: c gcc mingw printf


【解决方案1】:

从历史上看,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 上工作的说明符,而不是抱怨那些会起作用的说明符。

您可能想尝试以下代码片段,看看您的 MinGW 版本对"hhX" 的支持程度:

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

我不知道有什么建议可以解决您遇到的问题 - 我认为您可以修补 MinGW stdio.h 标头,使其在 printf 函数上具有 __attribute__((__format__ (gnu_printf, ...))) 属性(它们在较新的 stdio.h 中不存在,因此 GCC 将使用其默认的格式支持概念)。

【讨论】:

  • 当我使用上面的选项编译您的代码 sn-p 并打印 44 两次时,正如人们所期望的那样,您的代码没有发出警告;似乎只有snprintf 特别受到我安装警告的影响。然而,我在上面发布的代码打印了两次FA,因此该功能似乎工作正常。所以,本质上,警告确实是不正确的,它只是 MinGW 和 GCC 的一个怪癖?如果是这样,这就是我真正需要知道的——我只是想弄清楚我是否遗漏了代码中的某些内容,或者我是否可以忽略警告。
  • 谢谢。即使在 MinGW 4.9.1 __mingw_printf 上,它也适用于我而不会发出警告。将它包装在一个宏中以便通用。
【解决方案2】:

除了其他答案之外,这里还有一些关于 GCC 中 printf 格式检查的更多信息:

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

ms_printf 使 GCC 假定函数采用用于 Microsoft Visual Studio CRT printf 系列函数的格式字符串。这意味着GCC会抱怨zhhll,但会毫无警告地通过I64

gnu_printf 使 GCC 假定下面有 GNU libc printf 实现(或者可能只是一个 POSIX/C99 兼容的 printf 实现,我不确定)。因此 GCC 会抱怨 I64 和其他 Microsoft 扩展,但会接受 zhhll

printf 在为 Windows 编译时是 ms_printf 的别名,否则是 gnu_printf 的别名。

请注意,此检查与正在使用的实际 printf 实现完全正交。如果您编写自己的类似 printf 的函数并将 __attribute__((__format__ (FORMAT, ...))) 放在上面,这很容易看出 - GCC 会根据 FORMAT 抱怨不同的事情,但您可以在函数内做任何您想做的事情。

我知道的可用 printf 实现:

  • MinGW.org 和 MinGW-w64 工具链中的 MinGW ANSI STDIO(使用 -D__USE_MINGW_ANSI_STDIO=1 编译)。符合 ms_printf(完全?)和 gnu_printf 格式(部分 - 不支持位置参数)。
  • MSVCRT(编译时不使用-D__USE_MINGW_ANSI_STDIO=1)。符合ms_printf (duh...),对gnu_printf 的符合率很低,取决于运行时版本(旧版本不支持ll,新版本支持;zhh 不支持到目前为止的任何版本;不过,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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多