为啥 C 或 C++ 标准不明确将 char 定义为有符号或无符号?

Posted

技术标签:

【中文标题】为啥 C 或 C++ 标准不明确将 char 定义为有符号或无符号?【英文标题】: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:是的,但这并不能解释为什么它是由实现定义的。 【参考方案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 上加载带有符号扩展的字节,该指令仍然不如零扩展版本灵活

is char signed or unsigned by default on ios? char is unsigned by default on android NDK

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

【讨论】:

所谓的字符类型在 C 中有两种用途:存储字符,或访问原始存储。该标准实际上更多地关注第二种用法,因为该标准要求实现遵守有关字符类型的特殊保证,这在访问原始存储时通常是必不可少的,但在处理表示实际字符的数据时却无用地阻碍了优化。

以上是关于为啥 C 或 C++ 标准不明确将 char 定义为有符号或无符号?的主要内容,如果未能解决你的问题,请参考以下文章

在 C++11 标准中,为啥要依赖 char 类型的实现?

c++ 不使用 C 标准库将字符串和 int 转换为 char*

一个c++有关问题,当定义一个char 型数组时,用cin循环输入为啥会出错,怎么解决,代码如下

为啥从字符串常量转换为 'char*' 在 C 中有效但在 C++ 中无效

为啥 C 字符文字是整数而不是字符?

C++ c++初识