为了写出这个小程序我们先来了解几个知识点


(一)回车和换行

先以写作文为例子了解一下,当在一行中写了一半,由此处位置往下一行的操作叫做换行,回到该行的开头位置为回车。

而在c语言中\n帮我们完成了换行和回车两个动作,那单纯回车是——\r


(二)缓冲区概念

#include <stdio.h>
#include <unistd.h>int main()
{printf("hello world\n");sleep(3);return 0;
}

在LInux中如何查看sleep,man sleep即可

有什么现象:

即先打印hello world在休眠三秒

#include <stdio.h>
#include <unistd.h>int main()
{printf("hello world");sleep(3);return 0;
}

现象如下:

看到的是他是先休眠了,在打印的。那他的顺序真的是这样的吗?
不对,在 C 语言中,printf("hello world") 先执行,但输出内容会被暂存在标准输出缓冲区(stdout 缓冲区) 中,并未立即显示到屏幕。随后执行 sleep(3) 进行休眠,3 秒后程序结束时,缓冲区会被自动刷新,内容才显示出来。这就是为什么看起来像是 “先休眠再打印” 的原因。

为什么需要缓冲区?

标准输出(stdout)默认是行缓冲模式

  • 当输出内容包含换行符 \n 时,会自动刷新缓冲区
  • 若没有换行符,会积累到一定大小(通常是 4096 字节)或程序结束时才刷新

代码中 printf 没有换行符,因此内容暂存在缓冲区,直到程序结束才输出,中间被 sleep(3) 打断,造成了执行顺序的 “错觉”。

那如果我要强制刷新怎么办了?

先了解C语言程序中默认会打开3个输入输出流,即标准输入,标准输出,标准错误。标准输出为显示器(stdout),其中Liunx下一切皆文件,那显示器也是文件。
可以用 fflush 函数手动刷新指定流,立即输出缓冲区内容:

#include <stdio.h>
#include <unistd.h>  // 包含sleep函数的头文件int main()
{printf("hello world");fflush(stdout);  // 强制刷新标准输出缓冲区sleep(3);        return 0;
}

此时现象:

 为了看起来更顺眼我们可以在后面加一个换行。

又有上面的知识点我们可以写一个倒计时

#include <stdio.h>
#include <unistd.h>int main()
{int cut=10;while(cut>=0){printf("%-2d\r",cut);fflush(stdout);cut--;sleep(1);}printf("\n");return 0;
}

运行结果:

细节解释:

printf("%-2d\r",cut)为什么是%-2d

因为输入10,显示器会将它显示成1 0

但是我们要的就是10,所以要写成%2d,%2d是指定整数输出时至少占用 2 个字符宽度,但是c语言进行输出显示,默认是右对齐,所以要用%-2d,-符合是强制左对齐。

 \r的作用

回车符,光标回到行首

总结:

  • 每次输出时,%-2d 确保无论数字是一位还是两位,都占用固定的 2 个字符位置。
  • 左对齐的补空格方式,能保证光标回到行首后,新数字会完全覆盖上一次的输出(不会留下残留字符)。

(三)进度条的实现 

(1) 准备工作

依旧使用多文件,建立一个processbar.h,processbar.c ,test.c

其中processbar.c ,test.c包含processbar.h

(2) 实现

1. processbar.h代码:

#pragma once
#include <stdio.h>#define NUM 102
#define STYLE '#'extern void processbar();

小细节:

1.1 extern void processbar();

这里的extern声明表示processbar()函数在其他文件中定义,当前文件仅作声明。在单个模块(单文件)中确实可以省略,但在多文件项目中,使用extern可以清晰地表明这是一个外部函数,增强代码的可读性和规范性

1.2. #define NUM 102

保证每次输出的字符比上次多,定义一个数组,其中NUM为数据开辟的空间,使用宏定义方便统一修改,且可以避免在代码中硬编码数值,提高可维护性。

1.3. #define STYLE '#'

