1 同步、异步

1.1 同步与异步编程的区别

  • 同步(Synchronous)‌:
    任务按代码顺序‌阻塞执行‌,必须等待当前操作完成后才能执行下一操作。程序执行流在时间上严格耦合,逻辑简单但效率较低。

    类比1:排队买奶茶,必须等前一人完成才能轮到自己。

    类比2:想象你在厨房做饭,严格按照步骤执行:洗菜 → 切菜 → 炒菜,每一步都必须等前一步完成,整个过程是顺序且阻塞的。

  • 异步(Asynchronous)‌:
    任务触发后‌无需等待完成‌,程序继续执行后续代码。结果通过回调函数、事件循环或 Promise 等机制处理,效率高但逻辑复杂,适用于耗时长的操作(如网络请求、文件读写、硬件交互)。

    类比1:餐厅点餐后继续聊天,厨师后台备餐,完成后通知取餐。

    类比2:下单外卖(异步操作)→ 打扫房间(无需等待外卖)→ 外卖送达时收到通知,外卖的准备过程与打扫房间并行进行,外卖完成时通过通知(回调)告知你。

关键选择:任务是否依赖前序结果?是 → 同步;否 → 异步

1.2 代码示例说明

  • 同步示例:顺序执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <unistd.h> // 用于 sleep 函数

// 模拟耗时任务
void task1() {
printf("任务1开始\n");
sleep(2); // 模拟耗时2秒
printf("任务1完成\n");
}

void task2() {
printf("任务2开始\n");
sleep(1); // 模拟耗时1秒
printf("任务2完成\n");
}

int main() {
// 同步执行:先执行task1,再执行task2
task1();
task2();
return 0;
}

// 输出结果:
// 任务1开始
// 任务1完成
// 任务2开始
// 任务2完成

特点:任务2必须等待任务1完成后才能开始。程序总耗时 = 2秒(task1) + 1秒(task2) = 3秒。

  • 异步示例:并发执行(使用线程模拟)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <stdio.h>
#include <pthread.h> // 使用POSIX线程库
#include <unistd.h>

// 模拟耗时任务(异步执行)
void* task1(void* arg) {
printf("任务1开始\n");
sleep(2); // 模拟耗时2秒
printf("任务1完成\n");
return NULL;
}

void* task2(void* arg) {
printf("任务2开始\n");
sleep(1); // 模拟耗时1秒
printf("任务2完成\n");
return NULL;
}

int main() {
pthread_t thread1, thread2;

// 异步执行:同时启动两个线程
pthread_create(&thread1, NULL, task1, NULL);
pthread_create(&thread2, NULL, task2, NULL);

// 等待线程结束(主函数不退出)
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);

return 0;
}

// 输出结果(顺序可能不同):
// 任务1开始
// 任务2开始
// 任务2完成
// 任务1完成

特点:任务1和任务2同时运行,互不等待。程序总耗时 = 2秒(以耗时最长的任务为准)。

2 阻塞、非阻塞

2.1 阻塞与非阻塞的区别

  • 阻塞(Blocking)
    • 程序在调用某个操作时,必须等待该操作完成才能继续执行后续代码。
    • 如果操作未完成(如等待数据读取),程序会卡住(暂停),直到操作完成。
  • 非阻塞(Non-blocking)
    • 程序在调用某个操作时,立即返回,无需等待操作完成。
    • 如果操作未完成(如数据未准备好),程序会立即返回错误码(如 EAGAINEWOULDBLOCK),允许程序继续执行其他任务。

2.2. 形象化比喻

  • 阻塞
    想象你去餐厅点餐,服务员需要10分钟准备食物。你必须一直等在原地,直到食物上桌才能离开。
    • 程序行为:调用 read() 读取文件时,如果没有数据,程序会卡住,直到数据到达。
  • 非阻塞
    想象你去餐厅点餐,服务员告诉你“食物需要10分钟,你可以先去逛街,做好后我们会通知你”。你立即离开,去干其他事,等服务员通知你后再回来取餐。
    • 程序行为:调用 read() 时,如果没有数据,程序立即返回,并继续执行其他任务(如轮询或事件驱动)。

2.3 代码示例说明

  • 阻塞模式:文件读取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <unistd.h>

