如果char c = 0x80,为什么printf(“%d \ n”,c<< 1)输出-256?

时间:2010-10-22 23:25:05

标签: c integer-promotion

#include<stdio.h>
int main(void)
{
  char c = 0x80;
  printf("%d\n", c << 1);
  return 0;
}

在这种情况下,输出为-256。如果我写c << 0,则输出为-128

我不明白这段代码背后的逻辑。

5 个答案:

答案 0 :(得分:18)

char可能会在您的平台上签名,在这种情况下0x80代表-128(假设是补码)。

char用作<<运算符的操作数时,它会被提升为int(仍为-128)。所以当你应用左移时,你得到-256。从技术上讲,转移负值是实现定义的未定义,但你看到的是典型的行为。

答案 1 :(得分:6)

您的出发点已经存在问题:

char c = 0x80;

如果(在您的情况下似乎)char是签名类型,则您将整数常量128分配给仅保证最多可保留127的值的类型。然后,您的编译器可能会选择为您提供一些实现定义的值(我猜你的情况为-128)或发出范围错误。

然后你正在对该负值进行左移。这给出了未定义的行为。总共有几个实现定义的选择加上确定结果的未定义行为:

  • char
  • 的签名
  • 选择如何将128转换为signed char
  • char
  • 的宽度
  • int的符号表示(有三种可能性)
  • 选择如何在否定int
  • 上实施(或不实施)左移

查看所有这些案例可能是一个很好的练习,看看会有什么不同的结果。

总结一些建议:

  • 选择适当的常量来初始化变量
  • 不要使用普通char
  • 进行算术运算
  • 不要对签名类型进行左移

答案 2 :(得分:3)

c已分配0x80。假设8位字节,其二进制表示的值为10000000。显然,在您的平台上,char是签名类型。因此,0x80(即10000000)对应于-128。

<<应用于char时,会将其提升为int并保留该符号。因此,当向左移动一次时,使用32位整数,它变为11111111111111111111111100000000(二进制补码),即-256。

答案 3 :(得分:1)

只是旁注。从下到上的角度来看,逐位移位(和屏蔽)基于架构的字长(以位表示)。一个单词的长度因建筑而异。

See this Wiki page for word lengths by architecture

如果知道目标体系结构的字长,可以使用位移来乘以和(在某些情况下)除以比使用操作数更快的速度。

See this Wiki page for interesting diagrams of bit-shifting

由于位移代码依赖于体系结构,因此无法假设特定的位移代码将从架构到架构以相同的方式工作。然而,一旦熟悉不同体系结构的不同字长的想法,位移变得不那么神秘和可预测。

值得庆幸的是,今天我们有8,16,32和64位字长,并且只有8位字符长度。在古代计算时代,一个架构可能有12个,15个或23个字长(等等,令人作呕)。

答案 4 :(得分:0)

我想知道为什么你的编译器不会抱怨0x80不适合char,你的平台上只能表示-0x80到0x7F的值。

试试这段代码:

 #include <stdio.h>
 #include <limits.h>
 #include <stdlib.h>

 int main() {
      printf("char can represent values from %d to %d.\n", CHAR_MIN, CHAR_MAX);
      return EXIT_SUCCESS;
 }

您的情况称为OVERFLOW。