【问题标题】:Why don't the C or C++ standards explicitly define char as signed or unsigned?为什么 C 或 C++ 标准不明确将 char 定义为有符号或无符号?
【发布时间】:2013-03-10 02:41:34
【问题描述】:
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++ 标准”标准。但这个问题代表了这两种标准。
  • 通常标准会明确未定义的内容,以便实现灵活地执行他们认为适合(或快速)其平台的任何事情。
  • @teppic:不正确。 int 总是等价于signed intunsigned int 是一个独特的类型。
  • // b is always FALSE!!! char 未签名的实现上是正确的。
  • @WhozCraig:是的,但这并不能解释为什么它是由实现定义的。

标签: c++ c types compiler-construction standards


【解决方案1】:

主要是历史原因。

char 类型的表达式在大多数情况下会提升为int(因为很多 CPU 没有 8 位算术运算)。在某些系统上,符号扩展是执行此操作的最有效方式,它主张使普通的 char 签名。

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

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

指定了三种类型的 char:signed、plain 和 unsigned。一种 普通的char 可以表示为有符号或无符号,具体取决于 在实施时,与以前的做法一样。类型signed char 被引入以提供单字节有符号整数类型 那些将普通字符实现为无符号的系统。出于以下原因 对称性,关键字signed 允许作为类型名称的一部分 其他整数类型。

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

char 对象可以在int 所在的任何地方使用。在所有情况下 char 被转换为 int 通过将其符号传播到上部 结果整数的 8 位。这与两人的一致 用于字符和整数的补码表示。 (但是,符号传播功能在其他 实现。)

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

C 的直接前身是一种称为 B 的语言。B 是一种无类型语言,因此 char 是有符号还是无符号的问题不适用。有关 C 早期历史的更多信息,请参阅已故 Dennis Ritchie 的 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 == -1,或者255 == -1,当然是假的。

另一个需要注意的重要事情是unsigned charsigned char 和(普通)char 是三种不同的类型。 chareither unsigned char signed char 具有相同的表示;它是由实现定义的。 (另一方面,signed intint 是同一类型的两个名称;unsigned int 是一个不同的类型。(除了只是为了增加轻浮性,它是实现定义的位字段是否声明为普通的int 有符号或无符号。))

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

【讨论】:

  • 边栏:AS/400 和 OS/390 都充分利用了它们各自的 EBCDIC 字符集的位布局,用于在底层硬件中实现的基数树。很难获得比这些平台更多的实现定义。
  • 这如何适用于wchar_t
  • @ipc:不适用; wchar_t 是一个独特的类型。在 C 中,wchar_t 是在 <stddef.h> 中定义的 typedef。它是一个整数类型,但标准没有指定它的符号。在 C++ 中,它是一种独特的预定义整数类型,具有与其他整数类型之一相同的特征。
  • @KeithThompson:为什么? char[32|64]_t 未签名,wchar_t 实现定义的签名对我来说没有意义。
  • @ipc:你的意思是char[16|32]_t。两者都是 C 的最新添加(作为<uchar.h> 和 C++ 中的 typedefs(作为基本类型)。我同意字符类型通常是无符号的,但是当 wchar_t 被添加到可能不是的语言中时t 和现在一样清楚。char 具有实现定义的签名,原因是我试图在我的回答中解释。当wchar_t 被定义时,可能适用相同的原因。(我不认为甚至很清楚 wchar_t 必然是 Unicode。)
【解决方案2】:

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

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

why unsigned types are more efficent in arm cpu?

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

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

【讨论】:

  • 所谓的字符类型在 C 中有两种用途:存储字符,或访问原始存储。该标准实际上更多地关注第二种用法,因为该标准要求实现遵守有关字符类型的特殊保证,这在访问原始存储时通常是必不可少的,但在处理表示实际字符的数据时却无用地阻碍了优化。
猜你喜欢
  • 2010-10-29
  • 1970-01-01
  • 2018-03-09
  • 2014-08-20
  • 2011-04-19
  • 2017-04-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多