【问题标题】:Seg fault while parsing a line into ***char将一行解析为 ***char 时出现段错误
【发布时间】:2015-01-21 09:23:15
【问题描述】:

我的代码应该将一个字符数组解析为 ***char,因此它首先将其拆分为 '|' char 然后由空格、换行符等转换成单词。示例输入/输出:

I = ls -l | sort | unique

O =

  *cmds[1] = {"ls", "-l", NULL};
  *cmds[2] = {"sort", NULL};
  *cmds[3] = {"unique", NULL};

上面是指向char数组的指针,所以按单词分割,下面是***char,上面有指向上面指针的指针

  char **cmds[] = {1, 2, 3, NULL};

现在,我没有看到我的错误(可能是因为我对 C 语言不太熟练),但是程序在我从 parsePipe() 内部调用 parse(..) 函数的第二次给出了段错误。有人可以帮忙吗?

void parse(char *line, char **argv)
{
        while (*line != '\0') {     
        while (*line == ' ' || *line == '\t' || *line == '\n')
            *line++ = '\0'; 
        *argv++ = line;     
        while (*line != '\0' && *line != ' ' && *line != '\t' && *line != '\n'){
            line++;
        }
    }
    *argv = '\0';  
}

void parsePipe(char *line, char ***cmds)
{
    char *cmd = strtok(line, "|");
    int word_counter = 0;

    while (cmd != NULL) 
    {
       printf("Printing word -> %s\n", cmd);
       word_counter++; 
       parse(cmd, *cmds++);

       cmd = strtok(NULL, "|");
    }

    printf("This string contains %d words separated with |\n",word_counter);
}


void  main(void)
{
    char  line[1024];          
    char  **cmds[64]; 
    while (1) {    
        printf("lsh -> ");   
        gets(line);     
        printf("\n");
        parsePipe(line, cmds);
    }
}

【问题讨论】:

  • 这是什么? char *1[] = {"ls", "-l", NULL};你不能命名一个以数字开头的变量。
  • 这不是代码,它是一个示例 i/o 来展示它应该是什么样子..
  • argv 按照惯例是char**。不要把椅子称为桌子。
  • 您的程序是否打印任何输出?在您认为它崩溃之前尝试刷新缓冲区并查看它打印的内容。
  • 评论者应该如何区分带有意外错误的代码和带有故意错误的代码?

标签: c parsing pointers char segmentation-fault


【解决方案1】:

[评论太长]

这一行

*argv++ = line; /* with char ** argv */

指的是无效的内存,就像代码中的 *argv[n] (with char **argv[64]) 一样,它什么都不引用。

你使用的命名并没有让生活更轻松。

尝试以下命名:

void parse(char *line, char **cmd)
{
    while (*line != '\0') {     
        while (*line == ' ' || *line == '\t' || *line == '\n')
            *line++ = '\0'; 
        *cmd++ = line;     
        while (*line != '\0' && *line != ' ' && *line != '\t' && *line != '\n'){
            line++;
        }
    }
    *argv = '\0';  
}

void parsePipe(char *line, char ***cmdline)
{
    char *cmd = strtok(line, "|");
    int word_counter = 0;

    while (cmd != NULL) 
    {
       printf("Printing word -> %s\n", cmd);
       word_counter++; 
       parse(cmd, *cmdline++);

       cmd = strtok(NULL, "|");
    }

    printf("This string contains %d words separated with |\n",word_counter);
}


void  main(void)
{
    char  line[1024];          
    char  **cmdline[64]; 
    while (1) {    
        printf("lsh -> ");   
        gets(line);     
        printf("\n");
        parsePipe(line, cmdline);
    }
}

cmds 使用的内存都没有被分配。

所以

*cmd++ = line;

失败,因为cmd 没有指向任何地方,但是被取消引用并且代码尝试写入它指向的地方,这是无处可去的,这是无效的内存。


可以通过将char*** 传递给parse(char *** pcmd) 并计算找到的令牌来解决此问题

size_t nlines = 0;

...

++nlines.

和做一个

*pcmd = realloc(*pcmd, nlines + 1); /* Allocate one more as needed to later find the end of the array. */
(*pcmd)[nlines -1] = line;
(*pcmd)[nlines] = NULL; /* Initialise the stopper, marking the end of the array. */

对于找到的每个令牌。

显然你需要这样称呼它:

 parse(cmd, cmdline++);

为了完成所有这些工作,初始数组需要正确初始化(无论如何你都应该这样做):

char  **cmdline[64] = {0};

【讨论】:

  • 非常感谢,还有一件事 - 你使用 realloc,我们不应该先 malloc 变量吗?如果是这样,那么用什么,nlines = 0?
  • @user3885166:做void * p = NULL; p = realloc(p, 42);void * p = malloc(1); p = reallc(p, 42);甚至void * p = realloc(NULL, 42);是一样的
  • @user3885166:对于nlines = 0,您可以跳过realloc,只需将NULL 分配给*pcmd,或者如果从一开始就已经初始化为NULL,则什么也不做。如果您的编译器支持,您也可以使用(*pcmd) = realloc(pcmd, 0);,但请注意,对于后一种情况,您需要调用free(*pcmd)。如果您定义一个由*pcmd = NULL 指示的空“command”,则不需要这样做。
  • 不幸的是,当我使用 *pcmd = NULL 时,它又给了我段错误,所以这不太正确。我正确初始化了 **cmdline[64] = {0},但是一旦它到达 parse(),它就会再次出现段错误。
  • 使用调试器来跟踪您的代码并检查所有相关变量的值。
猜你喜欢
  • 2021-10-16
  • 1970-01-01
  • 2021-12-20
  • 1970-01-01
  • 2015-08-02
  • 2014-08-19
  • 2012-10-08
  • 1970-01-01
  • 2015-06-12
相关资源
最近更新 更多