为什么C或C ++标准没有明确地将char定义为signed或unsigned?

问题描述 投票:43回答:2
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定义为有符号或无符号以避免像上述代码那样的危险误用?

c++ c types compiler-construction standards
2个回答
49
投票

历史原因,大多数。

在大多数情况下,char类型的表达式被提升为int(因为许多CPU没有8位算术运算)。在某些系统上,符号扩展是最有效的方法,这使得简单的char签名。

另一方面,EBCDIC字符集具有高位设置的基本字符(即,值为128或更大的字符);在EBCDIC平台上,char几乎必须是未签名的。

ANSI C Rationale(1989年标准)在这个问题上没有太多可说的;第3.1.2.5节说:

指定了三种类型的char:signed,plain和unsigned。根据实施情况,普通的char可以表示为有符号或无符号,如在先前的实践中那样。引入类型signed char是为了在那些实现普通char为unsigned的系统上提供一个单字节有符号整数类型。出于对称的原因,允许使用关键字signed作为其他整数类型的类型名称的一部分。

更进一步,1975年的C Reference Manual的早期版本说:

char可能的任何地方都可以使用int物体。在所有情况下,char通过将其符号传播到结果整数的高8位来转换为int。这与用于字符和整数的二进制补码表示一致。 (但是,符号传播功能在其他实现中会消失。)

此描述比我们在后面的文档中看到的更具实现性,但它确实承认char可能是签名的或未签名的。在“符号传播消失”的“其他实现”上,将char对象提升为int将对8位表示进行零扩展,基本上将其视为8位无符号数量。 (该语言还没有signedunsigned关键字。)

C的直接前身是一种叫做B的语言.B是一种无类型语言,所以char被签名或未签名的问题不适用。有关C的早期历史的更多信息,请参阅已故的Dennis Ritchie's home page ,现在moved here

至于你的代码中发生了什么(应用现代C规则):

char c = 0xff;
bool b = 0xff == c;

如果普通的char是无符号的,那么c的初始化将它设置为(char)0xff,它在第二行中等于0xff。但如果简单的char被签名,那么0xffint类型的表达式)将转换为char - 但由于0xff超过CHAR_MAX(假设CHAR_BIT==8),结果是实现定义的。在大多数实现中,结果是-1。在比较0xff == c中,两个操作数都转换为int,使其等同于0xff == -1255 == -1,这当然是假的。

另一个需要注意的重要事项是unsigned charsigned char和(plain)char是三种不同的类型。 charunsigned charsigned char具有相同的代表性;它的实现定义了它是哪一个。 (另一方面,signed intint是同一类型的两个名字; unsigned int是一个独特的类型。(除此之外,只是为了添加到轻浮,它的实现 - 定义声明为int的位字段是有符号还是无符号。))

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


0
投票

char最初用于存储字符,因此无论是签名还是未签名都不重要。真正重要的是如何有效地在char上执行数学运算。所以依赖于系统,编译器会选择最合适的

在ARMv4之前,ARM没有本机支持加载半字和有符号字节。要加载一个有符号的字节,你必须LDRB然后签名扩展该值(LSL它然后ASR它退回)。这很痛苦,因此默认情况下char未签名。

why unsigned types are more efficent in arm cpu?

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

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

© www.soinside.com 2019 - 2024. All rights reserved.