Я немного запутался в том, как проверить, не удалось ли выделить память, чтобы предотвратить любое неопределенное поведение, вызванное разыменованным указателем NULL
. Я знаю, что malloc
(и аналогичные функции) могут завершаться с ошибкой и возвращать NULL
, и по этой причине возвращаемый адрес должен всегда проверяться перед продолжением работы с остальной частью программы. Чего я не понимаю, так это того, как лучше всего справляться с подобными делами. Другими словами: что программа должна делать, когда вызов malloc
возвращает NULL
?
Я работал над реализацией двусвязного списка, когда возникло это сомнение.
struct ListNode {
struct ListNode* previous;
struct ListNode* next;
void* object;
};
struct ListNode* newListNode(void* object) {
struct ListNode* self = malloc(sizeof(*self));
if(self != NULL) {
self->next = NULL;
self->previous = NULL;
self->object = object;
}
return self;
}
Инициализация узла происходит только в том случае, если его указатель был правильно размещен. Если этого не произошло, эта функция конструктора возвращает NULL
.
Я также написал функцию, которая создает новый узел (вызывая функцию newListNode
), начиная с уже существующего узла, а затем возвращает it.
struct ListNode* createNextNode(struct ListNode* self, void* object) {
struct ListNode* newNext = newListNode(object);
if(newNext != NULL) {
newNext->previous = self;
struct ListNode* oldNext = self->next;
self->next = newNext;
if(oldNext != NULL) {
newNext->next = oldNext;
oldNext->previous = self->next;
}
}
return newNext;
}
Если newListNode
возвращает NULL
, createNextNode
также возвращает NULL
и узел, переданный функции, не затрагивается.
Тогда Структура ListNode используется для реализации фактического связанного списка.
struct LinkedList {
struct ListNode* first;
struct ListNode* last;
unsigned int length;
};
_Bool addToLinkedList(struct LinkedList* self, void* object) {
struct ListNode* newNode;
if(self->length == 0) {
newNode = newListNode(object);
self->first = newNode;
}
else {
newNode = createNextNode(self->last, object);
}
if(newNode != NULL) {
self->last = newNode;
self->length++;
}
return newNode != NULL;
}
, если создание нового узла не удается, функция addToLinkedList
возвращает 0, а сам связанный список остается без изменений.
Наконец, давайте рассмотрим эту последнюю функцию, которая добавляет все элементы связанного списка в другой связанный список.
void addAllToLinkedList(struct LinkedList* self, const struct LinkedList* other) {
struct ListNode* node = other->first;
while(node != NULL) {
addToLinkedList(self, node->object);
node = node->next;
}
}
Как я должен обрабатывать вероятность того, что addToLinkedList
может вернуть 0? Для того, что я собрал, malloc
терпит неудачу, когда больше невозможно выделить память, поэтому я предполагаю, что последующие вызовы после сбоя выделения также будут неудачными, я прав? Таким образом, если возвращается 0, должен ли l oop немедленно прекратиться, поскольку в любом случае невозможно будет добавить какие-либо новые элементы в список? Кроме того, правильно ли складывать все эти проверки друг над другом так, как я это сделал? Разве это не избыточно? Было бы неправильно просто немедленно прекратить программу, как только ошибка mallo c? Я прочитал, что это будет проблематично c для многопоточных программ, а также, что в некоторых случаях программа может продолжать работать без дальнейшего выделения памяти, поэтому было бы неправильно рассматривать это как фатальную ошибку в любой возможный случай. Это правильно?
Извините за действительно длинный пост и спасибо за вашу помощь!