47
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 定义为有符号或无符号以避免像上面的代码这样的危险误用?

4

2 回答 2

53

历史原因,主要是。

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 位无符号量。(该语言还没有signedorunsigned关键字。)

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 charsigned intintunsigned intint

是的,这有点乱,我敢肯定,如果今天从头开始设计 C,它的定义会有所不同。但是 C 语言的每个版本都必须避免破坏(太多)现有代码,以及在较小程度上破坏现有实现。

于 2013-03-20T19:42:39.037 回答
0

char起初是为了存储字符,所以它是有符号还是无符号并不重要。真正重要的是如何char有效地进行数学运算。所以取决于系统,编译器会选择最合适的

在 ARMv4 之前,ARM 不支持加载半字和有符号字节。要加载有符号字节,您必须先使用 LDRB 然后对值进行符号扩展(LSL 向上然后 ASR 向下)。这很痛苦,因此默认情况下 char 是无符号的。

为什么无符号类型在 arm cpu 中更有效?

事实上,许多 ARM 编译器仍然unsigned char默认使用,因为即使您可以在现代 ARM ISA 上加载带有符号扩展的字节,该指令仍然不如零扩展版本灵活

而且大多数现代编译器还允许您更改 char 的符号而不是使用默认设置

于 2013-09-19T15:02:54.323 回答