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 定义为有符号或无符号以避免像上面的代码这样的危险误用?
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 定义为有符号或无符号以避免像上面的代码这样的危险误用?
历史原因,主要是。
char
在大多数情况下都会提升类型表达式int
(因为很多 CPU 没有 8 位算术运算)。在某些系统上,符号扩展是执行此操作的最有效方式,它主张制作明文char
签名。
另一方面,EBCDIC 字符集具有高位集的基本字符(即,值为 128 或更大的字符);在 EBCDIC 平台上,char
几乎必须是未签名的。
ANSI C Rationale (针对 1989 标准)在这个主题上没有太多可说的。第 3.1.2.5 节说:
指定了三种类型的 char:
signed
、plain 和unsigned
. 一个plainchar
可以表示为有符号或无符号,这取决于实现,如在先前的实践中。引入该类型signed char
是为了在那些将普通 char 实现为无符号的系统上提供单字节有符号整数类型。出于对称原因,signed
允许关键字作为其他整数类型的类型名称的一部分。
再往前追溯,1975 年的C 参考手册的早期版本说:
一个
char
对象可以在任何地方使用int
。在所有情况下, 通过将其符号传播到结果整数的高 8 位,将其char
转换为 an 。int
这与用于字符和整数的二进制补码表示一致。(但是,符号传播功能在其他实现中消失了。)
这个描述比我们在后面的文档中看到的更具体,但它确实承认char
可能是签名的或未签名的。在“符号传播消失”的“其他实现”中,将char
对象提升为int
将对 8 位表示进行零扩展,本质上将其视为 8 位无符号量。(该语言还没有signed
orunsigned
关键字。)
C 的直接前身是一种称为 B 的语言。B 是一种无类型语言,因此有char
符号或无符号的问题不适用。有关 C 早期历史的更多信息,请参阅已故的 Dennis Ritchie 的主页,现在移至此处。
至于您的代码中发生了什么(应用现代 C 规则):
char c = 0xff;
bool b = 0xff == c;
如果 plainchar
是无符号的,则初始化c
将其设置为,在第二行(char)0xff
中比较等于。0xff
但是如果 plainchar
是有符号的,那么0xff
(类型的表达式int
)被转换为char
-- 但由于0xff
超过 CHAR_MAX(假设CHAR_BIT==8
),结果是实现定义的。在大多数实现中,结果是-1
。在比较0xff == c
中,两个操作数都转换为int
,使其等价于0xff == -1
, 或255 == -1
,这当然是错误的。
另一个需要注意的重要事情是unsigned char
,signed char
和 (plain)char
是三种不同的类型。 具有与orchar
相同的表示;它是由实现定义的。(另一方面,and是同一类型的两个名称;是不同的类型。(除了只是为了增加轻浮性,它是实现定义的,声明为普通的位字段是有符号的还是无符号的。)) unsigned char
signed char
signed int
int
unsigned int
int
是的,这有点乱,我敢肯定,如果今天从头开始设计 C,它的定义会有所不同。但是 C 语言的每个版本都必须避免破坏(太多)现有代码,以及在较小程度上破坏现有实现。
char
起初是为了存储字符,所以它是有符号还是无符号并不重要。真正重要的是如何char
有效地进行数学运算。所以取决于系统,编译器会选择最合适的
在 ARMv4 之前,ARM 不支持加载半字和有符号字节。要加载有符号字节,您必须先使用 LDRB 然后对值进行符号扩展(LSL 向上然后 ASR 向下)。这很痛苦,因此默认情况下 char 是无符号的。
事实上,许多 ARM 编译器仍然unsigned char
默认使用,因为即使您可以在现代 ARM ISA 上加载带有符号扩展的字节,该指令仍然不如零扩展版本灵活
而且大多数现代编译器还允许您更改 char 的符号而不是使用默认设置