[英]Why do a bitwise-and of a character with 0xff?
我正在阅读一些实现简单解析器的代码。 名为scan
的 function 将一行分解为标记。 scan
有一个 static 变量bp
,它分配了要标记的行。 在赋值之后,空白被跳过。 见下文。 我不明白的是为什么代码会按位和bp
指向的字符0xff
,即* bp & 0xff
的目的是什么? 这怎么样:
while (isspace(* bp & 0xff))
++ bp;
与此不同:
while (isspace(* bp))
++ bp;
这是scan
function:
static enum tokens scan (const char * buf)
/* return token = next input symbol */
{ static const char * bp;
while (isspace(* bp & 0xff))
++ bp;
..
}
来自 C 标准(7.4 字符处理 <ctype.h>)
1 header <ctype.h> 声明了几个对字符分类和映射有用的函数。198)在所有情况下,参数都是一个 int,其值应表示为无符号字符或应等于宏 EOF 的值。 如果参数有任何其他值,则行为未定义。
在这次通话中
isspace(* bp)
由于 integer 促销活动,具有char
类型的参数表达式*bp
被转换为int
类型。
如果char
类型表现为signed char
类型并且表达式*bp
的值为负,则提升的int
类型的表达式的值也将为负并且不能表示为unsigned char
类型的值。
这会导致未定义的行为。
在这次通话中
isspace(* bp & 0xff)
由于按位运算符 & 类型的表达式* bp & 0xff
的结果值可以表示为int
unsigned char
类型的值。
所以这是一个技巧,而不是编写更清晰的代码
isspace( ( unsigned char )*bp )
function isspace
通常是这样实现的,它使用int
类型的参数作为具有 256 个值(从 0 到 255)的表中的索引。 如果int
类型的参数的值大于最大值 255 或负值(并且不等于宏 EOF 的值),则 function 的行为未定义。
从cppreference isspace() : The behavior is undefined if the value of ch is not representable as unsigned char and is not equal to EOF
。
当*bp
为负数时,例如它是-42
,那么它不能表示为unsigned char
,因为它是负数并且unsigned char
必须是正数或零。
在二进制补码系统上,值被符号扩展为更大的“宽度”,因此它们将设置最左边的位。 然后,当您采用更宽类型的0xff
时,最左边的位被清除,最终得到一个正值,小于或等于0xff
,我的意思是可以表示为unsigned char
。
注意 arguments 到&
经历隐式提升,所以*bp
的结果在调用isspace
之前被转换为int
。 让我们假设*bp = -42
例如并假设一个健全的平台,带有 8 位 char 已签名并且int
具有 32 位,然后:
*bp & 0xff # expand *bp = -42
(char)-42 & 0xff # apply promotion
(int)-42 & 0xff # lets convert to hex assuming twos-complement
(int)0xffffffd6 & 0xff # do & operation
(int)0xd6 # lets convert to decimal
214 # representable as unsigned char, all fine
如果没有& 0xff
,负值将导致未定义的行为。
我建议更喜欢isspace((unsigned char)*bp)
。
基本上最简单的isspace
实现看起来就像:
static const char bigarray[257] = { 0,0,0,0,0,...1,0,1,0,... };
// note: EOF is -1
#define isspace(x) (bigarray[(x) + 1])
在这种情况下你不能通过例如-42
,因为bigarray[-41]
是无效的。
你的问题:
这怎么样:
while (isspace(* bp & 0xff))
++ bp;
与此不同:
while (isspace(* bp))
++ bp;
不同之处在于,在第一个示例中,您始终将bp
处的最低字节传递给isspace
,这是由于按位与完整位掩码( 0b11111111
或0xff
)的结果。 isspace
的参数可能包含大于 1 个字节的类型。 例如, isspace
被定义为isspace(int c)
,所以你可以看到这里的参数是一个int
,它可能是多个字节,具体取决于你的系统。
简而言之,这是一个健全性检查,以确保isspace
仅比较bp
变量中的单个字节。
while (isspace(* bp & 0xff))
++ bp;
&&
while (isspace(* bp))
++ bp;
严格来说,如果bp
不引用unsigned char
,两者都是不正确的。
在这种情况下,它应该是:
while (isspace((unsigned char)(*bp & 0xff)))
++ bp;
或更好
while (isspace(*bp == EOF ? EOF : (unsigned char)(*bp & 0xff)))
++ bp;
如果参数不是EOF
或它没有unsigned char
的值,则 isspace 未定义
如果*bp
引用char
它必须是:
while (isspace((unsigned char)(*bp)))
++bp;
在 c 中,char 可以有符号或无符号https://en.wikipedia.org/wiki/C_data_types
当传递给isspace
时, bp
将被提升为 integer。 如果它是有符号的并且设置了高位,那么它将被符号扩展为负 integer。 这可能意味着它不是isspace
function https://linux.die.net/man/3/isspaceNo
请参阅http://cpp.sh/9mp2i了解它如何更改按位并更改 isspace 的值
如果我们假设 char 类型的位总是 8,
那么这里带有 0xff 的代码按位与运算符会让我们感到困惑。
但是,如果 char 类型并不总是 8 位,那又如何呢?
那么0xff可能还有别的意思吧?
实际上, char 类型并不总是 8 位,我们可以在 C99 标准中看到详细信息。 标准中的 char 类型未定义为 8 位。
以下是 C99 标准如何描述 char 类型的大小。
6.5.3.4 sizeof 运算符 当应用于具有char 、 unsigned char或signed char类型(或其限定版本)的操作数时,结果为 1 。 当应用于具有数组类型的操作数时,结果是数组中的总字节数。)当应用于具有结构或联合类型的操作数时,结果是此类 object 中的总字节数,包括内部和尾随填充。
6.2.5 类型声明为 char 类型的 object 大到足以存储基本执行字符集的任何成员。 如果基本执行字符集的成员存储在 char object 中,则其值保证为正。 如果任何其他字符存储在 char object 中,则结果值是实现定义的,但应在该类型可以表示的值范围内。
例如,德州仪器公司的 TMS320C28x DSP 有一个 16 位的字符。
对于编译器在此处指定, CHAR_BIT 为 16 (第 99 页)。
这似乎是一个现代处理器(目前正在出售),编译器支持 C99 和 C++03。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.