当前位置: 首页 > news >正文

从零开始,用C语言打造一个Linux终端进度条小程序

从零开始,用C语言打造一个Linux终端进度条小程序

在掌握了Linux基础指令、Vim编辑器、GCC/G++编译器以及Makefile自动化构建工具后,我们终于可以动手编写第一个具有实际意义的Linux小程序——进度条。这个小项目不仅是对前期知识的综合运用,更能帮助你深入理解缓冲区、回车换行等底层概念。无论你是C语言新手还是希望巩固Linux编程基础,这篇文章都能带你一步步实现一个功能完整的终端进度条。

一、前置知识:回车、换行与缓冲区

在开始编码之前,我们首先要理清两个容易被混淆的概念:回车换行。在C语言中,我们通常认为 \n 只是“换行”,但实际上它隐含了回车和换行两个动作。回车(Carriage Return, \r)让光标回到当前行首,而换行(Line Feed, \n)让光标移动到下一行。简单来说:

  • 回车(\r):光标回到行首,不换行
  • 换行(\n):光标移到下一行,但未必回到行首

回车(\r):使光标回到当前行的开头

换行(\n):使光标跳转到下一行的当前列位置

另一个关键概念是缓冲区。缓冲区是内存中一段预留的连续存储区域,用于临时缓存数据。当我们调用 printf 输出字符时,数据并不会立即写入终端,而是先存到标准输出缓冲区中。缓冲区刷新的触发条件包括:

1、缓冲区被写满(达到其预设容量);

2、遇到换行符(标准输出的行缓冲模式下,会触发主动刷新);

3、程序正常退出(进程终止时,系统会自动刷新未处理的缓冲区);

4、主动调用刷新接口(如)。

核心作用:通过“数据暂存+批量传输”,减少低速设备(如终端)的IO次数,平衡高速内存与低速外设之间的读写速度差,从而提升整体IO效率。理解这一点对于实现实时刷新的进度条至关重要。

二、实现简单的倒计时:小试牛刀

在正式编写进度条之前,我们先做一个简单的倒计时程序,来熟悉回车和缓冲区的应用。以下代码会在终端以每秒一次的频率,在同一行动态刷新显示从9到0的倒计时:

#include   // 包含标准输入输出库,提供 printf、fflush 等函数
#include  // 包含 Unix 标准库,提供 sleep 函数(Linux 系统专用)
int main()
{int cnt = 9;               // 定义倒计时起始值为 9while(cnt >= 0){printf("%d\r", cnt);   // 打印当前倒计时数值,并通过 \r(回车)使光标回到行首// 这样后续输出会覆盖当前行,实现“同一行刷新”效果fflush(stdout);        // 强制刷新标准输出缓冲区,确保数值立即显示(避免缓冲延迟)sleep(1);              // 程序暂停 1 秒(Linux 下 sleep 单位为秒)cnt--;}printf("\n");  // 倒计时结束后,输出换行符,使光标移至下一行(避免后续输出覆盖)return 0;
}

程序运行后,你会看到数字在同一位置逐秒递减,直到0后换行结束。这里的关键是使用 \r 让光标回到行首,再用 fflush(stdout) 强制刷新缓冲区,确保每次输出立即显示。

#include   // 包含标准输入输出库,提供 printf、fflush 等函数
#include  // 包含 Unix 标准库,提供 sleep 函数(Linux 系统专用)
int main()
{int cnt = 11;              // 定义倒计时起始值为 11while(cnt >= 0){printf("%2d\r", cnt);  // 打印当前倒计时数值(%2d 确保数字占 2 个字符宽度,对齐显示)// \r(回车符)使光标回到当前行首,后续输出会覆盖本行内容// 实现“同一行动态刷新”的倒计时效果fflush(stdout);        // 强制刷新标准输出缓冲区,确保数值立即显示(避免因缓冲导致的延迟)sleep(1);              // 程序暂停 1 秒(Linux 环境下 sleep 函数单位为秒)cnt--;                 // 倒计时数值减 1}printf("\n");  // 倒计时结束后,输出换行符,使光标移至下一行(避免后续内容与倒计时在同一行)return 0;
}

