Linux 第二十四章

🐶博主主页:@ᰔᩚ. 一怀明月ꦿ 

❤️‍🔥专栏系列:线性代数,C初学者入门训练,题解C,C的使用文章,「初学」C++,linux

🔥座右铭:“不要等到什么都没有了,才下定决心去做”

🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀

目录

fd的分配规则

重定向

dup2

命令行中的重定向

isspace

重定向的使用:就是<, >, >>等

那为什么要有2:标准错误

缓冲区

样例

理解样例(2中的样例)

什么叫作刷新

用户缓冲区和内核缓冲区


fd的分配规则

进程默认已经打开了0,1,2,我们可以直接使用0,1,2进行数据访问

[BCH@hcss-ecs-6176 11_28]$ cat myfile.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>#define FILE_NAME "log.txt"
int main()
{char buf[1024];ssize_t s=read(0,buf,1024);//ssize_t有符号整型//read的返回值是实际读取的字节数,1024是我们设置最大读取的字节数,buf接收数据的缓冲区//从fd所指向的文件中读取数据放到buf中if(s>0){buf[s]=0;//将中实际读取的最后一个字符尾加上0//printf("%s\n",buf);write(1,buf,strlen(buf));//将读取的buf的数据,输出到fd(1)指向的文件,1指向的就是标准输出流,屏幕}return 0;
}

文件描述符的分配规则:寻找最小的,没有被使用的数据位置,分配给指定的打开文件

[BCH@hcss-ecs-6176 11_28]$ cat myfile.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>#define FILE_NAME "log.txt"
int main()
{close(0);//关闭fd==0的文件,标准输入流被关闭(键盘)int fd=open(FILE_NAME,O_CREAT | O_WRONLY | O_TRUNC,0666);if(fd<0){perror("open");return 1;}printf("%s :fd:%d\n",FILE_NAME,fd);//输出被创建的FILE_NAME “log.txt"的文件描述符close(fd);return 0;
}[BCH@hcss-ecs-6176 11_28]$ ./myfile
log.txt :fd:0//此时创建的文件的文件描述符为0[BCH@hcss-ecs-6176 11_28]$ cat myfile.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>#define FILE_NAME "log.txt"
int main()
{close(2);//关闭fd==2的文件,标准错误流流被关闭(屏幕)int fd=open(FILE_NAME,O_CREAT | O_WRONLY | O_TRUNC,0666);if(fd<0){perror("open");return 1;}printf("%s :fd:%d\n",FILE_NAME,fd);//输出被创建的FILE_NAME “log.txt"的文件描述符close(fd);return 0;
}[BCH@hcss-ecs-6176 11_28]$ ./myfile
log.txt :fd:2//此时创建的文件的文件描述符为2[BCH@hcss-ecs-6176 11_28]$ cat myfile.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>#define FILE_NAME "log.txt"
int main()
{close(1);//关闭fd==1的文件,标准输出流被关闭(屏幕)int fd=open(FILE_NAME,O_CREAT | O_WRONLY | O_TRUNC,0666);if(fd<0){perror("open");return 1;}printf("%s :fd:%d\n",FILE_NAME,fd);//输出被创建的FILE_NAME “log.txt"的文件描述符close(fd);return 0;
}//什么也没有输出:因为把fd==1的文件关闭了,把标准输出流(屏幕关闭了),所以不输出到屏幕上
[BCH@hcss-ecs-6176 11_28]$ ./myfile
[BCH@hcss-ecs-6176 11_28]$

重定向

重定向的本质,就是修改fd指向的文件

dup2

在Linux中,dup2是一个系统调用函数,用于复制文件描述符。它的原型定义在头文件<unistd.h>中。dup2函数的作用是将一个文件描述符复制到另一个文件描述符上,如果另一个文件描述符已经被使用,那么先关闭它。

函数原型如下:

int dup2(int oldfd, int newfd);
参数说明:
oldfd:要复制的文件描述符。
newfd:要复制到的目标文件描述符。返回值:
如果成功,返回新的文件描述符(即newfd);如果失败,返回-1,并设置errno。
这个函数通常用于重新定向标准输入、标准输出和标准错误流,以及实现管道和重定向等功能。