int main() {
FILE *fp = fopen("data.txt", "r");
if (fp == NULL) {
perror("fopen");
return 1;
}

char buffer[100];
// 阻塞读取:如果没有数据,程序会一直等待
printf("等待数据...\n");
size_t bytes_read = fread(buffer, 1, sizeof(buffer), fp);
printf("读取到 %zu 字节数据\n", bytes_read);

fclose(fp);
return 0;
}

特点:如果 data.txt 中没有数据,fread()无限等待,程序卡住。适用于数据一定存在的场景(如读取本地文件)。

  • 非阻塞模式:文件读取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

int main() {
int fd = open("data.txt", O_RDONLY | O_NONBLOCK); // 设置非阻塞模式
if (fd == -1) {
perror("open");
return 1;
}

char buffer[100];
ssize_t bytes_read;

while (1) {
bytes_read = read(fd, buffer, sizeof(buffer));
if (bytes_read == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
printf("数据未准备好,继续等待...\n");
sleep(1); // 模拟轮询
continue;
} else {
perror("read");
break;
}
} else {
printf("读取到 %zd 字节数据\n", bytes_read);
break;
}
}

close(fd);
return 0;
}

特点:如果 data.txt 中没有数据,read()立即返回 EAGAIN 或 EWOULDBLOCK,程序不会卡住。通过轮询或事件驱动(如 select() / epoll)处理后续逻辑,适合高并发场景。

2.4 同步、异步、阻塞、非阻塞四个概念

2.4.1 四种组合模式

  • 同步阻塞 (Synchronous Blocking)

想象你去餐厅吃饭,点完菜后必须坐在餐桌前等待,直到服务员把菜端上来,期间不能做其他事情。这就是同步阻塞模式,你需要主动等待结果,并且在等待过程中不能做其他事情。

  • 同步非阻塞 (Synchronous Non-blocking)

还是在餐厅,你点完菜后可以在餐厅里自由活动(比如去洗手间、和朋友聊天),但需要时不时回到餐桌前询问服务员菜是否做好了。这就是同步非阻塞模式,你不需要一直等待,但需要主动轮询结果。

  • 异步阻塞 (Asynchronous Blocking)

这种模式比较少见,因为异步通常与非阻塞结合使用。想象你去餐厅吃饭,点完菜后坐在餐桌前等待,但服务员会在菜做好后主动通知你。虽然你可以做其他事情,但你选择坐在那里等待,这就是异步阻塞模式。

  • 异步非阻塞 (Asynchronous Non-blocking)

最理想的模式。你点完菜后可以离开餐厅去逛街,服务员会在菜做好后打电话通知你。这就是异步非阻塞模式,你不需要等待,也不需要主动询问,系统会在任务完成时通知你。

模式 解释
同步阻塞 这是最严格的模式,必须顺序执行,且阻塞过程不能干其他事情,必须等上面的任务完成。
同步非阻塞 这种模式也是顺序执行,但是阻塞过程可以干其他的事情,但是必须主动去询问上面的任务是否完成。
异步阻塞 这种模式比较少
异步非阻塞 这种模式最合理也最常见,异步/并发执行,发起任务后可立即返回,也无需主动发起询问任务是否完成,系统会在任务完成时通知你。

2.4.2 程序示例

(一)同步非阻塞(Synchronous Non-blocking)

场景:模拟一个“等待文件数据”的任务,但调用方不会被阻塞,而是需要主动轮询检查数据是否准备好。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <stdio.h>
#include <unistd.h> // for sleep()
#include <pthread.h>

// 模拟文件数据是否准备好的状态变量
int data_ready = 0;
char data[] = "Hello, Synchronous Non-blocking!";

// 模拟耗时任务(如读取文件)
void* prepare_data(void* arg) {
sleep(3); // 模拟耗时3秒
data_ready = 1; // 数据准备完成
return NULL;
}

int main() {
pthread_t thread;

// 启动异步任务(模拟读取文件)
pthread_create(&thread, NULL, prepare_data, NULL);

printf("主线程开始轮询检查数据是否准备好...\n");

// 同步非阻塞:主动轮询检查数据是否准备好
while (!data_ready) {
printf("数据未准备好,继续轮询...\n");
sleep(1); // 每隔1秒检查一次
}

printf("数据已准备好: %s\n", data);
pthread_join(thread, NULL); // 等待线程结束
return 0;
}

