• 对缓冲区的初步认识——制作进度条小程序


    前言

    这个小程序最好还是在Linux环境下实验,更能体现缓冲区作用

    预备知识

    回车和换行的区别

    我们经常把回车换行连起来说,但他们是一样的吗?
    换行:只有竖直方向改变
    在这里插入图片描述
    回车:只有横轴方向改变
    在这里插入图片描述
    回车换行:从第一行的末尾到第二行的起始位置
    在这里插入图片描述
    老式键盘的回车键 就是这个横竖造型

    在这里插入图片描述
    在c语言中\r是回车 \n是回车换行

    输出缓冲区

    我们暂且把缓冲区只是认为是内存的一块空间
    为什么要有缓冲区呢?如果没有缓冲区 cup要更多次访问硬件设备
    举个例子就像是我们宿舍扔垃圾总是攒得有点多了才开始扔,而不是有一点就扔

    /n 有清空输出缓冲区的作用

    例子:

    这里有两个小程序 唯一的区别就是 第5句是否加了\n

    #include 
    #include
    int main()
    {                                                                                                  
        printf("hello word");
        sleep(3);
       
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    #include 
    #include
    int main()
    {                                                                                                  
        printf("hello word\n");
        sleep(3);
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这两个程序执行的结果是 第二个程序直接打印了,第一个程序快结束时才打印 这是为什么呢?

    QQ2024229-164212

    是因为第一个程序的sleep先执行吗?不是的 没有多线程的情况都是从上到下执行的。答案是 \n是作为情况缓冲区的标志
    没有\n的时候hello word还在缓冲区了 程序结束了才释放
    怎么解决这个问题呢?
    我们在后面加fflush(stdout)
    有个问题这个参数好奇怪

    stdout是什么?

    在这里插入图片描述
    是一种文件类型的变量用于控制显示器的 ,我们在c语言阶段也学过File的指针来操作文件的读写。为什么能把显示器也看成文件呢?因为在Linux下一切皆文件

    验证一切皆文件

    我们在/dev/pts/ 1 中写的文件 却在另一个终端显示了!
    在这里插入图片描述

    为什么是\n行刷新?

    因为如果只追求效率 全部内容到缓冲区才刷新 这样会爆发式的内容增多,人来不及看。一行一行的刷新符合人的习惯。

    倒计时程序

    原理

    我们写1的时候光标到下一个格子,然后又返回来写2把1覆盖,同一个位置不停的刷新
    在这里插入图片描述

    代码实现

    #include                                                                                  
    #include // linux环境 如果windos 
    
       int main()
       {
         int cnt = 10;
         while(cnt >= 0)
         {
           printf("%2d\r",cnt);// 如果不加2 长度不统一 覆盖不完全 
           fflush(stdout);
          sleep(1);
          cnt--;
        }
        printf("\n"); // 这里的作用是避免命令提示符覆盖
        return 0;
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    为什么这里要强制刷新?没有会怎样?

    我们先把fflush屏蔽了 看看会发生什么
    在这里插入图片描述
    在这里插入图片描述

    只输出个0,为什么呢?
    因为我们写一个字符串到缓冲区里,光标就回车了,然后再写就被覆盖了直到写到最后一个0没有东西能覆盖它了,于是就只把0从缓冲区输出到屏幕了。

    为什么是输出的是格式是%2d?正常的行不行?

    我们在屏幕上输出123是数字还是字符串呢?答案其实是字符串
    如果我们就%d输出 看看会发什么
    从10一下就到90了 这是因为 9只有一位 而10 有两位 只能覆盖一位所以显示就是90只把1覆盖了
    在这里插入图片描述
    在这里插入图片描述

    我们已经了解了预备知识那么进入正题吧

    进度条程序

    我们要做的程序

    QQ202431-214911

    我们顶一个字符型数组bar长度为102用来存放进度#

    数组长度为什么是102

    因为我们用100个#来表示完全下载好 但是字符串结束的标准是’\0’所以我们至少要多用一个空间 但是为了保险再加一些也没关系

    然后我们初始化数组

    为什么我们要初始化数组?

    因为数组不初始化放的都是随机值,我们等下按字符串输出时会出现乱码,最好以‘/0’初始化因为字符串输出遇到’/0‘结束!

     memset(bar, '\0', sizeof(bar)); 
    
    • 1

    之后我们在定义一个变量cnt表示进度到哪了?
    每下载好一个bar[cnt] =‘#’ bar数组就存放一个字符 然后cnt++
    直到 cnt <= 100

    旋转的指针怎么实现的?

    我们固定了输出的字符串长度为100在相当于长度为101的位置不停的写 |/-\ 覆盖着写形成了动画效果就像连环画一样
    用一个字符数组储存|/-\ ,进度+1 就打印一个数组里面的内容 arr[p]
    为了不越界 p 还要模4

    vison1完整代码

    但是这个版本就是空架子,没有和具体的场景联系起来

     void processbar()
     {
    	 char bar[length];
    	 int cnt = 0;
    	 memset(bar, '\0', sizeof(bar)); 
    	 while (cnt < length) 
    	 {
    		 printf("[%-100s] [%3d%%] [%c]\r", bar,cnt, lable[cnt%4]); //-100左对齐:长度一百   %%:百分号
    		 bar[cnt++] = '#';
    		 fflush(stdout); // 如果不强制刷新的话屏幕不显示结果
    		 Sleep(50); // 毫秒为单位
    	 }
    	 printf("\n");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    vison2

    对于我们下载应用这个场景,我们的进度条函数应该知道文件的大小,和当前已经下载了多少时实的更新。所以我们考虑对进度条函数加两个参数 1个total 和 1currnt 代表总的大小 和已经下了的大小

     void processbar(double total, double current)
     {
    	 char bar[length];
    	 int cnt = 0;
    	 memset(bar, '\0', sizeof(bar)); 
    	 double rate = (current*100.0) / total;
    	 int loop = (int)rate;
    	 while (cnt <= loop)
    	 {
    	
    		 printf("[%-100s] [%.1lf%%] [%c]\r", bar, rate, lable[cnt % 4]); //-100左对齐:长度一百   %%:百分号
    		 bar[cnt++] = '#';
    		 fflush(stdout); // 如果不强制刷新的话屏幕不显示结果
    		 Sleep(50); // 毫秒为单位
    	 }
    	 printf("\n");
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    构造场景

    void loaddown() 
    {
        double filesize = 100 * 1024 * 1024 * 1.0; // 100M
        double bandwidth = 10 * 1024 * 1024 * 1.0; //1M/S
        double current = 0.0;
    
        printf("download begin, current: %lf\n", current);
     
        while (current <= filesize) 
        {
            processbar(filesize, current);
            current += bandwidth;
            Sleep(100);
         
        }
        printf("\ndownload completed\n");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    我们稍微调整一下进度条函数 调整了两个地方
    1.把sleep函数关闭了 因为我们每次都是从零打印的有sleep函数这个卡顿就明显了
    2.把printf提在循环外面了因为loaddown多次调用进度条函数在循环里面每次重零打印会卡

     void processbar(double total, double current)
     {
    	 char bar[length];
    	 int cnt = 0;
    	 memset(bar, '\0', sizeof(bar)); 
    	 double rate = (current*100.0) / total;
    	 int loop = (int)rate;
    	 while (cnt <= loop)
    	 {
    	 	//printf("[%-100s] [%.1lf%%] [%c]\r", bar, rate, lable[cnt % 4]); //-100左对齐:长度一百   %%:百分号
    		 bar[cnt++] = '#';
    		
    		// Sleep(50); // 毫秒为单位
    	 }
    	 printf("[%-100s] [%.1lf%%] [%c]\r", bar, rate, lable[cnt % 4]); //-100左对齐:长度一百   %%:百分号
    	 fflush(stdout); // 如果不强制刷新的话屏幕不显示结果
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    效果

    QQ202431-231356

    稍微改进一下download函数

    ♥1:每个文件的大小并不是一样的,我们要根据实际情况传参数。
    ♥2:我们把进度条函数当作download的回调函数。这样做有什么好处呢?
    我们以后修改进度条函数的时候可以不用修改download函数

    void loaddown(double filesize,callback_t cb) 
    {
    
        double current = 0.0;
    
        printf("download begin, current: %lf\n", current);
     
        while (current <= filesize) 
        {
            cb(filesize, current);
            current += bandwidth;
            Sleep(100);
         
        }
        printf("\ndownload completed\n");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    最终效果

    QQ202431-233851

    谢谢观看

    谢谢观看

  • 相关阅读:
    Redis之分布式锁
    量子笔记:多比特量子门
    GraphSAGE-pytorch-inductive
    适合小白学习的项目1832javaERP管理系统之成本管理Myeclipse开发mysql数据库servlet结构java编程计算机网页项目
    如何实现主机与容器之间数据的同步?以nginx:v1镜像为例,进行验证。提交操作步骤
    软件工程导论——第五章——总体设计
    常用面试/笔试开源小项目61~70
    iPhone取消Siri语音关机是好是坏
    SpringBoot中幕——配置文件properties与yml
    PMP成绩如何查询?
  • 原文地址:https://blog.csdn.net/2301_76180799/article/details/136372302