例如,下面的代码将标准输出重定向到文件output.txt中:

#include <unistd.h>
#include <fcntl.h>
int main() {int fd = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);if (fd == -1) {perror("open");return 1;}// 将标准输出重定向到文件描述符fd所指向的文件if (dup2(fd, STDOUT_FILENO) == -1) {perror("dup2");return 1;}// 关闭不再需要的文件描述符close(fd);// 此时标准输出已经重定向到output.txt文件printf("This will be written to output.txt\n");return 0;
}

在这个示例中,dup2函数将文件描述符fd复制到标准输出的文件描述符上,从而将标准输出重定向到了output.txt文件中

命令行中的重定向

[BCH@hcss-ecs-6176 11_28]$ echo "hello">log.txt
[BCH@hcss-ecs-6176 11_28]$ cat log.txt
hello
[BCH@hcss-ecs-6176 11_28]$ echo "hello">>log.txt
[BCH@hcss-ecs-6176 11_28]$ cat log.txt
hello
hello
[BCH@hcss-ecs-6176 11_28]$ cat <log.txt
hello
hello

isspace

在Linux中,isspace()是一个用于判断字符是否为空白字符的C标准库函数。它位于<ctype.h>头文件中。isspace()函数接受一个整型参数,该参数可被解释为unsigned char或EOF的值。它检查这个字符是否为空格、水平制表符、换行符、回车符、换页符或垂直制表符。如果是空白字符,isspace()函数返回一个非零值(true),否则返回0(false)。

以下是一个示例程序,演示了isspace()函数的使用:

#include <stdio.h>
#include <ctype.h>int main() {int ch;printf("请输入一个字符:");ch = getchar();if (isspace(ch)) {printf("输入的字符是空白字符。\n");} else {printf("输入的字符不是空白字符。\n");}return 0;
}


该程序首先提示用户输入一个字符,然后使用getchar()函数获取用户输入的字符。接下来,使用isspace()函数判断该字符是否为空白字符,并打印相应的提示信息。

注意,isspace()函数只能判断单个字符是否为空白字符,而不能判断字符串中的空白字符。如果需要判断整个字符串是否只包含空白字符,可以自行编写相应的逻辑。

重定向的使用:就是<, >, >>等

那为什么要有2:标准错误

[BCH@hcss-ecs-6176 testfile]$ cat myfile.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
int main()
{fprintf(stdout,"hello stdout\n");fprintf(stderr,"hello stderr\n");return 0;
}[BCH@hcss-ecs-6176 testfile]$ ./myfile
hello stdout
hello stderr
都是打在屏幕上但是为什么重定向的时候,会出现如下结果呢
[BCH@hcss-ecs-6176 testfile]$ ./myfile > log.txt
hello stderr
[BCH@hcss-ecs-6176 testfile]$ cat log.txt
hello stdout原因:因为重定向的时候,只是重定向了fd==1,而fd==2并没有,所以屏幕上还是会显示hello stderr,log.txt里含有hello stdout

那为什么要有2呢?

[BCH@hcss-ecs-6176 testfile]$ ./myfile >log.txt
hello stderr
[BCH@hcss-ecs-6176 testfile]$ ./myfile 1>log.txt
hello stderr
这两种写法是一样的,只是一个是省略的写法
[BCH@hcss-ecs-6176 testfile]$ ./myfile 1>log.txt 2>log.txt.error
[BCH@hcss-ecs-6176 testfile]$ ls
log.txt  log.txt.error  Makefile  myfile  myfile.c就是我们在写程序的时候,有时需要显示常规信息,例如printf输出的,有时需要显示错误信息
所以我们可以通过重定向把常规信息输出到一个文件,错误信息输出到另一个文件(方便我们统一排查,例如,日志)

linux实现自己的bash

缓冲区

预备知识
我们理解缓冲区:就是一部分内存(由提供的??)

