Я делаю симулятор оболочки для домашнего задания по курсу операционных систем. Нас попросили добавить команду «история», которая при вводе должна печатать список команд, которые ввел пользователь.
Я решил реализовать это с помощью массива истории, который динамически выделяет больше памяти в зависимости от размера новой команды.
Вот мой код:
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#define BUFFER_SIZE 100
//comment
int main(void)
{
close(2);
dup(1);
char command[BUFFER_SIZE];
char* hist = NULL;
// int counter = 0;
while (1)
{
fprintf(stdout, "my-shell> ");
memset(command, '\0', BUFFER_SIZE);
fgets(command, BUFFER_SIZE, stdin);
if (strncmp(command, "exit", 4) == 0)
{
break;
}
//alocate memory for the current position in the hist array
if (hist == NULL){
hist = (char*)malloc(sizeof(char)*strlen(command));
}
else{
hist = realloc(hist,sizeof(char)*strlen(command));
}
strcat(hist,command);
printf("the size of the boy: %d\n",(int) strlen(hist));
// counter += strlen(command);
int pid = fork();
char *argv[BUFFER_SIZE];
char *pch;
pch = strtok(command, " \n");
int i;
for(i=0; pch != NULL; i++){
argv[i] = pch;
pch = strtok(NULL, " \n");
}
argv[i] = NULL;
int hasAmpersand = 0;
//check if the last entered character was '&'
if (*argv[i-1]=='&'){
// printf("entered &:");
hasAmpersand = 1;
//replace it with '\0'
argv[i-1] = NULL;
}
if (pid == 0){ //child process execute sys call
if (strncmp(argv[0],"history",7) == 0){
printf("%s\n", hist);
exit(1);
}
else{
execvp(argv[0], argv);
printf("\nbad syntax\n");
}
}
else{
if (!hasAmpersand){ //wait for child
while(wait(NULL) != pid);
}
}
}
free(hist);
return 0;
}
Реализация работает до 6 команд, хранящихся в истории, но вылетает с ошибкой
realloc(): invalid next size
aborted
Мне было интересно, что вызывает проблему, и я хотел бы получить предложения по ее устранению. Спасибо.
ооооо так мне просто нужно добавить +1 байт и все получится?
Возможно. Я не могу гарантировать, что это единственная ошибка.
Кроме того: вы могу вызываете realloc()
, когда (hist == NULL)
, в этом отношении нет особого случая, но, как уже упоминалось, вы должны использовать strcpy
, а затем strcat
.
Спасибо, что указали на мою ошибку @FredLarson
И спасибо за совет @WeatherVane
Еще один совет: sizeof(char)
— это 1 по определению, поэтому умножение на него просто беспорядок.
Примечания: вам не нужно обнулять command
, прежде чем использовать его в fgets
. Вам не нужно использовать strncmp
для сравнения со строками фиксированного размера, strcmp
подойдет. Вместо объединения команд в большую строку рассмотрите возможность использования массива и strdup
команды; вам все равно придется увеличивать массив, но это гораздо более полезная структура данных, проще в управлении, и вы можете завершить ее нулевым значением.
Спасибо, ребята, за поддержку, каждый из вас добавил полезные комментарии, и это был действительно хороший опыт, чтобы задать вопрос! Я почистил код, и он работает как шарм (:
также... когда вы вызываете hist = realloc(hist,sizeof(char)*strlen(command));
и происходит сбой realloc, вы теряете данные, на которые ранее указывал hist
. Лучше сохранить результат realloc
во временную переменную и проверить его на значение null, прежде чем продолжить.
@FredLarson: Лично я считаю хорошей привычкой писать sizeof(char)
в любом случае, даже если в этом нет необходимости. На мой взгляд, это делает код более понятным. Также, если у вас нет привычки его писать, то если вы хотите например выделить память для объектов типа int
вместо этого, вы можете легко забыть умножить на sizeof(int)
.
Вам нужно место для завершающего конца строки '\0'
, поэтому добавьте его к выделенному размеру:
hist = malloc(sizeof(char)*strlen(command) + 1);
Аргумент перераспределение также необходимо скорректировать. Также вы, вероятно, захотите удалить завершающую новую строку после вызова fgets.
Вторым параметром realloc должен быть новый размер всего блока, а не только размер данных, которые вы «добавляете» в область памяти.
hist = realloc(hist, currentHistSize + sizeof(char)*strlen(command));
Убедитесь, что вы назвали currentHistSize так, как хотите.
strlen(command)
не дает вам достаточно байтов для храненияcommand
. Он не учитывает нулевой терминатор. Итак,strcat(hist, command);
списывает конец буфера. И это в любом случае должно бытьstrcpy
, так какhist
не инициализирован.