前言
为什么我会整理这部分的学习笔记呢?因为我在学习这部分内容是对缓冲区有了全新的认识(此时我还没有学过操作系统),想趁此记录一下我的学习心得。
一、前置知识
1. 回车换行
- 概念:回车(
\r
)是将光标移到行首;换行(\n
)是将光标移到下一行。在 C 语言中,\n
是回车换行组合动作。
2. 缓冲区
- 原理:C 语言程序默认打开标准输入(
stdin
)、标准输出(stdout
,对应显示器)、标准错误(stderr
)三个流。printf
输出内容先存入缓冲区,满足以下条件才刷新显示:- 遇到换行符(
\n
)。 - 缓冲区写满。
- 程序结束。
- 使用
fflush
函数强制刷新。
- 遇到换行符(
- 示例代码:
#include <unistd.h> #include <stdio.h> int main() { printf("hello world\n"); // 有换行,先执行 printf 再 sleep sleep(2); return 0; }
- 无换行时,
printf
内容存缓冲区,执行sleep
;若要强制刷新,需添加fflush(stdout)
:#include <unistd.h> #include <stdio.h> int main() { printf("hello world"); fflush(stdout); // 刷新 stdout 流,立即显示 sleep(2); return 0; }
- 无换行时,
二、实战:命令行倒计时实现
#include <unistd.h>
#include <stdio.h>
int main() {
int cnt = 10;
while (cnt >= 0) {
printf("%-2d\r", cnt); // \r 使光标回行首,覆盖显示;%-2d 数字居左对齐,占两位
fflush(stdout); // 无换行时强制刷新缓冲区
--cnt;
sleep(1); // 暂停 1 秒
}
printf("\n"); // 防止命令行覆盖倒计时显示
return 0;
}
三、进度条实现
版本一
1. processBar.h
#define NUM 102
#define BODY '='
#define RIGHT '>'
#define TOP 100
extern void processBar(int speed);
- 定义了进度条相关的常量和函数声明,
extern
用于告诉编译器processBar
函数在其他文件中定义。
2. processBar.c
文件
#include "processBar.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
const char* label = "|/-\\"; // 模拟旋转光标
void processBar(int speed) {
char bar[NUM];
memset(bar, '\0', sizeof(bar)); // 初始化进度条数组
int len = strlen(label);
int cnt = 0;
while (cnt <= TOP) {
printf("[%-100s][%d%%] %c\r", bar, cnt, label[cnt % len]); // 预留100个字符空间,左对齐显示进度条
fflush(stdout); // 手动刷新缓冲区
bar[cnt++] = BODY; // 更新进度条字符
if (cnt < 100) bar[cnt] = RIGHT; // 处理临界:在进度未达 100% 时,显示进度条头部的>符号。
usleep(speed);
}
printf("\n");
}
printf("%-100s%d%%%c\r", bar, cnt, label[cnt % len]);
:输出进度条、百分比和旋转光标,\r
使光标回到行首,实现覆盖效果。
3. main.c
文件
#include "processBar.h"
int main() {
processBar(100000); // 调用进度条函数
return 0;
}
- 调用
processBar
函数启动进度条。
版本二:进度条调用
1. processBar.h
#define NUM 102
#define BODY '='
#define RIGHT '>'
#define TOP 100
extern void processBar(int rate);
extern void initBar();
- 新增
initBar
函数声明,用于初始化进度条数组。
2. processBar.c
文件
#include <stdio.h>
#include <string.h>
#include <unistd.h>
char bar[NUM];
const char* label = "|/-\\";
// 用于设置进度条的颜色
#define GREEN "\033[0;32;32m"
#define NONE "\033[m"
void processBar(int rate) {
if (rate < 0 || rate > 100) return; // 确保传入的进度值在 0 - 100 之间
int len = strlen(label);
printf(GREEN"[%-100s]"NONE"[%d%%] %c\r", bar, rate, label[rate % len]); // 输出带颜色的进度条
fflush(stdout);
bar[rate++] = BODY;
if (rate < 100) bar[rate] = RIGHT;
else initBar(); // 当进度达到 100% 时,调用initBar函数初始化进度条数组。
}
void initBar() {
memset(bar, '\0', sizeof(bar));
}
3. main.c
文件
#include "processBar.h"
#include <unistd.h>
typedef void (*callback_t)(int); // 定义函数指针类型,用于回调进度条函数
// 模拟下载过程,通过回调函数cb展示下载进度
void downLoad(callback_t cb) {
int total = 1024, cur = 0;
while (cur <= total) {
int rate = cur * 100 / total; // 计算进度百分比
cb(rate); // 通过回调函数展示进度
usleep(50000); // 模拟下载耗时
cur += 10; // 更新已下载量
}
printf("\n");
}
// 在main函数中多次调用downLoad函数,模拟多个下载任务。
int main() {
printf("downLoad1:");
downLoad(processBar);
printf("downLoad2:");
downLoad(processBar);
printf("downLoad3:");
downLoad(processBar);
return 0;
}