为什么C中的字符串函数在使用char而不是unsigned char的数组上工作?

时间:2012-08-24 08:57:03

标签: c string

C标准库函数中,字符串的元素是char s。有没有一个很好的理由来决定它而不是unsigned char

unsigned char用于8位字符串虽然有一些优点:

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

6 个答案:

答案 0 :(得分:11)

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

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

这是实现定义的char是签名还是无符号类型,所以我认为问题相当于"为什么char完全存在,因为这可能是签名类型?"或者"为什么C不要求char未签名?"。

首先要知道的是,里奇添加了" char"在1971年输入B语言,C从那里继承。在此之前,B是面向字的而不是面向字节的(so says the man himself,参见" B&#34的问题。)

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

一旦char和字符串处理函数建立,将它们全部更改为unsigned char将是一个严重的重大变化(即几乎所有现有代码都将停止工作),并且其中一种方式是C几十年来,它一直试图培养其用户群,主要是避免灾难性的不兼容变化。因此,C进行这种改变会令人感到意外。

鉴于char将成为字符类型,并且(正如您所观察到的那样)它对于未签名很有意义,但是已经存在大量已经签名的char,我认为使char实现定义的签名是可行的妥协 - 现有代码将继续工作。如果它仅将char用作字符而不用于算术或顺序比较,则它也可以移植到char未签名的实现。

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

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

答案 1 :(得分:4)

好问题。由于标准没有将char定义为无符号或签名(这留给实现),我认为优先于char的优先级来自两个角度:

  • char输入的时间少于unsigned char,使得字符串操作的原型能够更好地阅读和使用。
  • 由于原始的ASCII规范是7位,因此对于C规范而言,有效值是在0到127还是0到255之间无关紧要.8位字符集的标准化发生得晚得多

答案 2 :(得分:4)

char的签名是实现定义的。

您所描述的问题的一个更清晰的解决方案是强制要求普通char无签名。

普通char可能有签名或无签名的原因部分是历史性的,部分与绩效有关。

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

在K& R1出版前3年从1975年引用C Reference Manual

  

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

EBCDIC需要8位无符号char,但当时尚不支持基于EBCDIC的机器。

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

char ch = '0';
ch ++;

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

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

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

答案 3 :(得分: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静默地截断为signed / unsigned char。然后在表达式a + b中,整数提升规则将a和b都隐式地提升为int类型。添加在两个int上执行。然后将结果静默地截断回有符号/无符号字符。

如果编译器能够证明优化不会影响上述任何一种晦涩难懂,那么它可以用理智的8位操作代替它。

答案 4 :(得分:3)

有三种相关类型:

  • signed char,用于存储小型有符号整数
  • unsigned char,用于存储未签名的小型汉堡
  • char,旨在存储字符

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

有一段时间C hadn't unsigned types [1]。 char被描述为已签名(参见第4页),但即使在那个时候,“符号传播功能在其他实现中也会消失”,因此它表现为已在地点签名,在其他地方未签名。我认为实现选择只是反映了最简单的实现(例如PDP-11,第一个C实现,MOVB做了符号扩展,我不记得有一种将字节移动到单词而不获取符号扩展名的方法。

如今,我所知道的大多数实现都使用了签名char。我所知道的唯一一个有无签名char的人是来自IBM的人是EBCDIC授权的支持(基本字符集中字符的字符代码必须是正数,而EBCDIC的大多数都在128以上)。

[1] Pointers where used instead...

答案 5 :(得分:1)

因为标准没有将char定义为signed char