Где я получаю доступ к нелегальной памяти? realloc(): неверный следующий размер

Я делаю симулятор оболочки для домашнего задания по курсу операционных систем. Нас попросили добавить команду «история», которая при вводе должна печатать список команд, которые ввел пользователь.

Я решил реализовать это с помощью массива истории, который динамически выделяет больше памяти в зависимости от размера новой команды.

Вот мой код:

#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

Мне было интересно, что вызывает проблему, и я хотел бы получить предложения по ее устранению. Спасибо.

strlen(command) не дает вам достаточно байтов для хранения command. Он не учитывает нулевой терминатор. Итак, strcat(hist, command); списывает конец буфера. И это в любом случае должно быть strcpy, так как hist не инициализирован.
Fred Larson 22.03.2022 22:54

ооооо так мне просто нужно добавить +1 байт и все получится?

Leon Gurin 22.03.2022 22:56

Возможно. Я не могу гарантировать, что это единственная ошибка.

Fred Larson 22.03.2022 22:56

Кроме того: вы могу вызываете realloc(), когда (hist == NULL), в этом отношении нет особого случая, но, как уже упоминалось, вы должны использовать strcpy, а затем strcat.

Weather Vane 22.03.2022 22:57

Спасибо, что указали на мою ошибку @FredLarson

Leon Gurin 22.03.2022 22:58

И спасибо за совет @WeatherVane

Leon Gurin 22.03.2022 22:59

Еще один совет: sizeof(char) — это 1 по определению, поэтому умножение на него просто беспорядок.

Fred Larson 22.03.2022 23:03

Примечания: вам не нужно обнулять command, прежде чем использовать его в fgets. Вам не нужно использовать strncmp для сравнения со строками фиксированного размера, strcmp подойдет. Вместо объединения команд в большую строку рассмотрите возможность использования массива и strdupкоманды; вам все равно придется увеличивать массив, но это гораздо более полезная структура данных, проще в управлении, и вы можете завершить ее нулевым значением.

Schwern 22.03.2022 23:09

Спасибо, ребята, за поддержку, каждый из вас добавил полезные комментарии, и это был действительно хороший опыт, чтобы задать вопрос! Я почистил код, и он работает как шарм (:

Leon Gurin 22.03.2022 23:23

также... когда вы вызываете hist = realloc(hist,sizeof(char)*strlen(command)); и происходит сбой realloc, вы теряете данные, на которые ранее указывал hist. Лучше сохранить результат realloc во временную переменную и проверить его на значение null, прежде чем продолжить.

bruceg 23.03.2022 01:15

@FredLarson: Лично я считаю хорошей привычкой писать sizeof(char) в любом случае, даже если в этом нет необходимости. На мой взгляд, это делает код более понятным. Также, если у вас нет привычки его писать, то если вы хотите например выделить память для объектов типа int вместо этого, вы можете легко забыть умножить на sizeof(int).

Andreas Wenzel 23.03.2022 02:05
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
11
48
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вам нужно место для завершающего конца строки '\0', поэтому добавьте его к выделенному размеру:

hist = malloc(sizeof(char)*strlen(command) + 1);

Аргумент перераспределение также необходимо скорректировать. Также вы, вероятно, захотите удалить завершающую новую строку после вызова fgets.

Ответ принят как подходящий

Вторым параметром realloc должен быть новый размер всего блока, а не только размер данных, которые вы «добавляете» в область памяти.

hist = realloc(hist, currentHistSize + sizeof(char)*strlen(command));

Убедитесь, что вы назвали currentHistSize так, как хотите.

Другие вопросы по теме