为什么我的解析函数 return return 不是所有预期的标记?

Why doesn't my parse function return return all expected tokens?

我编写了一个从标准输入读取命令行的程序,并将其传递给一个函数,该函数应该将其解析为标记。

这是解析函数:

char** parse_cmdline(char* cmdline) {
    char ** arr = malloc(10 * sizeof(char*));
    for (int i =0 ; i < 10; ++i)
        arr[i] = malloc(30 * sizeof(char));
    char * token = strtok(cmdline, " ");
    int i = 0;
    while(token != NULL) {
        if(i > 9) arr = realloc(arr, (i+10)*sizeof(char*) );
        arr[i] = token;
        token = strtok(NULL, " ");
        i++;
    }
    printf("flag1");
    return arr;
}

这就是我使用它的方式 main():

int main() {
    int status;
    pid_t pid;
    pid = fork();

    while(1) {      
        if(pid < 0) {
            status = -1;
            perror("Fork");
        } else if(pid == 0) {
            char* cmd;
            printf("$");
            if(fgets(cmd, sizeof cmd, stdin) == NULL) break;
            parse_cmdline(cmd);
        } else {
            if( waitpid(pid, &status, 0) != pid ) {
                status = -1;
            }
            break;
        }
    }


    return 0;
}

这是我提供给程序的输入示例:

ls l a

预期的输出应该是:

l

(即第二个参数,由我的parse函数打印)

实际上什么也没有发生。甚至 printf("flag1");印刷。但是如果我删除 char ** commands 并将 printf("%s", commands[0]); 放在 parse_cmdline 函数中,一切正常,除了我没有分配 return。为什么以及如何修复它?


根据要求,这是我的全部代码:

#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

char** parse_cmdline(char* cmdline) {
    char ** arr = malloc(10 * sizeof(char*));
    for (int i =0 ; i < 10; ++i)
        arr[i] = malloc(30 * sizeof(char));
    char * token = strtok(cmdline, " ");
    int i = 0;
    while(token != NULL) {
        if(i > 9) arr = realloc(arr, (i+10)*sizeof(char*) );
        arr[i] = token;
        token = strtok(NULL, " ");
        i++;
    }
    printf("%s\n", arr[1]);
    return arr;
}

首先,您没有为命令分配空间。将 cmd 的声明更改为如下内容:

char cmd[100];

不分配内存会导致未定义的行为,这(以及正确使用 fgets 修复了该问题)。但是你也应该从 fgets() 检查 100 个字符是否足够:

if (strstr(cmd, "\n") == NULL) {
    /* the user typed more than 100 characters */
}

因为如果它们还不够,那么您将要解析一个不完整的命令行,并且下一次循环迭代输入数据时,它将解析更多不完整的命令。

最后,strtok returns 指向标记 in cmd,所以所有这些字符数组你分配在你的开头parse 函数是内存泄漏,因为你用循环内 strtok 的指针替换了它们:

arr[i] = token;
/* this throws away the address of the 10-character array you allocated
 * at the beginning of the function. You can't free() that memory
 * anymore. Your program is "leaking" memory. */

严格来说,顺便说一句,您应该检查 realloc 是否返回有效地址或 NULLmalloc 也是。在这么小的程序中,您不太可能遇到这个问题,但这是正确的做法。

您还应该在使用过后处理已解析的命令。你用 mallocrealloc 分配了一个指针数组,但你从来没有在你的程序中 free 它们。即使程序很快结束,而程序是 运行,这就是内存泄漏。 (同样,小程序不太可能会出现问题,但这是一种很好的做法。)

这部分看起来很奇怪 - 请参阅内联评论:

char ** arr = malloc(10 * sizeof(char*));
for (int i =0 ; i < 10; ++i)
    arr[i] = malloc(30 * sizeof(char));     // Here you allocate memory
                                            // for holding a part of the command

char * token = strtok(cmdline, " ");
int i = 0;
while(token != NULL) {
    if(i > 9) arr = realloc(arr, (i+10)*sizeof(char*) );

    arr[i] = token;             // But here you overwrite the pointer value and
                                // and thereby create a memory leak

    token = strtok(NULL, " ");
    i++;
}

也许您想改为复制字符串 - 如:

strcpy(arr[i], token);   // Instead of arr[i] = token;

而且这一行看起来很奇怪:

if(i > 9) arr = realloc(arr, (i+10)*sizeof(char*) );

你增加 arr 以便它可以容纳更多 char* 但这次你没有像最初那样为新字符串分配内存。