当x是签名字符时,为什么mingw会对printf(“%d”,x)发出警告?

时间:2014-10-01 13:57:29

标签: c char printf mingw

编译时

signed char x=1;
printf("%d",x);

有了mingw,我收到以下警告:

format '%d' expects argument of type 'int *', but argument 2 has type 'signed char *' [-Wformat=]

我知道%d打印十进制整数而不是字符,还有一个(签名)char指针作为第二个参数传递。但是x不应该被提升为int然后被正确打印?

为什么mingw在这里警告我?

编辑:对不起,这个警告实际上是由scanf调用产生的:

signed char x;
scanf("%d",&x);

(其他代码在没有警告的情况下编译。)

我认为这是因为scanf会尝试为int编写字节数,而我只保留了char的存储空间,这可能会产生运行时错误......?

2 个答案:

答案 0 :(得分:0)

参数的类型必须与相应的转换说明符匹配,或者行为是 undefined ,这意味着编译器不必以任何特定的方式处理这种情况;您甚至收到警告的事实是因为编译器编写者超出了标准的要求(并且因为它是常见的错误)。

问题是%d指定相应的参数指向类型为int的对象,该对象很可能大于char类型的对象。您的scanf调用假设它有sizeof (int)个字节要写入,但您要写入的对象只有1个字节宽(根据定义sizeof (char) == 1)。一个可能的后果是写入错误的字节,导致最好意外输入。

例如,假设sizeof (int) == 4,x的地址为0x4000,并且这是 big-endian 架构,意味着多字节类型中的最高有效字节存储在指定的地址。这就是内存在x

的声明和初始化之后的样子
         +----+
 0x4000: | 01 |    <-- x
         +----+
 0x4001: | ?? |
         +----+
 0x4002: | ?? |
         +----+
 0x4003: | ?? |
         +----+

其中??表示未知字节值。现在,假设在scanf来电中输入2。由于转换说明符为%d,因此scanf调用将假定它正在写入int对象,这意味着您的内存将如下所示:

         +----+
 0x4000: | 00 |    <-- x
         +----+
 0x4001: | 00 |
         +----+
 0x4002: | 00 |
         +----+
 0x4003: | 02 |
         +----+

当您检查x时,它将包含0,这不是您所期望的。此外,x之外的内存已被修改,这可能会导致其他问题。

同样,这是基于特定架构细节的一个可能的结果;在小端架构上,它似乎可以工作,因为最不重要的字节位于0x4000,但你仍然在该对象的边界外写字。

修改

正如DevSolar指出的那样,如果您打算读取数值并将它们存储到%hhd对象(即,您希望存储值),则应该使用char转换说明符 2而非字符 '2')。如果要存储单个字符值('2''a''?'等),请使用%c转换说明符。

答案 1 :(得分:-1)

它警告你,因为你写了......

signed char x = 1;
printf( "%hhd", x ); // 'hh' length modifier to indicate char argument

此外,请参阅Blue Moon的评论:关于指针类型的错误消息表示scanf(),而不是printf(),以及scanf()中的错误长度修饰符是彻头彻尾的致命(将sizeof( int )个字节写入只能sizeof( signed char ) ...的地方。

如果不明显,长度修饰符也适用于scanf()

signed char x;
if ( scanf( "%hhd", &x ) == 1 )
{
    // do yourself a favor and DO check scanf() success
    // before acting on the results
}