[英]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.