运行流程

  1. 主线程启动一个子线程模拟读取文件(耗时3秒)。
  2. 主线程进入轮询状态,每隔1秒检查 data_ready 是否为1。主线程必须不断检查状态(data_ready 是否为1),这会消耗一定的CPU资源(如频繁调用 printf 或检查变量)。
  3. 子线程3秒后设置 data_ready = 1
  4. 主线程检测到数据就绪后继续执行。

关键点

  • 同步:主线程必须等待子线程完成任务(data_ready == 1)才能继续。
  • 非阻塞:主线程不会被挂起,而是主动轮询检查状态(while (!data_ready))。
  • 缺点:轮询浪费CPU资源(频繁检查 data_ready)。

(二)异步非阻塞(Asynchronous Non-blocking)

场景:同样模拟“等待文件数据”,但调用方完全不阻塞,而是通过回调函数处理结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

// 回调函数类型定义
typedef void (*Callback)(const char*);

// 模拟耗时任务(如读取文件)
void* prepare_data_async(void* arg) {
sleep(3); // 模拟耗时3秒
Callback callback = (Callback)arg;
callback("Hello, Asynchronous Non-blocking!"); // 完成后调用回调
return NULL;
}

int main() {
printf("主线程开始执行其他任务...\n");

// 定义回调函数
void on_data_ready(const char* result) {
printf("数据已准备好(通过回调): %s\n", result);
}

// 启动异步任务,传递回调函数
pthread_t thread;
pthread_create(&thread, NULL, prepare_data_async, (void*)on_data_ready);

// 主线程继续执行其他任务
for (int i = 1; i <= 5; ++i) {
printf("主线程正在做其他事情...%d秒\n", i);
sleep(1);
}

pthread_join(thread, NULL); // 等待线程结束(实际中可能不需要)
return 0;
}

运行流程

  1. 主线程启动子线程模拟读取文件(耗时3秒),并传递一个回调函数 on_data_ready
  2. 主线程 立即返回,继续执行其他任务(打印 主线程正在做其他事情...)。
  3. 子线程3秒后完成任务,调用回调函数 on_data_ready 输出结果。
  4. 主线程和子线程并行执行,主线程无需等待。

关键点

  • 异步:主线程无需等待任务完成,任务完成后通过回调通知主线程。
  • 非阻塞:主线程完全不阻塞,可以继续执行其他任务。
  • 优点:无需轮询,资源利用率高。

2.4.3 图解对比

1
2
3
4
5
6
7
8
9
10
11
12
13
// 同步非阻塞:
主线程:
├─ 启动异步任务(子线程准备数据) → 子线程开始执行
├─ 进入轮询循环:
│ ├─ 检查 data_ready? → 否 → 执行其他任务(如 sleep(1))
│ └─ data_ready == 1 → 退出循环,继续执行后续逻辑
└─ 数据就绪后处理结果

// 异步非阻塞:
主线程:
├─ 启动异步任务(子线程准备数据) → 子线程开始执行
├─ 立即返回 → 继续执行其他任务(如打印信息)
└─ 数据就绪后,系统自动调用回调函数处理结果

2.5 挂起、阻塞、等待三个概念

在编程中,挂起(Suspend)阻塞(Block)等待(Wait)这三个术语经常用于描述任务或线程的状态,它们都与任务不能继续执行有关,但发生的原因和机制有所不同。

2.5.1 挂起(Suspend)

挂起通常指的是任务被外部因素(如操作系统、用户请求)强行暂停执行,并且任务的状态被保存起来,以便后续恢复执行。挂起通常与调度有关,可能因为系统资源不足、用户干预或者调试等原因,属于被动原因。

  • 特点:任务被挂起后,它不再参与调度,不占用CPU,直到被恢复(Resume)。挂起操作可以由外部控制。
  • 形象化理解:就像你在看视频时按下了暂停键,视频画面停止在当前帧,当你再次按下播放键时,视频会从暂停的地方继续播放。

2.5.2 阻塞(Block)

