62

字节缓冲区应该是有符号字符还是无符号字符,还是只是一个字符缓冲区?C和C++之间有什么区别吗?

谢谢。

4

14 回答 14

50

如果您打算存储任意二进制数据,您应该使用unsigned char. 它是 C 标准保证没有填充位的唯一数据类型。每种其他数据类型都可能在其对象表示中包含填充位(即包含对象的所有位,而不是仅包含确定值的位)。填充位的状态未指定,不用于存储值。因此,如果您使用char一些二进制数据进行读取,则会将内容缩减到 char 的值范围(通过仅解释值位),但可能仍有一些位被忽略但仍然存在并由memcpy. 很像真实结构对象中的填充位。类型unsigned char保证不包含那些。这来自5.2.4.2.1/2(C99 TC2,这里的 n1124):

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

从最后一句可以看出,没有任何空间可供任何填充位使用。如果您使用char缓冲区的类型,您还会遇到溢出问题:将任何值显式分配给8位范围内的一个此类元素-因此您可能期望这样的分配是可以的-但不在范围内a char,即CHAR_MIN.. CHAR_MAX,这样的转换会溢出并导致实现定义的结果,包括信号的提升。

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

但是,对于字符串,选择的数据类型是char,字符串和打印函数可以理解。用于signed char这些目的对我来说似乎是一个错误的决定。

有关更多信息,请阅读this proposal其中包含对下一版本 C 标准的修复,该版本最终signed char也不需要任何填充位。它已被纳入工作文件

于 2009-03-17T11:53:46.650 回答
39

字节缓冲区应该是有符号字符还是无符号字符,还是只是一个字符缓冲区?C和C++之间有什么区别吗?

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

  • char= ASCII(或 UTF-8,但有符号阻碍)文本数据
  • unsigned char= 字节
  • signed char= 很少使用

并且有代码依赖于这种区别。就在一两周前,我遇到了一个错误,即 JPEG 数据被破坏,因为它被传递给char*我们的 Base64 编码函数的版本——它“有用地”替换了“字符串”中所有无效的 UTF-8。更改为BYTEakaunsigned char即可修复它。

于 2011-02-20T15:46:53.617 回答
12

这取决于。

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

如果缓冲区旨在保存二进制数据,则取决于您打算如何使用它。例如,如果二进制数据确实是数据样本的打包数组,这些数据样本是有符号的 8 位定点 ADC 测量值,那么signed char最好。

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

于 2009-03-17T08:03:26.253 回答
9

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

于 2009-03-17T09:49:02.770 回答
5

您应该使用charunsigned char但从不使用signed char。该标准在 3.9/2 中有以下内容

对于 POD 类型 T 的任何对象(基类子对象除外),无论该对象是否拥有类型 T 的有效值,构成该对象的底层字节 (1.7) 都可以复制到 char 或 unsigned 数组中char。如果将 char 或 unsigned char 数组的内容复制回对象,则该对象随后应保持其原始值。

于 2009-03-17T11:08:26.260 回答
3

最好将其定义为无符号字符。事实上 Win32 类型 BYTE 被定义为无符号字符。这之间没有C和C++之间的区别。

于 2009-03-17T08:01:18.877 回答
3

为了获得最大的可移植性,请始终使用无符号字符。有几个例子可以发挥作用。立即想到在具有不同字节序类型的系统之间共享的序列化数据。当执行移位或位掩码时,这些值是另一个值。

于 2009-03-17T10:05:31.967 回答
2

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


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

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

VS

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


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

于 2009-03-17T14:44:36.193 回答
0

如果您将一个元素提取到一个更广泛的变量中,那么它当然会被符号扩展或不扩展。

于 2009-03-17T07:55:25.617 回答
0

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

我不认为我曾经使用显式signed char来表示字节缓冲区。

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

于 2009-03-17T08:01:02.980 回答
0

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

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

于 2009-03-17T08:03:08.467 回答
0

你真的在乎吗?如果你不这样做,只需使用默认值 (char) 并且不要用无关紧要的事情弄乱你的代码。否则,未来的维护者会想知道你为什么使用有符号(或无符号)。让他们的生活更简单。

于 2009-03-17T08:06:58.803 回答
-1

如果你对编译器撒谎,它会惩罚你。

如果缓冲区包含刚刚通过的数据,并且您不会以任何方式操作它们,那没关系。

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

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

于 2009-03-17T14:57:30.437 回答
-1
typedef char byte;

现在你可以让你的数组是bytes 的。每个人都清楚您的意思,并且您不会丢失任何功能。

我知道这有点傻,但它使您的代码按照您的预期 100% 阅读。

于 2009-03-17T15:10:11.983 回答