低级运算对于编写系统程序、加密程序、图形程序等需要高执行速度或高效地使用空间的程序非常有用。
按位运算符
按位运算符访问单个位或位域,用于对整数或字符进行按位运算。C语言共提供了6个按位运算符。
符号 | 含义 |
---|---|
<< | 左移位 |
>> | 右移位 |
~ | 按位求反 |
& | 按位与 |
^ | 按位异或 |
| | 按位或 |
左移位和右移位运算符统称作移位运算符。移位运算符是二元运算符,使用形式为:
操作数 >>(或<<) 移动位数
移位运算符的操作数是整型和字符型的。左移位运算符将操作数的位向左移动,最左边的位溢出,最右边的位补0。右移位运算符将操作数的位向右移动,最右边的位溢出,如果操作数为无符号整数或非负值,则最左边的位补0,如果操作数为负值,则补0或补1由实现决定。因此考虑可移植性,只对无符号数进行移位运算。移位运算符并不改变操作数,可以与赋值运算符一起组合成复合运算符>>=
和<<=
。移动运算符的优先级比算术运算符低。
按位求反运算符~
是一元运算符,它的使用形式为:
~操作数
对操作数的每一个取反,将每一个0替换为1,将每一个1替换为0。按位与运算符&
对两个操作数相应的位进行逻辑与操作,按位异或运算符&
和按位或运算符|
分别对两个操作数相应的位进行逻辑异或操作和逻辑或操作。运算符~
、&
、^
的优先级高于|
运算符,但是它们的优先级都低于关系运算符。&
、^
和|
可以和赋值运算符=
一起组成复合赋值运算符&=, ^=, |=
。
操作数1 & 操作数2
操作数1 | 操作数2
操作数1 ^ 操作数2
按位运算符的常见应用包括设置位(使该位值为1)、将位清零、测试位(测试是否该位值为1)、修改位域和获取位域等。
- 设置位。如果要设置整型变量
i
的第j
位为1(起始位为第0位),可采用的通用形式为:i |= 1<<j
。 - 清空位。如果要将整型变量
i
的第j
为设为0,可采用的通用形式为:i &= ~(1<<j)
。 - 测试位。如果要测试整型变量
i
的第j
位是否为1,可采用的通用格式为:if (i & 1<<j)
- 修改位域。首先使用按位与清楚位域,再使用按位或设置新的位域。
- 获取位域。当位域处于数的末尾,通过按位与就可以获得它的值,否则要将其移位到数的末尾,再使用按位与获得它的值。
结构体中的位域
定义数据类型时,可以在每个成员的后面指定该成员所占位的长度,如以下形式:
struct
{
unsigned int 变量 : 位数;
signed int 变量 : 位数;
...
}
每一个成员都是整个结构体类型变量的一个位域,位数表示其占用位的个数。位域的类型必须是int
, unsigned int
和signed int
。但是由于使用int
型时不同编译器对最高位的处理不同,为了可移植性的考虑,将所有位域声明为unsigned int
或signed int
。
使用按位运算符往往可以达到和在结构体中使用位域的方法更快,但是从可读性上来说,结构体使用位域的方法更易读。位域可能是一个字节的一部分或跨越多个字节,因此没有地址的概念,不能为位域使用取地址符。
内存单元是计算机按字划分的存储单位,大小因计算机不同而不同,有8位,16位,32位和64位等。当编译器处理结构体类型变量的声明时,将位域逐个存放在内存单元中,直到剩下的空间不能够在存放下一个位域。这时,一些编译器会跳到下一存储单元继续存放,而另一些则会将位域拆开跨存储单元存储。具体的实现方式和位域在内存单元的存放顺序是由实现定义的。
C语言允许忽略位域的名称,未命名的位域用作对字段间的填充,以保证其他位域存储在适当的位置。另外指定位数为0的位域可以通知编译器将下一个位域放置在下一个存储单元的起始位置。
其他低级技术
字符占用一个字节,有时将字符当作是字节存储一些并不一定是字符形式的数据,这时候最好定义BYTE类型或WORD类型:
typedef unsigned char BYTE;
typedef unsigned int WORD;
另外,可以通过将一个整数强制转换为指针就可以构建一个指向某个特定的地址的指针。对于由段地址和基地址表示的实模式,可以通过非标准头提供的宏例如doc.h
中的MK_FP
宏构建一个包含特定地址的指针。
/*************************************
* bit_operator.c *
* *
* C语言中的位操作 *
*************************************/
#include <stdio.h>
typedef struct Test
{
unsigned int x : 8;
unsigned int : 8;
unsigned int : 0;
unsigned int p : 8;
} Test;
int main()
{
unsigned int i = 16;
/*按位运算符基本运算*/
unsigned int j = i << 2;
printf("i = %u, i = 0x%x\n", i, i);
printf("j = %u, j = 0x%x\n", j, j);
i = i >> 2;
printf("i = %u, i = 0x%x\n", i, i);
printf("(i<<2+1) = %u\n", i<<2+1);
printf("i<<(2+1) = %u\n", i<<(2+1));
j = ~j;
printf("j = %u, j = %x\n", j, j);
unsigned int k = 1;
printf("i | k = %u(0x%x)\n", i | k, i | k);
printf("i & k = %u(0x%x)\n", i & k, i & k);
printf("i ^ k = %u(0x%x)\n", i ^ k, i ^ k);
unsigned int m = 0;
/*设置m的第4位为1*/
m |= 1 << 4;
printf("设置m的第4位为1,m = %u\n", m);
/*测试m的第4位是否为1*/
if (m & 1<<4)
printf("m的第4位为1!\n");
/*设置m的第4位为0*/
m |= ~(1<<4);
printf("设置m的第4位为0,m = %u\n", m);
i = 60;
i = (i & ~0x001C) | 0x0014;
printf("替换i的位域后的结果为%u\n", i);
i = 60;
i >>= 2;
i &= (0x000F);
printf("i的3到6位为%x\n", i);
/*结构体变量中的位域*/
Test a;
a.x = 10;
a.p = 10;
printf("a.x = %u\n", a.x);
/*指针*/
printf("现在i的值为%u\n", i);
printf("i的地址为%p\n", &i);
char *p = NULL;
scanf("%p", &p);
unsigned int *pi = (unsigned int *)p;
printf("i = %u\n", *pi);
return 0;
}
参考文献
- K.N. King 著,吕秀峰 译. C语言程序设计-现代方法. 人民邮电出版社