1. 程式人生 > 其它 >位運算和大小端以及位移操作 bitwise & byte Endianness

位運算和大小端以及位移操作 bitwise & byte Endianness

大小端的概念大家都很熟悉了。

這個概念主要是針對 32bit或者 64bit機器中,多個位元組的排列順序

出處

這個詞很奇怪,查了下出處。

The Computer Science terms Big-Endian and Little-Endian were introduced by Danny Cohen 2 in 1980. The key term endian has its roots in the novel Gulliver’s Travels 3 by Jonathan Swift 4 where within a war occurs between two factions who are fighting over which end of a boiled egg should be opened for eating. The big end or the little end. Unsurprisingly, the same said book was the inspiration for the naming of the 

Gulliver library.

可以看到原出處是一個小說,裡面描述兩股勢力因為 吃水煮蛋應該從雞蛋的哪一端開始吃而發生了戰爭。類似於鹹甜豆腐腦的爭端。

Endianness就是指雞蛋的兩頭。作者起這個名字,我猜是想代表位元組序本質上是一個沒有什麼實質意義,卻又真實存在的分歧。

具體區別

一圖流:

個人覺得小端相對來說比較符合直覺:

高位在高地址,低位在低地址。

引發的問題

主要是在32位(或者64位)的機器上,如何解釋 2位元組或者單位元組的資料。

以及一些強制型別轉換可能出現的問題

bitwise endianness bit內部的大小端

上面討論的是,64位機器內部,8個位元組的順序問題

實際上,對於一個位元組內部的Bit,也存在一個對稱的問題,bit的排列順序是怎麼樣的

給定一個結構體:

struct Byte {
    UINT8 bit0:1;
    UINT8 bit1:1;
    UINT8 bit2:1;
    UINT8 bit3:1;
    UINT8 bit4:1;
    UINT8 bit5:1;
    UINT8 bit6:1;
    UINT8 bit7:1;
};

 問題來了, bit 0 到底是在 Byte的 LSB還是在MSB?換個問的方法,我要修改bit 0, 是 byte |= (1 << 0), 還是 byte |= (1 << 7)

簡而言之的結論:

不確定。

C99 §6.7.2.1, paragraph 10 says:

"The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined."

 

C99標準表示: 具體的bit order和compiler的實現相關。

所以用bit 來表示資料的方式,相容性需要提個醒。

最安全的方式使用結構體內的成員引用方式。

 

如果需要顯式的表示這些位置,最好用下面的定義方式:

 1 /* Each of these preprocessor directives defines a single bit,
 2    corresponding to one button on the controller.  
 3    Button order matches that of the Nintendo Entertainment System. */
 4 #define KEY_RIGHT  0b00000001
 5 #define KEY_LEFT   0b00000010
 6 #define KEY_DOWN   0b00000100
 7 #define KEY_UP     0b00001000
 8 #define KEY_START  0b00010000
 9 #define KEY_SELECT 0b00100000
10 #define KEY_B      0b01000000
11 #define KEY_A      0b10000000
12 
13 int gameControllerStatus = 0;
14 
15 /* Sets the gameControllerStatus using OR */
16 void KeyPressed( int key ) { gameControllerStatus |= key; }
17 
18 /* Clears the gameControllerStatus  using AND and ~ (binary NOT)*/
19 void KeyReleased( int key ) { gameControllerStatus &= ~key; }
20 
21 /* Tests whether a bit is set using AND */
22 int IsPressed( int key ) { return gameControllerStatus & key; }

 

關於位運算 bitwise operation

用了很多年C,關於左移和右移操作,經常還是搞不清楚到底操作符在哪邊。

這裡一次搞明白

C99的定義:

shift-expression:

  additive-expression

  shift-expression<<additive-expression

  shift-expression>>additive-expression

 

<< 或者 >>的 右側是具體移動的位數,左邊則是被運算元

x = y >> 2; // y 向右移動2位 
x = y << 2; // y 向左移動2位

 

logical shift & arithmic shift 邏輯位移和算數位移

當位移操作和有符號數搞在一起的時候,就需要非常小心。

單純的shift我們稱作logic shift

If the variable ch contains the bit pattern 11100101, then ch >> 1 will produce the result 01110010, and ch >> 2 will produce 00111001.

上面的ch,如果以無符號數來表示,則邏輯位移不會影響語義的表達。

但是如果以有符號數表示,則從一個負數變成了一個正數。如果用作乘法操作就非常危險了。

 

對於C語言,左移和右移是有區別的:

  • 左移永遠都是logical shift

A left shift is always a logical shift (the bits that are shifted off the end are discarded, including the sign bit).

  • 右移,在unsigned時是logical shift,在signed時候是arithmic shift : 即會複製之前的符號位到最高位。

For unsigned numbers, the bit positions that have been vacated by the shift operation are zero-filled. For signed numbers, the sign bit is used to fill the vacated bit positions. In other words, if the number is positive, 0 is used, and if the number is negative, 1 is used.