为什么C和C ++非常讨厌签名char?
为什么C允许使用“字符类型”访问对象:
6.5表达式 (C)
对象的存储值只能由具有以下类型之一的左值表达式访问:
- 一个字符类型。
但C ++只允许char和unsigned char ?
3.10左值和右值 (C ++)
如果程序试图通过以下类型之一以外的glvalue访问对象的存储值,则行为未定义:
- char或unsigned char类型。
签名char仇恨的另一部分(引自C ++标准):
3.9类型 (C ++)
对于普通可复制类型T的任何对象(基类子对象除外),无论对象是否保持类型T的有效值,组成对象的基础字节都可以复制到char或unsigned char数组中。 如果将char或unsigned char数组的内容复制回对象,则该对象应随后保持其原始值。
并从C标准:
6.2.6类型表示 (C)
存储在任何其他对象类型的非位字段对象中的值由n×CHAR_BIT位组成,其中n是该类型对象的大小(以字节为单位)。 可以将该值复制到unsigned char [n]类型的对象中(例如,通过memcpy); 生成的字节集称为值的对象表示。
我可以在stackoverflow上看到很多人说这是因为unsigned char是唯一保证没有填充位的字符类型,但是C99第6.2.6.2节整数类型说
signed char不应有任何填充位
那么这背后的真正原因是什么?
这是我对动机的看法:
在非二进制补码系统上, signed char
不适合访问对象的表示。 这是因为有两个可能的带signed char
表示具有相同的值(+0和-0),或者一个表示没有值(陷阱表示)。 在任何一种情况下,这都会阻止您对对象的表示执行最有意义的操作。 例如,如果您有一个16位无符号整数0x80ff
,则作为有signed char
一个或另一个字节将陷阱或比较等于0。
请注意,在这样的实现(非二进制补码)上,需要将plain char
定义为无符号类型,以便通过char
访问对象的表示以使其正常工作。 虽然没有明确的要求,但我认为这是从标准中的其他要求中得出的要求。
我认为你真正要问的是为什么signed char
被取消了所有允许打字为char*
作为特例的规则。 说实话,我不知道,特别是因为 – 据我所知 – signed char
也不能填充:
[C++11: 3.9.1/1]:
[..]一个char
,一个带signed char
和一个unsigned char
占用相同数量的存储空间并具有相同的对齐要求(3.11); 也就是说,它们具有相同的对象表示。 对于字符类型,对象表示的所有位都参与值表示。 [..]
经validation据表明它不仅仅是惯例 :
-
char
被视为ASCII的一个字节; -
unsigned char
被视为具有任意“二进制”内容的字节; 和 -
signed char
留在风中。
对我而言,似乎没有理由将其排除在这些标准规则之外,但老实说,我找不到任何相反的证据。 我将在标准措辞中将其归结为一种轻微莫名的怪异。
(可能我们必须向std-discussion
列表询问此事。)
使用字符类型来检查对象的表示是一种破解。 但是,这是历史性的,必须做一些调整以允许它。
大多数情况下,在编程语言中,我们需要强类型。 作为float
东西应该作为float
而不是作为int
。 这有许多好处,包括减少人为错误和启用各种优化。
但是,有时需要访问或修改对象的字节。 在C中,这是通过字符类型完成的。 C ++延续了这一传统,但它通过消除使用signed char
来实现这些目的,从而略微改善了这种情况。
理想情况下,创建一个新类型(比如byte
)并允许仅通过此类型对对象表示进行字节访问可能更好,因此将常规字符类型分开仅用作普通整数/字符。 也许有人认为使用char
和unsigned char
来支持这种改变的现有代码太多了。 但是,我从未见过用于访问对象表示的signed char
,因此可以安全地排除它。