【问题标题】:Segfault while using Strtok() in C在 C 中使用 Strtok() 时出现段错误
【发布时间】:2011-03-05 22:04:48
【问题描述】:

我正在使用 C 编写自己的 shell 并处理流重定向(">" 和 "

#include <stdio.h>
#include <stdlib.h>
#include <readline/readline.h>
#include <readline/history.h>

int main (){
    char *command, *mypath, *buffer, *arglist[1024], *pathlist[1024],
        **ap, *carrotfile1, *carrotfile2;
    char* tokenPtr = malloc(1024);
    buffer = malloc(1024);
    carrotfile1 = malloc(1024);
    carrotfile2 = malloc(1024);
    int loop = 1, code = 0, fail = 0;

    while (loop == 1){
        int argnum = 0, pathnum = 0;
        mypath = malloc(1024);
        if(getenv("MYPATH") == NULL)
            strcpy(mypath, "/bin#.");
        else
            strcpy(mypath, getenv("MYPATH"));
        printf("myshell$ ");
        command = readline("");

        if(strcmp(command, "exit") == 0 || strcmp(command, "quit") == 0)
            return 0;

        if(strcmp(command, "") == 0)
            continue;

        /*Tokenizes Command*/
        /*
          Code 1: > is present
          Code 2: < is present
          Code 3: Both Present
        */
        printf("seg?\n");
        tokenPtr = strtok(command, " "); /*Segfaults this line...*/
        printf("tokenPtr: %s", tokenPtr);
        while(tokenPtr != NULL){
            if(strcmp(tokenPtr, ">") == 0){
                if(code == 0)
                    code = 1;
                else if(code == 2)
                    code = 3;
                else{
                    printf("Error: Cannot have multiple equivalent redirects\n");
                    fail = 1;
                }
                tokenPtr = strtok(NULL, " ");
                strcpy(carrotfile1,tokenPtr);
                tokenPtr = strtok(NULL, " ");
                strcpy(arglist[argnum], tokenPtr);
                argnum++;
            }
            else if (strcmp(tokenPtr,"<") == 0){
                if(code == 0)
                    code = 2;
                else if(code == 1)
                    code = 3;
                else{
                    printf("Error: Cannot have multiple equivalent redirects\n");
                    fail = 1;
                }
                tokenPtr = strtok(NULL, " ");
                strcpy(carrotfile2, tokenPtr);
                tokenPtr = strtok(NULL, " ");
                strcpy(arglist[argnum], tokenPtr);
                argnum++;
            }
            else{
                tokenPtr = strtok(NULL, " ");
                strcpy(arglist[argnum], tokenPtr);
                argnum++;
            }
        }
    }
}

【问题讨论】:

  • 请注意code samples 应该是完整的(上面是)并且简洁(不是)。尽管方式不同,但这对所有相关人员都有好处。
  • 出于好奇,这个是功课,自学,是你自己想用的还是你希望发布的?
  • 谢谢,下次我会记住这一点。这是操作系统类问题的第二部分(第一部分是使用 PATH 查找和运行具有可变数量参数的命令)。我的第一部分和后台进程也在工作,流重定向是最后一部分。解析后,我知道如何关闭子进程中的文件描述符以使其正常工作。我可能会在此处发布完整的代码,以供寻找好示例的人使用(一旦我对其进行了一些重构)。
  • 您还应该使用strncpystrlcpy 而不是strcpy,否则您的代码将容易受到缓冲区溢出的影响。
  • @outis strncpy 很糟糕,因为它 a)不一定 NUL 终止结果,b)毫无意义地 NUl 填充结果。而且 strlcpy 不是标准 C。请参阅最近关于 SO 的讨论。

标签: c pointers operating-system segmentation-fault strtok


【解决方案1】:

在 OS X 10.6.5 下测试时,指定的行没有段错误。它坠毁的地方是:

strcpy(arglist[argnum], tokenPtr);

这是因为arglist(和pathlist)被分配了空间,而不是它们指向的任何字符串。这是arglist 的插图,它是一个指针数组(未初始化为原始代码,然后初始化为零)和一个数组数组。

当您在来自 arglist 的未初始化指针上调用 strcpy 时,您正在复制到一个随机内存位置(如第一张图片所示)。

要解决这个问题,您应该创建一个列表类型和函数来对列表进行操作。

typedef struct list {
    ...
} list_t;

// Initialize a new list. Pass NULL to dynamically allocate list structure.
list_t* createList(list_t* list);
// Destroy a list when done with it. You must free the list* if it was
// dynamically allocated.
void discardList(list_t* list);
// return first item in list
void* first(void);
// return last item in list. Could also be called "top"
void* last(void);
// append item to list
void push(void* item);
// remove & return last item
void* pop(void);
// remove & return first item
void* shift(void);
// prepend item to list
void unshift(void* item);

如果您将列表实现为数组,createList 将使用memsetarglist 归零。在原始代码中,您还可以选择在声明时将{0} 分配给arglist

char *arglist[1024] = {0};

如果您只需要在列表中存储字符串(创建string_list_t 类型)并且不需要一些操作(例如shiftunshift,则可以更改API。使用string_list_t,@ 987654342@可以为字符串分配空间,复制字符串项并将副本存储在列表结构中。

【讨论】:

  • 你是说 char *arglist[1024] 指向未分配的内存吗?我不确定我应该如何分配内存来解决它,我宁愿分配足够的内存以便它可以运行(不一定为每个字符串动态)。对于我使用 strsep 而不是 strtok 的路径并且没有这个问题。
  • arglist 是分配在堆栈上的数组。对于i 的任何值,arglist[i] 未分配。一种选择是将MAX_TOK_SIZE 宏(或int const)和arglist 声明为char arglist[MAX_TOK_SIZE][1024],尽管这会浪费相当多的内存。稍后我会用其他一些可能性更新我的答案。
  • strsep 单独不足以确保分配pathlist 的元素。你的意思是你将strsep的结果直接存储在pathlist中,而不是复制它们?
  • 我对这种区别有点困惑,我不认为我想做的事情应该那么困难。对于路径列表,我直接存储 strsep 的结果。在这里我尝试做同样的事情,唯一的区别是我必须先检查每个部分并确定它是“>”还是“
  • @fossuser:strsepstrtok 返回指向已分配内存的指针(指针指向您要标记的字符串)。当声明为指针数组时,arglistpathlist 中的指针最初指向随机内存位置。因此,您可以将strsepstrtok 返回的指针存储在arglist 中,但不能复制到arglist 中初始值指向的区域。在调试器中,在调用strcpy 之前查看commandarglist[argnum]tokenPtr
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多