🔥 脏脏a的技术站 🔥
「在代码的世界里,脏脏的技术探索从不设限~」
🚀 个人主页:脏脏a-CSDN博客
📌 技术聚焦:Linux下第一个小程序:进度条
📊 文章专栏:Linux
🔗 上篇回顾:Linux自动化构建工具:make/Makefile
在 Linux 世界开启编程之旅,从一个动态进度条小程序,带你吃透回车换行与行缓冲的底层奥秘。
目录
[1.1 回车和换行](#1.1 回车和换行)
[1.2 缓冲区(简单了解)](#1.2 缓冲区(简单了解))
[3.1 Verrsion1版本](#3.1 Verrsion1版本)
[3.2 进度条在下载场景下的应用](#3.2 进度条在下载场景下的应用)
一、前置知识
1.1 回车和换行
在学习 C 语言时,我们常认为 \n 只是 "换行"------ 让光标移到下一行开头。但实际上,它隐含了回车 和换行两个动作,二者的职责截然不同:
- 回车(\r) :使光标回到当前行的开头;
- 换行(\n) :使光标跳转到下一行的当前列位置。
1.2 缓冲区(简单了解)
缓冲区就是内存里一个 "临时放东西的小仓库"。
比如你用 printf 打印文字时,文字不会立马显示在屏幕上,而是先堆在这个 "小仓库" 里。
啥时候才显示呢?要么仓库堆满了,要么你打了个换行 \n(换行有刷新缓冲区的作用),要么程序跑完了 ------ 这时候 "仓库" 才把东西一次性搬出来给你看。
简单说,就是先攒一攒,再一起处理,省事儿又高效。
二、实现简单的倒计时
cpp
#include<stdio.h> // 包含标准输入输出库,提供 printf、fflush 等函数
#include<unistd.h> // 包含 Unix 标准库,提供 sleep 函数(Linux 系统专用)
int main()
{
int cnt = 9; // 定义倒计时起始值为 9
while(cnt >= 0) // 循环条件:当倒计时未结束(cnt 大于等于 0 时)
{
printf("%d\r", cnt); // 打印当前倒计时数值,并通过 \r(回车)使光标回到行首
// 这样后续输出会覆盖当前行,实现"同一行刷新"效果
fflush(stdout); // 强制刷新标准输出缓冲区,确保数值立即显示(避免缓冲延迟)
sleep(1); // 程序暂停 1 秒(Linux 下 sleep 单位为秒)
cnt--; // 倒计时数值减 1
}
printf("\n"); // 倒计时结束后,输出换行符,使光标移至下一行(避免后续输出覆盖)
return 0;
}
程序在终端以每秒一次的频率,在同一行动态刷新显示从 9 到 0 的倒计时,结束后换行。
cpp
#include<stdio.h> // 包含标准输入输出库,提供 printf、fflush 等函数
#include<unistd.h> // 包含 Unix 标准库,提供 sleep 函数(Linux 系统专用)
int main()
{
int cnt = 11; // 定义倒计时起始值为 11
while(cnt >= 0)
{
printf("%2d\r", cnt); // 打印当前倒计时数值(%2d 确保数字占 2 个字符宽度,对齐显示)
// \r(回车符)使光标回到当前行首,后续输出会覆盖本行内容
// 实现"同一行动态刷新"的倒计时效果
fflush(stdout); // 强制刷新标准输出缓冲区,确保数值立即显示(避免因缓冲导致的延迟)
sleep(1); // 程序暂停 1 秒(Linux 环境下 sleep 函数单位为秒)
cnt--; // 倒计时数值减 1
}
printf("\n"); // 倒计时结束后,输出换行符,使光标移至下一行(避免后续内容与倒计时在同一行)
return 0;
}
当倒计时从 11 开始,若用%d输出,两位数(如 10)变为一位数(如 9)时,前一个数的末尾字符(如 "0")无法被完全覆盖,会出现 "90""80" 等错误显示。而%2d会强制让数字占 2 个字符宽度,不足时补空格,确保后一个数字能完全覆盖前一个,避免错位问题。
三、进度条
进度条初步设想:

【Makefile文件】:

我们这是按声明放在头文件,定义放在源文件的写法写的,有些同学可能会疑问为啥依赖关系中没有头文件,这是因为源文件和头文件在同一个目录下,而源文件中包含的头文件,所以编译器自己能找到头文件
3.1 Verrsion1版本
cpp
//progressbar.h
#include<stdio.h> //printf fflush
#include<unistd.h> //unsleep 单位:微秒
#include<string.h> //strlen
#define NUM 101 //多给'\0'留了一个位置
#define pro '=' //进度条款式
void progressbar();
cpp
//progressbar.c
#include"progressbar.h"
const char* str = "\\|/-";//旋转光标的棍棍 \\是转义字符'\'
void progressbar()
{
char bar[NUM] = {0};//把数组先全部初始化为\0
int len = strlen(str);
int cnt = 100;
while(cnt<=100)
{
printf("[%-100s][%d%%][%c]\r",bar,cnt,str[cnt%len]);
fflush(stdout);
bar[cnt++] = pro;
if(cnt<100)//避免越界,下标为100的位置是给\0预留的位置,覆盖了可能导致程序崩溃
bar[cnt] = '>';
usleep(50000);
}
printf("\n");
}
cpp
#include"progressbar.h"
int main()
{
progressbar();
return 0;
}
【第一个版本运行完的结果】:

进度条数组与越界控制\]:
`char bar[NUM]` 存储进度字符,`if(cnt<100)` 限制 `bar[cnt] = '>'` 仅在合法下标内执行,避免越界覆盖终止符 `\0`。
\[旋转光标逻辑\]:
`str = "\\|/-"` 定义旋转字符,`str[cnt%len]` 通过取模循环切换字符,实现动态旋转效果,`len` 确保下标合法。
\[进度节奏控制\]:
`usleep(50000)` 暂停 50 毫秒,平衡进度条刷新速度与 CPU 占用,让进度变化直观可见。
#### 3.2 进度条在下载场景下的应用
**【progressbar.h】:**
```cpp
#include
