标签:http 移动 bit 信息 意义 大小 功能 += log
提供一种冷门奇怪的语法:位域定义。
引入:
有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几 个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。 这样就可以把几个不同的对象用一个字节的二进制位域来表示。
——摘自 csdn博客 sty124578 ?\(^{_1}\)
本文主要讲解了:
- 位域的使用
- 定义位域
- 位域运算特点
- 位域定义有什么用
- 搞大工程
- 处理模形如 $2^p(p\texttt{ 为整数且大于 }0)$ 的数的运算
- 位域在 OI 中的应用
- P2033 Chessboard Dance
先提供效果代码
struct test1 {
unsigned a:2;
};
struct test1 {
unsigned a:2;
};
如上,对于该结构体,在内存中是这样的(一格 = 一位):
bit|1|2|3|4|5|6|7|8|
| a | | | | | | |
struct test2 {
unsigned a:3;
int b:1;
int :2;
unsigned d:2;
};
对应在内存中的情况:
bit|1|2|3|4|5|6|7|8|
| a |b| | d |
^^^
空位不存储有用值
struct test2 {
unsigned a:3;
int b:1;
int :0;
long long d:8;
};
对应在内存中的情况:
----------------------------------------------
[Byte 1] bit|1|2|3|4|5|6|7|8|
| a |b| |
^^^^^^^
:0表示这个字节后续的位全部舍弃
----------------------------------------------
[Byte 2] bit|1|2|3|4|5|6|7|8|
| d |
----------------------------------------------
struct test3 {
short a:17; // 不合法,short 本身占 16 个字节
int b:17; // 合法
};
struct
里面定义运算结果自动上溢。
以下 \(a,b,c,d,e,f\) 定义如下:
struct T {
unsigned a : 3;
unsigned b : 3;
unsigned c : 3;
int d : 3;
int e : 3;
int f : 3;
};
// 已知 a = 3, b = 6, 计算 c = a + b
运算过程
bit | 4 | 3 | 2 | 1 |
----+---+---+---+---+
a | | 0 | 1 | 1 |
+ b | | 1 | 1 | 0 |
= | 1 | 0 | 0 | 1 |
c | | 0 | 0 | 1 |
// 第四 bit 值上溢,最终运算结果:c = 1
// 已知 d = 3, e = 2, 计算 f = d + e
运算过程
正负位
bit | 3 | 2 | 1 |
----+---+---+---+
a | 0 | 1 | 1 |
+ b | 0 | 1 | 0 |
= c | 1 | 0 | 1 |
// 虽然运算结果没有上溢,但由于 int 的特性,这里 c 的最终结果非 5 而是 -3
搞大工程时,一般会比较注重内存的节约。
例: 如果有两个 unsigned
型的数 \(a,b\),\(a\) 保证在使用中小于 \(2^5\),\(b\) 保证在使用中小于 \(2^3\)。那么使用两个 unsigned short
需要 \(2\) 个字节,而使用位域只需要一个字节,节约了空间。
不过,引用一段来自 csdn 论坛的话:
位域是只能用于结构体中,其目的是为了牺牲时间来节省空间,这在早年内存空间少时有意义,现在一般都是牺牲空间来节省时间,因此使用位域不是一个好主意。——arong1234 ?\(^{_2}\)
比如,有一次你需要存储一个整数 \(a\),且 \(0<a<8\)
\(a\) 会有可能加、减、乘一个数,得到的结果仍在 \(0<a<8\) 内,即在 \(\bmod\ 2^3\) 意义下运算。
那么用位域是个不错的选择。
```// 定义
struct number_save {
int a : 3;
} num;
// 使用
num.a += someNumber; // 无需写模运算即可取余(%8),自动溢出忽略大位上的值
```
什么?你觉得没什么用?
这可以节省一点点字符呀……而且很方便不是吗……
至少你还可以用来装逼啊……
什么?你说没有题目会用到“模形如 \(2^p(p\texttt{ 为整数且大于 }0)\) 的数”?
例子马上就来。
这里处理转向,使用位域将会非常方便。
首先定义移动用数组
const int xx[] = {1, 0, -1, 0}, yy[] = {0, -1, 0, 1};
注意到如果这样写,
然后考虑到 4 种情况,方向值 dir
是在 \(\bmod\ 2^2\) 意义下的。
所以定义
struct pos {
int x, y;
uint8 dir : 2;
void turn(char ch)
{
switch (ch) {
case 'l' : dir--; break; // left
case 'r' : dir++; break; // right
case 'b' : dir ^= 2; break; // back
}
}
};
注意到 turn
函数里,我没有使用模运算,而是直接加,减。(对于 turn back
操作,+2/-2
在 \(\bmod\ 2^2\) 意义下只需要改变第 2 个 bit 的值即可,所以对于 dir ^= 2
请感性理解)
这个东西在 \(OI\) 之中或许没什么很大的应用,内存上的优势也因为不能定义数组而微乎其微,连自动溢出的功能用处似乎也不大。
写这篇博客的初衷其实是:在写 P2033 时发现使用位域十分的简洁,所以想跟大家分享一下。
或许读者终其一生都不会使用位域,但多了解一点总是好的。
标签:http 移动 bit 信息 意义 大小 功能 += log
原文地址:https://www.cnblogs.com/hkxadpall/p/introduction-BitField.html