为什么要缓冲区?
缓冲区的主要作用是提高效率——提高使用者的效率

缓冲区因为能够暂存数据,必定要有一定的刷新方式:
1.无缓冲(立即刷新)
2.行缓冲(行刷新)
3.全缓冲(缓冲区满了,再刷新)

特殊情况:
1.强制刷新
2.进程退出的时候,一般要进行刷新缓冲区

一般对于显示器文件,行刷新(行缓冲)
对于磁盘上的文件,全缓冲(缓冲写满,再刷新)

样例

[BCH@hcss-ecs-6176 12_5]$ cat myfile.c
#include<stdio.h>
#include<string.h>
#include<unistd.h>
int main()
{fprintf(stdout,"C:hello fprintf\n");printf("C:hello printf\n");fputs("C:hello fputs\n",stdout);char * str="system call: hello write\n";write(1,str,strlen(str));fork();//注意fork的位置//如果fork在最前面,我们都能理解,因为子进程和父进程会同时执行//但是,fork在最后面,父进程已经运行完了,怎还能将代码打印两次呢?return 0;
}[BCH@hcss-ecs-6176 12_5]$ ./myfile
C:hello fprintf
C:hello printf
C:hello fputs
system call: hello write
[BCH@hcss-ecs-6176 12_5]$ ./myfile > log.txt
[BCH@hcss-ecs-6176 12_5]$ cat log.txt
system call: hello write
C:hello fprintf
C:hello printf
C:hello fputs
C:hello fprintf
C:hello printf
C:hello fputs
//c语言的接口的打印了两次,系统调用接口的打印了一次

理解样例(2中的样例)

1.当我们直接向显示器打印的时候,显示器文件的刷新方式是行刷新!而且你的代码输出的所有字符串,都有\n,fork之前,数据全部已经被刷新,包括sysstemcall

2.重定向到log.txt,本质是向磁盘文件中写入,我们系统对于数据的刷新方式已经由行刷新,变成了全缓冲!

3.全缓冲意味着缓冲区变大,实际写入的简单数据,不足以把缓冲区写满,fork执行的时候,数据区依就在就缓冲区中!

4.我们所谈的“缓冲区”和操作系统是没有关系的(printf/fprintf/fputs——底层都是封装的write),只能和C语言本身有关

5.C/C++提供的缓冲区,里面一定保存的是用户的数据,属不属于当前进程在运行时自己数据呢?属于
如果我们(进程)把数据交给了OS,这个数据就属于OS,不属于OS,不属于我自己的

6.当进程退出的时候,一般要进行刷新缓冲区,即便你的数据没有满足刷新条件!
这个过程,属于清空或写入操作

fork立马退出,任意一个进程退出的时候,刷新缓冲区,就要发生写时拷贝
write系统调用,没有使用C的缓冲区!!!
write直接写入到操作系统!不属于进程了!不发生写时拷贝了

什么叫作刷新

这个缓冲区在哪里
任何情况下,我们输入输出的时候,都要有一个FILE
FILE是一个结构体,FILE里面包含了fd,FILE提供一段缓冲区

例如输入缓冲区
例如输出缓冲区
每一个文件(linux所说的文件)都有一个缓冲区

用户缓冲区和内核缓冲区


我们日常使用的最的是C/C++提供的语言级别的缓冲区

我们模拟实现一下C标准库的函数——只做代码说明

注意:

在c语言头文件中,函数声明不用加extern,后面加分号结尾就行

但是变量一定要加extern,因为无法分清这个变量是声明还是定义