阻塞指的是任务因为等待某个资源(如I/O操作完成、锁的释放、信号量等)而主动让出CPU,进入等待状态。当资源可用时,任务会被唤醒继续执行。

  • 特点任务主动放弃CPU,进入阻塞状态,直到等待的条件满足。阻塞通常与同步机制(如互斥锁、条件变量)或I/O操作相关。
  • 形象化理解:你在等公交车,但车还没来,于是你坐在车站的长椅上休息(阻塞),直到公交车来了(条件满足),你才站起来上车(继续执行)。

2.5.3 等待(Wait)

等待是一个更广义的概念,它通常指任务因为某些条件不满足而暂停执行。等待可以是阻塞的一种表现形式,也可以包括其他形式的暂停(如忙等待)。在具体上下文中,等待往往与特定的同步原语(如条件变量)一起使用。

  • 特点:等待通常意味着任务在某个条件上停留,直到条件满足。它可以是阻塞的(让出CPU)也可以是非阻塞的(忙等待,即循环检查条件)
  • 形象化理解:你在餐厅等位,你可以选择坐在椅子上休息(阻塞等待)或者不断去问前台还有多久(忙等待)。

2.5.4 联系与区别

  • 联系:三者都表示任务暂时不能执行,但挂起和阻塞通常都是被动或主动地让出CPU,而等待则是一个更通用的术语。
  • 区别
    • 挂起通常是由外部发起的,任务自身无法控制,而阻塞等待往往是任务主动的行为(等待某个条件)。
    • 阻塞是等待的一种方式,即让出CPU的等待(非忙等),而等待也可以包括忙等待(不释放CPU,循环检查条件)。
    • 挂起后任务的状态被保存,且不参与调度;阻塞的任务则处于等待队列中,当条件满足时会被唤醒;等待则可能处于阻塞状态,也可能处于运行状态(忙等待)。

3 并发、并行

3.1 核心定义与本质区别

  • 1. 并发(Concurrency)
    • 定义:指多个任务在同一时间段内交替执行,但同一时刻只有一个任务在运行。
    • 本质:通过 CPU 时间片轮转(或事件驱动),让用户感觉多个任务同时进行,但实际是单核 CPU “伪同时” 处理。
    • 类比场景
      像一个厨师同时处理切菜、炒菜、煮汤三件事:切菜时暂停炒菜,煮汤时暂停切菜,通过快速切换完成所有任务。虽然看起来同时在做,但同一时间只做一件事。
  • 2. 并行(Parallelism)
    • 定义:多个任务在同一时刻真正同时执行,依赖多核 CPU 或多台计算机同时处理。
    • 本质:利用硬件资源(如多核处理器)将任务分解为多个子任务,同时并行计算。
    • 类比场景
      多个厨师分工合作,一人切菜、一人炒菜、一人煮汤,各自独立工作,同一时间内不同任务同步进行。

3.2 关键差异对比表

对比维度 并发(Concurrency) 并行(Parallelism)
执行方式 任务交替执行(伪同时) 任务同时执行(真同时)
硬件依赖 单核 CPU 即可(通过时间片切换) 需要多核 CPU 或多处理器(硬件支持)
解决问题 处理任务间的协作、资源竞争等复杂逻辑 加速计算密集型任务(如大数据处理)
典型场景 服务器处理多客户端请求、GUI 界面响应 科学计算、3D 渲染、区块链挖矿
编程模型 多线程、异步回调、事件循环 多进程、MPI(消息传递接口)、GPU 并行计算

3.3 程序示例

  • 并发编程:多线程实现,通过pthread_create创建线程,多个线程共享 CPU 时间片。
1
2
3
4
5
6
7
8
9
10
11
#include <pthread.h>
void* task1(void* arg) { /* 任务1逻辑 */ }
void* task2(void* arg) { /* 任务2逻辑 */ }
int main() {
pthread_t tid1, tid2;
pthread_create(&tid1, NULL, task1, NULL);
pthread_create(&tid2, NULL, task2, NULL);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
return 0;
}
  • 并行编程:多核并行,使用 OpenMP 指令让编译器自动将循环分配到多个核心。
1
2
3
4
5
6
7
8
9
10
#include <omp.h>
int main() {
int i, sum = 0;
#pragma omp parallel for reduction(+ : sum)
for (i = 0; i < 10000; i++) {
sum += i;
}
printf("Sum: %d\n", sum);
return 0;
}

