bits bytes and integer

课件

第一部分:

pdf 文件

第二部分:

pdf 文件

课程连接

https://www.bilibili.com/video/BV1iW411d7hd?p=2

https://www.bilibili.com/video/BV1iW411d7hd?p=3

如何表示 bit

2025033015380572.png

字节

2025033015380574.png

C 语言中变量的字节长度

不同的机器不一样

2025033015380595.png

布尔运算

2025033015380609.png

bit 向量布尔运算

2025033015380621.png

bit 和集合

2025033015380634.png
使用 bit 为来表示一个集合,布尔运算可以看做是对集合进行运算:

C 语言中的逻辑运算

2025033015380637.png

注意
不要和布尔运算符号混淆

图中有一个语句,p && *p,可以避免空指针引用。GPT 解释:

表达式 p && *p 用于 C 或 C++ 编程中,以确保安全地访问指针指向的值。这个表达式的目的是避免空指针访问,即访问一个未初始化或显式设置为 NULL 的指针,这通常会导致程序崩溃或未定义行为。

组件解释

操作过程
逻辑与操作 (&&): 这是一个短路逻辑操作符。它首先检查左侧的表达式(这里是 p),如果 p 为 NULL(在布尔上下文中等同于 false),则整个表达式立即求值为 false,右侧的表达式(*p)将不会被执行。这是关键的,因为它防止了在 p 为 NULL 时尝试解引用 p。
• 如果 p 不是 NULL,则表达式继续评估 *p。这里,*p 将检查 p 指向的值。如果该值为 0(或者在布尔上下文中视为 false),则整个表达式结果为 false。如果 *p 有一个非零值,整个表达式的结果是 true。

使用场景
这种模式经常出现在条件语句中,尤其是在处理可能为 NULL 的指针时。例如,在处理字符串或链表等数据结构时,经常需要检查指针是否为空,以及指向的数据是否有效或具有特定的值。

示例代码

char *p = get_string();
if (p && *p) {
printf("Pointer is not null and points to a non-zero value.\n");
} else {
printf("Pointer is null or points to zero.\n");
}

在这个例子中,如果 get_string() 返回一个非 NULL 指针,且指向的第一个字符不是字符串结束字符 \0(在 C 中字符串以 \0 结尾),则第一个 printf 语句将执行。如果指针是 NULL 或指向的内容是 \0,则执行第二个 printf 语句。这种检查方式非常有用,可以避免在访问字符串或数组内容时出现错误。

位移运算

2025033015380652.png

当使用右移运算符号的时候,大多数语言使用的是算术右移。但是 C 语言没有明确的规定是要使用逻辑右移还是算术右移。

如果移动的位数大于了本身的位数,大多数语言会先把移动的位数取模,再进行移动。

整数编码

2025033015380663.png

如何快速的计算补码表示的十进制数笔记中使用的原理其实就是这个幻灯片中所提到的。

4 个 bit 编码示例

4 个 bit 总共有 16 情况,即 0000-1111
2025033015380668.png

绘图代码:

import matplotlib.pyplot as plt
import numpy as np
 
# 生成所有4位二进制组合
binary = [f"{i:04b}" for i in range(16)]
unsigned_dec = [i for i in range(16)] # 无符号值
signed_dec = [i if i < 8 else i - 16 for i in range(16)] # 补码值
 
# 计算原码值(Sign-Magnitude)
original_dec = []
for b in binary:
sign_bit = b[0]
magnitude = int(b[1:], 2)
original_dec.append(-magnitude if sign_bit == '1' and magnitude != 0 else magnitude) # 处理-0为0
 
# 极坐标角度调整(北边起始,顺时针排列)
start_angle = np.pi/2 # 北边起始
theta = start_angle - np.linspace(0, 2*np.pi, 16, endpoint=False) # 顺时针递减
 
# 笛卡尔坐标计算
x = 1.2 * np.cos(theta)
y = 1.2 * np.sin(theta)
 
# 创建画布
fig, ax = plt.subplots(figsize=(12, 12))
ax.set_xlim(-1.5, 1.5)
ax.set_ylim(-1.5, 1.5)
ax.axis('off')
 
# 绘制标签(每行独立设置颜色)
vertical_gap = 0.1 # 行间距调整
for i in range(16):
# 动态水平对齐
ha = 'left'
if x[i] > 0.1: ha = 'left' # 右侧左对齐
elif x[i] < -0.1:ha = 'left' # 左侧右对齐
# 四行独立绘制
ax.text(x[i], y[i] + 1.5*vertical_gap, binary[i], ha=ha, va='center', color='blue', fontsize=20)
ax.text(x[i], y[i] + 0.5*vertical_gap, f"U: {unsigned_dec[i]}", ha=ha, va='center', color='green', fontsize=20)
ax.text(x[i], y[i] - 0.5*vertical_gap, f"S: {original_dec[i]}", ha=ha, va='center', color='purple', fontsize=20)
ax.text(x[i], y[i] - 1.5*vertical_gap, f"T: {signed_dec[i]}", ha=ha, va='center', color='red', fontsize=20)
 
# 绘制参考圆环
circle = plt.Circle((0.1, 0), 0.95, color='gray', fill=False, linestyle='--')
ax.add_artist(circle)
 
plt.tight_layout()
plt.show()

16 种情况表示无符号数

二进制十进制
00000
00011
00102
00113
01004
01015
01106
01117
10008
10019
101010
101111
110012
110113
111014
111115

16 种情况表示原码(有符号数)

二进制十进制(原码)
0000+0
0001+1
0010+2
0011+3
0100+4
0101+5
0110+6
0111+7
1000-0
1001-1
1010-2
1011-3
1100-4
1101-5
1110-6
1111-7

16 种情况表示补码(有符号数)

二进制十进制(补码)
00000
0001+1
0010+2
0011+3
0100+4
0101+5
0110+6
0111+7
1000-8
1001-7
1010-6
1011-5
1100-4
1101-3
1110-2
1111-1
优点缺点
零的唯一性:仅 0000 表示零,无 +0 和 -0 歧义。正负数范围不对称:负数比正数多一个最小负数(如 4 位补码范围为 -8 到 +7)。
运算统一性:加减法可直接用二进制运算,无需额外判断符号位。负数转换复杂性:需通过“取反加 1”步骤转换负数,对初学者不够直观。
硬件实现高效:无需设计特殊逻辑处理符号位和数值位。最小负数无对应正数:如 -8 无法取反得到 +8,导致绝对值运算溢出。
广泛兼容性:现代计算机均采用补码,统一标准简化系统设计。溢出检测复杂:需通过符号位变化判断溢出(如正数相加变负数)。
节省存储空间:与反码、原码相比,无需为两种零分配额外位。历史兼容性挑战:旧系统使用原码或反码时,数据迁移需额外转换步骤。

数的范围

2025033015380682.png

常用的数

2025033015380686.png
2025033015380690.png

补码和无符号数之间的转换

2025033015380699.png
2025033015380700.png
2025033015380709.png
2025033015380715.png
2025033015380734.png

todo https://www.bilibili.com/video/BV1iW411d7hd?t=3471.5&p=2