c语言 - 为什么要对一个字符进行比位和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;
..
}

来自 C 标准(7.4 字符处理)

1 标头声明了几个有用的函数 分类和映射字符.198)在所有情况下,参数都是 一个 int,其值应表示为无符号 字符或应等于宏 EOF 的值。如果参数有 任何其他值,行为都是未定义的。

在此通话中

isspace(* bp)

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

如果类型char的行为signed char类型,并且表达式*bp的值为负数,则类型int的提升表达式的值也将为负数,并且不能表示为类型unsigned char的值。

这会导致未定义的行为。

在此通话中

isspace(* bp & 0xff)

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

所以这是一个技巧,而不是编写更清晰的代码,如

isspace( ( unsigned char )*bp )

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

来自 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

请注意,&参数会进行隐式提升,因此*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;

不同之处在于,在第一个例子中,你总是将最下面的字节传递给bpisspace,这是由于具有完整位掩码(0b111111110xff)的按位AND的结果。要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 字符中可以是有符号的或无符号的 https://en.wikipedia.org/wiki/C_data_types

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

请参阅 http://cpp.sh/9mp2i 了解它如何更改位位并更改该空间看到的值

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

但是,如果char类型并不总是8位呢?
那么0xff可能有另一种含义,对吧?

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

以下是C99标准如何描述字符类型的大小。

6.5.3.4 大小运算符 应用于类型为 char、无符号 char 或有符号 char(或其限定版本)的操作数时结果为 1。当应用于具有数组类型的操作数时, 结果是数组中的总字节数。当应用于 具有结构或联合类型的操作数,结果是总计 此类对象中的字节数,包括内部字节数和尾随字节数 填充。

6.2.5 类型声明为 char 类型的对象足够大,可以存储基本执行字符集的任何成员。如果会员 的基本执行字符集存储在 char 对象中,其 值保证为正数。如果存储了任何其他字符 在 char 对象中,结果值是实现定义的,但 应在可以表示的值范围内 类型。

例如 德州仪器的 TMS320C28x DSP 有一个 16 位的字符.
对于编译器在此处指定,CHAR_BIT第 99 页上为 16

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

相关内容

  • 没有找到相关文章

最新更新