赞
踩
1. 小实验:
编写一个test.c
文件,:
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("你能看见我吗?\n");
sleep(1); // 暂停1秒
return 0;
}
编译并执行:
先打印,然后暂停一秒结束程序,很好理解。
2. 发现问题:
修改test.c
文件内容如下:
再次编译并执行:
发现运行可执行程序后,没有直接打印内容,而是隔了一秒钟,才打印,这好像和我们理解的不太一样,是怎么回事?
我们可以确定的是,一定是printf
先执行的,因为C语言代码一定是从上到下运行的,但是现象是字符没有打印。所以,我们可以断定,printf
其实早就运行了,只不过在sleep
期间,字符串没有被显示出来。在sleep
期间,字符串在输出缓冲区当中。
3. 缓冲区:
C/C++语言,会针对标准输出,给我们提供默认的缓冲区(stdout
)。我们可以使用fflush
函数,可以把一个流强制做刷新(标准输入输出流之后会讲,这里只需要知道它可以刷新输出缓冲区)。
验证:
编译并执行:
那为什么加\n
的程序,不需要刷新缓冲区?这是因为\n
是一种刷新策略,叫行刷新,默认就有刷新缓冲区的功能。
我们来写一个倒计时程序:
#include <stdio.h>
#include <unistd.h>
int main()
{
int cnt = 9;
while(cnt)
{
printf("%d\n", cnt);
cnt--;
sleep(1);
}
return 0;
}
运行一下:
可以发现它是换行打印倒计时,但是我们想让它只在一行打印,并且覆盖掉前一秒的秒数,如何做?\n
是换行加回车,我们现在的需求是只回车,不换行,可以通过\r
实现,但是\r
没有刷新缓冲区的功能。
修改如下:
运行一下:
1. 原理讲解:
我们期望的进度条形式如下:
进度条的风格是#
,右侧有一个百分数,提示当前具体进度,还有一个旋转光标,可以确定当进度条不动时,进程是还在进行还是卡住了。
#
,第二次输出##
,一次类推,#
越来越多。为了实现图中结果,我们可以通过不断回车,然后覆盖之前的#
实现,比如用##
覆盖#
;|/-\
这几个字符按顺序不断转换,实现旋转效果。2. 文件准备:
main.c
文件是主函数所在文件,进度条的具体实现在process.c
文件中,主函数文件通过头文件process.h
调用进度条的实现函数,各个文件内容如下(最重要是process.c
)。
process.c
文件:#include "process.h" #include <unistd.h> // sleep的头文件 #include <string.h> #define SIZE 101 #define MAX_RATE 100 #define STYLE '#' #define STIME 1000*40 const char* str="|/-\\"; // 旋转光标 void process() { // version 1 int rate = 0; char bar[SIZE] = {0}; // 全部初始化成'\0' int num = strlen(str); while(rate <= MAX_RATE) { printf("[%-100s][%d%%][%c]\r", bar, rate, str[rate%num]); // 打印百分号最好用%% fflush(stdout); usleep(STIME); // 单位是u秒 bar[rate++] = STYLE; } printf("\n"); }
main.c
文件:#include "process.h"
int main()
{
process();
return 0;
}
process.h
文件:#pragma once
#include <stdio.h>
void process();
Makefile
:无论是任何进度条,一定是和某种任务关联的!
process.c
文件:#include "process.h" const char* str="|/-\\"; // 旋转光标 void process_v1() { // version 1 int rate = 0; char bar[SIZE] = {0}; // 全部初始化成'\0' int num = strlen(str); while(rate <= MAX_RATE) { printf("[%-100s][%d%%][%c]\r", bar, rate, str[rate%num]); // 打印百分号最好用%% fflush(stdout); usleep(STIME); // 单位是u秒 bar[rate++] = STYLE; } printf("\n"); } // 不能一次将进度条打印完毕,否则无法平滑的和场景结合 // 该函数,应该根据rate,自动的打一次 void process_v2(int rate) { // version 2 static char bar[SIZE] = {0}; int num = strlen(str); if(rate <= MAX_RATE && rate >= 0) { printf("[%-100s][%d%%][%c]\r", bar, rate, str[rate%num]); // 打印百分号最好用%% fflush(stdout); bar[rate] = STYLE; } if(rate == MAX_RATE) memset(bar, '\0', sizeof(bar)); }
process.h
文件:#pragma once #include <string.h> #include <stdio.h> #include <unistd.h> #define SIZE 101 #define MAX_RATE 100 #define STYLE '#' #define STIME 1000*40 // 定义了一个函数指针类型,其中函数的参数是int,返回值是void typedef void callback_t(int); void process_v1(); void process_v2(int);
main.c
文件:#include "process.h" #define TARGET_SIZE 1024*1024 // 1MB,下载软件总大小 #define DSIZE 1024*10 // 模拟每次下载的单位大小 // 下载软件 void download(callback_t cb) { int target = TARGET_SIZE; // 下载软件总大小 int total = 0; // 目前下载了多少 while(total < target) { usleep(STIME); // 用简单的休眠时间,模拟本轮下载花费的时间 total += DSIZE; int rate = total*100/target; cb(rate); // 传一个比率 } printf("\n"); } int main() { download(process_v2); return 0; }
我们希望进度条在进度条函数内部循环打印,所以我们采用回调的方式,来进行某种任务的通知,动态更新进度条!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。