0

我正在尝试获取程序中输入的最后 10 个命令。我遇到的问题是我从命令行获取参数并将其存储到我的数组中,但我一直在我的数组中写入值,因为它们存储的是指针而不是实际的字符数组值。我想知道如何获取当前字符数组(将随着输入的每个命令而改变)并将该值存储到一个数组中。我知道这是一个指针问题,但我不确定如何解决这个问题

我的代码如下。我的 char 数组 history[] 如下所示,输入如下:

输入 a 历史[0] = a 输入 b 历史[0] = b 历史[1] = b 输入 c 历史[0] = c 历史[1] = c 历史[2] = c

#define MAX 256
#define CMD_MAX 10
#define HISTORY_MAX 10

int make_history(char *history[], int history_counter, char *input_line)
{
  if(history_counter < HISTORY_MAX)
  {
    history[history_counter] = input_line;
  }
  return 1;
}



int make_tokenlist(char *buf, char *tokens[])
{
   char input_line[MAX];
   char *line;
   int i,n;

   i = 0;
   line = buf;
   tokens[i] = strtok(line, " ");

   do
   {
    i++;
    line = NULL;
    tokens[i] = strtok(line, " ");
   }

   while(tokens[i] != NULL);

   return i;
 }

int main()
{
  char input_line[MAX], *tokens[CMD_MAX], *history[HISTORY_MAX];
  int i, n, history_counter;

  while(1)
  {
    printf("abc> ");

    if (gets(input_line) != NULL)
    {
     n= make_tokenlist(input_line, tokens);
    }
    else
    {
     printf("error?");
     return 1;
    }

    make_history(history,history_counter,input_line);

    if(strcmp(tokens[0],"history")==0)
    {
     for (i = 0; i < HISTORY_MAX; i++)
     {
       if(history[i]!=NULL)
       {
         printf("%d. %s \n", i, history[i]);
       }
     }
    } 

    for (i = 0; i < n; i++)
    {
     printf("extracted token is %s\n", tokens[i]);
    }

    history_counter++; //increment history counter
  }
}
4

1 回答 1

3

问题(我认为您理解)是您正在存储指向与input_line. 也就是说,里面的字符串history和input_line是同一个数据。

解决方案是制作副本并将其input_line存储history. 一个幼稚的实现(不起作用)如下:

int make_history(char *history[], int history_counter, char *input_line)
{
    if(history_counter < HISTORY_MAX)
    {
        char input_copy[MAX];
        strcpy(input_copy, input_line);
        history[history_counter] = input_copy;
    }
    return 1;
}

现在,您正在将该行的副本复制到内存中,该副本不会在其他任何地方使用 ( input_copy)。那应该有效,对吧?问题是声明这样的变量(称为静态分配)将变量的数据存储在堆栈上。当函数返回时,该函数放入堆栈的所有数据都将被销毁。当make_history()返回时,input_copy被销毁,并且由于history[history_counter]指向与 相同的数组input_copy,它现在指向的内存可能会被程序用于其他目的,并且不再包含输入行的副本。

您可以使用动态分配来解决仅存在于声明它的范围内的静态分配内存的问题。malloc()在所谓的堆上分配内存(在 C 中;在 C++ 中,您将使用new)。除非您告诉它自己释放内存,否则堆中的数据不会在程序运行时被破坏。例子:

int make_history(char *history[], int history_counter, char *input_line)
{
    if(history_counter < HISTORY_MAX)
    {
        char *input_copy = (char *)malloc(MAX * sizeof(char));
        strcpy(input_copy, input_line);
        history[history_counter] = input_copy;
    }
    return 1;
}

这将起作用,因为堆中的数据(由 分配malloc())将在make_history()返回后继续存在,直到您自己释放该内存或程序结束。请注意我是如何使用sizeof(char)的——这是因为原始类型(例如int, char)的大小可能会根据您正在编译的处理器类型、您正在使用的编译器等而有所不同。(编辑:请注意,sizeof(char) == 1由C 规范(我认为所有修订版),所有其他数据类型大小均以 . 的倍数给出char。)

但如果:

// history_counter == 0
make_history(history,history_counter,input_line);
// ...
// later, when history_counter == 0 again somehow
make_history(history, history_counter, another_input_line);

第二个make_history()所做的只是将指针从副本更改input_line为 another_input_line 的副本!它永远不会释放 first 的内存input_line,现在所有指向它的指针都消失了。这被称为内存泄漏(如果它们都那么简单,那就太好了!)。如果这在您的程序中是可能的(例如,您将history_counter循环返回到 0),您会想要解决这个问题。在您第一次分配该内存后,您可以重复使用它,对吗?

int make_history(char *history[], int history_counter, char *input_line)
{
    if(history_counter < HISTORY_MAX)
    {
        if(history[history_counter] == NULL)
        {
            history[history_counter] = (char *)malloc(MAX * sizeof(char));
        }
        strcpy(history[history_counter], input_line);
        history[history_counter] = input_copy;
    }
    return 1;
}

现在您正在检查您之前是否已经存储了一行的副本history[history_counter],如果没有,则malloc()首先检查内存。无论哪种方式,之后您都复制input_linehistory数组中。(您的代码还有另一个问题会破坏这一点;见下文。)

关于动态分配你应该知道的另一件事,因为这似乎是我们正在讨论的主题,那就是释放你使用分配的内存malloc(),使用free()。对于 C++,要释放使用 分配的内存new,请使用delete.


另一种可能的解决方案,正如您对问题的评论所提到的:您可以立即静态分配10*256*sizeof(char)字节main()。这是一个更简单的解决方案,但会一直使用内存,而不是仅在需要时使用。您的示例很简单,不需要那么多内存,但是在更大/更复杂的程序中,这种差异可能很重要。


顺便说一句,除了问题之外,您的代码中还有一些问题:

您永远不会在 中初始化history_countermain()因此在调用时它的初始值是未定义的make_history()。您应该将未初始化的变量(已声明,但未分配值)视为随机变量——实际上,该值取决于处理器/系统、编译器、操作系统,而不是您应该依赖的任何东西。因此,第二行main()应该是:

int i, n, history_counter = 0;

同样,您声明history为:

char *history[HISTORY_MAX];

并且您不会初始化指向NULL值的指针数组。稍后您检查:

if(history[i] != NULL)

但是您不能保证默认history[i]值为NULLall ihistory您应该首先将数组初始化为所有NULL值。我上面的最后一个实际答案的例子也依赖于这个初始化,如果你尝试free()一个未分配的地址,可能会发生不好的事情。

于 2013-01-26T03:28:40.530 回答