Утечка памяти после использования free() в функции разрушителя в c (согласно шине)

Я заново изучаю C и использую splint для проверки исходного кода.

Я пытаюсь сделать следующее:

  • создать структуру с помощью функции "конструктор"
  • уничтожить структуру с помощью функции «деструктор», которая освобождает память структуры.

Однако, когда я тестирую свой код с помощью splint, он выдает предупреждения, связанные с временным хранилищем в деструкторе, и утечкой памяти после вызова деструктора.

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

В любом случае, вот код:

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

// define the structure
struct Boring {
    int amount;
};

// the creator
static struct Boring *Boring_create(int amount) {
    struct Boring *really = malloc(sizeof(struct Boring));
    assert(really != NULL);
    really->amount=amount;
    return really;
}

// the destroyer
static void Boring_destroy(struct Boring *really) {
    assert( really != NULL );
    // free the memory of the Boring structure
    free(really);
}

int main( /*@unused@*/ int argc, /*@unused@*/ char *argv[]) {
    int amount = 5;
    struct Boring *tv = Boring_create(amount);
    printf("The TV is boring level %d\n",tv->amount);

    // destroy the tv!
    Boring_destroy(tv);

    printf("The TV is now boring level %d\n",tv->amount);
    return 0;
}
/* Output */
/*
* $> ./destroytv
* The TV is boring level 5
* The TV is now boring level -538976289 (or 0 depending on OS/compiler)
*/

Код компилируется и отлично работает с gcc.

Однако, когда я использую шину для проверки, шина выдает следующие предупреждения:

$> splint boringtv.c
destroytv.c: (in function Boring_destroy)    
destroytv.c: Implicitly temp storage really passed as only param: free (really)
 Temp storage (associated with a formal parameter) is transferred to a new non-temporary reference. The storage may be released or new aliases crated. (Use -temptrans to inhibit warning)
destroytv.c: (in function main)
destroytv.c: Fresh storage tv not released before return
 A memory leak has been detcted. Storage allocated locally is not released before the last reference to it is lost (use -mustfreefresh to inhibit warning)
 Fresh storage tv created

Первое предупреждение, чем больше я об этом думаю, тем меньше понимаю. Но я не прочитал достаточно руководства, чтобы оправдать правильный вопрос об этом.

Второе предупреждение, про утечку памяти, похоже сплинт просто не понимает, что память освобождается в другом месте, что мне кажется странным. Предупреждение исчезнет, ​​если я просто вызову free() в пределах main.

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


person hilcharge    schedule 26.06.2015    source источник
comment
В любом случае, вот суть кода — выложенный здесь основной код также создает фактические сообщения, которые вы разместили? И вы знаете, что вызываете неопределенное поведение с помощью printf("The TV is now boring level %d\n",tv->amount), верно?   -  person WhozCraig    schedule 26.06.2015
comment
правильный термин деструктор, а не разрушитель   -  person Vinicius Kamakura    schedule 26.06.2015
comment
Код скопирован, сообщение исправлено, чтобы отразить тот же код, за вычетом номеров строк. извините за путаницу, я удалил суть. Я не знал, что это неопределенное поведение.   -  person hilcharge    schedule 26.06.2015
comment
assert создает в вашем деструкторе ветку, которую splint может не понять полностью. Что произойдет, если вы прокомментируете это?   -  person Useless    schedule 26.06.2015
comment
Мой splint немного заржавел, но я вижу, что вы не аннотировали функцию Boring_destroy(), чтобы указать, что она получает исключительное право собственности на свой аргумент, что она должна сделать, чтобы безопасно уничтожить его. Если вы добавите такую ​​аннотацию, то splint должен будет жаловаться на ваше разыменование tv в printf() в конце main(), что является настоящей ошибкой.   -  person John Bollinger    schedule 26.06.2015
comment
Кроме того, никогда не используйте assert() для выполнения любого теста, без которого вы не хотите обойтись. Если утверждение когда-либо терпит неудачу, это должно быть признаком того, что программа неправильна, а не просто тем, что программа неудачна. Утверждение в Boring_destroy() может соответствовать этим критериям, а утверждение в Boring_create() — нет.   -  person John Bollinger    schedule 26.06.2015
comment
@JohnBollinger Спасибо за совет о том, как стать владельцем. Это сработало, как вы сказали, и я получил сообщение об ошибке использования телевизора после его освобождения. Этот вопрос SO говорит, как (он использует /*@only@*/). Я добавлю правильный ответ. Для assert интересно. Я следовал учебнику Learn C the Hard Way, который использует его после malloc и в деструкторе. Может быть, он просто пытался представить концепцию, а.   -  person hilcharge    schedule 27.06.2015


Ответы (1)


Утечки памяти, как и предполагалось, нет.

Чтобы сообщить splint, какая функция должна управлять памятью структуры, ввод функции деструктора должен быть аннотирован с помощью /*@only@*/.

static void Boring_destroy( /*@only@*/ struct Boring *really ) {...

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

В частности, единственная аннотация «указывает [s], что ссылка является единственным указателем на объект, на который она указывает». (ручная шина)

Аннотация удаляет оба предупреждения, упомянутые в исходном вопросе, и заменяет их предупреждением, указывающим, что tv используется после его уничтожения. Это новое предупреждение желательно, потому что, как упоминал WhozCraig в комментариях, обращение к памяти после ее освобождения является поведением undefined и, следовательно, его следует избегать.

Использованная литература:

person hilcharge    schedule 27.06.2015