c - 如何处理 malloc 失败并返回 NULL?
问题描述
我对如何检查内存分配是否失败以防止由取消引用的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
从一个已经存在的节点开始创建一个新节点(调用函数),然后返回它。
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,是否应该立即停止循环,因为无论如何都不可能向列表中添加任何新元素?另外,按照我的方式将所有这些检查堆叠在一起是否正确?不是多余的吗?一旦 malloc 失败就立即终止程序会不会是错误的?我读到这对于多线程程序来说是有问题的,而且在某些情况下,程序可能能够在没有任何进一步分配内存的情况下继续运行,因此在任何可能的情况下将其视为致命错误都是错误的. 这是正确的吗?
很抱歉这篇文章很长,感谢您的帮助!
解决方案
这取决于更广泛的情况。对于某些程序,简单地中止是正确的做法。
对于某些应用程序,正确的做法是缩小缓存并重试malloc
。对于某些多线程程序,只需等待(让其他线程有机会释放内存)并重试即可。
对于需要高度可靠的应用程序,您需要一个应用程序级解决方案。我使用并经过实战测试的一种解决方案是:
- 在启动时分配一个紧急内存池。
- 如果
malloc
失败,释放一些应急池。 - 对于无法正常处理
NULL
响应的调用,睡眠并重试。 - 有一个服务线程尝试重新填充紧急池。
- 让使用缓存的代码通过减少内存消耗来响应未满的紧急池。
- 如果您有能力卸载负载,例如,通过将负载转移到其他实例,则在紧急池未满时执行此操作。
- 对于需要分配大量内存的任意操作,请检查紧急池的级别,如果未满或未接近,请不要执行该操作。
- 如果紧急池变空,则中止。
推荐阅读
- python - 使用 BeautifulSoup 检测文本的存在
- c++ - 以std :: function作为c ++中的参数的selectionSort
- php - PHP 中的 SQL 循环到 JSON,有 2 个表,基于相关表的 id
- c# - 如何解决 Connection is closed-OleDBConnection 错误
- android - rxjava - 不按顺序发出的项目
- java - Java Spring:如何将maven依赖声明为gradle
- html - Angular8 PrimeNG 多选禁用属性不起作用
- reactjs - 如何在 Material UI 中自动调整 AutoComplete 字段的大小?
- javascript - 使用 CSS 和 JS 的 HTML 可折叠表格部分
- javascript - VSCode 调试断点 - nodejs