为什么C和C ++非常讨厌签名char?

时间:2014-01-17 01:21:36

标签: c++ c char

为什么C允许使用“字符类型”访问对象:

  

6.5表达式(C)

     

对象的存储值只能由具有以下类型之一的左值表达式访问:

     
      
  • 字符类型。
  •   

但是C ++只允许 char unsigned char

  

3.10 Lvalues and rvalues (C ++)

     

如果程序试图通过以下类型之一以外的glvalue访问对象的存储值,则行为未定义:

     
      
  • char或unsigned 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 Section 6.2.6.2整数类型

  

signed char不应有任何填充位

那背后的真正原因是什么?

3 个答案:

答案 0 :(得分:15)

这是我对动机的看法:

在非二进制补码系统上,signed char将不适合访问对象的表示。这是因为有两个可能的signed char表示具有相同的值(+0和-0),或者一个表示没有值(陷阱表示)。在任何一种情况下,这都会阻止您对对象的表示执行最有意义的操作。例如,如果您有一个16位无符号整数0x80ff,则一个或另一个字节(signed char)将陷阱或比较等于0。

请注意,在这样的实现(非二进制补码)上,普通char需要定义为无符号类型,以便通过char访问对象的表示以使其正常工作。虽然没有明确的要求,但我认为这是从标准中的其他要求中得出的要求。

答案 1 :(得分:8)

我认为你真正要问的是为什么signed char被取消所有允许打字char*作为特例的规则的资格。说实话,我不知道,特别是因为 - 据我所知 - signed char也不能填充:

  

[C++11: 3.9.1/1]: [..] charsigned charunsigned char占用相同数量的存储空间并具有相同的存储空间对准要求(3.11);也就是说,它们具有相同的对象表示。对于字符类型,对象表示的所有位都参与值表示。 [..]

Empirical evidence suggests that it's not much more than convention

  • char被视为ASCII的字节;
  • unsigned char被视为具有任意“二进制”内容的字节;和
  • signed char在风中飘扬。

对我而言,似乎没有理由将其排除在这些标准规则之外,但老实说,我找不到任何相反的证据。我将在标准措辞中将其归结为一种轻微莫名的怪异。

(可能我们必须询问std-discussion列表。)

答案 2 :(得分:7)

使用字符类型来检查对象的表示是一种破解。但是,它是历史性的,必须有一些条件允许它。

大多数情况下,在编程语言中,我们需要强类型。属于float的内容应以float而非int的形式进行访问。这有许多好处,包括减少人为错误和实现各种优化。

但是,有时需要访问或修改对象的字节。在C中,这是通过字符类型完成的。 C ++延续了这一传统,但它通过消除signed char用于这些目的而略微改善了这种情况。

理想情况下,最好创建一个新类型,比如byte,并允许仅通过此类型对对象表示进行字节访问,从而将常规字符类型分开,仅用作普通整数/字符。也许有人认为使用charunsigned char来支持此类更改的现有代码太多了。但是,我从未见过signed char用于访问对象的表示形式,因此可以安全地将其排除。