c - C将值存储到数组中而不覆盖的字符串

标签 c arrays pointers null

我正试着把最后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
  }
}

最佳答案

问题(我想您已经理解)是,您存储的指针指向与input_line相同的字符数组。换句话说,history和input_行中的字符串是相同的数据。
解决方法是复制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)-这是因为基元类型(例如intchar)可以根据您要编译的处理器类型、您正在使用的编译器等而在大小上有所不同(编辑:只需注意,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的副本更改为另一个输入行的副本!它从未释放第一个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_line数组中。(您的代码有另一个问题会破坏此功能;请参见下文。)
关于动态分配,您应该知道的另一件事是,要释放您使用history分配的内存,请使用malloc()。对于C++,使用free()释放内存,使用new
另一个可能的解决方案,如对您的问题的评论所述:您可以立即静态地在delete中分配10*256*sizeof(char)字节。这是一个更简单的解决方案,但是会一直消耗内存,而不是在需要的时候。您的示例很简单,不需要那么多内存,但在更大/更复杂的程序中,这种差异可能很重要。
顺便说一下,代码中除了以下问题之外的一些问题:
您从未在main()中初始化history_counter,因此在调用main()时,其初始值未定义。实际上,应该将未初始化的变量(已声明,但未分配值)视为随机变量,该值取决于处理器/系统、编译器、操作系统,而不是应该依赖的对象。因此,make_history()的第二行应该是:
int i, n, history_counter = 0;

同样,您声明main()为:
char *history[HISTORY_MAX];

而且您不会将指针数组初始化为history值。稍后检查:
if(history[i] != NULL)

但您不能保证NULL的默认值为history[i]。您应该首先将NULL数组初始化为所有i值。我在上面的最后一个例子中给出了我的实际答案,它也依赖于初始化的完成,如果您试图history一个未分配的地址,可能会发生不好的事情。

关于c - C将值存储到数组中而不覆盖的字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14533027/

相关文章:

c - 类型、表达式和数组维度

c++ - 已在 *.obj 中定义

c - 使用递归进行二分搜索而无需数组中的元素数量?

c - IndexOf 函数在 C 中不起作用

c++ - 静态函数与指向类的静态指针之间有什么区别

c - 如何解决头文件之间的这种相互依赖关系?

python - 如何使用 python 或 c 访问我的网络摄像头

C 字符串大小和数组

C程序将空格分隔的输入字符串转换为int数组

c++ - 在分配指针后,您可以为指针所指向的对象分配一个变量名吗?