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

Why do a bitwise-and of a character with 0xff?

我正在阅读一些实现简单解析器的代码。名为 scan 的函数将一行分成标记。 scan 有一个静态变量 bp 分配给要标记化的行。在赋值之后,空格被跳过。见下文。我不明白的是为什么代码对 bp 指向的字符与 0xff 进行按位运算,即 * bp & 0xff 的目的是什么?这是怎么回事:

while (isspace(* bp & 0xff))
    ++ bp;

与此不同:

while (isspace(* bp))
    ++ bp;

这里是 scan 函数:

static enum tokens scan (const char * buf)
                    /* return token = next input symbol */
{   static const char * bp;

    while (isspace(* bp & 0xff))
        ++ bp;

        ..
}

Your question:

How is this:

while (isspace(* bp & 0xff))
    ++ bp;

different from this:

while (isspace(* bp))
    ++ bp;

The difference is, in the first example you are always passing the lowermost byte at bp to isspace, due to the result of a bitwise AND with a full bitmask (0b11111111 or 0xff). It's possible that the argument to isspace contains a type that is larger than 1 byte. For example, isspace is defined as isspace(int c), so as you can see the argument here is an int, which may be multiple bytes depending on your system.

In short, it's a sanity check to ensure that isspace is only comparing a single byte from your bp variable.

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;

来自 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,嗯,一定是正数或零。

在二进制补码系统上,值是 sign extended 到更大的“宽度”,因此它们将设置最左边的位。然后当你使用更宽类型的 0xff 时,最左边的位被清除,你最终得到一个正值,小于或等于 0xff,我的意思是可以表示为 unsigned char .

请注意 & 的参数经过 implicit promotions,因此 *bp 的结果甚至在调用 isspace 之前就已转换为 int。让我们假设 *bp = -42 例如,并假设一个带有 8 位字符的健全平台已签名并且 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实现looks like just:

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] 只是无效的。

在 c 中,char 可以是有符号的或无符号的https://en.wikipedia.org/wiki/C_data_types

当传递给 isspace 时,bp 将被提升为整数。如果它是有符号的并且设置了高位,那么它将被符号扩展为负整数。这可能意味着它不是 isspace 函数 https://linux.die.net/man/3/isspaceNo

所要求的 unsigned char 或 EOF

请参阅 http://cpp.sh/9mp2i 以了解它如何更改位和更改 isspace 所见的值

来自 C 标准(7.4 字符处理

1 The header <ctype.h> declares several functions useful for classifying and mapping characters.198) In all cases the argument is an int, the value of which shall be representable as an unsigned char or shall equal the value of the macro EOF. If the argument has any other value, the behavior is undefined.

在本次通话中

isspace(* bp)

由于整数提升,类型 char 的参数表达式 *bp 被转换为类型 int

如果类型 char 的行为与类型 signed char 相同,并且表达式 *bp 的值为负,则类型 int 的提升表达式的值is 也将是负数,不能表示为 table 作为 unsigned char.

类型的值

这会导致未定义的行为。

在本次通话中

isspace(* bp & 0xff)

由于按位运算符 & 类型 int 的表达式 * bp & 0xff 的结果值可以表示为类型 unsigned char.

的值

所以这是一个技巧,而不是像

那样编写更清晰的代码
isspace( ( unsigned char )*bp )

函数 isspace 通常以这样的方式实现,它使用其 int 类型的参数作为具有 256 个值(从 0 到 255)的 table 中的索引.如果 int 类型的参数的值大于最大值 255 或负值(并且不等于宏 EOF 的值),则函数的行为未定义。

如果我们假设 char 类型的位总是 8,
那么这里带0xff的位与运算符会让我们感到困惑。

但是如果 char 类型不总是 8 位呢?
那么0xff可能还有别的意思吧?

实际上,char类型并不总是8位,我们可以在C99标准中看到详细信息。标准中的char类型没有定义为8位。

以下是C99标准对char类型大小的描述。

6.5.3.4 The sizeof operator When applied to an operand that has type char, unsigned char, or signed char, (or a qualified version thereof) the result is 1. When applied to an operand that has array type, the result is the total number of bytes in the array.) When applied to an operand that has structure or union type, the result is the total number of bytes in such an object, including internal and trailing padding.

6.2.5 Types An object declared as type char is large enough to store any member of the basic execution character set. If a member of the basic execution character set is stored in a char object, its value is guaranteed to be positive. If any other character is stored in a char object, the resulting value is implementation-defined but shall be within the range of values that can be represented in that type.

例如, Texas Instruments 的 TMS320C28x DSP 有一个 16 位的字符。
对于编译器,在第 99 页上将 hereCHAR_BIT 指定为 16

这似乎是一个现代处理器(目前正在销售),编译器支持 C99 和 C++03。