Mingw-w64在自己写的、使用 __attribute__((format)) 的函数中,当传入long double时会产生警告,但对printf不会。

编程语言 2026-07-07

请考虑下列程序。它包含一个名为 my_printf2 的函数,该函数被注解为接受 prinf-风格的格式化字符串与参数。

#include <stdarg.h>
#include <stdio.h>

void my_printf(const char* fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    vprintf(fmt, args);
    va_end(args);
}

__attribute__ ((__format__ (__printf__, 1, 2)))
void my_printf2(const char* fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    vprintf(fmt, args);
    va_end(args);
}

int main(int argc, char* argv[])
{
    // These two calls produce no warning, as I'd expect
    printf("long double:%Lf double:%f\n", 3.14159L, 3.14159);
    my_printf("long double:%Lf double:%f\n", 3.14159L, 3.14159);

    // This call produces a warning.
    // warning: format ‘%Lf’ expects argument of type ‘double’,
    // but argument 2 has type ‘long double’ [-Wformat=]
    // The resulting program seems to work correctly, though.
    my_printf2("long double:%Lf double:%f\n", 3.14159L, 3.14159);

    // warning: format ‘%d’ expects argument of type ‘int’,
    // but argument 2 has type ‘double’ [-Wformat=]
    printf("%d\n", 42.0);
}

当我用 mingw-w64 15.2 进行编译并启用警告(x86_64-w64-mingw32-gcc main.c -Wall),编译器会对对 my_printf2 的调用给出警告(参见代码中的警告),但对于第一次调用 printf 或对 my_printf 的调用则不会。对 printf 的最后一次调用表明编译器确实会生成 -Wformat 警告。

程序的输出是

long double:3.141590 double:3.141590
long double:3.141590 double:3.141590
long double:3.141590 double:3.141590
0

因此输出是正确的,除了最后一行,正如预期。

为什么会这样?为什么编译器会对 my_printf2 的调用发出警告?

解决方案

正如John Bollinger指出,MinGW-w64在其 stdio.h 中有这段:

#if defined(__clang__)
#define __MINGW_PRINTF_FORMAT __printf__
#define __MINGW_SCANF_FORMAT  __scanf__
#elif defined(_UCRT) || __USE_MINGW_ANSI_STDIO
#define __MINGW_PRINTF_FORMAT __gnu_printf__
#define __MINGW_SCANF_FORMAT  __gnu_scanf__
#else
#define __MINGW_PRINTF_FORMAT __ms_printf__
#define __MINGW_SCANF_FORMAT  __ms_scanf__
#endif

__MINGW_PRINTF_FORMAT__MINGW_SCANF_FORMAT 随后被用来为处理格式字符串的函数添加属性,因此如果我想实现一个包装器来包裹 printf,并让MinGW-w64正确地产生 -Wformat 警告,那么我可以像下面这样从问题中定义 my_printf2

__attribute__ ((__format__ (__MINGW_PRINTF_FORMAT, 1, 2)))
void my_printf2(const char* fmt, ...) { /* ... */ }

当然,为了在不同编译器之间实现可移植性,我将不得不把属性相关的内容包装到另一个宏中。仍在权衡是否值得为获得这些警告付出所有麻烦。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章