Свободная память используется для структуры в C, но не работает (код из C Primer Plus 6th Edition)

Я запускаю код из листинга 17.2, P781, C Primer Plus 6th Edition.

/* films2.c -- using a linked list of structures */    
#include <stdio.h>    
#include <stdlib.h>   /* has the malloc prototype */    
#include <string.h>   /* has the strcpy prototype */    
#define TSIZE 45      /* size of array to hold title */                                                                                                                  

struct film {    
    char title[TSIZE];    
    int rating;    
    struct film *next;  /* points to next struct in list */    
};    

char *s_gets(char *st, int n);    


int main(void)    
{    
    struct film *head = NULL;    
    struct film *prev, *current;    
    char input[TSIZE];    

    /* Gather and store information */    
    puts("Enter first movie title:");    
    while (s_gets(input, TSIZE) != NULL && input[0] != '\0') {    
        current = (struct film *) malloc(sizeof(struct film));    
        if (head == NULL)   /* first structure */    
            head = current;    
        else   /* subseqent structures */    
            prev->next = current;    
        current->next = NULL;    
        strcpy(current->title, input);    
        puts("Enter your rating <0-10>:");    
        scanf("%d", &current->rating);    
        while (getchar() != '\n')    
            continue;    
        puts("Enter next movie title (empty line to stop):");    
        prev = current;    
    } 

    /* show list of movies */    
    if (head == NULL)    
        printf("No data entered.");    
    else    
        printf("Here is the movie list:\n");    
    current = head;    
    while (current != NULL) {
        printf("Movie: %s     Rating: %d\n",
                current->title, current->rating);
        current = current->next;
    }

    /* Program done, so free allocated memory */
    current = head;
    while (current != NULL) {
        free(current);
        printf("Movie: %s, Rating: %d\n",                                                                                                                                
                current->title, current->rating);
        current = current->next;
    }
    printf("Bye!\n");

    return 0;
}

char *s_gets(char *st, int n)
{
    char *ret_val;
    char *find;

    ret_val = fgets(st, n, stdin);
    if (ret_val) {
        find = strchr(st, '\n');  // look for newline
        if (find)    // if the address is not NULL,
            *find = '\0';    // place a null character there
        else
            while (getchar() != '\n')
                continue;      // dispose of rest of line
    }
    return ret_val;
}

Мой вопрос в том, что после free(current), current->title и current->rating все еще есть, а current->next нет NULL. Я не знаю почему.

Освобождение current не делает его NULL. Он просто не может изменить его, так как он передается по значению. Он просто указывает на недопустимое местоположение. Разыменование это UB.

Eugene Sh. 30.07.2019 18:25

О, это код из книги? Выброси это.

Eugene Sh. 30.07.2019 18:30

да книга неправильная. После звонка free вы никогда больше не сможете прикасаться к этому воспоминанию. Если вы это сделаете, компьютер может взорваться. Большинство не будет, но Deathstation 9000, безусловно, будет.

Zan Lynx 30.07.2019 18:32

Эта книга была первоначально опубликована в 1984 году, и хотя в то время она, возможно, была хорошим справочником, сейчас она ужасно устарела, если этот код все еще присутствует в ней. Обычный метод написания неряшливого кода на C "не беспокойтесь, все в порядке" больше не работает.

tadman 30.07.2019 19:37

@tadman: 6-е издание было опубликовано в 2013 году. Если этот код находится в 6-м издании, об этом необходимо сообщить как об ошибке. Ни gcc, ни clang не обнаруживают ошибку, поэтому неудивительно, что слишком не была обнаружена, но valgrind ее ловит.

Keith Thompson 31.07.2019 04:12

@ЕвгенийШ. Почему функция printf может печатать правильные значения после того, как current указывает на недопустимое местоположение?

mathsyouth 31.07.2019 04:13

@KeithThompson Может быть, эта книга отличается, но многие старые книги получают очень незначительные обновления, которые никогда не решают такие серьезные проблемы, как эта, только опечатки и тому подобное.

tadman 31.07.2019 04:13

@KeithThompson@tadman Прошу прощения за свою ошибку. Оригинальный код содержит функцию printf. Я обнаружил, что цикл для освобождения памяти странный, поэтому я добавил функцию printf, чтобы определить, работает ли функция освобождения. Однако после добавления он все еще работает. Меня смущает функция free. Как работает free? Какую память освобождает функция free?

mathsyouth 31.07.2019 04:21

@mathsyouth: free делает память доступной для дальнейшего распределения. Это не (обязательно) делает невозможным доступ к нему. Доступ к освобожденной памяти имеет неопределенное поведение. Вам, программисту, решать, как этого избежать; реализация, вероятно, не поможет вам.

Keith Thompson 31.07.2019 08:53
Стоит ли изучать 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
9
89
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

По крайней мере, эта петля неверна

current = head;
while (current != NULL) {
    free(current);
    printf("Movie: %s, Rating: %d\n",                                                                                                                                
            current->title, current->rating);
    current = current->next;
}

там должен быть

current = head;
while (current != NULL) {
    printf("Movie: %s, Rating: %d\n",                                                                                                                                
            current->title, current->rating);
    struct film *tmp = current;
    current = current->next;
    free(tmp);
}

В противном случае вы пытаетесь использовать недопустимый удаленный указатель.

Use-after-free — довольно вопиющая ошибка, особенно при обучении.

tadman 30.07.2019 19:38

Спасибо за вашу точку зрения. Однако я не понимаю, почему он все еще работает, когда я пытаюсь использовать недопустимый удаленный указатель. «работает» означает, что функция по-прежнему печатает значения переменных.

mathsyouth 31.07.2019 04:07

@mathsyouth: Такова природа неопределенного поведения. Что может случиться с худший, так это то, что код "работает".

Keith Thompson 31.07.2019 04:13
Ответ принят как подходящий

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

Код неверен, как указано в других ответах.

На одной из последующих страниц автор показывает правильную функцию освобождения связанного списка. Он говорит

The code saves the address of the next node because the call to free(), in principle, may make the contents of the current node (the one pointed to by *plist) no longer available.

Хотя технически это правильно, этот отрывок лишает автора права преподавателя C. Вот исправленный отрывок.

The code saves the address of the next node because the call to free() makes the contents of the current node (the one pointed to by *plist) no longer available, and accessing it is undefined behaviour.

По-видимому, автор никогда не упоминает термин «неопределенное поведение». Это неприемлемо в любом серьезном учебнике C.

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