mystdio.h
[BCH@hcss-ecs-6176 c_lib]$ cat mystdio.h
#pragma once//防止头文件重复包含#define SIZE 4096
#define FLUSH_NONE 1
#define FLUSH_LINE (1<<1)
#define FLUSH_ALL (1<<2)typedef struct myFILE
{int fileno;int flag;char buffer[SIZE];int end;
}myFILE;extern  myFILE* my_fopen(const char* path,const char* mode);
extern  int my_fputs(const char* s,myFILE* stream);
extern  int my_fwrite(const char* s,int num,myFILE* stream);
extern  int my_fflush(myFILE* stream);
extern  int my_fclose(myFILE* stream);mystdio.c
[BCH@hcss-ecs-6176 c_lib]$ cat mystdio.c
#include"mystdio.h"
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include<stdlib.h>
#include<unistd.h>#define DFL_MODE 0666myFILE* my_fopen(const char* path,const char* mode)
{int fd=0;int flag=0;if(strcmp(mode,"r")==0){flag |= O_RDONLY;}else if(strcmp(mode,"w")==0){flag |=(O_CREAT | O_TRUNC | O_WRONLY);}else if(strcmp(mode,"a")){flag |=(O_CREAT|O_WRONLY|O_APPEND);}else{}if(flag & O_CREAT){fd=open(path,flag,DFL_MODE);}else{fd=open(path,flag);}if(fd<0){errno=2;return NULL;}myFILE* fp=(myFILE*)malloc(sizeof(myFILE));if(fp==NULL){errno=3;return NULL;}fp->fileno=fd;fp->end=0;fp->flag=FLUSH_LINE;return fp;
}
int my_fwrite(const char* s,int num,myFILE* stream)
{//写入过程memcpy(stream->buffer+stream->end,s,num);stream->end+=num;//判断是否需要刷新if((stream->flag& FLUSH_LINE)&&stream->end>0&&stream->buffer[stream->end-1]=='\n'){//"abc\ndt";这种情况处理my_fflush(stream);}return num;
}
int my_fflush(myFILE* stream)
{if(stream->end>0){write(stream->fileno,stream->buffer,stream->end);stream->end=0;}return 0;
}
int my_fclose(myFILE* stream)
{my_fflush(stream);return close(stream->fileno);
}main.c
[BCH@hcss-ecs-6176 c_lib]$ cat main.c
#include"mystdio.h"
#include<stdio.h>
#include<string.h>
#include<unistd.h>
int main()
{myFILE* fp=my_fopen("./log.txt","w");if(fp==NULL){perror("my_fopen");return 1;}int cnt=20;const char* msg="haha,this is my stdio lib\n";while(cnt--){my_fwrite(msg,strlen(msg),fp);sleep(1);}my_fclose(fp);return 0;
}Makefile
[BCH@hcss-ecs-6176 c_lib]$ cat Makefile
mytest:mystdio.c main.cgcc -o $@ $^
.PHONY:clean
clean:rm -f mytestmake
[BCH@hcss-ecs-6176 c_lib]$ make
gcc -o mytest mystdio.c main.c
[BCH@hcss-ecs-6176 c_lib]$ ./mytest
[BCH@hcss-ecs-6176 c_lib]$ ls
log.txt  main.c  Makefile  mystdio.c  mystdio.h  mytest
[BCH@hcss-ecs-6176 c_lib]$ cat log.txt
haha,this is my stdio lib
haha,this is my stdio lib
haha,this is my stdio lib
haha,this is my stdio lib
haha,this is my stdio lib
haha,this is my stdio lib
haha,this is my stdio lib
haha,this is my stdio lib
haha,this is my stdio lib
haha,this is my stdio lib
haha,this is my stdio lib
haha,this is my stdio lib
haha,this is my stdio lib
haha,this is my stdio lib
haha,this is my stdio lib
haha,this is my stdio lib
haha,this is my stdio lib
haha,this is my stdio lib
haha,this is my stdio lib
haha,this is my stdio lib

  🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/9485.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue3.0(五) reactive全家桶

文章目录 1 reactive1.1 reactive的应用1.2 reactive的特点1.3 reactive的注意1.4 reactive的局限性 2 toRefs3 isReactive4 shallowReactive5 readonly5.1 readonly 详细信息5.2 readonly函数创建一个只读的响应式对象5.3 如何修改嵌套在只读响应式对象中的对象? 6 isReadonl…

小程序(三)

