过去两周我一直试图让这个工作无效。 我有一个项目来创建一个实现解析和内置命令的shell。我遇到的问题是当我将char *传递给我的解析函数并返回时,当我尝试访问它的任何部分时,我得到了一个段错误。我已经尝试了不同的方法,包括一个持有char **的结构都有相同的问题,所以我猜测它是我的解析器的问题。我将不胜感激任何帮助。 parser.c的代码:
#define BUFSIZE 1024
#define TOK_BUFSIZE 64
#define TOK_DELIM " \t\r\n\a"
char*** Parse(char *line0){
char* null_ptr = 0;
char*** cmd = malloc(MAX_SIZE * sizeof(char**));
/*
char arg[] = argument
char* argv[] = argument array
char** cmd[] = array of argument arrays
*/
int bufsize = MAX_SIZE, cmdp = 0, argp = 0, com = FALSE, redir = FALSE;
char *token;
char* line = malloc(100*sizeof(char));
strcpy(line,line0);
token = strtok(line, TOK_DELIM);
while (token){
if (*token == ';'){ // new command string
char* tmp1 = malloc(BUFSIZE * sizeof(char));
char** tmpa = malloc(BUFSIZE * sizeof(char*));
strcpy(tmp1, token);
tmp1[sizeof(token)] = null_ptr;
tmpa[0]=tmp1;
cmd[cmdp] = tmpa;
argp = 0;
cmdp++;
com = FALSE;
redir = FALSE;
}
else if (*token == '>' || *token == '<' || token == ">>"){ // redirects
argp = 0;
char* tmp1 = malloc(BUFSIZE * sizeof(char));
char** tmpa = malloc(BUFSIZE * sizeof(char*));
strcpy(tmp1, token);
tmp1[sizeof(token)] = null_ptr;
tmpa[argp]=tmp1;
argp++;
printf("Redirect: %s\n",tmp1);
com = FALSE;
redir = TRUE;
}
else if (*token == '|'){ // pipe
printf("PIPE\n");
cmdp++;
argp = 0;
com = FALSE;
}
else if (redir){ // redirect file name
// redirect token stored in arg[]
char* tmp1 = malloc(BUFSIZE * sizeof(char));
char** tmpa = malloc(BUFSIZE * sizeof(char*));
strcpy(tmp1, token);
tmp1[sizeof(token)] = null_ptr;
tmpa[argp]=tmp1;
cmd[cmdp]=tmpa;
argp = 0;
cmdp++;
redir = FALSE;
com = FALSE;
printf("File: %s\n", token);
}
else if (token == "&") // background
{
cmdp++;
argp = 0;
char* tmp1 = malloc(BUFSIZE * sizeof(char));
char** tmpa = malloc(BUFSIZE * sizeof(char*));
strcpy(tmp1, token);
tmp1[sizeof(token)] = null_ptr;
tmpa[0]=tmp1;
cmd[cmdp]=tmpa;
printf("Background");
}
else if (!com && !redir){ // command entered
argp = 0;
char* tmp1 = malloc(BUFSIZE * sizeof(char));
char** tmpa = malloc(BUFSIZE * sizeof(char*));
strcpy(tmp1, token);
tmp1[sizeof(token)] = null_ptr;
tmpa[argp] = tmp1;
argp++;
printf("Command %s\n", token);
com = TRUE;
}
else if (com){ // argument to command, all other redirects and pipes taken care of
char* tmp1 = malloc(BUFSIZE * sizeof(char));
char** tmpa = malloc(BUFSIZE * sizeof(char*));
strcpy(tmp1, token);
tmp1[sizeof(token)] = null_ptr;
tmpa[argp] = tmp1;
argp++;
printf("Argument: %s\n", token);
//cmd[cmdp] = argv; // save current working argument array
//cmdp++;
}
// end of if else statements
token = strtok(NULL, TOK_DELIM);
} // end of while
cmdp++;
cmd[cmdp] = NULL;
return &cmd;
}
答案 0 :(得分:0)
当我在命令行上编译代码时输入:
gcc /path/to/yourcodefilename.c -Wall -Wextra
但是将/path/to/yourcodefilename.c
替换为包含最终调用函数的main函数的代码的实际文件名(我的文件是test2.c),我收到了警告。第一个是:
./test2.c:21: error: 'aaa' undeclared (first use in this function)
./test2.c:21: error: (Each undeclared identifier is reported only once
./test2.c:21: error: for each function it appears in.)
我收到了其中一些。 &#34; AAA&#34;被您在函数中使用的以前未定义的内容命名。这包括单词TRUE和FALSE。要更正此问题,您可以在程序的顶部使用:
#define FALSE n
#define TRUE y
其中n和y分别代表false和true。另一种纠正方法是包含包含&#34; TRUE&#34;的定义的头文件。和&#34;错误&#34;。
我在几行中注意到的第二件事是:
warning: assignment makes integer from pointer without a cast
确保您不会将数据从一种类型转换为另一种类型。例如,不要将字符变量设置为指针值。
例如,更改:
tmp1[sizeof(token)] = null_ptr;
为:
tmp1[sizeof(token)] = '\0';
因为指定char*
的索引意味着指定char
,而null_ptr的类型为char*
且char*
和char
不相同。我所做的是分配了一个char
的空值。
我希望这有助于您进行一些故障排除
答案 1 :(得分:0)
这里有几个问题:
您分配cmd
及其子阵列。您在函数末尾返回该数组的地址。地址的类型为char ****
,这不是正确的返回类型。更糟糕的是:该地址是局部变量的地址,在返回后立即超出范围。返回从malloc
获得的句柄:
char ***Parse(char *line0)
{
char ***cmd = malloc(MAX_SIZE * sizeof(*cmd));
// fill cmd
return cmd;
}
您的代码不必要很长,主要是因为您编写了分配内存,复制字符串并显式空终止它的步骤。 (其他人已经指出你没有正确地进行空终止。你也分配了一个固定大小的1024字节的实际字符串长度,这是非常浪费的。)你可以写一个函数来复制字符串或使用非标准但广泛可用的strdup
;这将使您的代码更易于阅读。
所有临时分配都难以遵循。例如,在分支if (!com && !redir)
中,您分配给tmpa
,但您从未将该值存储在cmd
中。重定向分支也是如此。
启动新命令时也不清楚。在解析第一个令牌之前,遇到管道之后或遇到分号之后应该有一个新命令。您还可以启动重定向和背景&符号的新命令。
比较token == ">>"
始终为false:token
是line
中的地址,">>"
是存储在静态内存中的字符串文字。您应该使用strcmp
来比较两个字符串。
通常,您希望在cmdp
增加时分配新列表。在这种情况下,argp
将重置为零。否则,您只需附加到当前命令。
我认为通过将一切都视为特殊而使事情复杂化。我建议简化代码并暂时保留重定向和背景。调用命令时可以轻松解析它们。 (您的代码使用redir
和com
设置状态,但它在重定向后从不强制执行文件名。例如,当所有令牌都到位时,您可以轻松地执行此操作。)
下面的代码只将管道和分号视为命令分隔符。当命令是管道时,管道令牌将添加到以下命令之前:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define MAX_SIZE 32
#define TOK_DELIM " \t\r\n\a"
char *sdup(const char *str)
{
size_t len = strlen(str);
char *dup = malloc(len + 1);
if (dup) {
memcpy(dup, str, len);
dup[len] = '\0';
}
return dup;
}
char ***parse(char *line0)
{
char *token;
char *line = sdup(line0);
token = strtok(line, TOK_DELIM);
if (token == NULL) return NULL;
char ***cmd = malloc(MAX_SIZE * sizeof(char **));
int cmdp = 0;
int argp = 0;
cmd[0] = malloc(MAX_SIZE * sizeof(*cmd[0]));
while (token) {
if (strcmp(token, ";") == 0 || strcmp(token, "|") == 0) {
// begin new command
cmd[cmdp][argp++] = NULL;
cmdp++;
if (cmdp + 1 == MAX_SIZE) break;
argp = 0;
cmd[cmdp] = malloc(MAX_SIZE * sizeof(*cmd[0]));
// prepend pipe token
if (*token == '|') {
cmd[cmdp][argp++] = sdup(token);
}
} else {
// append to current command
if (argp + 1 < MAX_SIZE) {
cmd[cmdp][argp++] = sdup(token);
}
}
token = strtok(NULL, TOK_DELIM);
}
// null-terminate arg and cmd lists
cmd[cmdp][argp] = NULL;
cmdp++;
cmd[cmdp] = NULL;
return cmd;
}
int main()
{
char ***cmd = parse("echo start ; ls -l | wc > output ; echo stop");
char ***p = cmd;
while (*p) {
char **q = *p;
while (*q) {
printf("'%s' ", *q);
free(*q);
q++;
}
puts("");
free(*p);
p++;
}
free(cmd);
return 0;
}
进一步评论:
我不确定当前格式是否适合该任务。最好有一个树结构来处理管道,分号以及&&
和||
,然后让叶节点包含参数链接列表的命令。
使用strtok
进行标记需要在所有标记之间使用空格,但标点符号通常可以在没有显式空格的情况下编写,例如:"./a.out>kk&"
。所以你需要一种更好的解析方法。
目前,您为每个字符串分配空间,以后必须释放。如果您创建一个令牌结构,将令牌描述为原始字符串的只读视图,则可以不进行分配。但是,视图不是以空值终止的,因此您需要比较一下这对于启动指针和长度的工作,例如。