编译此MCVE
#include <stdio.h>
#include <climits>
#include <limits>
int main()
{
unsigned long x = ULONG_MAX;
printf( "1:=%lu - 2:=%lu 3:=%ld\n", std::numeric_limits<size_t>::max(), x, x );
}
使用这些gcc/clang [any version]选项-Wall -Werror -pedantic
不会失败。
这是输出:
1:=18446744073709551615 - 2:=18446744073709551615 3:=-1
我预计会出现错误,因为我提供unsigned int
,但格式说明符为signed int
。
使用PowerPC gcc 4.8进行编译会失败,如预期的那样:
错误:格式'%lu'需要'long unsigned int'类型的参数,但是 参数2的类型为'unsigned int'[-Werror = format =]
我的问题是:
为什么gcc / clang编译这些格式说明符?对我来说,我会说PowerPC gcc是正确的,因为这是一个严重的符号/无符号问题,错误的格式说明符显示错误的结果。
答案 0 :(得分:5)
根据GCC(最低版本5.0)文档,
-Wformat-signedness 如果指定了 -Wformat ,如果格式字符串需要无符号参数且参数已签名,则也会发出警告,反之亦然。
此设置不是默认设置,因为标准不要求它。如果您想要超过标准,则需要使用正确的标志。在您的情况下,您通过 -Wall 使用 -Wformat ,而不是 -Wformat-signedness 。请记住, -Wall 不会打开所有警告,只是几乎所有人都同意的子集。
编译器会检查类型安全性,因此在删除long-specifier时会出现警告的原因。除此之外,如果数据没有丢失(如签名到无符号或无符号签名或从int到long的促销等),他们可以随意做任何事情。实际上,clang没有标志检查格式(AFAIK)。
答案 1 :(得分:1)
以不恰当的方式使用printf
是undefined behavior的一个实例。实施可以do非常糟糕的事情(包括你梦寐以求的事情)。
BTW,使用C ++,您最好使用std::cout及其operator <<
,这样不易出错。
为什么gcc / clang编译这些格式说明符?
大多数情况下,它没有(但是,编译器可能知道有关printf
和有时优化对它的调用,因为您的<stdio.h>
标头将使用特定{ {1}} function attributes)。在您的情况下,编译器可能会调用标准C库提供的可变参数format
。 没有编译格式说明符(只是将文字常量作为第一个参数传递给printf
)。
BTW,printf
中size_t
的控件格式字符串为printf
最后,语言规范没有定义编译器应该警告的内容。