【问题标题】:How do I dynamically allocate memory for an array of strings in C?如何为 C 中的字符串数组动态分配内存?
【发布时间】:2016-04-14 04:53:44
【问题描述】:

我阅读了之前关于 C 中动态数组的问题,但是我无法将答案与我的问题联系起来。

我正在使用 fgets 从stdin 获取命令,删除换行符,然后希望将每个由空格分隔的命令存储在动态分配的字符串数组中。但是,我在分配和重新分配内存的正确方法上遇到了很多麻烦。我正在使用clang 编译并不断收到分段错误11。然后我使用-fsanitize=address 并不断收到:

==2286==错误:AddressSanitizer:地址上的堆缓冲区溢出 0x60200000eeb8 在 pc 0x000108fb6f85 bp 0x7fff56c49560 sp 0x7fff56c49558 在 0x60200000eeb8 线程 T0 写入大小为 8

这是我的代码:

// Sets a delimiter to split the input
const char *seperator = " ";

char *token = strtok(line, seperator);

char **cmds = (char **) malloc(sizeof(char) * sizeof(*cmds));
// Adds first token to array of delimited commands
cmds[0] = token;

int count = 1;
while (token != NULL) {
    token = strtok(NULL, sep);
    if (token != NULL) {
        cmds = (char **) realloc(cmds, sizeof(char) * (count + 1));
        // Adds next token array of delimited commands
        cmds[count] = token;
        count++;
    }
}

【问题讨论】:

标签: c malloc dynamic-memory-allocation realloc strtok


【解决方案1】:

您没有分配足够的内存。 cmds 是一个指针数组,所以每个元素都是sizeof(char *) 字节,而不是sizeof(char) 字节。

在初始分配时您想要 1 char *,然后在后续分配中您想要 count + 1

另外,don't cast the return value of malloc,因为这可以隐藏其他问题,并且不要忘记检查故障。

char **cmds = malloc(sizeof(char *) * 1);
if (cmds == NULL) {
    perror("malloc failed");
    exit(1);
}
...
        cmds = realloc(cmds, sizeof(char *) * (count + 1));
        if (cmds == NULL) {
            perror("reallocfailed");
            exit(1);
        }

【讨论】:

    【解决方案2】:
    • 第一个 malloc 是错误的。如果在分配 cmd 之前通过 *cmd 取消引用 cmd,您会得到什么?
    • 它还使用sizeof(char),这是错误的..

    正确的方法是..

    // strtok modifies the string. So use writable string
    char line[80] = "Hello my name is anand";
    char *token = strtok(line, sep);
    int count = 0;
    // Alloc array of char* for total number of tokens we have right now
    char **cmds = (char **) malloc(sizeof(char*) * (count + 1));
    
    while (token != NULL) 
    {
        /**
         * Alloc memory for the actual token to be stored.. 
         * token returned by strtok is just reference to the existing string 
         * in 'line'
         */
        cmds[count] = malloc(sizeof(char) * ((strlen(token) + 1)));
        // Adds tokens to array of delimited commands
        strcpy(cmds[count], token);
        count++;
    
        token = strtok(NULL, sep);
        if (token != NULL) 
        {
            // resize array of tokens to store an extra token
            char ** newCmds = (char **) realloc(cmds, sizeof(char*) * (count + 1));
            // only if realloc was successful then use it.
            if (newCmds != NULL)
            {
                 cmds = newCmds;
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      首先,sizeof(char) 根据定义始终为 1。并且编码不会使您的代码更具可读性。

      但是指向char 的指针需要sizeof(char*) 字节(取决于机器和ABI,通常是8 或4 字节)。如果使用GCC,我至少建议使用gcc -Wall -Wextra -g 编译您的代码。

      最后,我发现你的代码效率有点低。您在每个循环中都调用realloc。我会维护一个包含分配大小的变量

       int allocsize = 4; // allocated size in number of elements
       char **cmds = malloc(allocsize*sizeof(char*));
       if (!cmds) { perror("malloc"); exit(EXIT_FAILURE); };
      

      (顺便说一句,总是检查malloc 的结果;它可能失败)。

      为了避免每次realloc-ing,我会以几何方式增加分配的大小,所以在循环内:

       if (count>=allocsize) {
          int newallocsize = (4*allocsize)/3+10;
          cmds = realloc (cmds, newallocsize*sizeof(char*));
          if (!cmds) { perror("realloc"); exit(EXIT_FAILURE); };
          allocsize = newallocsize;
       }
      

      或者,除了保留三个变量:cmdscountallocsize,您还可以使用以 struct 结尾的单个 struct(并保留其分配和使用的大小)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-09-29
        • 2017-03-28
        • 2020-04-13
        • 1970-01-01
        • 1970-01-01
        • 2018-06-16
        • 1970-01-01
        相关资源
        最近更新 更多