定义进度条的显示字符,因为风格多变,当需要改变进度条样式时,只需修改这一处定义即可
增强了代码的灵活性

 2. test.c代码:

调用这个小程序

#include "processbar.h"
#include <unistd.h>int main()
{processbar();return 0;
}

3. processbar.c程序:

#include "processbar.h"
#include<string.h>
#include<unistd.h>const char* lable="|/-\\-";//这个加载的那个圈圈void processbar()
{char bar[NUM];memset(bar,'\0',sizeof(bar));//初始化数组int cnt=0;while(cnt<=100){printf("[%-100s][%d%%][%c]\r",bar,cnt,lable[cnt%5]);fflush(stdout);//强制刷新输出缓冲区,确保进度条实时显示bar[cnt++]=STYLE;usleep(100000);}printf("\n");
}

运行结果: 

小细节:

3.1. const char* b="|/-\\-";

定义了一个字符数组,包含几个特殊字符,用于实现进度条前的 "旋转加载" 效果(类似转圈动画)。这里的\\是转义字符,表示一个反斜杠。

3.2. usleep

Linux 系统中用于暂停程序执行的函数,声明在<unistd.h>头文件中。

作用:

让当前进程暂停指定的微秒数(1 微秒 = 1/1000000 秒)

3.3. "[%-100s][%d%%][%c]\r"

  • [%-100s]:左对齐的 100 字符宽度区域,显示当前进度
  • [%d%%]:显示当前百分比,%%是转义字符,表示%
  • [%c]:显示旋转动画字符
  • \r:回车符,使光标回到行首,实现覆盖输出效果

4. makefile文件:

processbar:processbar.c test.c@gcc -o $@ $^
.PHONY:	clean
clean:rm -f processbar

小细节:

 4.1processbar:processbar.c test.c

  • 这是一个规则,processbar 是目标(可执行文件),processbar.c 和 test.c 是依赖文件
  • 意思是:要生成 processbar 这个可执行文件,需要先有 processbar.c 和 test.c 这两个源文件
  • 多依赖文件之间用空格隔开

4.2@gcc -o $@ $^

  • @表示执行命令时不在终端显示该命令本身
  • $@ → 目标文件名(要生成的结果)
  • $^ → 所有依赖的源文件(用来生成目标的原材料)

4.3.PHONY:    clean

  • 声明 clean 是一个伪目标(不是实际的文件)
  • 作用是避免当前目录下有同名文件 clean 时,导致该规则失效

(3)进度条的调式和优化

上述进度条存在一定缺陷:

1.功能封装不合理,复用性差

  • 进度条逻辑与控制强耦合:用户无法根据实际场景(如下载、解压等不同任务)控制进度条的更新时机和速度,只能被动执行函数内置的固定节奏。
  • 无法适配实际业务场景:实际开发中,进度条的更新应与任务进度(如下载字节数、处理数据量)绑定,而初始代码的进度完全由函数内部的 cnt 自增控制,与真实任务进度脱节,用户无法通过外部参数传递实际进度。

2. 扩展性差,难以二次使用

  • 单次使用限制:初始代码中,processbar() 函数的进度条状态(数组 bar)是局部变量,每次调用函数都会重新初始化并执行一次完整的进度条动画。这导致它只能正确运行一次,若用户需要在程序中多次显示进度条(如多个任务依次执行),必须重新调用函数,但函数内部逻辑固定,无法重置或复用状态。

3. 用户交互设计不合理

  • 参数缺失,用户体验差:用户使用时,只能调用 processbar() 函数执行一个固定的进度动画,无法传递实际任务的总进度(如 “总大小 1000MB,当前下载 500MB”),导致进度条与真实任务无关,仅为一个 “虚假动画”。
  • 状态不透明:用户无法知道进度条当前的实际进度(如百分比),只能被动等待动画结束,缺乏对任务进度的感知。

4. 细节实现的局限性

  • 局部变量导致状态无法延续:进度条的字符数组 bar是 processbar() 函数的局部变量,每次函数调用都会重新初始化,因此无法在多次调用中保留进度状态,导致无法实现 “分阶段更新进度”(如每次调用更新 10%)。

