如何解决 "unknown conversion type character `z' in format" 特定于编译器的警告?

How do I work around the "unknown conversion type character `z' in format" compiler-specific warning?

我正在处理交叉编译到多个目标架构的代码。

我在搜索 Stack Overflow 时查看了 of 的 "printf size_t unknown conversion type character" 警告,但是这些帖子似乎都与 minGW 有关,所以这些答案基本上是 ifdef反对_WIN32,不适用于我的基本上相同问题的实例,即printf不将“%zu”识别为size_t的格式说明符,但mips 交叉编译器。

是否存在使 libc 能够将“%zu”识别为 size_t 的格式说明符的现有编译器标志(针对著名的交叉编译器)?

$ cat ./main.c
// main.c

#include <stdio.h>

int main( int argc, char* argv[] )
{
  size_t i = 42;
  printf( "%zu\n", i );
  return 0;
}

$ /path/to/mips_fp_le-gcc --version
2.95.3
$ 
$ file /path/to/libc.so.6
/path/to/libc.so.6: ELF 32-bit LSB pie executable, MIPS, MIPS-I version 1 (SYSV), dynamically linked, interpreter /lib/ld.so.1, for GNU/Linux 2.2.15, not stripped, too many notes (256)
$ 
$ /path/to/mips_fp_le-gcc -mips2 -O2 -EL -DEL -pipe -Wall -Wa,-non_shared -DCPU=SPARC -DLINUX -D_REENTRANT -DPROCESS_AUID -DTAGGING -fPIC -I. -I../../../root/include -I../include -I../../../common/include -I../../..
/root/include  -DDISABLE_CSL_BITE -DDISABLE_DNS_LOOKUP     -DOS=UNIX -DLINUX -DPOSIX_THREADS -D__USE_GNU -D_FORTIFY_SOURCE=2 -DHANDLE_CSL_DUPLICATES  -DOS=UNIX -DLINUX -DPOSIX_THREADS -D__USE_GNU -D_FORTIFY_SOURCE=2 -DHANDLE_CSL_DUPLICATES  -DOS=UNIX -DLINUX -DPOSIX_THREADS -D__USE_GNU -D_FORTIFY_SOURCE=2 -DHANDLE_C
SL_DUPLICATES  -DOS=UNIX -DLINUX -DPOSIX_THREADS -D__USE_GNU -D_FORTIFY_SOURCE=2 -DHANDLE_CSL_DUPLICATES -o ./main.o -c main.c 
main.c: In function `main':
main.c:6: warning: unknown conversion type character `z' in format
main.c:6: warning: too many arguments for format

如果粗体问题的直接答案是"no",还有哪些其他可能的解决方案?想到的可能性是...

  1. register_printf_function()
  2. 将格式说明符包装在目标特定的宏中(类似于

...还有其他想法吗?我非常喜欢不涉及特定于目标的预处理器代码的解决方案,因此上述两个都不理想。

我认为(但不确定)交叉编译器版本是旧的;较新版本 的著名工具链 known/guaranteed 有一个 libc 将“%zu” 识别为 size_t 的格式说明符?


更新:这个交叉编译器好像不能识别-std=c99;将它添加到编译器标志会产生错误“cc1: unknown C standard 'c99'

我使用一个大型代码库,该代码库是在几个不同的编译器下编译的,其中一些是旧的并且不理解 %z,所以我们只做

printf("size = %d", (int)size);

当然,这是小尺寸的简单方法。如果尺寸可能很大,其他选择是

printf("size = %u", (unsigned)size);

printf("size = %lu", (unsigned long)size);

(还有其他明显的可能性)。

您的 gcc 不支持 z 作为长度修饰符。跟MIPS没关系,一点区别都没有,就是2.95.3版本不支持。

1998 年 2 月 9 日添加了对 Z 长度修饰符的支持,由 Andreas Schwab "c-common.c (format_char_info): Add new field zlen." 提交。在此之前,Z 有一个 gcc 扩展作为 size_t 的转换类型说明符(而不是长度修饰符)。此代码在 gcc 2.95.3 中,因此它应该识别 Z,但不能识别 z

Joseph Myers 于 2000 年 7 月 17 日添加了对 z 的支持,"c-common.c (scan_char_table): Allow "z“diouxXn 格式的长度修饰符”。尽管及时早于 gcc 2.95.3,但它位于 gcc 3 分支中,直到 gcc 3.0 才发布。所以你的旧编译器根本没有得到它。

所以您可以更改您的代码以使用 Z,它仍然受支持。您还可以根据编译器版本定义一个宏:

#if __GNUC__ < 3
#define PZ "Z"
#else
#define PZ "z"
#endif  

然后像在printf("The size is %"PZ"u\n", sizeof(int));中那样使用它您仍然需要修改您的代码。但它最终不会有任何不同,因为格式字符串在预处理器之后,在较新的编译器上仍然是 %zu 而在旧的编译器上仍然是 %Zu 。将 size_t 参数转换为其他东西的想法实际上会改变代码的结果,因为在某些情况下它们将被转换为 larger/smaller 类型,具体取决于 size_t 是什么以及什么你投到。

或者,如果您可以构建您的工具链,您可以修补 gcc 以了解 z。我认为在 "c-common.c" 中使用 zlen 的 case 语句中的一行更改就可以了。

register_printf_function() 是 glibc 的一部分,它是 printf() 代码所在的位置。它将允许您在 运行 时间用新格式扩展 printf。在编译时你无法用它做任何改变编译器的事情。而且我不相信 gcc 在使用 register_printf_function() 时进行 printf 类型检查时能够知道添加了新格式。