提问人:Dr Linh Chi Nguyen 提问时间:7/2/2023 最后编辑:Vlad from MoscowDr Linh Chi Nguyen 更新时间:7/3/2023 访问量:107
在 C 语言中释放链表中的节点的微小改进
minor improvement in freeing nodes in linked list in C
问:
看看这段代码,它想要释放 C 语言链表内所有错位的节点:
ptr = list;
while (ptr != NULL)
{
node *next = ptr->next;
free(ptr);
ptr = next;
}
原因是,在释放当前节点之前,我们需要一个临时变量来存储指向下一个节点的指针。
你看到它在循环内吗?因此,每次循环运行时,都会创建一个新的临时节点来存储指向下一个节点的指针。如果我们在循环外创建变量节点,而在循环内部重新分配它,会更好吗?node *next = ptr->next;
喜欢这个:
ptr = list;
node *next = NULL;
while (ptr != NULL)
{
next = ptr->next;
free(ptr);
ptr = next;
}
会更有效率吗?
答:
这两个代码段之间的差异可以忽略不计。两个代码段具有相同的时间复杂度,即 ,其中 n 是链表中的节点数。它们遍历链表一次,释放每个节点。在第二个代码中初始化指针的附加步骤不会显著影响性能。O(n)
next
评论
next
next
next
创建了一个新的临时节点来存储指向下一个节点的指针
否,已创建一个新的临时节点指针。
这两个版本都可能被优化成完全相同的汇编代码,这可以在这个演示中看到,其中 clang 也生成两个相同的函数和 gcc。
如果我们在循环外创建变量节点,而在循环内部重新分配它,会更好吗?
相信你的编译器会为这种微不足道的优化发出高效的代码 - 否则会得到一个更好的编译器。为清楚起见,编写代码。
节省您宝贵的编码时间,进行重大改进。
过早的优化真的是万恶之源吗?
您是否看到节点 *next = ptr->next;在循环中吗?因此 每次循环运行时,都会创建一个新的临时节点,用于 存储指向下一个节点的指针。
你错了。如果在 for 循环中包含调用 的语句,如下所示printf
ptr = list;
while (ptr != NULL)
{
node *next = ptr->next;
printf( "&next = %p\n", ( void * )&next );
free(ptr);
ptr = next;
}
您将看到在 for 循环的每次迭代中输出变量的相同地址。该变量是在循环的每次迭代中从抽象 mashine 的角度正式创建的。实际上在这条线的物理层面上next
node *next = ptr->next;
使用了与第二个代码片段中相同的 assignemnt
next = ptr->next;
其中变量在 for 循环之前声明。
因此,在生成的代码级别上没有区别。
但是,从编写程序的角度来看,您应该在使用它们的最小范围内声明变量。
此外,引入的变量是冗余的。在 for 循环之后,指针将是一个悬空指针。因此,您需要将其设置为 for 循环之后。相反,你可以写ptr
list
NULL
while ( list != NULL )
{
node *next = list->next;
free( list );
list = next;
}
如您所见,没有冗余变量,for 循环后面的指针将等于 。list
NULL
建议的优化不会改变任何事情:局部变量用于存储指针,无论其范围是否是循环体的局部变量都没有区别。启用优化的编译器将分析变量的使用情况,并为两种方法生成相似(如果不是完全相同)的代码。next
next
第一个提供多种好处:
next
以最小的范围进行定义,使下一个程序员更容易一目了然地分析其有限的用途。- 在循环之前初始化是一个额外的无用步骤,编译器可能会消除它,但下一个程序员想知道是否在循环之后使用并且需要初始化。
next
next
- 第一个代码片段是一个经典的成语。使用经典习语可以减轻代码审查和校对的心理负担。
如果这个循环恰好是一个瓶颈,你应该在尝试优化之前测试它,你可能应该调查链表是否是最适合你的目的的数据结构,如果是的话,你可以使用其他方法来分配节点,如果你能一次释放它们:从预先分配的数组中分配节点是提高缓存效率和分配/取消分配性能的有效方法。
请注意,除非列表为空,否则指针在循环后将具有无效值。您应该将其设置为显式或直接在循环中使用它:list
NULL
while (list != NULL) {
node *next = list->next;
free(list);
list = next;
}
评论
list
ptr
void deleteList(node* list);
list
list
NULL
评论
ptr
list
node *next
for(...) { auto a = someExistingObject; }