C字符串将值存储到数组中而不进行覆盖

时间:2013-01-26 01:54:50

标签: c arrays pointers null

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

我的代码如下。我的char数组历史记录[]如下所示:

输入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
  }
}

1 个答案:

答案 0 :(得分: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()在所谓的堆<分配内存/ em>(在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) - 这是因为原始类型(例如intchar)的大小可能会根据您编译的处理器类型而不同,编译器就是使用等(编辑:只是注释,sizeof(char) == 1由C规范定义(我认为所有修订版),所有其他数据类型大小以{{1}的倍数给出}}。)

但是如果:

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()的副本更改为another_input_line的副本!它永远不会释放第一个input_line的内存,现在它的所有指针都消失了。这被称为内存泄漏(如果它们就这么简单就会很棒!)。如果你的程序中有可能(例如,你让input_line循环回0),你可能想解决这个问题。在您第一次分配该内存后,您可以重复使用它,对吗?

history_counter

现在你正在检查你之前是否已经在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_line复制到history数组中。 (你的代码还有另外一个问题会破坏它;见下文。)

您应该了解动态分配的另一件事,因为这似乎是我们所关注的主题,就是释放您使用malloc()分配的内存,使用free()。对于C ++,要释放使用new分配的内存,请使用delete


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


顺便说一句,代码中的一些问题不是问题:

您永远不会在history_counter中初始化main(),因此当您致电make_history()时,其初始值未定义。您应该将未初始化的变量(已声明但未赋值)视为随机变量 - 实际上,该值取决于处理器/系统,编译器,操作系统,并且您不应该依赖它。因此,main()的第二行应为:

int i, n, history_counter = 0;

同样,您将history声明为:

char *history[HISTORY_MAX];

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

if(history[i] != NULL)

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