繁体   English   中英

为什么用 0xff 对字符进行按位与?

[英]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 ,这是由于按位与完整位掩码( 0b111111110xff )的结果。 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 运算符 当应用于具有charunsigned charsigned 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM