繁体   English   中英

为什么printf和cout对此代码给出不同的输出?

[英]Why does printf and cout gives different output for this code?

int main(){
    ll a=pow(2,32);
    cout <<a<<endl;
    cout << (-1<<1)<<endl;
    printf("%x",-1<<1);
}

对于上面的代码,我得到以下输出:

4294967296
-2
fffffffe

十进制的4294967296等于十六进制的fffffffe ,基本上是2^32 为什么printf和cout的行为不同? 以及这种转变是如何工作的?

printf格式标志%x表示以十六进制打印整数值。

也有一个流操纵器来完成此操作( std::hex ),但是您没有使用它。 当您向没有操纵器的流输出整数值时,其输出以10为底。

看到这里有关的更多信息printf格式标志, 并在这里关于流操纵信息。

移位运算符<<工作方式在C ++ 03 Standard(14882:2003)中进行了描述:

5.8移位运算符

1 /移位运算符<<和>>从左到右分组。 shift-expression:加法表达式加法表达式<<加法表达式加法表达式>>加法表达式

操作数应为整数或枚举类型,并执行整数提升。 结果的类型是提升后的左操作数的类型。 如果右操作数为负或大于或等于提升后的左操作数的位长度,则该行为是不确定的。

2 / E1 << E2的值是E1(被解释为位模式)左移的E2位位置; 空出的位为零。 如果E1具有无符号类型,则将结果的值乘以E1乘以幂2的幂2,如果E1具有无符号长类型,则将结果取ULONG_MAX + 1为模,否则为UINT_MAX + 1。

[注意:常量ULONG_MAX和UINT_MAX在标头中定义)。 ]

在您的情况下,二进制值-1的每一位都为1 s。 对于32位值,则:

11111111 11111111 11111111 11111111 

如果使用<<将此值左移1位,则会得到:

11111111 11111111 11111111 11111110 

在基数10中为-2。 由于操作-1<<1对LHS使用负数,因此整个表达式为带符号(而非无符号)类型。

第一,

以十六进制表示的fffffffe,基本上是2 ^ 32

是错的。 FFFFFFFE = 4294967294,即2 ^ 32-2(对于无符号整数)或-2(对于2的补码中的32位有符号整数)。

其次, printf("%x", ...)将输出一个无符号的十六进制整数(即unsigned int ),在大多数现代系统上,该整数为32位。 long long a = 2 << 32需要64位整数才能正确存储它(或更准确地说,至少需要33位整数),因此,当您使用cout << a ,您正在调用ostream& operator<<(ostream&, long long) ,其类型正确。 也就是说,由于printf说明符使用的类型与C ++ operator<<重载使用的强类型,因此您遇到了溢出问题。

该代码调用了未定义的行为,因为您正尝试左移负数, C ++ 11标准草案 5.8 移位运算符说( 强调我的意思 ):

E1 << E2的值是E1左移E2位的位置; 空出的位为零。 如果E1具有无符号类型,则结果的值为E1×2E2,以比结果类型中可表示的最大值大的模数减少1。 否则,如果E1具有带符号类型且非负值 ,并且E1×2E2在结果类型中可表示,则这是结果值;否则,结果为0。 否则, 行为是undefined

草案C99标准6.5.7节的按位移位运算符也是如此

printf指定无效的转换说明符也是未定义的行为 ,您指定%x期望有一个无符号的int但结果是带符号的 7.19.6.1节中的C99草案 fprintf函数9段说:

如果转换规范无效,则行为是不确定的。248)如果任何参数不是对应转换规范的正确类型,则行为是不确定的。

%x将输出格式化为以十六进制显示值,因此您应该将std::hex传递给cout以执行相同的操作:

std::cout << std:::hex << (-1<<1) << endl;
             ^^^^^^^^^

这将产生与您需要告​​诉其以十六进制打印的方式相同的正确答案

cout << hex<<(-1<<1)<<endl;
printf("%x",-1<<1);

%x是十六进制,其中cout只是直接输出数字(int)-1是0xFFFFFFFF向左移动,您必须向左添加0,即最后一个F(二进制中的1111变为1110,即E)我想更清楚地回答您的观点。

为了便于阅读,我们使用带符号的8位整数:

-1 bitwise is 11111111

现在左移1:

-1 << 1

你得到:

11111110 which is -2

但是,使用转换说明符%x告诉printf()11111111设为符号,以便打印出fe

第一种情况是有效的: pow(2,32)返回精确值2 32作为double ,当您将其转换为long long时,它仍然是精确值(我假设这就是神秘的ll类型)。 使用cout打印此文件是完全有效的。

(-1<<1)的情况下,左移负数会产生不确定的行为。 即使编译器确实将其定义为-2(大多数情况下也是如此),传递负数以与printf%x说明符(要求使用无符号类型)一起使用也是一种不确定的行为。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM