首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【Linux】Linux第一个小程序 - 进度条

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

作者头像
用户11872857
发布2025-12-17 16:04:03
发布2025-12-17 16:04:03
750
举报

前言

前面我们已经学习了 Linux 基础指令,以及 vim 编辑器、GCC/G++ 编译器、Makefile 等工具的使用。今天,我们就结合这些已掌握的基础知识,编写一个 Linux 环境下的第一个小程序 —— 进度条。

📚 Linux 入门篇

-【 Linux 历史溯源与指令入门 】

-【 Linux 指令进阶 】

-【 Linux 权限管理 】

🔧 Linux 工具篇

-【 yum + vim 】

-【 sudo白名单配置 + GCC/G++ 】

-【 自动化构建:make + Makefile 】


目录

一、前置知识

1、 回车和换行

2、 缓冲区

二、实现简单的倒计时

三、进度条

1、Verrsion 版本

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


一、前置知识

1、 回车和换行

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

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

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

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

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

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

二、实现简单的倒计时

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

代码语言:javascript
复制
#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
复制
#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
复制
//progressbar.h

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

void progressbar();
代码语言:javascript
复制
//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
复制
#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
复制
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#define NUM 101
#define pro '='

void progressbar(int date);
void initbar();

【progressbar.c】:

代码语言:javascript
复制
#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
复制
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)) 初始化进度条数组,避免多次下载时状态残留。

这种应用场景就是利用回调函数实现多次下载

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-12-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、前置知识
    • 1、 回车和换行
    • 2、 缓冲区
  • 二、实现简单的倒计时
  • 三、进度条
    • 1、Verrsion 版本
    • 2、进度条在下载场景下的应用
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档