4 回调函数callback

4.1 回调函数的基本概念

回调函数是一种函数指针的应用,回调函数作为参数传递给另一个函数(称为 “调用者”),允许调用者在特定事件或条件发生时执行这个函数。简单来说,回调函数是由他人调用的函数,而非直接在代码中调用。

(函数指针,啥语言都一样,只不过各自有各自叫法,本质就是函数指针,指向函数的指针,真因为有函数指针,你才可以将函数作为变量传递给另一个函数)

4.2 回调函数与普通函数的区别

特性 普通函数 回调函数
调用方式 由程序员显式调用(如 func() 作为参数传递给其他函数,由其他函数调用
控制权 由程序员决定何时调用 由调用者(函数)决定何时调用
灵活性 固定逻辑,无法动态改变调用的逻辑 动态传递逻辑,灵活性更高
应用场景 普通流程控制(如计算、判断) 异步操作、事件驱动、解耦逻辑

4.3. 为什么要使用回调函数

  • (1)解耦与灵活性
    • 解耦:调用者不需要知道回调函数的具体实现细节,只需知道它的接口(参数和返回值)。
      例如:硬件驱动模块不需要知道用户如何处理传感器数据,只需调用回调函数。
    • 灵活性:可以在运行时动态切换不同的回调函数,适应不同需求。
      例如:一个排序算法可以接受不同的比较函数作为回调,实现升序或降序排序。
  • (2)异步处理
    • 在嵌入式系统中,许多操作是异步的(如定时器、中断、通信协议)。
      回调函数可以在这些异步事件发生时通知主程序,避免阻塞主流程。
  • (3)事件驱动
    • 回调函数常用于事件驱动的场景(如按键按下、传感器触发)。
      例如:当用户按下按钮时,系统会调用预先注册的回调函数处理按键事件。

4.4 回调函数的常见使用场景

  • (1)硬件中断处理
    • 在嵌入式系统中,当硬件(如按键、定时器)触发中断时,会调用回调函数处理中断事件。
      例如:定时器每隔1秒触发一次,调用回调函数更新系统时间。
  • (2)通信协议
    • 在串口通信或网络通信中,数据接收完成时会调用回调函数处理数据。
      例如:当串口接收到一帧数据后,调用用户定义的回调函数解析数据。
  • (3)状态机或事件驱动框架
    • 在事件驱动的框架中,回调函数用于处理不同的状态或事件。
      例如:GUI系统中,用户点击按钮时调用回调函数执行对应操作。
  • (4)通用库的设计
    • 通用库(如排序、过滤算法)通过回调函数允许用户自定义逻辑
      例如:C标准库的 qsort 函数通过回调函数实现自定义排序规则。

4.5 举例嵌入式场景:模拟定时器回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
#include <unistd.h> // 用于 sleep 函数

// 定义回调函数类型
typedef void (*TimerCallback)(void);

// 模拟定时器(每隔1秒触发一次)
void start_timer(TimerCallback callback, int duration_seconds) {
while (1) {
sleep(duration_seconds); // 等待指定时间
callback(); // 调用回调函数
}
}

// 用户定义的回调函数
void timer_handler() {
static int count = 0;
printf("定时器触发第 %d 次\n", ++count);
}

int main() {
// 启动定时器并注册回调函数
start_timer(timer_handler, 1);
return 0;
}

5 几个常用关键字

5.1 volatile关键字

(一)volatile的主要作用

  • 防止编译器优化

编译器在进行优化时,可能会假设某些变量的值在代码块中不会被修改(除非程序显式更改)。volatile 的作用就是打破这种假设,确保每次访问变量时都直接从内存中读取,避免因为优化导致程序行为异常。

  • 应用于特殊场景
    • 硬件寄存器(Memory-Mapped I/O):硬件设备的状态寄存器可能被外部硬件修改,程序必须每次读取最新值。
    • 中断服务程序(ISR)中修改的变量:主程序需要感知到中断中对变量的修改。
    • 多线程环境中的共享变量:其他线程可能修改了变量的值。
    • 信号处理函数中修改的变量:主程序需要根据信号处理函数的修改做出响应。
    • 调试变量:某些调试信息可能由外部调试器修改。

(二)使用 volatile 的示例

示例 :模拟中断服务修改变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

volatile int flag = 0; // 使用 volatile

void* thread_func(void* arg) {
sleep(2); // 模拟中断延迟
flag = 1; // 模拟中断服务修改变量
return NULL;
}

int main() {
pthread_t tid;
pthread_create(&tid, NULL, thread_func, NULL);

while (flag == 0) {
// 等待 flag 被设置为 1
}

printf("Flag is now 1!\n");
pthread_join(tid, NULL);
return 0;
}

说明:

  • flag 被声明为 volatile,表示其值可能在主程序之外被修改(如中断线程)。
  • 如果未使用 volatile,编译器可能会将 flag 缓存到寄存器中,主循环可能无法检测到 flag 的变化,导致程序陷入死循环。
  • 由于是线程间通信,更推荐使用线程同步机制(如互斥锁、条件变量),但 volatile 是基础保障。

(三)不使用 volatile 的后果

如果不使用 volatile,编译器可能会做出以下优化行为,从而引发程序错误:

编译器优化行为 后果
将变量值缓存到寄存器 无法检测到变量在程序之外的修改
删除重复读取操作 导致循环无法退出,或逻辑错误
重新排序指令 导致硬件行为异常或数据不一致
1
2
3
4
5
6
7
int flag = 0;

void check_flag() {
while (flag == 0) {
// 编译器可能将 flag 读入寄存器并缓存
}
}

如果 flag 在中断中被设置为 1,但由于未使用 volatile,编译器可能将循环优化为:

1
2
3
4
mov r0, #0
loop:
cmp r0, #0
beq loop

即,寄存器值始终为 0,导致循环永不退出。

简要解释原因:

(四)何时需要使用 volatile

场景 是否需要 volatile
硬件寄存器 ✅ 必须使用
中断服务程序中修改的变量 ✅ 必须使用
多线程共享变量 ✅ 推荐使用,但更应使用线程同步机制
信号处理函数中修改的变量 ✅ 必须使用
单线程中未被修改的局部变量 ❌ 不需要

(五)小结

  • volatile 的核心作用是禁止编译器对变量进行优化,确保每次访问都从内存中读取。
  • 它适用于外部因素可能修改变量值的场景,如硬件寄存器、中断、多线程和信号处理。
  • 在多线程或并发编程中,volatile 是基础保障,但不能替代线程同步机制(如互斥锁、原子操作)。
  • 不使用 volatile 时,编译器可能优化掉变量访问,导致程序行为与预期不符,甚至陷入死循环。

6 内存单位解析

6.1 基本定义

  • KB(Kilobyte)
    • 含义:KB 是 千字节(KiloByte),表示存储容量的单位。
    • 换算:1 KB = 1024 字节(B)(基于二进制,即 2¹⁰)。
    • 用途:常用于描述文件大小、内存分配、存储容量等。
    • 示例:一个文本文件大小为 5 KB,表示占用 5 × 1024 = 5120 字节的空间。
  • kb(kilobit)
    • 含义:kb 是 千比特(kiloBit),表示数据传输速率的单位。
    • 换算:1 kb = 1000 比特(bit)(基于十进制,即 10³)。
    • 用途:常见于网络带宽描述(如宽带速率、下载速度)。
    • 示例:网络速度标注为 100 Mbps(兆比特每秒),即 100,000 kbps。

6.2 大小写区别

  • B vs b

    • B(Byte):字节,是计算机存储的基本单位,1 字节 = 8 位(bit)。

    • b(bit):位,是二进制数据的最小单位(0 或 1)。

    • 关键区别:

      • 1 Byte = 8 bit,即 1 B = 8 b。
    • KB 与 Kb 的关系:

      • 1 KB = 8 Kb(因为 1 字节 = 8 位)。

      • 1 kB = 8 kb(如果 kB 表示 1000 字节,kb 表示 1000 位)。

  • K vs k

    • K(大写):通常表示 1024(基于二进制,即 2¹⁰),用于存储容量(如 KB、MB、GB)。
    • k(小写):通常表示 1000(基于十进制,即 10³),用于数据传输速率(如 kb、MBps)。