为了解决这个问题我将代码进行了完善,我想要解决上面问题和形成如下进度条:

processbar.h

#pragma once
#include <stdio.h>#define NUM 102
#define TOP 100
#define STYLE '='
#define end '>'extern void processbar(int rate);
extern void Initbar();

processbar.c

#include "processbar.h"
#include<string.h>
#include<unistd.h>const char* lable="|/-\\-";
char bar[NUM];#define GREEN "\033[0;32;32m"
#define END "\033[m"void processbar(int rate)
{if(rate<0||rate>100)return;int len=strlen(lable);printf(GREEN"[%-100s]"END"[%d%%][%c]\r",bar,rate,lable[rate%len]);fflush(stdout);bar[rate++]=STYLE;if(rate<100)bar[rate]=end;
}void Initbar()
{//清空为'\0',避免首次使用时的随机乱码memset(bar,'\0',sizeof(bar));
}

小细节:

1.void Initbar()函数

用来解决扩展性差,难以二次使用问题,在调用进度条之前先调用这个函数。此时在processbar.h中也得进行声明。

2.void processbar(int rate)
接收 rate 参数(0~100 的百分比),进度由外部传入,调用者可根据实际场景(如下载了 30%)灵活控制显示内容。

3.全局数组 char bar[NUM]

配合 Initbar() 初始化函数。数组生命周期与程序一致,进度状态可跨多次 processbar() 调用累积,支持多次任务(例如连续下载多个文件时,每次调用 Initbar() 重置即可)。

4.#define GREEN "\033[0;32;32m"和#define END "\033[m"

ANSI 颜色控制宏:设置绿色文本,END用于恢复默认颜色

这些是博主搜的颜色替换,大家可以自己选然后改变

ANSI 颜色控制码 - 前景色
#define FG_BLACK   "\033[30m"   // 黑色
#define FG_RED     "\033[31m"   // 红色
#define FG_GREEN   "\033[32m"   // 绿色
#define FG_YELLOW  "\033[33m"   // 黄色
#define FG_BLUE    "\033[34m"   // 蓝色
#define FG_MAGENTA "\033[35m"   // 洋红色
#define FG_CYAN    "\033[36m"   // 青色
#define FG_WHITE   "\033[37m"   // 白色

ANSI 颜色控制码 - 背景色
#define BG_BLACK   "\033[40m"   // 黑色背景
#define BG_RED     "\033[41m"   // 红色背景
#define BG_GREEN   "\033[42m"   // 绿色背景
#define BG_YELLOW  "\033[43m"   // 黄色背景
#define BG_BLUE    "\033[44m"   // 蓝色背景
#define BG_MAGENTA "\033[45m"   // 洋红色背景
#define BG_CYAN    "\033[46m"   // 青色背景
#define BG_WHITE   "\033[47m"   // 白色背景

test.c

#include "processbar.h"
#include <unistd.h>typedef void (*callback_t)(int);void downLoad(callback_t cb)
{int total=1000;int curr=0;while(curr<=total){usleep(100000);int rate=curr*100/total;cb(rate);curr+=10;}printf("\n");
}int main()
{Initbar();downLoad(processbar);return 0;
}

小细节:

1. typedef void (*callback_t)(int);

是 C 语言中定义函数指针类型的语法

作用:给一个 “参数为 int、返回值为 void 的函数指针” 起一个别名 callback_t,简化函数指针的使用

具体解释:

void (*)(int):这是一个函数指针的 “原型”,表示:

  • 指向的函数返回值为 void(无返回值)。
  • 指向的函数接收一个 int 类型的参数。

callback_t:这是我们给这个函数指针类型起的别名。

运行结果:


以上就是进度条的知识点了,后续的完善工作我们将留待日后进行。希望这些知识能为你带来帮助!如果觉得内容实用,欢迎点赞支持~ 若发现任何问题或有改进建议,也请随时与我交流。感谢你的阅读!     

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/91220.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/91220.shtml
英文地址,请注明出处:http://en.pswp.cn/web/91220.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在macOS上使用VS Code和Clang配置C++开发环境

本文基于VS Code官方文档&#xff0c;详细介绍如何在macOS系统下配置Clang/LLVM编译器与VS Code的C开发环境。通过本文&#xff0c;你将学会如何搭建开发环境、创建并调试C程序&#xff0c;适合C初学者和需要在macOS上进行C开发的开发者。 前提条件 在开始配置前&#xff0c;…

Ganttable 基于工时的进度分析

时间进度分析是 Ganttable 提供的高级进度管理功能&#xff0c;它基于实际工作时长&#xff0c;结合计划预估工时&#xff0c;可精准计算项目及任务的完成度。开启进度分析开启进度分析功能的操作如下&#xff1a;在时间管理页面&#xff0c;点击右上角的 “设置” 按钮&#x…

duiLib 自定义资源目录

前面的demo&#xff0c;把布局文件放在默认目录了&#xff0c;想着应该也可以自定义资源路径。先debug看下默认目录是什么路径。设置调试选项&#xff0c;调试信息格式改为程序数据库&#xff08;/Zi&#xff09;再调试项目&#xff0c;选中监视1&#xff1a;在监护窗口中查看变…

YOLO-01目标检测基础

1、概念目标检测&#xff08;Object Detection&#xff09;是计算机视觉中的一个重要领域&#xff0c;它涉及到识别图片或视频某一帧中的物体是什么类别&#xff0c;并确定它们的位置。通常用于多个物体的识别&#xff0c;可以同时处理图像中的多个实例&#xff0c;并为每个实例…

Linux->动静态库

目录 引入&#xff1a; 一&#xff1a;动静态库的介绍 1&#xff1a;库的本质 2&#xff1a;库的类别及优缺点 3&#xff1a;动态链接 4&#xff1a;静态链接 二&#xff1a;头文件和库的查找 三&#xff1a;静态库的制作和使用 1&#xff1a;制作 2&#xff1a;指令打…

【LY88】双系统指南及避坑

一. Windows重装&#xff08;前提是Windows可正常使用&#xff0c;优点是无需U盘&#xff09; 1. PE工具和系统镜像 机械师只只提供的资源链接 完成微PE工具的安装并下载了系统镜像之后&#xff0c;&#xff08;如果要装ubuntu的话&#xff09;需确认磁盘分区格式和引导项。前…

Ubuntu22.04.1搭建php运行环境

步骤 1: 更新你的系统 首先&#xff0c;确保你的系统是最新的。打开终端并运行以下命令&#xff1a; sudo apt update sudo apt upgrade步骤 2: 安装Apache Web服务器 使用Apache作为你的Web服务器。运行以下命令&#xff1a; sudo apt install apache2安装完成后&#xff0c;你…

防止飞书重复回调通知分布式锁

## 场景销售订单下&#xff0c;明细25明细款&#xff0c;发起飞书审批&#xff0c;飞书设置自动审核通过&#xff0c;导致会收到两次审核通过通知加了分布式锁 &#xff0c;仍导致执行业务执行两遍了String lockKey "feihsu-approvalNotify:" instanceCode; RLock …

数据结构:下三角矩阵(Lower Triangular Matrix)

目录 什么是下三角矩阵&#xff1f; 我们要存哪些元素&#xff1f;一共几个&#xff1f; 推导索引映射公式 核心问题&#xff1a;给定 (i,j)&#xff0c;如何计算 k&#xff1f; 什么是下三角矩阵&#xff1f; 一个 n n 的矩阵&#xff0c;如果它在主对角线以上的所有元…

力扣209:长度最小的子数组

力扣209:长度最小的子数组题目思路代码题目 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其总和大于等于 target 的长度最小的 子数组 [numsl, numsl1, …, numsr-1, numsr] &#xff0c;并返回其长度。如果不存在符合条件的子数组&#xff0c;返回…

采购管理系统哪家性价比高?

