Ответ 1
Вам не разрешено вызывать free
в нераспределенной памяти, стандарт четко указывает (слегка перефразированный, мой акцент):
Функция
free
заставляет пространство, на которое указывает его аргумент, освобождается, то есть становится доступным для дальнейшего выделения. Если аргумент является нулевым указателем, никаких действий не происходит. В противном случае, если аргумент не соответствует указателю, ранее возвращенному функцией управления памятью, или если пространство было освобождено вызовом free или realloc, поведение undefined.
Что произойдет, например, если адрес, который вы дважды освобождаете, был перераспределен в середине нового блока, а код, который он выделил, только что сохранил что-то там, которое выглядело как настоящий заголовок блока malloc? Как:
+- New pointer +- Old pointer
v v
+------------------------------------+
| <Dodgy bit> |
+------------------------------------+
Хаос, это что.
Функции распределения памяти - это инструмент, как бензопила, и, если вы используете их правильно, у вас не должно быть проблем. Однако, если вы неправильно их используете, последствия - это ваша собственная ошибка, либо развращение памяти, либо еще хуже, либо отсечение одного из ваших рук: -)
И в отношении комментария:
... он может изящно общаться с конечным пользователем о том, что он удваивает свободное место.
За исключением записи всех вызовов malloc
и free
, чтобы убедиться, что вы не освобождаете двойной блок, я не вижу его работоспособным. Это потребует огромных накладных расходов и все равно не устранит все проблемы.
Что произойдет, если:
- thread Выделенная и освобожденная память по адресу 42.
- поток B выделил память на адрес 42 и начал использовать его.
- thread Освобождение этой памяти второй раз.
- поток C назначил память адресу 42 и начал использовать его.
Затем у вас есть потоки B и C, которые думают, что они владеют этой памятью (они не обязательно должны быть потоками выполнения, я использую термин thread здесь как просто часть кода, который работает - все это может быть в один поток выполнения, но вызываемый последовательно).
Нет, я думаю, что текущие malloc
и free
просто отлично, если вы используете их правильно. Во что бы то ни стало подумайте о реализации своей собственной версии, я не вижу в этом ничего плохого, но я подозреваю, что вы столкнетесь с довольно сложными проблемами производительности.
Если вы хотите реализовать свою собственную оболочку вокруг free
, вы можете сделать ее более безопасной (за счет небольшого снижения производительности), в частности, с помощью myFreeXxx
вызовов ниже:
#include <stdio.h>
#include <stdlib.h>
void myFreeVoid (void **p) { free (*p); *p = NULL; }
void myFreeInt (int **p) { free (*p); *p = NULL; }
void myFreeChar (char **p) { free (*p); *p = NULL; }
int main (void) {
char *x = malloc (1000);
printf ("Before: %p\n", x);
myFreeChar (&x);
printf ("After: %p\n", x);
return 0;
}
Результат кода состоит в том, что вы можете вызвать myFreeXxx
указателем на указатель, и он будет выглядеть следующим образом:
- освободить память; и
- установите указатель на NULL.
Этот последний бит означает, что если вы снова попытаетесь освободить указатель, он ничего не сделает (потому что освобождение NULL специально включено стандартом).
Он не защитит вас от всех ситуаций, например, если вы сделаете копию указателя в другом месте, освободите оригинал, затем освободите копию:
char *newptr = oldptr;
myFreeChar (&oldptr); // frees and sets to NULL.
myFreeChar (&newptr); // double-free because it wasn't set to NULL.
Если вы используете C11, есть лучший способ, чем необходимость явного вызова другой функции для каждого типа теперь, когда C имеет возможность перекомпоновки времени выполнения. Вы можете использовать общий выбор для вызова правильной функции, сохраняя при этом безопасность типа:
#include <stdio.h>
#include <stdlib.h>
void myFreeVoid (void **p) { free (*p); *p = NULL; }
void myFreeInt (int **p) { free (*p); *p = NULL; }
void myFreeChar (char **p) { free (*p); *p = NULL; }
#define myFree(x) _Generic((x), \
int** : myFreeInt, \
char**: myFreeChar, \
default: myFreeVoid )(x)
int main (void) {
char *x = malloc (1000);
printf ("Before: %p\n", x);
myFree (&x);
printf ("After: %p\n", x);
return 0;
}
С этим вы просто вызываете myFree
и выбираете правильную функцию, основанную на типе.