应该缓冲区的字节签名或无符号字符缓冲区?
应该字节的缓冲区被签名字符或无符号字符或只是一个字符缓冲区? C和C ++之间的任何区别?
谢谢。
应该字节的缓冲区被签名字符或无符号字符或只是一个字符缓冲区? C和C ++之间的任何区别?
语言如何对待这个细微差别。 约定如何对待它的巨大差异。
-
char
= ASCII(或UTF-8,但签名有问题) 文本数据 -
unsigned char
=字节 -
signed char
=很less使用
有代码依赖于这种区别。 就在一两个星期前,我遇到了一个错误,那就是JPEG数据被传递给了我们的Base64编码函数的char*
版本 – 它“有用地”replace了“string”中的所有无效的UTF-8。 改为BYTE
又名unsigned char
只需要解决它。
如果你打算存储任意的二进制数据,你应该使用unsigned char
。 它是C标准中唯一保证没有填充位的数据types。 每个其他数据types可能在其对象表示中包含填充位(即包含对象所有位的填充位,而不是仅包含确定值的填充位)。 填充位的状态是未指定的,不用于存储值。 所以,如果你使用char
读取一些二进制数据,那么事情会被减less到一个字符的值范围(通过只解释值的位),但是可能还有一些被忽略但仍然存在并被memcpy
读取的位。 就像真正的结构对象中的填充位一样。 typesunsigned char
保证不包含这些。 从5.2.4.2.1/2
(C99 TC2,n1124):
如果在expression式中使用chartypes的对象的值被视为有符号整数,则
CHAR_MIN
的值应与SCHAR_MIN
的值相同,并且CHAR_MAX
的值应与SCHAR_MAX
的值相同。 否则,CHAR_MIN
的值应为0,并且CHAR_MAX
的值应与CHAR_MAX
的相同。 值UCHAR_MAX
等于2^CHAR_BIT − 1
从最后一句来看,没有任何填充位留下空间。 如果你使用char
作为你的缓冲区的types,你也有溢出的问题:为8
位范围内的一个这样的元素显式地赋值 – 所以你可能期望这样的赋值是OK的 – 但不在一个char
范围,即CHAR_MIN
, CHAR_MAX
,这样的转换溢出并导致实现定义的结果,包括信号的CHAR_MAX
。
即使有关上述的任何问题可能不会在真正的实现中显示(实现的质量很差),您最好从一开始就使用正确的types,即unsigned char
。
然而,对于string,select的数据types是char
,这可以通过string和打印函数来理解。 为这些目的使用signed char
看起来对我来说是一个错误的决定。
有关更多信息,请阅读this proposal
,其中包含对C标准的下一个版本的修正,最终将要求signed char
没有任何填充位。 它已经被纳入工作文件 。
这取决于。
如果缓冲区是为了保存文本,那么将其声明为char
数组可能是有意义的,并让平台为您决定是否默认签名或未签名。 例如,这将使您在执行的运行时库中传入和传出数据的麻烦最小。
如果缓冲区的目的是保存二进制数据,那么这取决于你打算如何使用它。 例如,如果二进制数据实际上是经过有符号8位定点ADC测量的数据样本的打包数组,则有signed char
将是最好的。
在大多数现实世界的情况下,缓冲区就是这样,一个缓冲区,而且您并不关心各个字节的types,因为您在批量操作中填充了缓冲区,而您即将把它传递给parsing器来解释复杂的数据结构并做一些有用的事情。 在这种情况下,以最简单的方式申报。
如果它实际上是一个8位字节的缓冲区,而不是机器的默认语言环境中的string,那么我会使用uint8_t
。 并不是说char中的字符不是一个字节(或者一个字节是八位字节),而是“这是一个八位字节的缓冲区”,而不是“这是一个string”,这通常是有用的文档。
你应该使用char或unsigned char,但从来没有签名字符 。 该标准在3.9 / 2中有以下内容
对于PODtypesT的任何对象(基类子对象除外),无论对象是否保存Ttypes的有效值,构成对象的基础字节(1.7)都可以复制到char或unsigned char.如果char或unsigned char数组的内容被复制回到对象中,则该对象将随后保持其原始值。
最好把它定义为unsigned char。 Infact Win32typesBYTE被定义为unsigned char。 C和C ++之间没有区别。
为了最大可移植性,总是使用无符号字符。 有几个例子可以发挥作用。 序列化的数据跨不同的endiantypes的系统共享立即想到。 当执行移位或位掩码时,值是另一个值。
int8_t vs uint8_t的select与比较ptr为NULL的情况类似。
从function的angular度来看,与NULL比较与0相比是相同的,因为NULL是#define for 0。
但个人而言,从编码风格的angular度来看,我select比较我的指针为NULL,因为NULL #define暗示维护代码的人正在检查一个错误的指针…
VS
当有人看到一个比较0表明你正在检查一个具体的价值。
由于上述原因,我会使用uint8_t。
如果将元素提取到更广泛的variables中,它当然会被签名扩展或不扩展。
如果我想强调数据的二进制性,我应该更喜欢无签名的,因为它感觉更“生”,不太容易说“嘿,这只是一小堆”。
我不认为我曾经使用明确的有signed char
来表示一个字节的缓冲区。
当然,第三个select是尽可能地将缓冲区表示为void *
。 很多常见的I / O函数都和void *
一起工作,所以有时需要使用什么样的整数types才能完全封装,这很好。
几年前我遇到了一个C ++控制台应用程序的问题,该应用程序的打印字符的ASCII值高于128,这是通过从char转换到unsigned char来解决的,但是我认为在保持chartypes的同时也是可以解决的。
目前,大多数C / C ++函数都使用char,现在我理解这两种语言都好多了,所以在大多数情况下我使用char。
你真的在乎吗? 如果你不这样做,只需要使用默认的(char),不要把你的代码弄得不重要。 否则,未来的维护者将不知道为什么使用签名(或未签名)。 让他们的生活更简单。
typedef char byte;
现在你可以让你的数组成为byte
。 每个人都明白你的意思,而且你不会失去任何function。
我知道这有点愚蠢,但它会使您的代码按照您的意图100%读取。
如果你对编译器说谎,它会惩罚你。
如果缓冲区包含刚刚通过的数据,并且不会以任何方式操纵它们,则无关紧要。
但是,如果您必须对缓冲区内容进行操作,那么正确的types声明将使您的代码更简单。 没有“int val = buf [i]&0xff;” 废话。
所以,想一想数据究竟是什么以及如何使用它。