在企业数字化转型进程中&#xff0c;采购管理系统已成为降本增效的核心工具。但面对市场上五花八门的产品&#xff0c;“性价比” 成为企业选型时的关键考量 —— 既要功能贴合业务需求&#xff0c;又要成本可控&#xff0c;还需兼顾实施效率与长期扩展性。以下从性价比维度解析…

轻松打造Unity小游戏AR体验

目录 AR会话初始化 平面追踪与相机定位 用户交互处理 实时渲染 Unity 小游戏宿主现已支持 AR 功能&#xff0c;本文介绍如何从零开始创建一个可以在Unity小游戏宿主上运行的AR小游戏&#xff0c;欢迎大家试用&#xff01; 想为你的小游戏注入虚实交融的魔力吗&#xff1f;…

IFCVF驱动+vhost-vfio提高虚拟机网络性能

​​IFCVF (Intel FPGA Virtual Function)​​ 是 Intel 为其基于 FPGA 的智能网卡开发的 ​​SR-IOV 虚拟功能驱动​​,属于 ​​PF4 (Physical Function 4)​​ 架构的一部分。它是专为高性能网络虚拟化场景设计的硬件加速解决方案。 云计算智能网卡(soc)或DPU场景下,IFC…

Hook捕获并拦截文件创建行为

需要用到minhook 先编译DLL #include <Windows.h> #include <string> #include <TlHelp32.h> #include <Shlwapi.h>#include "MinHook.h" // 自动选择正确的MinHook库 #pragma comment(lib, "Shlwapi.lib") #if defined(_M_X64) …

图像平滑处理

图像平滑处理四种常用方式1. 均值滤波 (cv2.blur())2. 高斯滤波 (cv2.GaussianBlur())3. 中值滤波 (cv2.medianBlur())4、双边滤波 (cv2.bilateralFilter())总结存图时遇到一个中文版乱码问题四种常用方式 平滑处理&#xff08;也称为模糊处理&#xff09;&#xff0c;用于减少…

fortigate的waf功能

在系统管理----可见功能----web应用防火墙打开waf功能Web 应用程序防火墙 &#xff08;WAF&#xff09; 配置文件可以检测和阻止已知的 Web 应用程序攻击。您可以将 WAF 配置文件配置为使用签名和约束来检查 Web 流量。您还可以强制实施 HTTP 方法策略&#xff0c;该策略控制与…

AI Compass前沿速览:可灵创意工坊、字节Coze StudioCoze Loop、通义万相2.2 、智谱GLM-4.5、腾讯混元3D世界模型开源

AI Compass前沿速览&#xff1a;可灵创意工坊、字节Coze Studio&Coze Loop、通义万相2.2 、智谱GLM-4.5、腾讯混元3D世界模型开源 AI-Compass 致力于构建最全面、最实用、最前沿的AI技术学习和实践生态&#xff0c;通过六大核心模块的系统化组织&#xff0c;为不同层次的学…

SpringCloud之Gateway

SpringCloud之Gateway 官网地址&#xff1a; https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories 1. 什么是gateway Spring Cloud Gateway 是Spring Cloud官方推出的第二代网关框架&#xff0c;定位于取代 Net…

关于获取某目录及子目录下所有文件且不包含隐藏文件

最近比较忙&#xff0c;很少写blog了&#xff01;&#xff01;&#xff01;关于获取目录及子目录下所有文件是常遇到的功能&#xff0c;一般通过递归遍历实现。而生产场景中&#xff0c;一般是遍历nas上的目录&#xff0c;在nas上利用File.listFiles(),在linux系统上无法获取含…

docker可视化管理工具lazydocker

Lazydocker 是一个用 Go 语言编写的命令行 Docker 管理工具。它提供了一个简洁、直观的终端界面&#xff0c;支持键盘和鼠标操作&#xff0c;可通过方向键与快捷键实时查看和管理容器、镜像、网络等资源&#xff0c;大幅简化了原本复杂的命令行操作&#xff0c;提升操作效率。2…