《Linux C编程实战》笔记:实现自己的myshell

2024-01-03 20:52

本文主要是介绍《Linux C编程实战》笔记:实现自己的myshell,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

ok,考完试成功复活

这次是自己的shell命令程序的示例

流程图:

关键函数

1.void print_prompt()

函数说明:这个函数打印myshell提示符,即“myshell$$”.

2.void get_input(char *buf)

函数说明:获得一条指令,buf用来存放输入的命令。命令过长会终止程序;以换行符\n作为结束

3.void explain_input(char *buf,int *argcount,char arglist[100][256])

函数说明:解析buf中存放的命令,把选项放在arglist中,同时把argcount的值改成选项的个数。比如“ls -l /tmp”,则arglist[0],arglist[1].arglist[2]分别是"ls","-l","/tmp"。

4.do_cmd(int argcount,char arglist[100][256])

函数说明:执行命令

5.int find_command(char *command)

函数说明:功能是分别在当前目录,/bin目录,/usr/bin目录下查找命令的可执行程序。

函数源码

准备代码

首先是一些头文件和定义的代码

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<dirent.h>
//头文件里都是以前出现过的,我就不再说每个的用处了
#define normal 0 //一般的命令
#define out_redirect 1 //输出重定向
#define in_redirect 2 //输入重定向
#define have_pipe 3 //命令中有管道
void print_prompt();//打印提示符
void get_input(char *);//得到输入的命令
void explain_input(char *,int *,char [][256]);//对输入的命令进行解析
void do_cmd(int,char [][256]);//执行命令
int find_command(char *);//查找命令中的可执行程序

main函数

因为这个程序模块化的很清楚,直接看main函数不会看不懂

int main(int argc,char **argv){int i;int argcount=0;//这个是存选项数的char arglist[100][256];//这个是存选项的char **arg=nullptr;char *buf=nullptr;//这个是得到输入的数组//buf=new char[256];buf=(char *)malloc(256);//用C的malloc声明if(buf==nullptr){perror("malloc failed");//perror也出现过好多次了,详见我以前写的内容exit(-1);}while (1)//循环读入命令{memset(buf,0,256);//先把buf清空print_prompt();//打印提示符get_input(buf);//读入命令//如果是exit或logout就退出if(strcmp(buf,"exit\n")==0||strcmp(buf,"logout\n")==0)break;for(i=0;i<100;i++)//把arglist清空arglist[i][0]='\0';argcount=0;explain_input(buf,&argcount,arglist);//通过这个函数去解释buf里面的命令,函数会更新argcount和arglistdo_cmd(argcount,arglist);//执行命令}if(buf!=nullptr){//退出后记得清空内存free(buf);buf=nullptr;}exit(0);
}

print_prompt函数

最简单的一集,打印提示符就完了

void print_prompt(){printf("myshell$$ ");
}

get_input函数

void get_input(char *buf){int len=0;char ch;//古法读入字符串,长度最长是256,遇到换行停止ch=getchar();while (len<256&&ch!='\n'){buf[len++]=ch;ch=getchar();}if(len==256){printf("command is too long \n");exit(-1);}buf[len]='\n';len++;buf[len]='\0';//记得最后添个'\0'
}

explain_input函数

void explain_input(char *buf,int *argcount,char arglist[][256]){//解释后的结果会存到arglist里//具体解释的方式就是根据空格分割,分割后的每个字符串存到arglist里//也就C语言没有split函数,不然至于这么麻烦吗...char *p=buf;char *q=buf;int number=0;while (1){if(p[0]=='\n') break;if(p[0]==' ') p++;//首先跳过空格走到一个字符串的开头else{q=p;//然后指针q指向这个字符串的开头,用q来遍历字符串直到下一个空格或者换行(换行代表结束)number=0;while (q[0]!=' '&&q[0]!='\n'){number++;//顺便记录字符串的长度(其实不用number也行,q-p不就得了,书上反正用了number)q++;}//循环后现在p是字符串的开头,q是字符串结尾的下一个空格位置strncpy(arglist[*argcount],p,number+1);//把分割的字符串赋值给arglistarglist[*argcount][number]='\0';//C风格的字符串记得结尾加'\0'(*argcount)++;选项数加一p=q;//p直接跳到q的位置继续循环}}
}