⚠️ 注意事项:当倒计时从两位数(如10)变为一位数(如9)时,如果只用 %d 格式化,前一个数的末尾字符(如“0”)无法被完全覆盖,会出现“90”“80”等错误显示。使用 %2d 强制让数字占2个字符宽度,不足时补空格,就能完美避免错位问题。这个技巧在进度条显示百分比时同样适用。

三、进度条初步实现:从零到一

有了倒计时的基础,我们开始构建进度条。首先设计一个清晰的目录结构:将声明放在头文件(processbar.h)中,定义放在源文件(processbar.c)中,主程序放在 main.c 中,并通过Makefile自动化构建。

这里有一个常见疑问:为什么依赖关系中没有头文件?因为源文件通过 #include 包含了头文件,编译器会自动查找并处理它们,无需在Makefile中显式列出。

Version 1:基础进度条

我们先实现一个最简版本,包含进度条主体和旋转光标:

//progressbar.h
#include //printf fflush
#include //unsleep 单位:微秒
#include //strlen
#define NUM  101 //多给'\0'留了一个位置
#define pro  '=' //进度条款式
void progressbar();
//progressbar.c
#include"progressbar.h"
const char* str = "\\|/-";//旋转光标的棍棍 \\是转义字符'\'
void progressbar()
{char bar[NUM] = {0};//把数组先全部初始化为\0int len = strlen(str);int cnt = 0;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");
}
#include"progressbar.h"
int main()
{progressbar();return 0;
}

运行结果

这个版本的核心要点包括:

  • 进度条数组与越界控制bar[SIZE+1] 存储进度字符,i < SIZE 限制下标合法,避免越界覆盖终止符 \0
  • 旋转光标逻辑const char *lable = "|/-\\" 定义旋转字符,cnt %= 4 通过取模循环切换,实现动态旋转效果。
  • 进度节奏控制usleep(50000) 暂停50毫秒,平衡刷新速度与CPU占用,让进度变化直观可见。

四、进阶应用:模拟下载场景

基础进度条虽然能展示进度,但在实际应用中(如下载文件),我们需要更灵活的架构。下面我们通过函数指针实现回调机制,模拟多次分段下载的场景:

//progressbar.h
#include
#include
#include
#define NUM 101
#define pro '='
void progressbar(int date);
void initbar();
//progressbar.c
#include"progressbar.h"
const char* str = "\\|/-";
char bar[NUM] = {0};
void progressbar(int rate)
{if(rate<0 || rate>100) return ;int len = strlen(str);printf("[%-100s][%d%%][%c]\r",bar,rate,str[rate%len]);fflush(stdout);bar[rate++] = pro;if(rate<100)bar[rate] = '>';
}
void initbar()
{memset(bar, '\0', sizeof(bar));
}
//main.c
typedef void (*call)(int);
void downLoad(call ca)
{int total = 1000; // 1000MBint cnt = 0;      // 0MBwhile (cnt <= total){usleep(50000); // 模拟下载时间int rate = cnt * 100 / total; // 更新进度ca(rate); // 通过回调,展示进度cnt += 10; // 循环下载了一部分}printf("\n");
}
int main()
{printf("download 1: \n");downLoad(progressbar);initbar();printf("download 2: \n");downLoad(progressbar);initbar();printf("download 3: \n");downLoad(progressbar);initbar();printf("download 4: \n");downLoad(progressbar);initbar();printf("\nDownload complete!!!\n");return 0;
}

应用场景运行结果

这个进阶版本引入了几个重要设计模式:

  • 进度刷新与缓冲控制\r 使光标回退覆盖旧内容,fflush(stdout) 强制刷新缓冲区,保证进度条实时可见。
  • 下载回调与进度计算download()rate = total * 100 / current 计算进度百分比,通过函数指针 fp 回调 process() 展示进度,usleep(100000) 模拟分段下载。
  • 数组初始化逻辑memset(bar, '\0', sizeof(bar)) 初始化进度条数组,避免多次下载时状态残留。

