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

【Linux】Linux第一个小程序 - 进度条

一、前置知识
1、 回车和换行

在学习 C 语言时,我们常认为\n只是 “换行”—— 让光标移到下一行开头。但实际上,它隐含了回车换行两个动作,二者的职责截然不同:

  • 回车(\r):使光标回到当前行的开头
  • 换行(\n):使光标跳转到下一行的当前列位置
2、 缓冲区

缓冲区(Buffer)是内存中一段预留的连续存储区域,用于临时缓存数据,实现数据读写的 “批量处理”,以匹配不同设备 / 模块间的速度差异。

以标准输出(printf)为例:当调用printf输出字符时,数据并不会直接写入终端设备,而是先被暂存到标准输出缓冲区中。缓冲区的刷新(即数据实际输出)通常满足以下触发条件:

  1. 缓冲区被写满(达到其预设容量);
  2. 遇到换行符\n(标准输出的行缓冲模式下,\n会触发主动刷新);
  3. 程序正常退出(进程终止时,系统会自动刷新未处理的缓冲区);
  4. 主动调用刷新接口(如fflush(stdout))。

核心作用:通过 “数据暂存 + 批量传输”,减少低速设备(如终端)的 IO 次数,平衡高速内存与低速外设之间的读写速度差,从而提升整体 IO 效率。

二、实现简单的倒计时

在实现进度条之前,我们先做一个简单的计时器,来熟悉前面讲过的知识。

代码语言:javascript

AI代码解释

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

程序在终端以每秒一次的频率,在同一行动态刷新显示从 9 到 0 的倒计时,结束后换行。

代码语言:javascript

AI代码解释

#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文件】:

我们这是按声明放在头文件,定义放在源文件的写法写的,有些同学可能会疑问为啥依赖关系中没有头文件,这是因为源文件和头文件在同一个目录下,而源文件中包含的头文件,所以编译器自己能找到头文件

1、Verrsion 版本

代码语言:javascript

AI代码解释

//progressbar.h #include<stdio.h> //printf fflush #include<unistd.h> //unsleep 单位:微秒 #include<string.h> //strlen #define NUM 101 //多给'\0'留了一个位置 #define pro '=' //进度条款式 void progressbar();

代码语言:javascript

AI代码解释

//progressbar.c #include"progressbar.h" const char* str = "\\|/-";//旋转光标的棍棍 \\是转义字符'\' void progressbar() { char bar[NUM] = {0};//把数组先全部初始化为\0 int 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"); }

代码语言:javascript

AI代码解释

#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 占用,让进度变化直观可见。

2、进度条在下载场景下的应用

【progressbar.h】:

代码语言:javascript

AI代码解释

#include<stdio.h> #include<unistd.h> #include<string.h> #define NUM 101 #define pro '=' void progressbar(int date); void initbar();

【progressbar.c】:

代码语言:javascript

AI代码解释

#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】:

代码语言:javascript

AI代码解释

typedef void (*call)(int); void downLoad(call ca) { int total = 1000; // 1000MB int cnt = 0; // 0MB while (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; }

【应用场景版本运行结果】:

进度条数组与越界控制:

char bar[NUM]存储进度字符,if(rate<100)限制bar[rate] = '>'仅在合法下标执行,避免越界覆盖终止符\0

旋转光标逻辑:

str = "\\|/-"定义旋转字符,str[rate%len]通过取模循环切换字符,实现动态旋转效果,len确保下标合法。

进度刷新与缓冲控制:

printf\r使光标回退覆盖旧内容,fflush(stdout)强制刷新缓冲区,保证进度条实时可见。

下载回调与进度计算:

downLoadrate = cnt*100/total计算进度百分比,通过函数指针ca(rate)回调progressbar展示进度,cnt+=10模拟分段下载。

数组初始化逻辑:

initbarmemset(bar, '\0', sizeof(bar))初始化进度条数组,避免多次下载时状态残留。这种应用场景就是利用回调函数实现多次下载

http://www.jsqmd.com/news/458317/

相关文章:

  • 从实验室到万吨产线:青岛福尔蒂以校企联合将博士论文转化为抗静电母粒量产方案
  • 用实力说话!降AIGC软件 千笔AI VS 文途AI,本科生专属推荐
  • 5 分钟手把手教你打造 AI 知识库!附 OpenClaw「龙虾」养成指南(建议收藏)
  • 存储过程(SQL)
  • openclaw 常用命令
  • 【Linux】深入浅出 Linux 自动化构建:make 与 Makefile 的实用指南
  • C语言进阶指南(类型转换、整型提升)
  • 显卡(Graphics Processing Unit,GPU)架构详细解读
  • 学生成绩管理系统(MySQL)
  • 基于Spring Cloud的电商系统设计与实现——用户与商品模块的研究(下)
  • 完美解决org.mybatis.spring.MyBatisSystemException nested exception is org.apache.ibatis.reflection.Refl
  • 【C语言-第33章 标准输入输出】-002篇
  • 深度解析 Android 开发(影像类 APP 方向)职位:技术全景、面试指南与职业进阶
  • 最新SQL Server 2022保姆级安装教程【附安装包】
  • 【C语言-第34章 字符与字符串的输入输出】-001篇
  • Flutter 组件 genkit 的适配 鸿蒙Harmony 实战 - 驾驭大模型开发套件、实现鸿蒙端 AI 智能流式响应与提示词工程自动化方案
  • Epson M-G366PDG惯性测量单元:精准导航与卓越性能的理想选择
  • 福尔蒂技服团队驻厂年支持1860+工时,一次性良品率达99.27%
  • 基于Java+SSM+Django小工程预算系统(源码+LW+调试文档+讲解等)/小型工程预算软件/小型工程项目预算工具/简易工程预算系统/工程预算软件小型版/小型工程成本估算系统
  • 给SQL server数据库表字段添加注释SQL,附修改、删除注释SQL及演示
  • 最容易上手的AI找谁
  • 为什么你需要OpenClaw?从单Agent到多Agent的进化之路
  • 5 模型评估方法:准确率与混淆矩阵的实战解读
  • ISO9071外的质控实践:福尔蒂研发-QA-中试‘铁三角’机制(含架构与甘特图)
  • 最新最详细的配置Node.js环境教程
  • 私有化部署DeepSeek并SpringBoot集成使用(附UI界面使用教程-支持语音、图片)
  • flask-django基于python的疫苗发布和接种管理系统的设计与实现
  • 以引擎源码抄写+UE独立游戏相结合
  • 简易星露谷模组二次开发之旅:捐赠追踪、颜色优化与动物状态警告
  • 【2025最新】基于SpringBoot+Vue的船舶维保管理系统管理系统源码+MyBatis+MySQL