我正在为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中一样。
答案 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} };