在C
标准库函数中,字符串的元素是char
s。有没有一个很好的理由来决定它而不是unsigned char
?
将unsigned char
用于8位字符串虽然有一些优点:
答案 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
,使得字符串操作的原型能够更好地阅读和使用。答案 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以上)。
答案 5 :(得分:1)
因为标准没有将char定义为signed char