printf格式说明符不正确

时间:2017-02-18 12:11:14

标签: c++ gcc

编译此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是正确的,因为这是一个严重的符号/无符号问题,错误的格式说明符显示错误的结果。

2 个答案:

答案 0 :(得分:5)

根据GCC(最低版本5.0)文档,

  

-Wformat-signedness 如果指定了 -Wformat ,如果格式字符串需要无符号参数且参数已签名,则也会发出警告,反之亦然。

此设置不是默认设置,因为标准不要求它。如果您想要超过标准,则需要使用正确的标志。在您的情况下,您通过 -Wall 使用 -Wformat ,而不是 -Wformat-signedness 。请记住, -Wall 不会打开所有警告,只是几乎所有人都同意的子集。

编译器会检查类型安全性,因此在删除long-specifier时会出现警告的原因。除此之外,如果数据没有丢失(如签名到无符号或无符号签名或从int到long的促销等),他们可以随意做任何事情。实际上,clang没有标志检查格式(AFAIK)。

答案 1 :(得分:1)

以不恰当的方式使用printfundefined behavior的一个实例。实施可以do非常糟糕的事情(包括你梦寐以求的事情)。

BTW,使用C ++,您最好使用std::cout及其operator <<,这样不易出错。

  

为什么gcc / clang编译这些格式说明符?

大多数情况下,它没有(但是,编译器可能知道有关printf有时优化对它的调用,因为您的<stdio.h>标头将使用特定{ {1}} function attributes)。在您的情况下,编译器可能会调用标准C库提供的可变参数format没有编译格式说明符(只是将文字常量作为第一个参数传递给printf)。

BTW,printfsize_t的控件格式字符串为printf

最后,语言规范没有定义编译器应该警告的内容。