应该使用字节缓冲区来签名还是使用unsigned char缓冲区?

时间:2009-03-17 07:52:04

标签: c++ c char buffer

字节缓冲区应该是char或unsigned char还是char缓冲区? C和C ++之间有什么区别吗?

感谢。

14 个答案:

答案 0 :(得分:47)

如果您打算存储任意二进制数据,则应使用unsigned char。它是唯一保证C标准没有填充位的数据类型。每个其他数据类型可以在其对象表示中包含填充位(即包含对象的所有位的填充位,而不是仅包含确定值的位)。填充位的状态未指定,不用于存储值。因此,如果您使用char读取一些二进制数据,事情将被减少到char的值范围(通过仅解释值位),但可能仍然存在被忽略但仍然存在的位由memcpy阅读。很像真正的struct对象中的填充位。类型unsigned char保证不包含这些类型。接下来是5.2.4.2.1/2(C99 TC2,n1124):

  

如果在char中使用char类型的对象的值被视为有符号整数   表达式,CHAR_MIN的值应与SCHAR_MINCHAR_MAX的值相同   SCHAR_MAX的值应与CHAR_MIN的值相同。否则,值   CHAR_MAX应为0,UCHAR_MAX的值应与UCHAR_MAX的值相同   2^CHAR_BIT − 1char应等于8

从最后一句话可以看出,任何填充位都没有空间。如果使用char作为缓冲区的类型,则还存在溢出问题:明确地将任何值分配给CHAR_MIN位范围内的一个此类元素 - 因此您可能期望这样的分配没关系 - 但不在CHAR_MAX范围内,unsigned char .. char,这样的转换会溢出并导致实现定义的结果,包括信号的提升。

即使有任何关于上述问题的问题可能不会在实际实现中显示(实际上非常质量差),您最好从头开始使用正确的类型,这是signed char

但是,对于字符串,所选的数据类型为signed char,字符串和打印函数将理解这一点。将{{1}}用于这些目的看起来对我来说是错误的决定。

有关详细信息,请阅读this proposal,其中包含下一版C标准的修复程序,最终将要求{{1}}没有任何填充位。它已经合并到working paper中。

答案 1 :(得分:33)

  

是否应该对字节缓冲区进行签名   char或unsigned char或简单的char   缓冲? C和C之间的任何差异   C ++?

语言如何对待它的细微差别。约定如何处理它的巨大差异。

  • char = ASCII(或UTF-8,但签名会妨碍那里)文字数据
  • unsigned char = byte
  • signed char =很少使用

还有依赖这样的区别的代码。就在一两个星期前,我遇到了一个错误,其中JPEG数据被破坏,因为它被传递到我们的Base64编码函数的char*版本 - 这有助于“帮助”替换“字符串中的所有无效的UTF-8” ”。只需更改为BYTE又名unsigned char即可。

答案 2 :(得分:12)

取决于。

如果缓冲区旨在保存文本,那么将它声明为char的数组可能是有意义的,并让平台在默认情况下决定是签名还是无符号。例如,这将使您在将数据传入和传出实现的运行时库时遇到的麻烦最少。

如果缓冲区旨在保存二进制数据,那么它取决于您打算如何使用它。例如,如果二进制数据实际上是经过签名的8位定点ADC测量的数据样本的打包数组,那么signed char将是最佳的。

在大多数实际情况中,缓冲区只是一个缓冲区,并且您并不真正关心单个字节的类型,因为您在批量操作中填充了缓冲区,并且您即将通过它关闭解析器来解释复杂的数据结构并做一些有用的事情。在这种情况下,以最简单的方式声明它。

答案 3 :(得分:9)

如果它实际上是8位字节的缓冲区,而不是机器默认语言环境中的字符串,那么我将使用uint8_t。并不是说有很多机器,其中char不是一个字节(或一个字节是八位字节),但是使''这是一个八位字节的缓冲区'而不是'这是一个字符串'的语句通常是有用的文档。

答案 4 :(得分:5)

您应该使用 char unsigned char ,但不要使用 signed char 。该标准在3.9 / 2

中有以下内容
  

对于任何对象(除了   POD类型T的基类子对象,   该对象是否成立   类型T的有效值,底层证券   构成对象的字节(1.7)可以   被复制到char或者数组中   unsigned char.If的内容   char或unsigned char的数组是   复制回对象,   对象随后应持有其   原始价值。

答案 5 :(得分:4)

最好将其定义为unsigned char。 Infact Win32类型BYTE定义为unsigned char。 C& C之间没有区别这之间的C ++。

答案 6 :(得分:3)

为了获得最大的可移植性,请始终使用unsigned char。有几种情况可以发挥作用。我们可以立即想到在不同端序类型的系统之间共享的序列化数据。当执行移位或位掩码时,值是另一个。

答案 7 :(得分:2)

int8_t vs uint8_t的选择类似于将ptr与NULL进行比较时的选择。


从功能的角度来看,与NULL比较与0比较相同,因为NULL是0的#define。

但就个人而言,从编码风格的角度来看,我选择将我的指针与NULL进行比较,因为NULL #define意味着维护代码的人正在检查错误的指针......

VS

当有人看到比较为0时,它表示您正在检查特定值。


由于上述原因,我会使用uint8_t。

答案 8 :(得分:0)

如果您将元素提取到更宽的变量中,它当然会被符号扩展。

答案 9 :(得分:0)

应该而且应该......我倾向于更喜欢无符号,因为它感觉更“原始”,不那么诱人说“嘿,这只是一堆小ints”,如果我想强调数据的二进制性。

我认为我从未使用显式signed char来表示字节缓冲区。

当然,三分之一的选择是尽可能将缓冲区表示为void *。许多常见的I / O函数与void *一起使用,因此有时可以完全封装使用什么整数类型的决定,这很好。

答案 10 :(得分:0)

几年前,我遇到了一个C ++控制台应用程序的问题,该应用程序打印了大于128的ASCII值的彩色字符,这是通过从char切换到unsigned char来解决的,但我认为它在保持字符类型时也可以解决。

目前,大多数C / C ++函数都使用char,我现在对这两种语言的理解要好得多,所以在大多数情况下我使用char。

答案 11 :(得分:0)

你真的在乎吗?如果不这样做,只需使用默认值(char),不要将代码弄得乱七八糟。否则,未来的维护者将不知道为什么你使用签名(或未签名)。让他们的生活更简单。

答案 12 :(得分:-1)

如果你欺骗编译器,它会惩罚你。

如果缓冲区包含刚刚通过的数据,并且您不会以任何方式操纵它们,则无关紧要。

但是,如果必须对缓冲区内容进行操作,那么正确的类型声明将使您的代码更简单。没有“int val = buf [i]& 0xff;”无义。

因此,请考虑数据实际是什么以及您需要如何使用它。

答案 13 :(得分:-1)

typedef char byte;

现在你可以使你的数组​​成为byte个。每个人都明白你的意思,你不会失去任何功能。

我知道这有些愚蠢,但它会使您的代码按预期100%读取。