十三、自定义组件 &#xff08;二&#xff09;数据方法声明位置 在js文件中 A、数据声明位置&#xff1a;data中 B、方法声明位置methods中&#xff0c;这点和普通页面不同&#xff01; Component({/*** 组件的属性列表*/properties: {},/*** 组件的初始数据*/data: {isCh…

在RK3588开发板使用FFMpeg 结合云服务器加SRS实现摄像头数据推流到云端拱其他设备查看

今天测试了一把在开发板把摄像头数据推流到云端服务器&#xff0c;然后给其他电脑通过val软件拉取显示摄像头画面&#xff0c;浅浅记录一下大概步骤 1.开发板端先下载ffmpeg apt install ffmpeg2.云服务器先安装SRS的库 云服务器我使用ubuntu系统&#xff0c;SRS是个什么东西&…

怎么开发付费视频系统_轻松拥有知识付费平台

在信息爆炸的时代&#xff0c;我们每天都在被海量的内容所包围。但你有没有想过&#xff0c;如何将这些内容变得更加有价值&#xff0c;更加个性化&#xff0c;甚至能够为你带来经济收益&#xff1f;今天&#xff0c;就让我带你走进一个全新的领域——付费视频系统&#xff0c;…

电解制氢电源-零排放氢源生成器

电解制氢电源&#xff1a;氢能源的制造者 氢能作为一种清洁、高效的能源&#xff0c;正逐渐成为我国能源战略的重要组成部分。电解制氢电源作为氢能产业链的核心环节&#xff0c;对于实现氢能产业的可持续发展具有重要意义。 电解制氢电源是一种利用电能将水分解为氢气和氧气的…

Django项目之电商购物商城 -- 新增收货地址

Django项目之电商购物商城 – 新增收货地址 在项目中新增收货地址我们需要根据前端的设置来写 在这里我们看到新增收货地址的方法发送的是一个ajax请求 , 使用的是post方法 , 其路由为addresses/create 分析完毕后开始写视图以及路由 一. 设置视图以及路由 因为新增地址依…

vue数据大屏并发请求

并发? 处理并发 因为js是单线程的&#xff0c;所以前端的并发指的是在极短时间内发送多个数据请求&#xff0c;比如说循环中发送 ajax , 轮询定时器中发送 ajax 请求. 然后还没有使用队列, 同时发送 的. 1. Promise.all 可以采用Promise.all处理并发&#xff0c; 当所有pro…

传输层协议——UDP协议

目录 一、传输层 二、再谈端口号 端口号的划分 知名端口号 pidof netstat命令 三、UDP协议 1、UDP协议格式 2、UDP协议特点 3、UDP协议的缓冲区 四、基于UDP的应用层协议 一、传输层 上一篇文章我们所讲到的HTTP协议和HTTPS协议&#xff0c;是属于应用层协议。我们…

vue3.x + echarts 5.x + ant-design-vue 4.x + monaco-editor v3 新增版本切换功能

前言 1. 因为vue架构中&#xff0c;大多数包都是通过npm / yarn 等工具直接安转到node_modules 使用 2. 多个版本切换时&#xff0c;不可能全部安装echarts版本 3. 所以思路围绕如何通过cdn动态引入echarts一、添加工具代码 loadScript 路径 utils/loadScript.js export de…

【NodeMCU实时天气时钟温湿度项目 2】WIFI模式设置及连接

第一专题内容&#xff0c;请参考 【NodeMCU实时天气时钟温湿度项目 1】连接点亮SPI-TFT屏幕和UI布局设计-CSDN博客 第三专题内容&#xff0c;请参考 【NodeMCU实时天气时钟温湿度项目 3】连接SHT30传感器&#xff0c;获取并显示当前环境温湿度数据&#…

致鸿物流器材有限公司揭示2024数字物流供应链与技术装备展新动态

参展企业介绍 广东致鸿物流器材有限公司&#xff0c;前身为广州致鸿物流器材有限公司&#xff0c;成立于2002年初&#xff0c;是一家在中国仓储笼行业成立多年的专业仓储笼制造公司&#xff0c;公司员工约400名&#xff0c;日产仓储笼制造规模近8000个&#xff0c;在全国范围内…

