一、铺垫知识
1.回车符 和 换行符的区别
回车符’\r’ 的效果(让光标回到当前行开头) 和 换行符’\n’ 理论上的效果(把光标移动到下一行同一位置):
但实际上单纯的换行作用意义不大,所以在语言层面上,当你单独使用 ‘\n’ 时,它会同时执行回车 + 换行的效果。结论,在语言层面上,使用 ‘\n’ 或 "\r\n"的效果一模一样。
2.用户缓冲区问题
- 执行以下代码观察现象:
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("hello world");
sleep(2);
return 0;
}
现象:大约2s后,“hello world” 才被打印到屏幕上。
为什么会出现这种奇怪的现象,代码不是按顺序执行的吗?不应该是先执行printf函数打印,再执行sleep函数休眠吗?
分析:代码肯定是按顺序执行的,出现这种现象实际与用户缓冲区有关。在打印函数与显示器文件之间有一个缓冲区,叫用户缓冲区,printf函数实际上也并不什么打印函数,更确切的说它应该是一种拷贝函数,它会把字符串内容拷贝到用户缓冲区,而当用户缓冲区刷新的时候,用户缓冲区中的内容才会被拷贝到显示器文件(内容被拷贝到显示器文件,用户就能看到内容被打印到显示器的效果)。
用户缓冲区的刷新策略(一般来说是按照行刷新):
(1)遇到 ‘\n’,就会把 ‘\n’ 前的所以内容刷新到显示器文件
(2) 程序结束时,会把用户缓冲区的所有内容刷新到显示器文件
现象解释:printf函数把 “hello world” 拷贝到用户缓冲区中,接着执行sleep函数休眠2s后程序结束,程序结束时用户缓冲区中的所有内容会被刷新到显示器文件。
补充:
sleep函数:让程序休眠指定秒数再向后执行
- 验证第一种用户缓冲区刷新策略:
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("hello world\n"); // 在结尾加上'\n'换行符
sleep(2);
return 0;
}
现象:“hello world\n” 瞬间被打印到屏幕上,大约等待2s后程序结束。
结论:字符串结尾加上’\n’之后,字符串内容瞬间就刷新到显示器文件,而不是等待2s程序结束时才刷新,这证明了第一种刷新策略是正确有效的。
- 实际上除了前面两种用户缓冲区的刷新策略之外,系统还为用户提供了一种强制刷新缓冲区的函数:
fflush函数可以强制将用户缓冲区中的内容刷新到显示器文件,
使用方法:fflush(stdout)
C程序在启动的时候,默认打开了3个流:
• stdin - 标准输入流,指键盘文件
• stdout - 标准输出流,指显示器文件
• stderr - 标准错误流
代码验证:
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("hello world"); // 字符串结尾没加 '\n'
fflush(stdout);
sleep(2);
return 0;
}
现象:“hello world\n” 瞬间被打印到屏幕上,大约等待2s后程序结束。
结论:字符串结尾没加’\n’,字符串内容还是能瞬间就刷新到显示器文件,这证明fflush函数可以强制将用户缓冲区中的内容刷新到显示器文件。
二、进度条程序初版(含视频演示效果)
main.c:
#include "progress.h"
int main()
{
progress();
return 0;
}
progress.h:
#pragma once
#include <stdio.h>
#include <unistd.h>
void progress();
progress.c(进度条函数实现):
#include "progress.h"
void progress()
{
char bar[101] = {'\0'};
const char* cnt = "|/-\\"; // '\\'是一个转义字符,⽤于表示⼀个反斜杠,防止它被解释为⼀个转义序列符
int count = 0;
while(count<=100)
{
printf("[%-100s][%-3d%%][%c]\r",bar,count,cnt[count%4]);
fflush(stdout);
usleep(50000);
bar[count] = '#';
count++;
}
printf("\n");
}
视频演示进度条程序初版代码运行效果(链接: 进度条初版)
补充:
usleep函数:让程序休眠指定微秒数再向后执行
三、进度条程序(加入使用场景)
main.c(模拟下载软件的使用场景:下载函数):
#include "progress.h"
void download(double total,double speed) // total:软件总大小;speed:下载速度
{
double current_total = 0.0;
while(current_total<=total)
{
progress(current_total,total);
usleep(10000); // 每隔10000微秒进行一次下载
current_total += speed; // 每次下载,进度增加一个下载速度
}
printf("\n");
}
int main()
{
printf("程序下载中:\n");
download(1024.0,2.0);
return 0;
}
progress.h:
#pragma once
#include <stdio.h>
#include <unistd.h>
void progress(double,double);
progress.c(进度条函数:进度条不会一次性走完,每次调用进度条函数会打印出当前下载进度):
#include "progress.h"
void progress(double current,double total)
{
double cur_progress = current/total*100; // 计算出当前进度
char bar[101] = {'\0'};
int bar_count = (int)cur_progress;
for(int i = 0;i < bar_count;i++)
bar[i] = '#';
const char* cnt = "|/-\\";
static int count = 0;
printf("[%-100s][%.1lf%%][%c]\r",bar,cur_progress,cnt[count%4]);
fflush(stdout);
count++;
}