Что происходит с блоком malloc, если вы его не используете? - PullRequest
2 голосов
/ 11 апреля 2009

Рассмотрим следующий код C:

int main(){  
    int* c;  
    c = (int*)malloc(sizeof(int));  
    c = 0xdeadbeef;  
    free(c);  
    return 0;  
}

Это будет ошибка по умолчанию, потому что вы пытаетесь освободить c, а это не то, что раньше было malloc'ом. Мой вопрос: что происходит с блоком, который я только что написал? Очевидно, что c больше не указывает на него, поэтому его нельзя использовать, но он все еще считается частью «свободного» списка или это явная утечка памяти?

Ответы [ 7 ]

12 голосов
/ 11 апреля 2009

Это утечка. Он будет возвращен ОС после завершения вашей программы.

10 голосов
/ 11 апреля 2009

Память все еще выделена, что вызывает утечку памяти. Хотели бы вы по-другому? Для машины / компилятора действительно нет способа узнать, что выделенная вами память должна быть восстановлена. Если бы это было неправильное поведение, ваш код работал бы вероятностно: вы никогда не могли бы доверять коду.

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

1 голос
/ 11 апреля 2009

Ну, imo, segfault, который вы получаете в этот момент, не потому, что вы пытаетесь освободить память, которую вы не выделяли ранее, segfault происходит, потому что вы пытаетесь сослаться на адрес памяти, который операционная система не предоставила вам разрешение. для вас (это определение ошибки сегментации).

В некоторых экспериментах говорится, что вы будете запускать свой пример кода в valgrind, вы получите это как вывод:

    ==6945== Invalid free() / delete / delete[]
    ==6945==    at 0x402265C: free (vg_replace_malloc.c:323)
    ==6945==    by 0x80483D5: main (bla.c:6)
    ==6945==  Address 0x7 is not stack'd, malloc'd or (recently) free'd
    ==6945== 
    ==6945== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 11 from 1)
    ==6945== malloc/free: in use at exit: 4 bytes in 1 blocks.
    ==6945== malloc/free: 1 allocs, 1 frees, 4 bytes allocated.

Так что это утечка памяти 'pur sang'. Теперь предположим, что вы измените код так, чтобы указатель, который вы пытаетесь освободить, был «рядом» с указанным вами указателем (чтобы операционная система все еще знала, что у вас есть к ней доступ, операционная система не предоставляет доступ к памяти на границах байтов). Скажем, мы модифицируем код следующим образом:

int main(){  
    int* c;  
    c = (int*)malloc(sizeof(int));  
    c++;
    free(c);  
    return 0;  
}

При запуске этого приложения вы больше не будете получать ошибку сегментации (генерируемую ядром), а предупреждение от glibc (владельца malloc () и free ())

    edb@Flying-Spaghetti-Monster:/tmp$ ./a.out 
    *** glibc detected *** ./a.out: free(): invalid pointer: 0x0804a00c ***
    ... followed by a trace

Итак, вы пытаетесь освободить некоторую память, о которой ядро ​​знает, что оно принадлежит вам, но о которой glibc не может вспомнить, передавая ее вам. Если вы запустите это в valgrind (который работает путем замены функции lib (), malloc (), realloc (), ... в libc и выполнения учета самостоятельно), вы получите такой вывод:

    ==6955== Invalid free() / delete / delete[]
    ==6955==    at 0x402265C: free (vg_replace_malloc.c:323)
    ==6955==    by 0x80483D2: main (bla.c:5)
    ==6955==  Address 0x418a02c is 0 bytes after a block of size 4 alloc'd
    ==6955==    at 0x4022AB8: malloc (vg_replace_malloc.c:207)
    ==6955==    by 0x80483C0: main (bla.c:3)
    ==6955== 
    ==6955== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 11 from 1)
    ==6955== malloc/free: in use at exit: 4 bytes in 1 blocks.
    ==6955== malloc/free: 1 allocs, 1 frees, 4 bytes allocated.
1 голос
/ 11 апреля 2009

Пожалуйста, не зажигайте меня, но мне не ясно, в чем суть вашего вопроса. Очевидно, что то, что вы делаете, находится в прямом противоречии с тем, что язык намеревается сделать. Это равносильно тому, чтобы сказать «что произойдет, если я заполню бензобак моей машины жидкостью для огнетушителя, просто потому, что сопло огнетушителя окажется в отверстии бензобака».
Я не пытаюсь быть придурком, я просто не понимаю, почему этот конкретный вопрос? Я могу думать о бесконечном множестве вопросов, которые используют преимущества указателей, и задаться вопросом о том, как много способов использовать их неправильно, что приводит к сбою приложения. Есть ли что-то, чего вы пытаетесь достичь, что бы делал ваш код *, если бы вы только могли найти правильный способ сделать это? Или вам интересно, есть ли какой-то внутренний механизм, который отслеживает ваши указатели и помогает вам восстановить эту память, если вы случайно потеряли ее? (Если так, то на этот вопрос был дан ответ выше).

1 голос
/ 11 апреля 2009

После malloc (), c - это переменная, содержащая адрес памяти. В этом случае c имеет значение адреса памяти первого выделенного вами байта.

Когда вы говорите c = 7, вы говорите "c теперь указывает на адрес памяти '7'". Поскольку в этом случае адрес памяти «7» не был выделен вашему процессу, вы не можете освободить его (). Теоретически возможно, что адрес памяти «7» (или 0xa73c930bf, или как вы его установили) действительно был выделен вашему процессу, и в этом случае free () будет успешным.

Однако после изменения значения c память все еще выделяется. У вашего процесса все еще есть память и данные - вы только что потеряли указатель. Вы не знаете, где начинается эта память.

Это нормальная ситуация. В C у вас могут быть разные переменные, указывающие на этот адрес памяти. У вас может даже быть «int», а не «int *», хранящий этот адрес памяти. C не пытается отследить, сохранили ли вы этот конкретный адрес памяти, который на самом деле является просто числом. Это было бы невозможной задачей, так как любая попытка отследить потребовала бы потери некоторой гибкости, предлагаемой указателями C.

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

Однако при выходе из программы вся память, выделенная вашему процессу, освобождается и становится доступной для использования другими программами.

1 голос
/ 11 апреля 2009

Этот код не будет зависать, если вы измените c = 7; на *c = 7;

0 голосов
/ 11 апреля 2009

Обратите внимание, что значение, возвращаемое malloc(), преобразовывать не нужно, поскольку преобразование void* -> int* выполняется автоматически.

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

c = malloc(sizeof *c);

Так что теперь, если вы измените тип c, вам вообще не нужно переписывать выделение.

[Изменить]

Также было бы неплохо проверить, что распределение прошло успешно (т.е. c != NULL).

...