位运算和大小端以及位移操作 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 pattern11100101
, thench >> 1
will produce the result01110010
, andch >> 2
will produce00111001
.
上面的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.