【问题标题】:Can someone help me understand how to allocate properly in c?有人可以帮助我了解如何在 c 中正确分配吗?
【发布时间】:2010-09-30 22:04:42
【问题描述】:

我认为我没有正确理解如何为我想做的事情分配内存。

我希望我的程序将命令行中的参数存储到一个名为 Command 的结构数组中,其中包含 char **args。例如,如果我运行

./test.c echo hello : ls -l

我希望它像这样存储它

commands[0].args[0]= echo
commands[0].args[1]= hello
commands[1].args[0]= ls
commands[1].args[1]= -l

但是我的代码是以这种方式存储它

commands[0].args[0]= echo
commands[0].args[1]= hello
commands[0].args[2]= ls
commands[0].args[3]= -l
commands[1].args[0]= ls
commands[1].args[1]= -l

有人可以帮我理解为什么将 ls -l 存储在 2 个地方吗?这是我的代码:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

typedef struct test {
   char **args;
} Command;


int main(int argc, char *argv[])
{
   int i, j, k;
   Command *commands;

   j = k = 0;
   commands = (Command *)malloc(argc * sizeof(Command));

   for (i = 1; i < argc; i++)
   {
      if (strcmp(argv[i], ":") == 0)
      {
         j++;
         k = 0;
      }
      else {
         commands[j].args = (char **)realloc(commands[j].args, (k+1) * sizeof(char*));
         commands[j].args[k++] = argv[i];
      }
   }

   for (i = 0; i <= j; i++)
   {
      for (k = 0; k < 5; k++)
      {
         printf("commands[%d].args[%d]= %s\n", i, k, commands[i].args[k]);
      }
   }
   return EXIT_SUCCESS;
}

【问题讨论】:

  • for (k = 0; k &lt; 5; k++) 是未定义的行为
  • 您不能 realloc 之前未分配的东西(或设置为 NULL)。但这就是你为commands[j].args 所做的事情。
  • 我发现 commands[0].args[2] 和 commands[1].args[0] 是同一个地址...我该如何解决/更改这个?
  • commands[0].args[2] 从未在您的初始代码中分配。幸运的是,commands[1] 的空间是在 commands[0] 之后立即分配的。所以,当你试图超越 commands[0].args 的范围时,你碰巧打印出了 commands[1].args[0]。这是未定义的行为!问题是您正试图查看一个不存在的数组成员 commands[0].args[2]。使用以下建议将您的 for 循环打印限制为每个命令的实际长度。

标签: c memory-management


【解决方案1】:

您的数据存储结构无法判断commands[j] 中有多少个字符串是有效的。所以我认为就像你期望的那样,它在commands[0]commands[1] 中分别放置了两个指针。但是随后您的打印循环查看commands[0].args[k] for k 一直到4,即使它只查看前两个是有效的。当您开始查看commands[0].args[2] 时,结果是未定义的。 (显示程序中其他地方的内存、崩溃和着火只是程序在使用未定义行为时允许做的一些事情。)

要弄清楚每个命令中有多少个参数,您可以在struct test 中添加一个计数器成员。或者可能分配比参数多一个指针,并在最后一个参数后放置一个 NULL。

【讨论】:

    【解决方案2】:

    这是我将如何分配内存:

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    typedef struct cmd_s {
        int num;
        char **args;
    } cmd_t;
    
    void print_cmds(cmd_t *c, int num) {
        int i, j;
        for (i=0;i<=num;i++) {
            for (j=0;j<c[i].num;j++)
                printf("cmds[%d][%d] = %s\n", i, j,c[i].args[j]);
        }
    }
    
    int main(int argc, char *argv[]) {
        int i, j = 0, k = 0;
        cmd_t *cmds;
    
        cmds = (cmd_t *)malloc(sizeof(cmd_t));
        cmds[0].args = NULL;
        cmds[0].num = 0;
    
        for (i=1;i<argc;i++) {
            if (strcmp(argv[i], ":") == 0) {
                cmds = (cmd_t *)realloc(cmds, (sizeof(cmd_t) * ++j) + 1);
                cmds[j].args = NULL;
                cmds[j].num = 0;
                continue;
            }
            cmds[j].args = (char **)realloc(cmds[j].args, sizeof(char *) * ++cmds[j].num);
            cmds[j].args[cmds[j].num-1] = (char *)malloc(50);
            strcpy(cmds[j].args[cmds[j].num-1], argv[i]);
        }
    
        print_cmds(cmds, j);
    
        for (i=0;i<=j;i++) {
            for(k=0;k<cmds[i].num;k++)
                free(cmds[i].args[k]);
            free(cmds[i].args);
        }
    
        free(cmds);
        return 0;
    }
    

    【讨论】:

    • 当我使用 ./test.c 运行时,回显你好吗:ls -l。它在第二个命令中存储了 ls -l 你好吗。我发现这是由于 cmds[0].args[2] 和 cmds[1].arsg[2] 是同一个地址。我该如何解决这个问题?
    • cmds[1].args[2] 超出了数组的范围。您应该在运行结束时具有以下值: cmds[1].num = 2; cmds[1].args[0] = "ls"; cmds[1].args[1] = "-l"。 Args 是一个长度为 2 的数组,因此它对于 cmds[1].args[0] 和 cmds[2].args[1] 以外的元素的行为是未定义的。 C 标准不保证 cmds[2].args[] 的行为。永远不要尝试访问 cmds[1].args[2]、cmds[1].args[3] 等。
    【解决方案3】:

    每个Command 结构只有一个arg

    也许你应该考虑

    typedef struct test {
       char **args[5];
    } Command;
    

    然后设计一个更好的数据结构,比如列表的列表。

    【讨论】:

      【解决方案4】:

      也许您应该将 args 的长度存储在结构中?

      typedef struct test {
          char ** args;
          unsigned length;
      } Command;
      

      另外,也许您应该考虑使用 C 字符串库的一些内置功能。例如,strtok 使用您指定的分隔符拆分字符串。

      【讨论】:

        猜你喜欢
        • 2022-11-19
        • 1970-01-01
        • 2020-01-22
        • 2017-02-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-07-07
        相关资源
        最近更新 更多