以下代码
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
),因此您的比较将始终为假。因此发出警告。
其他答案提供了可能的解决方案。