do_cmd函数

这个函数才是关键好吧,执行输入的指令

void do_cmd(int argcount,char arglist[][256]){int flag=0;//重定向的标志int how=0;//具体是什么重定向,取值是define的那些值int background=0;//标志是否有后台运行符 &int status;//这是给waitpid用的int i;int fd;//这是文件描述符char *arg[argcount+1];char *argnext[argcount+1];//这是给管道用的char *file;//文件名pid_t pid;//进程号for(i=0;i<argcount;i++){arg[i]=(char *)arglist[i];//首先先把arglist的值复制到arg里面(其实我也不是很明白为什么要重开一个数组)}arg[argcount]=nullptr;//数组多开的那个放空指针//查看命令是否有后台运行符for(i=0;i<argcount;i++){if(strncmp(arg[i],"&",1)==0){//看一下最开始的函数描述,这个示例代码的后台运行符&只能出现在最后if(i==argcount-1){background=1;arg[argcount-1]=nullptr;break;}else{//所以如果不是出现在最后就提示命令错误printf("wrong command\n");return;}}}for(i=0;arg[i]!=nullptr;i++){if(strcmp(arg[i],">")==0){flag++;how=out_redirect;if(arg[i+1]==nullptr)flag++;}if(strcmp(arg[i],"<")==0){flag++;how=in_redirect;if(i==0)flag++;}if(strcmp(arg[i],"|")==0){flag++;how=have_pipe;if(arg[i+1]==nullptr)flag++;if(i==0)flag++;}}if(flag>1){printf("wrong command\n");return;}if(how==out_redirect){for(i=0;arg[i]!=nullptr;i++){if(strcmp(arg[i],">")==0){file=arg[i+1];arg[i]=nullptr;}}}if(how==in_redirect){for(i=0;arg[i]!=nullptr;i++){if(strcmp(arg[i],"<")==0){file=arg[i+1];arg[i]=nullptr;}}}if(how==have_pipe){for(i=0;arg[i]!=nullptr;i++){if(strcmp(arg[i],"|")==0){arg[i]=nullptr;int j;for(j=i+1;arg[j]!=nullptr;j++)argnext[j-i-1]=arg[j];argnext[j-i-1]=arg[j];break;}}}if((pid=fork())<0){printf("fork error\n");return;}switch (how){case 0:if(pid==0){if(!(find_command(arg[0]))){printf("%s:command not fount\n",arg[0]);exit(0);}execvp(arg[0],arg);exit(0);}break;case 1:if(pid==0){if(!(find_command(arg[0]))){printf("%s:command not fount\n",arg[0]);exit(0);}fd=open(file,O_RDWR|O_CREAT|O_TRUNC,0644);dup2(fd,1);execvp(arg[0],arg);exit(0);}break;case 2:if(pid==0){if(!(find_command(arg[0]))){printf("%s:command not fount\n",arg[0]);exit(0);}fd=open(file,O_RDONLY);dup2(fd,0);execvp(arg[0],arg);exit(0);}break;case 3:if(pid==0){pid_t pid2;int status2;int fd2;if((pid2=fork())<0){printf("fork2 error\n");return;}else if(pid2==0){if(!(find_command(arg[0]))){printf("%s:command not fount\n",arg[0]);exit(0);}fd2=open("/tmp/youdonotknowfile",O_WRONLY|O_CREAT|O_TRUNC,0644);dup2(fd2,1);execvp(arg[0],arg);exit(0);}if(waitpid(pid2,&status2,0)==-1)printf("wait for child process error\n");if(!(find_command(arg[0]))){printf("%s:command not fount\n",arg[0]);exit(0);}fd2=open("/tmp/youdonotknowfile",O_RDONLY);dup2(fd,0);execvp(argnext[0],argnext);if(remove("/tmp/youdonotknowfile"))printf("remove error");exit(0);}break;default:break;}if(background==1){printf("[process id %d]\n",pid);return;}if(waitpid(pid,&status,0)==-1)printf("wait for child process eerror\n");
}

