输出负整数到%u格式说明符

时间:2017-12-10 14:36:49

标签: c format-specifiers

考虑以下代码

void SlideLcd::dialValueChanged(int value)
{
  double v = (double)((double)value / 100);
  lcd->display(v);
}

%d接受负数,因此在第一种情况下输出为-121。 案例2和案例3的输出是4294967175.我不明白为什么?

4 个答案:

答案 0 :(得分:2)

待办事项

  

2 32 - 121 = 4294967175

由于printf

%会解释您提供的数据

  • %d有符号整数,值从-2 31 到2 31 -1
  • %u无符号整数,值从0到2 32 -1

在二进制中,两个整数值(-121和4294967175)都是(当然)相同:

`0xFFFFFF87`

请参阅Two's complement

答案 1 :(得分:1)

printf使用名为可变参数的东西。如果你对它们做一个简短的研究,你会发现使用它们的函数不知道你传递给它的输入的类型。因此,必须有一种方法来告诉函数它必须如何解释输入,并且您正在使用格式说明符。

在您的特定情况下,c是一个8位有符号整数。因此,如果您将其设置为其中的文字-121,则会记住:10000111。然后,通过整数提升机制,您将其转换为int:11111111111111111111111110000111

使用“%d”,告诉printf将11111111111111111111111110000111解释为有符号整数,因此输出为-121。但是,对于“%u”,您告诉printf 11111111111111111111111110000111 无符号 整数,因此它会输出4294967175

编辑:正如评论中所述,实际上行为在C中是未定义的。那是因为你有多种方法来编码负数(符号和模数,一个补码,......)和其他方面(如endianness,如果我没错,会影响这个结果)。因此,结果被称为实现定义。因此,您可能会获得不同的输出,而不是4294967175。但我解释的对于相同字符串的不同解释以及可变参数中数据类型的损失的主要概念仍然存在。

尝试将数字转换为基数10,首先将其转换为纯二进制数,然后知道它以32位二进制补码存储...您会得到两个不同的结果。但如果我不告诉你你需要使用哪种解释,那二进制字符串就可以代表一切(一个4字符的ASCII字符串,一个数字,一个小的8位2x2图像,你的安全组合, ...)。

编辑:您可以想到“%< format_string>”作为该字符串的一种“扩展”。你知道,当你创建一个文件时,你通常会给它一个扩展名,实际上它是文件名的一部分,以便记住该文件的格式/编码存储。假设您在PC上将自己喜欢的歌曲保存为song.ogg文件。如果您在song.txtsong.odtsong.pdfsongsong.akwardextension中重命名该文件,则不会更改文件的内容。但是如果你尝试用通常与.txt或.whatever相关联的程序打开它,它会读取文件中的字节,但是当它试图解释字节序列时它可能会失败(这就是为什么如果你打开song.ogg使用Emacs或VIm或任何文本编辑器,你看起来像垃圾信息,如果你打开它,例如,GIMP,GIMP无法读取它,如果你用VLC打开它,你听你最喜欢的歌曲)。扩展只是提醒您:它提醒您如何解释该位序列。由于printf不知道该解释,你需要提供一个解释,如果你告诉printf有符号整数是非符号的,那么就像用Emacs打开song.ogg一样......

答案 2 :(得分:1)

printf是一个具有可变参数的函数。在这种情况下,"默认参数促销"在调用函数之前应用于参数。在您的情况下,c首先从char转换为int,然后发送到printf。转换不依赖于相应的'%'格式的说明符。此int参数的值可以解释为4294967175或-121,具体取决于签名。 C标准中的相应部分是:

  

6.5.2.2函数调用

     

6 - ...如果表示被调用函数的表达式具有不包含a的类型   原型,对每个参数执行整数提升,并对其进行参数化   将float类型提升为double。这些被称为默认参数   促销。

     

7-如果表示被调用函数的表达式具有包含原型的类型,   参数被隐式转换,就像通过赋值一样,转换为类型   相应的参数,将每个参数的类型作为不合格的版本   其声明的类型。函数原型声明符中的省略号表示法导致   参数类型转换在最后声明的参数后停止。默认参数   促销是在尾随参数上进行的。

答案 3 :(得分:1)

如果char在您的编译器中签名(这是最可能的情况)并且是8位宽(极有可能),那么c+=10将溢出它。有符号整数的溢出会导致未定义的行为。这意味着您无法推断您获得的结果。

如果char未签名(在大多数PC平台上不太可能),请参阅其他答案。