C语言程序设计的源码和书籍, 所有源码都是可执行的,整理不易家人们
https://kdocs.cn/join/gi76b0d?f=101 ➕绿泡泡:jianyuePros, 备注:C 程序设计;五个元子下载使用;
「《C 程序设计》(第五版)谭浩强主编,清华大学出版社,2017 年和源码」
文章目录
12.1 位运算符C语言提供了六种位运算符:
& 按位与
| 按位或
^ 按位异或
~ 取反
<< 左移
>> 右移
12.1.1 按位与运算
【例 12.1】
#include <stdio.h>
#include <stdlib.h>
main()
{
int a = 9, b = 5, c;
c = a & b;
printf("a=%d\nb=%d\nc=%d\n", a, b, c);
}
12.1.2 按位或运算
例如:9|5 可写算式如下:
00001001
|00000101
00001101 (十进制为 13)可见 9|5=13
【例 12.2】
#include <stdio.h>
#include <stdlib.h>
main()
{
int a = 9, b = 5, c;
c = a | b;
printf("a=%d\nb=%d\nc=%d\n", a, b, c);
}
12.1.3 按位异或运算
按位异或运算符“^”是双目运算符。其功能是参与运算的两数各对应的二进位相异
或,当两对应的二进位相异时,结果为 1。参与运算数仍以补码出现,例如 9^5 可写成算式
如下:
00001001
^00000101
00001100 (十进制为 12)
【例 12.3】
#include <stdio.h>
#include <stdlib.h>
main()
{
int a = 9;
a = a ^ 5;
printf("a=%d\n", a);
}
12.1.4 求反运算
求反运算符~为单目运算符,具有右结合性。其功能是对参与运算的数的各二进位按位
求反。
例如~9 的运算为:
~(0000000000001001)结果为:1111111111110110
12.1.5 左移运算
左移运算符“<<”是双目运算符。其功能把“<< ”左边的运算数的各二进位全部左移若干
位,由“<<”右边的数指定移动的位数,高位丢弃,低位补 0。
例如:
a<<4
指把 a 的各二进位向左移动 4 位。如 a=00000011(十进制 3),左移 4 位后为 00110000(十进
制 48)。
12.1.6 右移运算
右移运算符“>>”是双目运算符。其功能是把“>> ”左边的运算数的各二进位全部右移若干
位,“>>”右边的数指定移动的位数。
例如:
设 a=15,
a>>2
表示把 000001111 右移为 00000011(十进制 3)。
应该说明的是,对于有符号数,在右移时,符号位将随同移动。当为正数时,最高位补
0,而为负数时,符号位为 1,最高位是补 0 或是补 1 取决于编译系统的规定。Turbo C 和很
多系统规定为补 1。
【例 12.4】
#include <stdio.h>
main()
{
unsigned a, b;
printf("input a number: ");
scanf("%d", &a);
b = a >> 5;
b = b & 15;
printf("a=%d\tb=%d\n", a, b);
}
请再看一例!
【例 12.5】
#include <stdio.h>
main()
{
char a = 'a', b = 'b';
int p, c, d;
p = a;
p = (p << 8) | b;
d = p & 0xff;
c = (p & 0xff00) >> 8;
printf("a=%d\nb=%d\nc=%d\nd=%d\n", a, b, c, d);
}
12.2 位域(位段)
在C语言中,位域(也称为位段)是一种特殊的数据结构,允许你以位为单位来定义和访问结构体中的成员。位域的使用能够节省存储空间,尤其是在处理硬件寄存器或需要精确控制内存使用的场景中尤为重要。位域的声明通常在结构体中进行,通过指定成员的宽度来定义该成员占用的位数。
基本语法
位域的定义语法一般如下:
struct 结构体名 {
类型名 成员名 : 位数;
类型名 成员名 : 位数;
// 更多成员...
};
类型名
:通常是一个整型类型(如int
、unsigned int
),决定了位域成员的有符号或无符号属性及大小。成员名
:结构体中的位域成员名称。位数
:指定该成员所占用的位数。
示例
下面是一个简单的位域结构体示例,用于表示一个具有状态标志的结构,每个标志只需要一位:
#include <stdio.h>
struct Flags {
unsigned int READONLY : 1; // 只读标志,占1位
unsigned int WRITE : 1; // 写入标志,占1位
unsigned int EXECUTE : 1; // 执行标志,占1位
unsigned int RESERVED : 29; // 保留位,占29位
};
int main() {
struct Flags flags;
flags.READONLY = 1; // 设置只读标志
flags.WRITE = 1; // 设置写入标志
flags.EXECUTE = 0; // 清除执行标志
printf("READONLY: %d, WRITE: %d, EXECUTE: %d\n",
flags.READONLY, flags.WRITE, flags.EXECUTE);
return 0;
}
注意事项
- 内存对齐:编译器可能会在位域之间或之后插入填充位以保证对齐,这取决于具体的编译器和体系结构。因此,位域结构的实际大小可能大于其成员位数之和。
- 类型选择:尽管可以使用
signed int
,但通常推荐使用unsigned int
,因为有符号位域的行为在不同编译器中可能有所不同。 - 位段的顺序和大小端问题:位域的布局(高位在前还是低位在前)和大小端序依赖于目标系统的字节序,这可能影响位域成员的访问和解释。
- 位段的移植性:由于位域的具体实现细节(如对齐、填充等)依赖于编译器,因此使用位域可能会影响代码的可移植性。
位域是C语言中的高级特性,正确且高效地使用它可以优化存储和处理特定类型数据的效率,但同时也需要对其限制和潜在问题有所了解。
【例 12.6】
#include <stdio.h>
main()
{
struct bs
{
unsigned a : 1;
unsigned b : 3;
unsigned c : 4;
} bit, *pbit;
bit.a = 1;
bit.b = 7;
bit.c = 15;
printf("%d,%d,%d\n", bit.a, bit.b, bit.c);
pbit = &bit;
pbit->a = 0;
pbit->b &= 3;
pbit->c |= 1;
printf("%d,%d,%d\n", pbit->a, pbit->b, pbit->c);
}
上例程序中定义了位域结构 bs,三个位域为 a,b,c。说明了 bs 类型的变量 bit 和指向
bs 类型的指针变量 pbit。这表示位域也是可以使用指针的。程序的 9、10、11 三行分别给三
个位域赋值(应注意赋值不能超过该位域的允许范围)。程序第 12 行以整型量格式输出三个域
的内容。第 13 行把位域变量 bit 的地址送给指针变量 pbit。第 14 行用指针方式给位域 a 重
新赋值,赋为 0。第 15 行使用了复合的位运算符"&=“,该行相当于:
pbit->b=pbit->b&3
位域 b 中原有值为 7,与 3 作按位与运算的结果为 3(111&011=011,十进制值为 3)。同样,程
序第 16 行中使用了复合位运算符”|=",相当于:
pbit->c=pbit->c|1
其结果为 15。程序第 17 行用指针方式输出了这三个域的值。
12.3 本章小结
- 位运算是C语言的一种特殊运算功能, 它是以二进制位为单位进行运算的。位运算
符只有逻辑运算和移位运算两类。位运算符可以与赋值符一起组成复合赋值符。如
&=,|=,^=,>>=,<<=等。 - 利用位运算可以完成汇编语言的某些功能,如置位,位清零,移位等。还可进行数
据的压缩存储和并行运算。 - 位域在本质上也是结构类型,不过它的成员按二进制位分配内存。其定义、说明及
使用的方式都与结构相同。 - 位域提供了一种手段,使得可在高级语言中实现数据的压缩,节省了存储空间,同
时也提高了程序的效率。