为什么C或C ++标准没有将char定义为signed或unsigned?
int main() { char c = 0xff; bool b = 0xff == c; // Under most C/C++ compilers' default options, b is FALSE!!! }
C或C ++标准都没有将char指定为有符号或无符号,它是实现定义的。
为什么C / C ++标准没有明确地将char定义为signed或unsigned,以避免像上面的代码那样危险的错误使用?
历史原因大多。
char
types的expression式在大多数情况下被提升为int
(因为很多CPU没有8位算术运算)。 在某些系统上,签名扩展是最有效的方法,这个方法可以使得简单char
被标记。
另一方面,EBCDIC字符集具有设置了高位的基本字符(即,值为128或更大的字符)。 在EBCDIC平台上, char
必须是未签名的。
ANSI C理论 (针对1989年的标准)在这个问题上没有太多的说法; 第3.1.2.5节说:
指定了三种types的字符:
signed
,plain和unsigned
。 一个普通的char
可以表示为有符号的或无符号的,这取决于实施方式,如在先前的实践中那样。 引入typessigned char
是为了在实现普通字符为unsigned的系统上提供一个字节的有符号整数types。 出于对称的原因,被signed
的关键字被允许作为其他整型的types名称的一部分。
回顾一下,从1975年的C参考手册早期版本说:
一个
char
对象可能在int
任何地方使用。 在所有的情况下,通过在结果整数的高8位传播它的符号,char
被转换为int
。 这与用于字符和整数的二进制补码表示是一致的。 (但是,在其他实现中,符号传播function会消失。)
这个描述比我们在后面的文档中看到的更具体的实现,但是它确实承认char
可以是有符号的,也可以是无符号的。 在“符号传播消失”的“其他实现”上,将char
对象提升为int
将对8位表示进行零扩展,本质上将其视为8位无符号数量。 (该语言还没有signed
或未unsigned
关键字。)
C的前身是一种叫B的语言。B是一种无types的语言,所以char
被签名或未签名的问题不适用。 有关C的早期历史的更多信息,请参阅已故丹尼斯·里奇的主页 ,现在移到这里 。
至于你的代码中发生了什么(应用现代的C规则):
char c = 0xff; bool b = 0xff == c;
如果plain char
是无符号的,那么c
的初始化将它设置为(char)0xff
,它等于第二行中的0xff
。 但是,如果使用纯char
进行签名,则0xff
( int
types的expression式)将转换为char
– 但由于0xff
超过了CHAR_MAX(假设CHAR_BIT==8
),因此结果是实现定义的 。 在大多数实现中,结果是-1
。 在比较0xff == c
,两个操作数都转换为int
,使其等于0xff == -1
或255 == -1
,这当然是错误的。
另外需要注意的是, unsigned char
, signed char
和(plain) char
是三种不同的types。 char
与unsigned char
或 signed char
具有相同的表示forms; 它是实现定义的。 (另一方面, signed int
和int
是同一types的两个名字; unsigned int
是一个不同的types(除了只是添加到轻浮的地方,它是实现定义的,是否声明为int
的位域是有符号的或无符号。))
是的,这一切都是一团糟,我相信如果C是从头开始devise的,那么它的定义就会有所不同。 但是,C语言的每一个版本都不得不避免破坏(太多)现有的代码,并且在现有的实现方面的程度较低。
char首先是为了存储字符,所以有符号或无符号并不重要。 真正重要的是如何有效地对char进行math运算。 所以依靠这个系统,编译器会select最合适的
在ARMv4之前,ARM本身不支持加载半字和有符号字节。 要加载一个有符号的字节,你必须对LDRB进行符号扩展(LSL,然后ASR回降)。 这很痛苦,所以char默认是无符号的。