использование функции free () вызывает ошибку во время выполнения - PullRequest
0 голосов
/ 05 января 2019

У меня есть структура с именем Person, которая содержит два атрибута - имя и фамилия. После успешного динамического выделения памяти для переменной типа Person, присвоения значений атрибутам, я хотел бы освободить память, но продолжаю получать ошибку времени выполнения (окно программы просто вылетает) это код:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef struct {
char firstName[15];
char lastName[15];
} Person;

void main(){
int len = 0;
char  firstName[]="danny", lastName[]="johnes";

Person *temp = (Person*)malloc(sizeof(Person));
if (temp == NULL)
    return;

    len = strlen(firstName);
    temp->firstName[len] = (char*)malloc(sizeof(char)*(len));
    if (temp->firstName == NULL)
        return;
    strcpy(temp->firstName, firstName);

    len = strlen(lastName);
    temp->lastName[len] = (char*)malloc(sizeof(char)*(len));
    if (temp->firstName == NULL)
        return;
    strcpy(temp->lastName, lastName);

freePerson(temp);
system("pause");
return;
}

Эта функция используется для освобождения памяти:

void freePerson(Person* ps) {
    if (ps != NULL) {
        free(ps->firstName);
        free(ps->lastName);
        free(ps);
    }
}

Все, что я хочу от кода - это сохранить имя в динамически распределенной структуре и освободить его. Позже я планирую заменить жестко запрограммированные имена значениями, введенными из файла.

Есть идеи по поводу ошибки? Спасибо.

Ответы [ 4 ]

0 голосов
/ 05 января 2019

Вот как бы я написал это:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define FIRSTNAME_MAXLEN 15
#define LASTNAME_MAXLEN 15

typedef struct
{
        char firstName[FIRSTNAME_MAXLEN+1];
        char lastName[LASTNAME_MAXLEN+1];
} person_t;

void freePerson(person_t *ps) {
        if (ps) {
                free(ps); ps=NULL;
        }
}

int main(){
        const char *firstName="danny";
        const char *lastName="johnes";

        person_t *temp = calloc(1, sizeof(person_t));
        if (!temp) return 1;

        strncpy(temp->firstName, firstName, FIRSTNAME_MAXLEN);
        strncpy(temp->lastName, lastName, LASTNAME_MAXLEN);

        printf("test: firstname: %s\n", temp->firstName);
        printf("test: lastname: %s\n", temp->lastName);
        freePerson(temp);

        return 0;
}

Вы выделяете достаточно места в куче и очищаете вещи с помощью calloc (), затем копируете свою строку с помощью strncpy (), ограничивая зарезервированные байты и избегая переполнения буфера. В конце вам нужно освободить () память, возвращаемую функцией calloc (). Поскольку вы выделили char firstName [] и char lastName [] внутри своей структуры, вам не нужно резервировать другую память с помощью malloc () для этих членов, а также не нужно их освобождать ().

0 голосов
/ 05 января 2019

У вас уже есть место, выделенное для firstName, поэтому вы должны скопировать имя в пределах ограничений по размеру (15 байт). Вы можете сделать это лучше всего с помощью snprintf следующим образом:

snprintf(temp->firstName, sizeof(temp->firstName), "%s", firstName);

То же самое относится к lastName. Помните, что оба могут быть усечены, если длина превышает размер поля.

Другой вариант - динамическое распределение полей. Тогда ваши члены структуры должны быть указателями, а не массивами символов:

typedef struct {
    char *firstName;
    char *lastName;
} Person;

Затем вы можете выделить и присвоить имена следующим образом:

temp->firstName = strdup(firstName); // (same for lastName)

Но учтите, что вы должны освободить эти поля отдельно, если вы хотите освободить весь элемент.

0 голосов
/ 05 января 2019

Если вы не хотите указывать максимальный размер для имен в структуре, вам нужно объявить их как указатели, а не как массивы.

typedef struct {
    char *firstName;
    char *lastName;
} Person;

Затем вы должны присвоить результат malloc() члену, не индексируя его. Вам также нужно добавить 1 к strlen(firstName), чтобы освободить место для нулевого терминатора.

temp->firstName = malloc(strlen(firstName)+1);
if (temp->firstName == NULL) {
    return;
}
strcpy(temp->firstName, firstName);
0 голосов
/ 05 января 2019

Как минимум 5 выпусков:

  1. Чтобы продублировать строку, убедитесь, что в выделении достаточно места для символов , включая нулевой символ .

В противном случае strcpy() записывает вне выделения, что составляет неопределенное поведение (UB).

    len = strlen(firstName);
    // temp->firstName[len] = (char*)malloc(sizeof(char)*(len ));
    temp->firstName = (char*)malloc(sizeof(char)*(len + 1));
    //                                                + 1 
    ...
    strcpy(temp->firstName, firstName);

То же самое для lastName.

  1. Также присваивайте указателю, а не char. @ Barmar

  2. Person члены являются массивами. Для динамического выделения они должны быть указателями. @ NthDeveloper

    typedef struct {
      // char firstName[15];
      // char lastName[15];
      char *firstName;
      char *lastName;
    } Person;
    

  1. 2-й тест неправильный

    // if (temp->firstName == NULL)
    if (temp->lastName == NULL)
    
  2. int против size_t.

int len = 0; предполагает, что длина строки соответствует int. Хотя это очень часто встречается, тип, возвращаемый из strlen(), равен size_t. Этот тип unsigned имеет правильный размер для индексации и определения размера массива - не слишком широкий и не слишком узкий. Не ключевой вопрос в этом коде ученика.

// int len = 0;
size_t len = 0;

Совет: приведение не требуется. Выделите объекту, на который указывает ссылка, а не типу. Проще кодировать правильно, просматривать и поддерживать.

// Person *temp = (Person*)malloc(sizeof(Person));
Person *temp = malloc(sizeof *temp);

// temp->firstName[len] = (char*)malloc(sizeof(char)*(len + 1));
temp->firstName = malloc(sizeof *(temp->firstName) * (len + 1));

Совет: Хотя это и не стандарт C, многие платформы предоставляют strdup() для выделенных и копируемых строк. Образец strdup() код .

temp->firstName = strdup(firstName);

Совет: Вероятно, наиболее ценный: хороший компилятор с хорошо включенными предупреждениями должен был предупреждать о temp->firstName[len] = (char*)malloc(sizeof(char)*(len));, так как это сомнительное несоответствие типов в назначении. Эти предупреждения сохраняют вас и нас все время. Убедитесь, что в вашей следующей компиляции включены все предупреждения.

...