C中的简单字符解释

这是我的代码

#include void main() { char ch = 129; printf("%d", ch); } 

我得到输出为-127。 这是什么意思?

这意味着char是一个8位变量,只能容纳2 ^ 8 = 256个值,因为声明是char chch是有signed变量,这意味着它可以存储127个负值和正值。 当你要求超过127时,该值从-128开始。

可以把它想象成一些街机游戏,你从屏幕的一边走到另一边:

ch = 50;

  -----> 50 is stored |___________________________________|___________| since it fits -128 0 50 127 between -127 and 128 

ch = 129;

  --- 129 goes over --> 127 by 2, so |__|____________________________________________| it 'lands' in -128 -127 0 127 -127 

但!! 你不应该依赖它,因为它是未定义的行为!


为了纪念Luchian Grigore,这里有一点代表:

char是一个容纳8位或一个字节的变量。 所以我们有8个0和1个努力代表你想要的任何价值。 如果char是带signed变量,它将表示它是正数还是负数。 你可能读到了代表符号的那一位,这是真实过程的抽象; 事实上,它只是电子产品中首批实施的解决方案之一。 但是这样一个简单的方法有一个问题,你将有两种方式来表示0(+0和-0):

 0 0000000 -> +0 1 0000000 -> -0 ^ ^ |_ sign bit 0: positive |_ sign bit 1: negative 

保证不一致!! 因此,一些非常聪明的人想出了一个名为Ones’Complement的系统,它代表一个负数,作为其正面对应的否定(NOT操作):

 01010101 -> +85 10101010 -> -85 

这个系统……有同样的问题。 0可以表示为00000000 (+0)和11111111 (-0)。 然后是一些聪明的人创造了Two’s Complement,它将保留前面方法的否定部分,然后加1,因此删除那个讨厌的-0并给我们一个shiny的新数字到我们的范围:-128!。 那么我们的产品系列现在如何?

 00000000 +0 00000001 +1 00000010 +2 ... 01111110 +126 01111111 +127 10000000 -128 10000001 -127 10000010 -126 ... 11111110 -2 11111111 -1 

因此,当我们的小处理器尝试向变量添加数字时,这应该可以了解发生了什么:

  0110010 50 01111111 127 +0000010 + 2 +00000010 + 2 ------- -- -------- --- 0110100 52 10000001 -127 ^ ^ ^ |_ 1 + 1 = 10 129 in bin _| |_ wait, what?! 

是的,如果你查看上面的范围表,你可以看到最多127( 01111111 )二进制文件很好,花花公子,没有什么奇怪的事情发生,但是在第8位设置为-128( 10000000 )之后,这个数字不再被解释保持其二进制幅度,但两个补语表示。 这意味着,二进制表示,变量中的位,1和0,我们心爱的char的核心,确实拥有129 ……它在那里,看看它! 但是,邪恶的处理器读到这一点-127导致变量HAD被signed破坏了它在第一维欧几里德空间中通过实数线的臭味转移的所有正面潜力。

这意味着您遇到了未定义的行为

任何结果都是可能的。

char ch=129; 是UB,因为129不是特定设置的char的可表示值。

您的char很可能是一个8位有符号整数,使用二进制补码存储。 这样的变量只能表示-128到127之间的数字。如果你做“127 + 1”,它会回绕到-128。 所以129相当于-127。

这是因为char在一个字节上编码,因此8位数据。

事实上, char具有以7位编码的值,并且对于符号具有一位, unsigned char具有其值的8位数据。

这意味着:

将abcdefgh分别取为8位(a为最左边的位,h为最右边的位),该值用符号编码,bcdefgh用二进制格式编码为实数值:

42(十进制)= 101010(二进制)存储为:abcdefgh 00101010

从内存中使用此值时:a为0:数字为正,bcdefgh = 0101010:值为42

放129后会发生什么:

129(十进制)= 10000001(二进制)存储为:abcdefgh 10000001

当从存储器中使用该值时:a为0:数字为负,我们应该减去1并反转该值中的所有位,因此(bcdefgh – 1)反转= 1111111:值为127数字为-127

在您的系统上:char 129与8位有符号整数-127具有相同的位。 无符号整数从0到255,有符号整数-128到127。

相关(C ++):

您可能还有兴趣阅读什么是无符号字符的最佳答案?

正如@jmquigley指出的那样。 这是严格未定义的行为,您不应该依赖它。 在C / C ++中允许有符号整数溢出

char类型是一个8位有符号整数。 如果您在二进制补码表示中解释无符号字节129 的表示,则得到-127。

char类型可以是signedunsigned ,由编译器决定。 大多数编译器都将其标记为“已签名”。

在您的情况下,编译器以静默方式将整数129转换为其带符号的变量,并将其置于8位字段中,从而产生-127。

char是8位,已签名 。 它只能保存-128到127的值。当您尝试为其分配129时,您将看到您看到的结果,因为指示签名的位被翻转。 想到它的另一种方式是数字“包裹”。

普通char是有符号还是无符号,是实现定义的行为 。 这是C语言中一个非常愚蠢,模糊的规则。 intlong等保证被签名,但char可以是有符号无符号的,这取决于编译器的实现。

在您的特定编译器上, char显然已签名。 这意味着,假设您的系统使用两个补码,它可以保持-128到127的值。

您尝试将值129存储在此类变量中。 这会导致未定义的行为 ,因为您会得到整数溢出。 严格地说,当你这样做时,任何事情都可能发生。 该程序可以打印“hello world”或开始拍摄无辜的旁观者,并且仍然符合ISO C.实际上,大多数(所有?)编译器将实现这种未定义的行为作为“环绕”,如其他答案中所述。

总而言之,您的代码依赖于标准未明确定义的两种不同行为。 了解这种不可预测的代码的结果如何以某种方式结束的价值有限。 这里重要的是要认识到代码是模糊的,并学习如何以不模糊的方式编写代码。

例如,代码可以重写为:

unsigned char ch = 129;

甚至更好:

 #include  ... uint8_t ch = 129; 

根据经验,请务必遵循MISRA-C:2004中的这些规则:

6.1普通字符类型仅用于存储和使用字符值。

6.2 signedunsigned char类型仅用于存储和使用数值。