代码里没有注释的地方是在这里具体说明,单靠注释应该还是很难说清的

首先是这段代码

//查看命令里是否有重定向符号for(i=0;arg[i]!=nullptr;i++){if(strcmp(arg[i],">")==0){flag++;how=out_redirect;if(arg[i+1]==nullptr)flag++;}if(strcmp(arg[i],"<")==0){flag++;how=in_redirect;if(i==0)flag++;}if(strcmp(arg[i],"|")==0){flag++;how=have_pipe;if(arg[i+1]==nullptr)flag++;if(i==0)flag++;}}if(flag>1){printf("wrong command\n");return;}

首先要说明,本程序只能支持处理一个重定向符,所以既有输入重定向又有输出重定向是错误的,这是最底下flag>1的一个意思,即重定向符太多了。

但是代码里还有其他地方也有flag++的操作,这是为什么?

这需要一点点linux重定向的知识。你光写个>是没用的,肯定是要加文件 比如这样的命令:

echo "hello" > test.txt

所以像 '>'后面就不能是空了

还有像管道符,它肯定不能是命令的第一个。

不过这个示例代码里,我怀疑它是故意的,>也不能是第一个吧,<后面我也不知道可以不跟文件名的命令,所以最好是把管道那段的判断替换<和>。

所以如果出现这种错误,flag就会超过1,然后报命令格式错误。

然后是这里

if(how==out_redirect){for(i=0;arg[i]!=nullptr;i++){if(strcmp(arg[i],">")==0){file=arg[i+1];//得到文件名arg[i]=nullptr;}}}if(how==in_redirect){for(i=0;arg[i]!=nullptr;i++){if(strcmp(arg[i],"<")==0){file=arg[i+1];//得到文件名arg[i]=nullptr;}}}if(how==have_pipe){for(i=0;arg[i]!=nullptr;i++){if(strcmp(arg[i],"|")==0){arg[i]=nullptr;int j;for(j=i+1;arg[j]!=nullptr;j++)//把管道符后面的全都存到argnext里来argnext[j-i-1]=arg[j];argnext[j-i-1]=arg[j];break;}}}

这段还是能理解的,主要是获得文件名,文件名 自然是跟在重定向符后面了。(你看这里的<后面就跟文件名了,所以我说上面有问题)

这里设置arg[i]=nullptr我估计是为了减少遍历吧,示例代码里for循环的终止条件都是arg[i]!=nullptr 处理完这部分在此处设为nullptr,这样后面遍历就不用遍历到这里了、

对于管道,像一般的管道命令:echo "Hello, World!" | grep "Hello"

实际上相当于两个shell命令,所以会用到子进程来操作(实际上整个命令的处理都是要用子进程),那么就要单独把管道符后面的命令先存起来了

最后是这么一长串

    //创建子进程if((pid=fork())<0){printf("fork error\n");return;}switch (how)//根据how来进行不同的处理{case 0://命令符中不含重定向和管道if(pid==0){//pid=0是子进程if(!(find_command(arg[0]))){//看这个命令是不是系统有的printf("%s:command not fount\n",arg[0]);exit(0);}execvp(arg[0],arg);//***exit(0);}break;case 1://有输出重定向if(pid==0){if(!(find_command(arg[0]))){printf("%s:command not fount\n",arg[0]);exit(0);}fd=open(file,O_RDWR|O_CREAT|O_TRUNC,0644);//向文件里写dup2(fd,1);execvp(arg[0],arg);exit(0);}break;case 2://有输入重定向if(pid==0){if(!(find_command(arg[0]))){printf("%s:command not fount\n",arg[0]);exit(0);}fd=open(file,O_RDONLY);//只需要从文件里读,所以只用O_RDONLYdup2(fd,0);execvp(arg[0],arg);exit(0);}break;case 3://有管道if(pid==0){pid_t pid2;int status2;int fd2;//再创建一个子进程if((pid2=fork())<0){printf("fork2 error\n");return;}else if(pid2==0){//这是管道的子进程了if(!(find_command(arg[0]))){printf("%s:command not fount\n",arg[0]);exit(0);}fd2=open("/tmp/youdonotknowfile",O_WRONLY|O_CREAT|O_TRUNC,0644);dup2(fd2,1);execvp(arg[0],arg);exit(0);}if(waitpid(pid2,&status2,0)==-1)printf("wait for child process error\n");if(!(find_command(arg[0]))){printf("%s:command not fount\n",arg[0]);exit(0);}fd2=open("/tmp/youdonotknowfile",O_RDONLY);dup2(fd,0);execvp(argnext[0],argnext);if(remove("/tmp/youdonotknowfile"))printf("remove error");exit(0);}break;default:break;}if(background==1){printf("[process id %d]\n",pid);return;}if(waitpid(pid,&status,0)==-1)printf("wait for child process error\n");
}

最主要的代码无疑是execvp(arg[0],arg);这是什么意思呢?

其实在《Linux C编程实战》笔记:进程操作之退出,执行,等待-CSDN博客 出现过,不过还是稍微解释一下

execvp是让子进程执行另一个程序,那这个程序是啥?就是我们给出的参数arg[0]所指定的可执行文件,比如说

char *args[] = {"ls", "-l", NULL}; execvp("ls", args);

那子进程就会执行ls命令。

那arg[0]其实就是我们的指令了,arg也是我们指令的具体参数,所以子进程会帮我们执行指令。

还要再解释一下,execvp会先在当前路径下找有没有名为arg[0]的可执行文件,没有的话会在环境变量下找,而默认的环境变量是包含/bin的,所以为什么我们要先find_command。而像shell命令都是已经存好了的,无需我们再写一个。这样执行execvp会到/bin下执行一般的shell命令。

dup2函数在《Linux C编程实战》笔记:一些系统调用-CSDN博客有讲解,不知道的可以去看看

当然如果连open调用也不知道的话可以看这里《Linux C编程实战》笔记:文件读写-CSDN博客

 有个疑问是怎么就实现了输入输出的重定向?

dup2(fd, 0): 这一行代码将文件描述符 fd 复制到标准输入文件描述符 0 上。这意味着标准输入现在被重新定向到你打开的文件 file

答案就在这个dup2里,0是默认的标准输入文件描述符,自然1是标准输出文件描述符。我们的fd已经打开了指定的文件,然后通过dup2就指定了程序的标准输入输出重定向

如果你想将程序的输出重定向到一个文件,你可以使用类似如下的代码:

int fd = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (fd == -1) {perror("open");exit(EXIT_FAILURE);
}// 将标准输出重定向到文件描述符 fd
dup2(fd, 1);// 执行输出操作,结果将写入 output.txt 文件
printf("This will be written to output.txt\n");// 关闭文件描述符 fd
close(fd);

虽然execvp更改了子进程执行的程序,但是标准输入输出是保持的,这也是为什么在输入重定向的例子中,通过 dup2 将文件描述符复制到标准输入后,execvp 执行的新程序将从这个文件描述符读取输入

至于管道那部分,实际上就是创建了一个子进程,让子进程先执行管道符|前面部分的命令,同时使用输出重定向把结果放到一个临时文件里,等子进程结束再在父进程执行|后面的命令,同时使用输入重定向从那个临时文件里读取之前运行的结果。最后把临时文件删除了。

至于waitpid这个函数,直接看《Linux C编程实战》笔记:进程操作之退出,执行,等待-CSDN博客有讲,内容太多我就不复述了。

find_command函数

主要就是目录的遍历

目录遍历不会的看这里《Linux C编程实战》笔记:目录操作-CSDN博客

int find_command(char *command){DIR *dp;struct dirent *dirp;const char *path[]={"./","/bin","/usr/bin",nullptr};//从这几个目录里找//这个意思是像 ./a.out这种命令,就不用看./了,所以加了2if(strncmp(command,"./",2)==0)command=command+2;int i=0;while (path[i]!=nullptr){if((dp=opendir(path[i]))==nullptr)printf("can not open /bin \n");while ((dirp=readdir(dp))!=nullptr){if(strcmp(dirp->d_name,command)==0){//找到了closedir(dp);return 1;}}closedir(dp);i++;}return 0;//没找到
}

最后打了半天的代码,也是幸好能成功运行

源码copy

最后把所有代码一整个都放在这,不过是没有注释的,方便大家直接copy

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<dirent.h>
#define normal 0
#define out_redirect 1
#define in_redirect 2
#define have_pipe 3
void print_prompt();
void get_input(char *);
void explain_input(char *,int *,char [][256]);
void do_cmd(int,char [][256]);
int find_command(char *);
int main(int argc,char **argv){int i;int argcount=0;char arglist[100][256];char **arg=nullptr;char *buf=nullptr;//buf=new char[256];buf=(char *)malloc(256);if(buf==nullptr){perror("malloc failed");exit(-1);}while (1){memset(buf,0,256);print_prompt();get_input(buf);if(strcmp(buf,"exit\n")==0||strcmp(buf,"logout\n")==0)break;for(i=0;i<100;i++)arglist[i][0]='\0';argcount=0;explain_input(buf,&argcount,arglist);do_cmd(argcount,arglist);}if(buf!=nullptr){free(buf);buf=nullptr;}exit(0);
}
void print_prompt(){printf("myshell$$ ");
}
void get_input(char *buf){int len=0;char ch;ch=getchar();while (len<256&&ch!='\n'){buf[len++]=ch;ch=getchar();}if(len==256){printf("command is too long \n");exit(-1);}buf[len]='\n';len++;buf[len]='\0';
}void explain_input(char *buf,int *argcount,char arglist[][256]){char *p=buf;char *q=buf;int number=0;while (1){if(p[0]=='\n') break;if(p[0]==' ') p++;else{q=p;number=0;while (q[0]!=' '&&q[0]!='\n'){number++;q++;}strncpy(arglist[*argcount],p,number+1);arglist[*argcount][number]='\0';(*argcount)++;p=q;}}
}
void do_cmd(int argcount,char arglist[][256]){int flag=0;int how=0;int background=0;int status;int i;int fd;char *arg[argcount+1];char *argnext[argcount+1];char *file;pid_t pid;for(i=0;i<argcount;i++){arg[i]=(char *)arglist[i];}arg[argcount]=nullptr;for(i=0;i<argcount;i++){if(strncmp(arg[i],"&",1)==0){if(i==argcount-1){background=1;arg[argcount-1]=nullptr;break;}else{printf("wrong command\n");return;}}}for(i=0;arg[i]!=nullptr;i++){if(strcmp(arg[i],">")==0){flag++;how=out_redirect;if(arg[i+1]==nullptr)flag++;}if(strcmp(arg[i],"<")==0){flag++;how=in_redirect;if(i==0)flag++;}if(strcmp(arg[i],"|")==0){flag++;how=have_pipe;if(arg[i+1]==nullptr)flag++;if(i==0)flag++;}}if(flag>1){printf("wrong command\n");return;}if(how==out_redirect){for(i=0;arg[i]!=nullptr;i++){if(strcmp(arg[i],">")==0){file=arg[i+1];arg[i]=nullptr;}}}if(how==in_redirect){for(i=0;arg[i]!=nullptr;i++){if(strcmp(arg[i],"<")==0){file=arg[i+1];arg[i]=nullptr;}}}if(how==have_pipe){for(i=0;arg[i]!=nullptr;i++){if(strcmp(arg[i],"|")==0){arg[i]=nullptr;int j;for(j=i+1;arg[j]!=nullptr;j++)argnext[j-i-1]=arg[j];argnext[j-i-1]=arg[j];break;}}}if((pid=fork())<0){printf("fork error\n");return;}switch (how){case 0:if(pid==0){if(!(find_command(arg[0]))){printf("%s:command not fount\n",arg[0]);exit(0);}execvp(arg[0],arg);exit(0);}break;case 1:if(pid==0){if(!(find_command(arg[0]))){printf("%s:command not fount\n",arg[0]);exit(0);}fd=open(file,O_RDWR|O_CREAT|O_TRUNC,0644);dup2(fd,1);execvp(arg[0],arg);exit(0);}break;case 2:if(pid==0){if(!(find_command(arg[0]))){printf("%s:command not fount\n",arg[0]);exit(0);}fd=open(file,O_RDONLY);dup2(fd,0);execvp(arg[0],arg);exit(0);}break;case 3:if(pid==0){pid_t pid2;int status2;int fd2;if((pid2=fork())<0){printf("fork2 error\n");return;}else if(pid2==0){if(!(find_command(arg[0]))){printf("%s:command not fount\n",arg[0]);exit(0);}fd2=open("/tmp/youdonotknowfile",O_WRONLY|O_CREAT|O_TRUNC,0644);dup2(fd2,1);execvp(arg[0],arg);exit(0);}if(waitpid(pid2,&status2,0)==-1)printf("wait for child process error\n");if(!(find_command(arg[0]))){printf("%s:command not fount\n",arg[0]);exit(0);}fd2=open("/tmp/youdonotknowfile",O_RDONLY);dup2(fd,0);execvp(argnext[0],argnext);if(remove("/tmp/youdonotknowfile"))printf("remove error");exit(0);}break;default:break;}if(background==1){printf("[process id %d]\n",pid);return;}if(waitpid(pid,&status,0)==-1)printf("wait for child process error\n");
}
int find_command(char *command){DIR *dp;struct dirent *dirp;const char *path[]={"./","/bin","/usr/bin",nullptr};if(strncmp(command,"./",2)==0)command=command+2;int i=0;while (path[i]!=nullptr){if((dp=opendir(path[i]))==nullptr)printf("can not open /bin \n");while ((dirp=readdir(dp))!=nullptr){if(strcmp(dirp->d_name,command)==0){closedir(dp);return 1;}}closedir(dp);i++;}return 0;
}

这篇关于《Linux C编程实战》笔记:实现自己的myshell的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/567032

相关文章

Linux使用cut进行文本提取的操作方法

《Linux使用cut进行文本提取的操作方法》Linux中的cut命令是一个命令行实用程序,用于从文件或标准输入中提取文本行的部分,本文给大家介绍了Linux使用cut进行文本提取的操作方法,文中有详... 目录简介基础语法常用选项范围选择示例用法-f:字段选择-d:分隔符-c:字符选择-b:字节选择--c

Nginx实现动态封禁IP的步骤指南

《Nginx实现动态封禁IP的步骤指南》在日常的生产环境中,网站可能会遭遇恶意请求、DDoS攻击或其他有害的访问行为,为了应对这些情况,动态封禁IP是一项十分重要的安全策略,本篇博客将介绍如何通过NG... 目录1、简述2、实现方式3、使用 fail2ban 动态封禁3.1 安装 fail2ban3.2 配

Java中实现订单超时自动取消功能(最新推荐)

《Java中实现订单超时自动取消功能(最新推荐)》本文介绍了Java中实现订单超时自动取消功能的几种方法,包括定时任务、JDK延迟队列、Redis过期监听、Redisson分布式延迟队列、Rocket... 目录1、定时任务2、JDK延迟队列 DelayQueue(1)定义实现Delayed接口的实体类 (

将java程序打包成可执行文件的实现方式

《将java程序打包成可执行文件的实现方式》本文介绍了将Java程序打包成可执行文件的三种方法:手动打包(将编译后的代码及JRE运行环境一起打包),使用第三方打包工具(如Launch4j)和JDK自带... 目录1.问题提出2.如何将Java程序打包成可执行文件2.1将编译后的代码及jre运行环境一起打包2

Java使用Tesseract-OCR实战教程

《Java使用Tesseract-OCR实战教程》本文介绍了如何在Java中使用Tesseract-OCR进行文本提取,包括Tesseract-OCR的安装、中文训练库的配置、依赖库的引入以及具体的代... 目录Java使用Tesseract-OCRTesseract-OCR安装配置中文训练库引入依赖代码实

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

MySQL8.0设置redo缓存大小的实现

《MySQL8.0设置redo缓存大小的实现》本文主要在MySQL8.0.30及之后版本中使用innodb_redo_log_capacity参数在线更改redo缓存文件大小,下面就来介绍一下,具有一... mysql 8.0.30及之后版本可以使用innodb_redo_log_capacity参数来更改

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定