11

C标准库函数中,字符串的元素是chars。是否有充分的理由决定它而不是unsigned char

使用unsigned char8 位字符串有一些优点,虽然很小:

  • 它更直观,因为我们通常将 ASCII 代码记忆为无符号值,并且在处理二进制数据时,我们更喜欢无符号的 0x00 到 0xFF 范围,而不是处理负数。所以我们必须投。
  • 使用无符号整数可能更快/更有效,或者在某些处理器上生成更小的代码。
4

6 回答 6

12

C 提供了三种不同的字符类型:

  • char表示一个字符(C 也将其称为“字节”)。
  • unsigned char表示字节大小的位模式,或无符号整数。
  • signed char表示一个字节大小的有符号整数。

它是char有符号类型还是无符号类型是实现定义的,所以我认为这个问题相当于“为什么char存在这种可能有符号的类型?” 或“为什么 C 不需要char无符号?”。

首先要知道的是,Ritchie 在 1971 年将“char”类型添加到 B 语言中,而 C 从那里继承了它。在此之前,B 是面向字的而不是面向字节的(这个人自己也是这么说的,请参阅“B 的问题”。)

完成后,我的两个问题的答案可能是 C 的早期版本没有无符号类型。

一旦char建立了字符串处理函数,将它们全部更改为unsigned char将是一个严重的破坏性更改(即几乎所有现有代码都将停止工作),而 C 几十年来试图培养其用户群的方法之一是主要是避免灾难性的不兼容变化。所以 C 做出这样的改变是令人惊讶的。

鉴于这char将是字符类型,并且(如您所见)它是无符号的很有意义,但是已经存在许多对 char 进行签名的实现,我想使 char 的签名implementation-defined 是一个可行的折衷方案——现有代码将继续工作。如果它char仅用作字符而不用于算术或顺序比较,它也可以移植到char无符号的实现中。

与 C 的一些古老的实现定义的变体不同,实现者仍然选择有符号字符(英特尔)。C 标准委员会不禁观察到有些人似乎出于某种原因坚持使用签名字符。无论这些人的原因是什么,当前的还是历史的,C 必须允许它,因为现有的 C 实现依赖于它被允许。因此,强制char无符号在可实现目标列表中远低于强制int成为 2 的补码,而 C 甚至没有做到这一点。

一个补充问题是“为什么英特尔仍然指定char在其 ABI 中签名?”,我不知道答案,但我猜他们从来没有机会在没有大规模中断的情况下做其他事情。也许他们甚至喜欢他们。

于 2012-08-24T09:26:25.173 回答
4

好问题。由于该标准没有定义char为无符号或有符号(这留给实现),我猜这个偏好char来自两个角度:

  • char键入所需的时间少于unsigned char,从而使字符串操作函数的原型更易于阅读和使用。
  • 由于最初的 ASCII 规范是 7 位的,因此对于 C 规范而言,有效值是在 0 到 127 还是 0 到 255 范围内都无关紧要。8 位字符集的标准化已经发生了很久。
于 2012-08-24T09:17:01.243 回答
4

的签名char是实现定义的。

对于您所描述的问题,一个更简洁的解决方案是强制要求plainchar必须是未签名的。

plainchar可能是已签名或未签名的原因部分是历史原因,部分与性能有关。

C 的早期版本没有无符号类型。由于 ASCII 仅涵盖 0 到 127 的范围,因此假设制作char有符号类型没有特别的缺点。一旦做出决定,一些程序员可能已经编写了依赖于它的代码,后来的编译器将其保留char为有符号类型以避免破坏此类代码。

引用1975 年的C 参考手册,即 K&R1 出版前 3 年:

字符(已声明,以下称为char)是从 ASCII 集中选择的;它们占据 8 位字节的最右边 7 位。也可以将chars 解释为带符号的 2 的补码 8 位数字。

EBCDIC 需要 8 位 unsigned char,但显然当时还不支持基于 EBCDIC 的机器。

至于性能,char在许多情况下, type 的值被隐式转换为int(假设int可以表示 type 的所有值,char通常是这种情况)。这是通过“整数促销”完成的。例如,这个:

char ch = '0';
ch ++;

不只是执行 8 位增量。它将chfrom的值转换charint,将结果加 1,并将总和从 转换回intchar将其存储在 中ch。(编译器可以生成任何可证明达到相同效果的代码。)

将带符号的 8 位转换为带符号char的 32 位int需要符号扩展。将 8 位无符号char转换为 32 位有符号int需要对目标的高 24 位进行零填充。(这些类型的实际宽度可能会有所不同。)根据 CPU 的不同,其中一种操作可能比另一种更快。在某些 CPU 上,进行普通char签名可能会导致生成的代码更快。

(我不知道这种影响的大小。)

于 2012-08-24T09:26:17.490 回答
3

不,没有充分的理由。char 的符号是实现定义的,也没有任何充分的理由。不存在使用负数索引的任何类型的符号表。

我认为这一切都源于错误的、奇怪的假设,即有 8 位整数,然后有“字符”,其中“字符”是某种神奇而神秘的东西。

这只是 C 标准的许多非理性缺陷之一,从恐龙在地球上行走的时代继承下来。char 的神秘符号没有给语言增加任何东西,除了可能由隐式整数提升引起的与符号相关的错误。

编辑:

很可能他们让 char 被签名是因为他们希望它的行为与其他整数类型一样:short、int、long,这些都由标准保证在默认情况下总是被签名。

使用无符号整数可能更快/更有效,或者在某些处理器上生成更小的代码。

你最终得到什么类型并不完全直观。每当您在表达式中使用 char 作为操作数时,它总是会被提升为 int。类似地,常量字符 'a'、'\n' 等是 int 类型,而不是 char。C 语言强制编译器根据隐式提升规则(称为“整数提升”和“通常的算术转换”/“平衡”)提升类型。

一旦升级完成,编译器可以将类型优化为最有效的类型,前提是它可以证明优化不会改变结果。

如果您有此代码:

char a = 'a';
char b = 'b';
char c = a + b;

字里行间有很多不为人知的事情。首先,文字“a”和“b”被默默地截断int为有符号/无符号字符。然后在表达式a + b中,a 和 b 都被整数提升规则隐式提升为int类型。加法是在两个上执行的int。然后将结果静默截断回有符号/无符号字符。

如果编译器可以证明优化不会影响上述任何模糊性,它可能会将其全部替换为理智的 8 位操作。

于 2012-08-24T09:08:44.223 回答
3

有以下三种相关类型:

  • signed char, 旨在存储小的有符号整数
  • unsigned char, 旨在存储小的无符号整数
  • char, 旨在存储字符

我认为您真正想知道的是为什么不是char无符号类型?

曾经有一段时间C 没有无符号类型[1]。char被描述为已签名(请参阅第 4 页),但即使在那个时候,“符号传播功能在其他实现中消失了”,因此它的行为已经在地方签名,在其他地方未签名。而且我认为实现选择只是反映了对他们来说最简单的方法(例如,在 PDP-11 上,第一个 C 实现是针对它进行MOVB符号扩展的,我不记得有一种方法可以移动字节到一个字没有得到符号扩展)。

如今,我知道的大多数实现都在使用 signed char。我所知道的唯一一个未签名char的是来自 IBM 的那些是支持 EBCDIC 授权的(基本字符集中字符的字符代码必须是正数,并且 EBCDIC 大部分都在 128 以上)。

[1]使用指针代替...

于 2012-08-24T09:20:53.577 回答
1

因为标准没有将 char 定义为有符号字符

于 2012-08-24T09:05:25.497 回答