华为OD机试 - 手机App防沉迷系统(Java 2024 C卷 100分)

华为OD机试 2024C卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测试…

自动驾驶学习2-毫米波雷达

1、简介 1.1 频段 毫米波波长短、频段宽,比较容易实现窄波束,雷达分辨率高,不易受干扰。波长介于1~10mm的电磁波,频率大致范围是30GHz~300GHz 毫米波雷达是测量被测物体相对距离、相对速度、方位的高精度传感器。 车载毫米波雷达主要有24GHz、60GHz、77GHz、79GHz四个频段。 …

积鼎CFDPro颗粒流仿真 | 基于拉格朗日粒子追踪方法,模拟复杂颗粒的流动现象

颗粒流仿真是通过数值模拟手段模拟由大量固体颗粒构成的系统的动态行为&#xff0c;能够详尽刻画颗粒间的碰撞、扩散、堆积、破碎、混合等微观交互&#xff0c;以及与流体介质的相互作用&#xff0c;从而预测颗粒流在各种工况下的宏观表现。颗粒流仿真能够揭示隐藏的风险因素&a…

Java 变量类型

Java 变量类型 在 Java 语言中&#xff0c;所有的变量在使用前必须声明。 声明变量的基本格式如下&#xff1a; type identifier [ value][, identifier [ value] …] ; 格式说明&#xff1a; type – 数据类型。 identifier – 是变量名&#xff0c;可以使用逗号 , 隔开来…

Bert 在 OCNLI 训练微调

目录 0 资料1 预训练权重2 wandb3 Bert-OCNLI3.1 目录结构3.2 导入的库3.3 数据集自然语言推断数据集路径读取数据集数据集样例展示数据集类别统计数据集类加载数据 3.4 Bert3.4 训练 4 训练微调结果3k10k50k 0 资料 【数据集微调】 阿里天池比赛 微调BERT的数据集&#xff0…

想学PR的有福了,一小时学会PR剪视频

想学PR的有福了&#xff0c;一小时学会PR剪视频 Pr是什么软件&#xff1f;教程介绍及教程展示教程领取结语下期更新预报 Pr是什么软件&#xff1f; Pr是指Adobe Premiere Pro&#xff0c;它是由Adobe公司开发的一款专业级的视频编辑软件。这款软件广泛应用于电影、电视和网页视…

SQL统计语句记录

1.达梦数据库 统计指定单位的12个月份的业务数据 SELECT a.DEPT_ID, b.dept_name, a.USER_NAME, count(a.dept_id) as count, sum(case when to_char(a.CREATE_TIME,yyyy-mm) 2023-01 THEN 1 else 0 end) as one,sum(case when to_char(a.CREATE_TIME,yyyy-mm) 2023-02 T…

【JavaEE 初阶(四)】多线程进阶

❣博主主页: 33的博客❣ ▶️文章专栏分类:JavaEE◀️ &#x1f69a;我的代码仓库: 33的代码仓库&#x1f69a; &#x1faf5;&#x1faf5;&#x1faf5;关注我带你了解更多线程知识 目录 1.前言2.常见的锁策略2.1悲观锁vs乐观锁2.2轻量级锁vs重量级锁2.3自旋锁vs挂起锁2.4读写…

【数据结构(邓俊辉)学习笔记】栈与队列01——栈应用(栈混洗、前缀后缀表达式、括号匹配)

文章目录 0. 概述1. 操作与接口2. 操作实例3. 实现4. 栈与递归5. 应用5.1 逆序输出5.1.1 进制转换5.1.1.1 思路5.1.1.2 算法实现 5.2 递归嵌套5.2.1 栈混洗5.2.1.1 混洗5.2.1.2 计数5.2.1.3 甄别 5.2.2 括号匹配5.2.2.1 构思5.2.2.2 实现5.2.2.3 实例 5.3 延迟缓冲5.3.1 中缀表…