目录
[(1) make执行下述代码会出现何现象?](#(1) make执行下述代码会出现何现象?)
[(2) make执行下述代码会出现何现象?](#(2) make执行下述代码会出现何现象?)
[(3) 如何让不带【\n】的字符串自动行刷新?](#(3) 如何让不带【\n】的字符串自动行刷新?)
[(1) process.c](#(1) process.c)
[(2) process.h](#(2) process.h)
[(3) main.c](#(3) main.c)
[(4) 效果演示](#(4) 效果演示)
进度条动态效果演示
进度条
1、补充:回车与换行
(1) 回车:
解释为将光标移至行首,转义表达为【\r】。
(2) 换行:
解释为将光标移至下一行,转义表达为【\n】。
(3) 老式打字机:
在早期的打字机上,回车操作是指将打印机的打印头(Carriage)复位到行的起始位置,以便开始新的一行;换行操作是指使滚筒上卷,带动纸张向上移动一行的距离,以开启新的一行。
2、行缓冲区
前提:makefile里是下述内容,会方便执行。
bash
$ vim Makefile
1 SRC=$(wildcard *.c)
2 OBJ=$(SRC:.c=.o)
3 BIN=processbar
4
5 $(BIN):$(OBJ)
6 gcc -o $@ $^
7
8 %.o:%.c
9 gcc -c $<
10
11 .PHONY:clean
12 clean:
13 rm -f $(OBJ) $(BIN)
(1) make执行下述代码会出现何现象?
cpp
$ vim myproc.c
1 #include <stdio.h>
2 int main()
3 {
4 printf("hello Linux!\n");
5 sleep(3);
6 return 0;
7 }
现象:我们会观察到显示屏先出现"hello Linux",再停留三秒后才启动下一命令行。
解释:【\n】可以行刷新,程序走到\n会自动刷新在缓冲区的这一行字符,所以先显示字符串了。
(2) make执行下述代码会出现何现象?
cpp
$ vim myproc.c
1 #include <stdio.h>
2 int main()
3 {
4 printf("hello Linux!");
5 sleep(3);
6 return 0;
7 }
现象:显示器上三秒后才出现字符串,新的命令行也紧跟字符串后面。为什么printf明明在sleep前面,却在休眠后显示?
解释 :首先要明确的是,在程序内printf是比sleep先执行的 ,但是显示器没有显示是因为,【\r】没有行刷新功能,在休眠期间,"hello Linux"一直在缓冲区,没有被刷新 ,最后又被显示出来是因为程序退出会自动刷新一次缓冲区。
(3) 如何让不带【\n】的字符串自动行刷新?
众所周知,**运行程序时,系统会默认打开三个文件:stdin,stdout,stderr,分别对应的设备文件是键盘,显示器,显示器。**而我们使用的printf会把字符串写到标准输出stdout中,但由于没有【\n】,字符串就留在缓冲区。
要解决该问题,我们介绍一个函数:fflush是 C 语言标准库中的一个函数,用于控制输出流。它定义在 <stdio.h>
头文件中,其作用是将指定的输出流(通常是文件流)中的缓冲区内容强制写入到文件或设备中。这个函数可以用来确保缓冲区中的数据被立即输出,而不是等到缓冲区满了或者程序结束时。
cpp
$ vim myproc.c
1 #include <stdio.h>
2 int main()
3 {
4 printf("hello Linux!");
5 fflush(stdout);
6 sleep(3);
7 return 0;
8 }
执行上述代码,我们发现与第一组代码执行现象一致。
3、练手:倒计时代码
cpp
1 #include <stdio.h>
2 #include<unistd.h>
3 int main()
4 {
5 int num = 10;
6 while(num >= 0)
7 {
8 printf("%-2d\r",num); // 左对齐,位宽为2
9 num--;
10 fflush(stdout); // [\r]不能行刷新,要用到fflush
11 sleep(1);
12 }
13 printf("\n");
14 return 0;
15 }
4、进度条代码
(1) process.c
cpp
$ vim process.c
1 #include<stdio.h>
2 #include<string.h>
3 #include<unistd.h>
4
5 #define NUM 101
6 #define STYLE '='
7
8 void process_v1()
9 {
10 char buffer[NUM];
11 memset(buffer,0,sizeof(buffer)); // buffer数组里全设置成0
12 const char* table = "|/-\\"; // [\\]表示[\]
13 int len = strlen(table);
14
15 int cnt = 0;
16 while(cnt <= 100)
17 {
18 printf("[%-100s][%d%%][%c]\r",buffer,cnt,table[cnt%len]);
19 fflush(stdout);
20 buffer[cnt] = STYLE;
21 cnt++;
22 usleep(50000); // 以微秒为单位休眠
23 }
24 printf("\n");
25 }
这样的进度条已经初具模型了,但是我们使用进度条一般是一般加载资源一边刷新进度条,而不是单纯的自己循环,接下来我们实现第二个版本。版本2的不用自主实现循环而是与资源下载相关联,资源下载实现在了main函数所在的文件。
cpp
27 void FlushProcess(double total, double current)
28 {
29 char buffer[NUM];
30 memset(buffer,0,sizeof(buffer));
31 const char* table = "|/-\\";
32 int len = strlen(table);
33
34 static int cnt = 0; // 全局静态
35 int num =(int)(current*100/total); // 确定填充的大小范围
36 for(int i = 0;i < num;i++) // 填充"=",不用自己循环
37 {
38 buffer[i] = STYLE;
39 }
40 // 比例
41 double rate = current/total;
42 cnt %= len; // 因为可能发生资源加载很慢,但旋转轴依然要保持速度旋转的情况
43 printf("[%-100s][%.1f%%][%c]\r",buffer,rate*100,table[cnt]);
44 cnt++;
45 fflush(stdout);
46 }
(2) process.h
cpp
$ vim process.h
1 #include<stdio.h>
2 #include<unistd.h>
3 void process_v1();
4 void FlushProcess(double total,double current);
(3) main.c
cpp
$ vim main.c
1 #include"process.h"
2 #include<string.h>
3 #include<unistd.h>
4
5 double total = 1024.0;
6 double speed = 1.0;
7
8 void DownLoad()
9 {
10 double current = 0.0;
11 while(current <= total)
12 {
13 FlushProcess(total,current);
14 usleep(3000); // 充当下载数据
15 current += speed;
16 }
17 printf("\nDownLoad %.2lfMB Done\n",current);
18 }
19
20 int main()
21 {
22 //process_v1();
23 DownLoad();
24 return 0;
25 }
5、回调函数应用
回调函数是一种特殊的函数,它被作为参数传递给另一个函数,并在后者内部被调用。在这个代码中,
DownLoad
和UpLoad
函数都接受一个callback_t
类型的参数,这个参数就是回调函数。它接受一个callback_t
类型的参数cb
,这个参数指向一个回调函数。在下载过程中,DownLoad
函数和UpLoad
函数会周期性地调用这个回调函数来更新下载进度。
在 main
函数中,DownLoad
和 UpLoad
函数被调用,并且都传递了同一个回调函数 FlushProcess
。
cpp
1 #include"process.h"
2 #include<string.h>
3 #include<unistd.h>
4
5 typedef void (*callback_t)(double total, double current); // 函数指针
6
7 double total = 1024.0;
8 double speed = 1.0;
9
10 void DownLoad(callback_t cb)
11 {
12 double current = 0.0;
13 while(current <= total)
14 {
15 cb(total,current); // 回调函数
16 usleep(3000); // 充当下载数据
17 current += speed;
18 }
19 printf("\nDownLoad %.2lfMB Done\n",current);
20 }
21
22 void UpLoad(callback_t cb)
23 {
24 double current = 0.0;
25 while(current <= total)
26 {
27 cb(total,current); // 回调函数
28 usleep(3000); // 充当下载数据
29 current += speed;
30 }
31 printf("\nUpLoad %.2lfMB Done\n",current);
32 }
33
34
35 int main()
36 {
37 //process_v1();
38 DownLoad(FlushProcess);
39 UpLoad(FlushProcess);
40 return 0;
41 }