以下代码
char buffer[BSIZE];
...
if(buffer[0]==0xef)
...
给编译器警告“由于数据类型的范围有限,比较总是错误的”。当我将检查更改为
if(buffer[0]==0xffffffef)
这感觉非常违反直觉。char根据十六进制的特定字节值检查 a 的正确方法是什么?(除了使其未签名)
以下代码
char buffer[BSIZE];
...
if(buffer[0]==0xef)
...
给编译器警告“由于数据类型的范围有限,比较总是错误的”。当我将检查更改为
if(buffer[0]==0xffffffef)
这感觉非常违反直觉。char根据十六进制的特定字节值检查 a 的正确方法是什么?(除了使其未签名)
有什么问题:
if (buffer[0] == '\xef')
?
要了解为什么buffer[0] == 0xef会触发警告而buffer[0] == 0xffffffef不会触发,您需要准确了解该表达式中发生的情况。
首先,==运算符比较两个表达式的值,而不是底层表示 -0xef是数字 239,并且只会比较等于该数字;同样0xffffffef是数字 4294967279 并且只会比较等于。
0xef常量和C中的常量没有区别239:两者都有类型int和相同的值。如果您char的范围为 -128 到 127,那么当您评估 时buffer[0] == 0xef,buffer[0]将提升为int,这使其值保持不变。因此它永远不能比较等于0xef,所以警告是正确的。
但是,常量和 4294967279之间可能存在差异;0xffffffef十进制常量总是有符号的,但十六进制常量可能是无符号的。在您的系统上,它似乎有一个无符号类型 - 可能unsigned int(因为该值太大而无法存储在 中int,但小到足以存储在 中unsigned int)。当您评估buffer[0] == 0xffffffef时,将buffer[0]提升为unsigned int。这使任何正值都保持不变,但是通过将负值相加UINT_MAX + 1来转换它们;对于char范围为 -128 到 127 的 a,提升的值在 0 到 127 或 4294967168 到 4294967295 的任一范围内。 0xffffffef位于此范围内,因此比较可能返回 true。
如果您存储的是位模式而不是数字,那么您应该首先使用unsigned char。或者,您可以通过将指向它的指针转换为来检查对象的位模式unsigned char *:
if (((unsigned char *)buffer)[0] == 0xef)
(这显然更方便地通过使用单独的类型变量来完成unsigned char *)。
正如PaulR所说,您也可以使用buffer[0] == '\xef'- 这有效,因为'\xef'它被定义为具有位模式 0xef 的对象在转换为 int 时所int具有的值的常量;char例如。在带有符号字符的 2s 补码系统上,'\xef'是一个值为 -17 的常数。
发生这种情况是因为buffer内容是 type char。使它们unsigned char起作用:
if ((unsigned char) (buffer[0]) == 0xef)
就像处理任何其他负数一样:
if (buffer[0]== -0x6f)
但通常你想使用 unsigned char 作为数据类型:
unsigned char buffer[BSIZE];
...
if(buffer[0]==0xef)
使用有符号字符的原因非常少见。更罕见的是使用“char without sign specification”的原因,它可以在不同的平台上签名或未签名。
明确说明原因:char有符号或无符号是实现定义的。如果您的编译器char默认视为已签名,0xef则将大于最大可能signed char值(即 127 或0x7f),因此您的比较将始终为假。因此发出警告。
其他答案提供了可能的解决方案。