文章目录
一、屏幕缓冲区
屏幕缓冲区是指在输出设备(如终端或控制台)
之前,数据被存储的内存区域
。
应用程序将输出数据写入这个缓冲区,然后系统统一将缓冲区的内容刷新到屏幕上。
缓冲区的使用提高了I/O操作的效率,因为它允许数据批量刷新,而不是每次输出都进行一次I/O操作。
1、缓冲区刷新模式:
缓冲区的刷新模式决定了什么时候会将缓冲区内容刷新到终端屏幕上。主要有以下几种模式:
全缓冲模式(Fully Buffered):
- 数据只有在缓冲区满时才会被刷新到屏幕上。通常用于文件I/O。
行缓冲模式(Line Buffered):
- 当遇到换行字符(LF, \n)时,缓冲区内容会被刷新到屏幕上。常见于终端I/O。
- 一些标准输出流(如
stdout
)在连接到终端时默认使用行缓冲模式。
无缓冲模式(Unbuffered):
- 数据每次写入缓冲区后立即被刷新到屏幕上。适用于需要实时输出的场景,如错误输出流(如
stderr
)通常是无缓冲的。
2、换行(LF, \n
)和回车(CR, \r
)
在屏幕缓冲区的刷新以及终端输出中,换行字符和回车字符有着重要的作用:
换行(Line Feed, \n
):
功能:将光标移到下一行。
在行缓冲模式下:
- 当输出流遇到换行字符时,缓冲区的内容会被立即刷新到屏幕上,从而实现行缓冲的机制。
- 在许多现代终端和编程环境中,输出一个换行字符通常意味着会将当前行的数据刷新到屏幕。
回车(Carriage Return, \r):
功能:将光标移动到当前行的起始位置(即行首),但不移动到下一行。
用途:
- 常用于覆盖同一行内容,如进度条或动态日志输出。输出回车字符后,下一次的输出会从行首开始,覆盖当前行的内容。
3、换行回车在屏幕缓冲区中的作用
行缓冲模式:换行字符(\n)会触发缓冲区刷新,将内容显示到屏幕上。
回车字符(\r
):不触发缓冲区刷新,而是移动光标,通常与手动刷新缓冲区结合使用,以实现动态行更新。
缓冲区刷新模式(全缓冲、行缓冲和无缓冲)控制了何时将数据从缓冲区刷新到屏幕。
换行字符(\n
): 在行缓冲模式下起到触发缓冲区刷新的作用,同时将光标移到下一行。
回车字符(\r
): 将光标移动到行首,常用于覆盖当前行的内容,而不触发缓冲区刷新。
综合使用换行和回车字符,可以实现灵活的终端输出效果,特别在实时显示和动态更新场景中非常常见。
4、老式打字机
缓冲区刷新相当于老式打字机或写作文,当从左向右写满了会进行光标向左回退,换行,继续打印。
5、行缓冲区
接下来直接看代码
代码一!什么现象?
#include <stdio.h>
#include <unistd.h> //sleep()函数头文件
int main()
{
printf("hello linux!\n");
sleep(3);
return 0;
}
它会打印完hello linux!
换行后休眠三秒。
代码二!什么现象??
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("hello linux!");
sleep(3);
return 0;
}
没有\n以后,程序等待了三秒打印出hello linux!
代码三!什么现象???
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("hello linux!");
fflush(stdout);
sleep(3);
return 0;
}
打印完hello linux!
光标休眠三秒后结束程序。
二、倒计时程序
利用缓冲区特点我们简单实现一个倒计时程序
#include
#include
int main()
{
int i = 10;
while(i >= 0)
{
printf("%-2d\r", i); // \n
fflush(stdout);
i--;
sleep(1);
}
printf("\n");
return 0;
}
三、进度条代码
process.c
#include "process.h"
#include <string.h>
#include <unistd.h>
#define NUM 101
#define STYLE '='
// vesion1
void process_v1()
{
char buffer[NUM];
memset(buffer, 0, sizeof(buffer));
const char *lable="|/-\\";
int len = strlen(lable);
int cnt = 0;
while(cnt <= 100)
{
printf("[%-100s][%d%%][%c]\r", buffer, cnt, lable[cnt%len]);
fflush(stdout);
buffer[cnt]= STYLE;
cnt++;
usleep(50000);
}
printf("\n");
}
// verison2
void FlushProcess(double total, double current)
{
char buffer[NUM];
memset(buffer, 0, sizeof(buffer));
const char *lable="|/-\\";
int len = strlen(lable);
static int cnt = 0;
// 不需要⾃⼰循环,填充#
int num = (int)(current*100/total); // 11.0 / 1000
int i = 0;
for(; i < num; i++)
{
buffer[i] = STYLE;
}
double rate = current/total;
cnt %= len;
printf("[%-100s][%.1f%%][%c]\r", buffer, rate*100, lable[cnt]);
cnt++;
fflush(stdout);
}
process.h
#pragma once
#include <stdio.h>
void process_v1();
void FlushProcess(double total, double current);
main.c
#include "process.h"
#include <stdio.h>
#include <unistd.h>
double total = 1024.0;
double speed = 1.0;
void DownLoad()
{
double current = 0;
while(current <= total)
{
FlushProcess(total, current);
// 下载代码
usleep(3000); // 充当下载数据
current += speed;
}
printf("\ndownload %.2lfMB Done\n", current);
}
int main()
{
DownLoad();
DownLoad();
DownLoad();
DownLoad();
DownLoad();
DownLoad();
DownLoad();
DownLoad();
return 0;
}
Makefile
SRC=$(wildcard *.c)
OBJ=$(SRC:.c=.o)
BIN=processbar
$(BIN):$(OBJ)
gcc -o $@ $^
%.o:%.c
gcc -c $<
.PHONY:
clean:
rm -f $(OBJ) $(BIN)