简体   繁体   English

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

[英]If char c = 0x80, why does printf(“%d\n”, c << 1) output -256?

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

The output is -256 in this case. 在这种情况下,输出为-256 If I write c << 0 then the output is -128 . 如果我写c << 0那么输出是-128

I don't understand the logic behind this code. 我不明白这段代码背后的逻辑。

char may be signed on your platform, in which case 0x80 represents -128 (assuming two's complement). char可以在您的平台上签名,在这种情况下, 0x80代表-128(假设是2的补码)。

When a char is used as an operand with the << operator, it is promoted to int (still -128). char被用作<<运算符的操作数时,它被提升为int (仍然是-128)。 So when you apply the left-shift, you get -256. 所以当你应用左移时,你得到-256。 Technically, shifting negative values is implementation-defined undefined, but what you see is typical behaviour. 从技术上讲,转移负值是实现定义的未定义,但您看到的是典型行为。

Already your starting point is problematic: 您的出发点已经存在问题:

char c = 0x80;

If (as seemingly in your case) char is a signed type, you are assigning the integer constant 128 to a type that is only guaranteed to hold values up to 127 . 如果(看似在你的情况下) char是一个有符号的类型,你将整数常量128一个只能保证最多值为127 Your compiler then may choose to give you some implementation defined value ( -128 in your case I guess) or to issue a range error. 然后,您的编译器可能会选择为您提供一些实现定义的值(在您猜测的情况下为-128 )或发出范围错误。

Then you are doing a left shift on that negative value. 然后你正在对该负值进行左移。 This gives undefined behavior. 这给出了未定义的行为。 In total you have several implementation defined choices plus undefined behavior that determine the outcome: 总共有几个实现定义的选择加上确定结果的未定义行为:

  • signedness of char 签名的char
  • the choice of how to convert 128 to signed char 选择如何将128转换为signed char
  • the width of char char的宽度
  • the sign representation of int (there are three possibilities) int的符号表示(有三种可能性)
  • the choice on how to implement (or not) left shift on negative int 关于如何在负int上实现(或不)左移的选择

It may be a good exercise for you to look up all these case an to see what the different outcomes may be. 查看所有这些案例可能是一个很好的练习,以了解不同的结果。

In summary some recommendations: 总结一些建议:

  • choose an appropriate constant to initialize a variable 选择一个适当的常量来初始化变量
  • don't do arithmetic with plain char 不要使用普通char算术
  • don't do left shift on signed types 不要对签名类型进行左移

c is assigned 0x80 . c被分配0x80 Assuming 8-bit bytes, its value in binary representation, is 10000000 . 假设8位字节,其二进制表示的值为10000000 Apparently, on your platform, char is a signed type. 显然,在您的平台上, char是签名类型。 So, 0x80 (ie 10000000 ) corresponds to -128. 因此, 0x80 (即10000000 )对应于-128。

When << is applied to a char value, it is promoted to int and the sign is preserved. <<应用于char值时,它将被提升为int并保留符号。 So, when shifted once to the left, with 32-bit integers, it becomes 11111111111111111111111100000000 (two's complement) which is -256. 因此,当向左移动一次时,使用32位整数,它变为11111111111111111111111100000000 (二进制补码),即-256。

Just a side-note. 只是一个侧面说明。 From a bottom up perspective, bit-wise shifting (and masking) is based on an architecture's word-length (expressed in bits). 从下到上的角度来看,逐位移位(和屏蔽)基于架构的字长(以位表示)。 The length of a word, varies from architecture to architecture. 一个词的长度因建筑而异。

See this Wiki page for word lengths by architecture 按体系结构查看此Wiki页面的单词长度

If one knows the word length of the target architecture, one can use bit-shifting to multiply, and divide (in some cases), faster than using operands. 如果知道目标体系结构的字长,则可以使用位移来乘法,并且比使用操作数更快地除(在某些情况下)。

See this Wiki page for interesting diagrams of bit-shifting 有关位移的有趣图表,请参阅此Wiki页面

Since bit-shifted code is architecture dependent, one cannot assume a specific piece of bit-shifted code will work the same way from architecture to architecture. 由于位移代码依赖于体系结构,因此无法假设特定的位移代码将从架构到架构以相同的方式工作。 However, once one is familiar with the idea of different word lengths for different architectures, bit-shifting becomes less mysterious and more predictable. 然而,一旦熟悉不同体系结构的不同字长的想法,比特移位变得不那么神秘和可预测。

Thankfully, today we have 8, 16, 32, and 64 bit word lengths, and exclusively 8 bit character lengths. 值得庆幸的是,今天我们有8,16,32和64位字长,并且只有8位字符长度。 In the days of ancient computing, an architecture might have a 12, or a 15, or a 23 bit word length (etc., ad nauseum). 在古代计算的时代,架构可能具有12或15或23位字长(等等,令人作呕)。

I wonder why your compiler do not complain with a warning that 0x80 does not fit in char, which on your platform can represent only values from -0x80 to 0x7F. 我想知道为什么你的编译器不会抱怨0x80不适合char,你的平台上只能表示-0x80到0x7F的值。

Try this piece of code: 试试这段代码:

 #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;
 }

Your situation is called OVERFLOW. 您的情况称为OVERFLOW。

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

相关问题 printf在C中不输出char数组 - printf does not output char array in C 文件可以以0x80字符结尾吗? - Can a file end with a 0x80 char? C 语言中的按位运算 (0x80, 0xFF, &lt;&lt; ) - Bitwise operation in C language (0x80, 0xFF, << ) 为什么((unsigned char)0x80)&lt;&lt; 24的符号扩展到0xFFFFFFFF80000000(64位)? - Why does ((unsigned char)0x80) << 24 get sign extended to 0xFFFFFFFF80000000 (64-bit)? 什么是 char i=0x80 以及为什么在位移位中没有发生溢出 - what is char i=0x80 and why overflow did not happen in bit shifting 为什么redlib将INT8_MIN定义为(-0x80)而不是(-0x7F)或(-INT8_MAX - 1) - Why does redlib define INT8_MIN as (-0x80) and not as (-0x7F) or (-INT8_MAX - 1) C 按位运算中的 0x01 和 0x80 代表什么? - What are 0x01 and 0x80 representative of in C bitwise operations? 为什么printf(“%c”,1)返回笑脸而不是编码char为1 - Why does printf( “%c”, 1) return smiley face instead of coded char for 1 如果`char`转换为`int`,为什么``%c&#39;`存在于`printf`中? - Why does `“%c”` exist in `printf` if `char` is converted to `int`? 为什么printf(“%d \ n”,printf(“%d \ b”,a))以这种方式工作? - Why does printf(“%d\n”, printf(“%d\b”, a)) work this way?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM