Linux C项目 | 迷你命令行解释器 (2)
2026/9/3 20:35:11 网站建设 项目流程

模块二实现:split_line( ) 拆分命令和参数

模块一已经实现了获取一整行字符串命令,但是后面执行程序的时候并不是直接拿这一整行字符串去执行程序,而是需要得到一个一个的参数, 如:输入命令"ls -l /home" → 得到 "ls","-l" ,"/home" 参数,使用数组去存储它们

这时候需要考虑:这一个一个的参数应该怎么存放?

字符串也就是字符数组,存放到内存使用 char * 可以表示一个字符串。得到的多个命令参数即多个字符串,也就相当于多个char * 。现在要把这多个char * (char类型的指针)存放起来,并且要方便管理访问 → 可以使用指针数组将这些char * 保存起来,数组的每个元素是 char * ,然后需要有一个指针去指向首元素方便管理访问,即 char ** 一个指向char * 类型的指针

画草图理解一下

设计split_line( ) → 传入用户输入的命令行字符串 → 按照指定分隔符切分成多个参数保存 → 返回指向保存这些字符指针的数组指针

1.需要用到C标准库提供的切割函数strtok

strtok(line," ") → 按空格切割,返回值是切出来的字符串首地址,没有内容了返回NULL

使用方法

char *token; // 需要字符指针去接收切割出来的字符串首地址 char line[] = "ls -l /home"; token = strtok(line," "); // 按空格切割 token → "ls" token = strtok(NULL," "); // token → "-l" token = strtok(NULL," "); // token → "/home"

注意:第一次调用传入line,接下去要传NULL,意思是继续切刚才那个字符串

优化一下,循环到切完

char *token; // 需要字符指针去接收切割出来的字符串首地址 char line[] = "ls -l /home"; token = strtok(line," "); // 按空格切割 token → "ls" while(c != NULL){ token = strtok(NULL," "); }

模块二第一版核心代码:

char **split_line(char *line){ // 形参char *line 用于接收传入的命令字符串首地址 返回值类型是char **,返回一个指向char *数组的指针 #define TOK_BUFFER_SIZE 64 // 宏定义参数个数缓冲区大小 64即可,因为参数个数不会很多 int bursize = TOK_BUFFER_SIZE; // 初始化缓冲区 用一个变量去接收 因为不够用的时候需要扩容 char *token = NULL; // 用于接收切割出来的字符首地址 int position = 0; // 记录存放位置 // malloc一块存放字符指针的内存 char **tokens = malloc(sizeof(char *) * bursize); // 用指向char *的指针变量去接收 // 有mallc就要进行检查 if(!tokens){ fputs("malloc申请内存失败\n",stderr); // 因为不需要格式化输出 这里使用fputs输出到标准错误流 exit(EXIT_FAILURE); // 异常退出 } // 切割 token = strtok(line," "); // 按空格切割命令字符串 返回切割出来的字符首地址,如果没有内容返回NULL // 后续的字符串循环切割 while(token != NULL){ // 没切割完就继续循环切割 // 存放 先存放上次切割出来的字符串 tokens[position] = token; position++; // 继续后续切割 token = strtok(NULL," "); } return tokens; // 返回指针数组 }

核心逻辑实现了,还有以下问题:

1.指针数组没有置空NULL,无法判断结束

2.还未处理超过64个参数的越界问题

3.目前只能处理空格分隔的命令

修改后的代码:

char **split_line(char *line){ // 形参char *line 用于接收传入的命令字符串首地址 返回值类型是char **,返回一个指向char *数组的指针 #define TOK_BUFFER_SIZE 64 // 宏定义初始参数指针容量为64,不够时动态扩容 #define TOK_DELIM " \t\r\n\a" // 宏定义所有分隔符 int bufsize = TOK_BUFFER_SIZE; // 初始化缓冲区 用一个变量去接收 因为不够用的时候需要扩容 char *token = NULL; // 用于接收切割出来的字符串首地址 int position = 0; // 记录存放位置 // malloc一块存放字符指针的内存 char **tokens = malloc(sizeof(char *) * bufsize); // 用指向char *的指针变量去接收 // 有mallc就要进行检查 if(!tokens){ fputs("malloc申请内存失败\n",stderr); // 因为不需要格式化输出 这里使用fputs输出到标准错误流 exit(EXIT_FAILURE); // 异常退出 } // 1.切割 token = strtok(line,TOK_DELIM); // 按指定分隔符切割命令字符串 返回切割出来的字符首地址,如果没有内容返回NULL // 后续的字符串循环切割 while(token != NULL){ // 没切割完就继续循环切割 // 2.存放 先存放上次切割出来的字符串 tokens[position] = token; position++; // 数组满了进行扩容 if(position >= bufsize){ bufsize += TOK_BUFFER_SIZE; tokens = realloc(tokens,bufsize * sizeof(char *)); if(!tokens){ fputs("realloc申请内存失败\n",stderr); exit(EXIT_FAILURE); } } // 继续后续切割 token = strtok(NULL,TOK_DELIM); } // 存放完后 将最后一个元素置空 才能够判断数组结束 tokens[position] = NULL; return tokens; // 返回指针数组 }

注意:realloc的时候,如果申请失败的话返回NULL,tokens的指针指向NULL,这样的话原来指向的内存地址就丢失了,会发生内存泄漏,因为realloc失败不会释放旧内存

项目代码realloc失败之后就马上结束进程,回收进程占用的内存包括泄漏的那块就内存,所以不会造成太大问题

模块二已设计开发完成,梳理一下链路:

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询