C代码神秘地改变结构的值(有趣的神秘代码行为)

时间:2015-02-24 02:02:22

标签: c arrays struct

我正在为C中的示例UNIX shell实现历史记录功能。我创建了一个名为History的结构数组,其中每个struct元素包含一个整数(历史编号)和命令(一个字符串数组,每个字符串作为命令的参数)。重要部分用***表示。

struct HistoryElement
{
  int NumberOfCommandGiven;
  char * command[MAXLINE/2+1];
};  

    int main(void)
    {
    char iBuffer[MAXLINE]; 
    char *args[MAXLINE/2+1];
    int bgrnd;           
    int numberofcommand = 0; //The most recent number given to a command entered (for history).
    int currentposition;
    int historysize = 12;
    struct HistoryElement* History = malloc(historysize*sizeof(struct HistoryElement)); //Create an array of HistoryElements.
    struct HistoryElement blank = { -1, "NothingHere" };
    int j=0;
    //clear out any garbage contents of the history and replace with placeholder element called blank.
    for(j=0;j<historysize;j++) {
      History[j]=blank;
    }

    while (1) {           
      bgrnd = 0;
      printf("IansSh: ");
      fflush(0);
      setup(iBuffer, args, &bgrnd); 

      //Test that the entry was stored correctly -- This outputs "ls" before calling history, but after calling history is outputs "history".  How did History[0] get changed???
      printf("History[0].command equals %s \n",History[0].command[0]);  

      if(strcmp(args[0],"history")==0 || strcmp(args[0],"h")==0) { //***
        //WHY is this section rewriting my array of structs?
        int i;
        for(i=0;i<historysize;i++) {
           if(History[i].NumberOfCommandGiven!=-1)
           {
              if(History[i].NumberOfCommandGiven!=0) {
                  printf("%d : %s \n",History[i].NumberOfCommandGiven, History[i].command[0]);
           }
           else {
               printf("%d : Command was removed due to a more recent command of the same entry. \n", History[i].NumberOfCommandGiven);
           }
        }
       }
      }
    //***
    else {
    printf("Got into final else");
    pid_t errorchecker; 
    errorchecker = fork(); //Create child process
    numberofcommand++;
    //Add the command to history: ***
    struct HistoryElement input;
    input.NumberOfCommandGiven = numberofcommand;
    memcpy(input.command, args, sizeof(args));
    History[numberofcommand-1%historysize] = input;
    //Remove any old entries of that exact command from the history, if they exist:
    int k =0;
    for(k=0;k<historysize;k++)
      {
        if(History[k].command==input.command && History[k].NumberOfCommandGiven!=numberofcommand) 
          {
        History[k].NumberOfCommandGiven=0;
          }   
      }
    if(errorchecker < 0) {
      printf("An error occurred when trying to fork a child process.");
      continue;
    }
    else if(errorchecker==0) {
      //Execute the command, of course:
      execvp(args[0],args);
      // Print the error if an error occurred.
      char* err = strerror(err);
      printf("IansSh error occurred (You most likely entered an invalid command): %s: %s\n", args[0], err);
    }
    if(bgrnd==0) {
      //if specified (ie. an '&' was not included), wait on child process to finish before continuing.
      int child;
      waitpid(errorchecker, &child, 0);
    }
      }
    }
    free(History);
}

在我的代码中,有一个专门用于调用命令'history'的部分。这当然并不意味着将自己作为命令实际添加到历史中,而是用于逐行显示历史。但是,由于某种原因,代码的这一部分正在重写我的结构数组的值,将每个命令的条目更改为“历史记录”。因此,当我做测试时,我会得到类似的东西:

ls -l
(successfully outputs results of ls -l)
ls
(successfully outputs results of ls)
history
1 : history
2 : history

相反,1和2分别对应于'ls -l'和'ls',就像它们存储在main中的struct中一样。

1 个答案:

答案 0 :(得分:2)

在这里,您要创建一个第二个字段为char *[]

的结构
struct HistoryElement input;
input.NumberOfCommandGiven = numberofcommand;
memcpy(input.command, args, sizeof(args));
History[numberofcommand-1%historysize] = input;

修改 首先,正如@MattMcNabb指出的那样,注意你的优先权! numberofcommand-1%historysize被评估为numberofcommand-(1%historysize),这会导致写出数组的末尾(可能是段错误)。

这个memcpy复制了args中的指针,但没有为args中的实际字符串分配和复制内存。为此你需要遍历args(而不是memcpy)并使用strdup来创建字符串的新副本。

int i;
for (i = 0; args[i] != NULL; ++i) {
    input.command[i] = strdup(args[i]);
    if (input.command[i] == NULL) {
          /* handle allocation error */
    }
}
input.command[i] = NULL;

您以后也需要释放所有这些字符串,因此您不应该在不释放任何已分配的指针的情况下覆盖结构。

struct HistoryElement old = History[(numberofcommand-1)%historysize];
int i;
for (i = 0; old.command[i] != NULL; ++i) {
   free(old.command[i]);
   old.command[i] = NULL;
}
History[(numberofcommand - 1) % historysize] = input;

但是最初的blank如何适应这个?我们不能释放“NothingHere”,并且无论如何都要打破初始化:

struct HistoryElement blank = { -1, {NULL} };