这种回调机制非常实用,在TypeScript、Python、C++、Java、JavaScript等现代编程语言中都有广泛应用。例如,在JavaScript中,我们可以用回调函数处理异步请求的进度;在Python中,tqdm 库就是基于类似原理实现的进度条工具。

[AFFILIATE_SLOT_1]

五、总结与扩展思考

通过这个小项目,我们不仅实现了一个功能完整的终端进度条,更重要的是深入理解了以下核心概念:

  • 回车与换行的区别\r\n 的各自职责及其组合使用
  • 缓冲区刷新机制fflush(stdout) 在实时显示中的关键作用
  • 格式化输出与越界控制:使用 %2d 等格式符避免显示错位
  • 回调函数设计模式:通过函数指针实现灵活的进度通知机制

延伸思考:如果你想进一步优化,可以尝试:

  • 添加颜色支持(如用ANSI转义码让进度条变色)
  • 支持多线程下载时的并发进度显示
  • 将进度条封装成可复用的C库,供其他项目调用
[AFFILIATE_SLOT_2]

这个进度条小程序的实现思路,与TypeScript、Python、C++、Java、JavaScript等语言中的进度条库(如Python的tqdm、JavaScript的progress)一脉相承。掌握底层原理后,无论使用哪种语言,你都能轻松实现或定制自己的进度显示工具。

\n\nfflush(stdout)
http://www.jsqmd.com/news/824430/

相关文章:

  • TestDisk PhotoRec:免费开源数据恢复终极指南
  • 3D视觉感知芯片:专用SoC如何突破性能、功耗与成本的不可能三角
  • 清理 DBMS 用户管理中的不一致映射,别让 ABAP 用户和数据库用户各走各路
  • Jetson AGX Orin到手后,第一件事不是装CUDA,而是先搞定这个源(附nvidia-l4t-apt-source.list配置)
  • PUBG-Logitech压枪脚本深度解析:多线程架构与状态机优化实战指南
  • 5分钟学会用ASCII字符绘制专业流程图:告别复杂设计软件
  • CLIP-as-service网络优化终极指南:带宽压缩与传输协议选择
  • EASY-HWID-SPOOFER深度解析:内核级硬件信息欺骗实战指南
  • Go语言接口设计与最佳实践
  • 冲刺3
  • 基于ReAct框架的AI智能体:如何让LLM通过Google搜索获取实时信息
  • 2026年金华整装行业综合实力推荐榜 - GrowthUME
  • 如何在Linux系统上快速部署Photoshop CC 2022:终极安装与优化指南
  • 如何使用ChatGPT for Google:让搜索结果与AI回答完美协作的终极指南
  • 逆向实战:用X32dbg和Spy++联手定位MFC窗口消息处理函数(附详细堆栈分析)
  • 使用,也作为 prop 传给子组件
  • 为什么你的v7作品总像“高级PPT”?揭秘神经渲染层重构带来的3重美学偏移,附赠私密调试参数包(仅开放48小时)
  • 从棋盘格到精准感知:ROS camera_calibration实战单目与双目相机标定
  • 白细胞介素-17(IL-17):炎症与免疫调节中的关键细胞因子
  • FPGA与以太网:从MII接口到UDP通信的实战解析
  • Open UI5 源代码解析之1423:FilterItemFlex.js
  • 终极免费工具:XHS-Downloader小红书内容采集全攻略
  • ledger购买渠道:官方资料的多入口一致性说明 - GrowthUME
  • 如何将Stable Diffusion无缝集成到Photoshop工作流中?
  • ORT Reporter输出格式全解析:生成SPDX、CycloneDX和静态HTML报告的终极指南
  • 题解:P16429 应试玉符
  • Pytorch图像去噪实战(九十三):数据集版本管理实战,保证每次训练数据可追溯、可回滚
  • 从零构建Claude代码:深入Transformer架构与自回归生成实现
  • 2026库尔勒智能锁安装/销售/维修/开锁服务深度横向测评,本地品牌选型避坑指南 - GrowthUME
  • Multiavatar国际化设计:如何代表全球多元文化与种族的终极指南