济南网站建设老威,虚拟空间网站回收池有什么作用,做电商运营还是网站运营哪个好,wordpress电子书与公众号内存泄漏#xff1a;
内存泄漏#xff08;Memory Leak#xff09;是指程序中已动态分配的内存由于某种原因程序未释放或无法释放#xff0c;导致系统内存的浪费#xff0c;严重时会导致程序运行缓慢甚至崩溃。这种情况在长时间运行的程序或大型系统中尤为常见#xff0c…内存泄漏
内存泄漏Memory Leak是指程序中已动态分配的内存由于某种原因程序未释放或无法释放导致系统内存的浪费严重时会导致程序运行缓慢甚至崩溃。这种情况在长时间运行的程序或大型系统中尤为常见因为它会随着时间的推移逐渐累积未释放的内存。
内存泄漏的原因 忘记释放内存这是最常见的内存泄漏原因。程序员在申请内存后由于疏忽或其他原因忘记了在不再需要时释放它。 异常终止如果程序在执行过程中因为异常如错误、崩溃等而终止那么它可能无法执行正常的清理代码导致内存泄漏。 缓存机制不当有时为了优化性能程序会缓存一些数据。如果缓存的数据量没有得到有效控制或者缓存的清理策略不合理就可能导致内存泄漏。 全局变量和静态变量全局变量和静态变量的生命周期与程序相同如果它们被用于存储动态分配的内存地址并且这些内存在使用完毕后没有被释放就会导致内存泄漏。 第三方库或API的使用使用第三方库或API时如果没有正确遵循其内存管理规则也可能导致内存泄漏。
valgrind: 它主要用于内存调试、内存泄漏检测以及程序性能分析。
内存碎片 内存碎片是指系统中所有不可用的空闲内存这些碎片之所以不能被使用是因为负责动态分配内存的分配算法使得这些空闲的内存无法使用。内存碎片可以分为外碎片和内碎片两种类型下面分别进行详细介绍
一、内存碎片的定义及分类
1. 外碎片
定义外部碎片指的是还没有被分配出去不属于任何进程但由于太小了无法分配给申请内存空间的新进程的内存空闲区域。产生原因频繁的内存分配和释放、不同大小的内存分配、内存对齐问题等都可能导致外碎片的产生。
2. 内碎片
定义内部碎片就是已经被分配出去能明确指出属于哪个进程却不能被利用的内存空间。产生原因假设有一块连续空闲内存空间当某个进程请求的内存大小与空闲内存块不完全匹配时系统可能会分配一个稍大一点的内存块给该进程从而产生内部碎片。
二、内存碎片的产生原因
频繁的内存分配和释放这是导致内存碎片的主要原因之一。不同大小的内存分配当系统中分配的内存大小不一致时也会导致碎片的产生。内存对齐的问题内存分配时如果没有进行对齐也容易导致碎片。
双向有头链表
双向有头链表是一种链表数据结构与单向链表不同双向链表中的每个节点都包含两个指针一个指向下一个节点另一个指向前一个节点。
创建链表
Dlink_t *creat_doulink()
{Dlink_t *pdoulink (Dlink_t *)malloc(sizeof(Dlink_t));if(NULL pdoulink){perror(malloc fail);return NULL;}pdoulink-phead NULL;pdoulink-clen 0;pthread_mutex_init((pdoulink-mutex),NULL);return pdoulink;
}
遍历链表
双向遍历由于每个节点都包含前后两个指针因此可以很方便地从前往后或从后往前遍历链表。
void doulink_for_each(DLink_t *pdoulink, int dir)
{if (is_empty_doulink(pdoulink))return;DLink_Node_t *p pdoulink-phead;if (dir)//正向{while (p ! NULL){printf(%d %s %d\n, p-data.id, p-data.name, p-data.score);p p-pnext;}}else//反向{while (p-pnext ! NULL){p p-pnext;}while (p ! NULL){printf(%d %s %d\n, p-data.id, p-data.name, p-data.score);p p-ppre;}}printf(\n);}
头插
int push_doulink_head(Dlink_t *pdoulink,DataType data)
{Dlink_Node_t *pnode (Dlink_Node_t*) malloc(sizeof(Dlink_Node_t));if(NULL pnode){perror(fail malloc);return -1;}pnode-data data;pnode-ppre NULL;pnode-pnext NULL;if(is_empty_doulink(pdoulink)){pdoulink-phead pnode;}else{pnode-pnext pdoulink-phead;pdoulink-phead-ppre pnode;pdoulink-phead pnode;}pdoulink-clen;return 0;
}
尾插
int push_doulink_end(Dlink_t *pdoulink,DataType data)
{Dlink_Node_t *pnode (Dlink_Node_t *)malloc(sizeof(Dlink_Node_t));if(pnode NULL){perror(fail malloc);return -1;}pnode-data data;pnode-ppre NULL;pnode-pnext NULL;if(is_empty_doulink(pdoulink)){pdoulink-phead pnode;}else{Dlink_Node_t *p pdoulink-phead;while(p-pnext ! NULL){p p-pnext;}p-pnext pnode;pnode-ppre p;}pdoulink-clen;return 0;
}
头删
int pop_doulink_head(Dlink_t *plink)
{if(is_empty_doulink(plink)){return 0;}Dlink_Node_t *p plink-phead;plink-phead p-pnext;p-ppre NULL;free(p);plink-clen--;return 0;
}
尾删
int pop_doulink_end(Dlink_t *plink)
{if(is_empty_doulink(plink))return 0;Dlink_Node_t *p plink-phead;if(NULL p-pnext){pop_doulink_head(plink);}while(p-pnext-pnext ! NULL){p p-pnext;}free(p-pnext);p-ppre p;p-pnext NULL;plink-clen--;
}
查找
Dlink_Node_t *find_data(Dlink_t *plink,char *name)
{if(is_empty_doulink(plink))return 0;int i 1;Dlink_Node_t *p plink-phead;while(p ! NULL){if(strcmp(name, p-data.name)0){break;}p p-pnext;if(p NULL){return NULL;}}return p;
}
修改
int change_data(Dlink_t *plink,char *name,int score)
{Dlink_Node_t *ptmp;ptmp find_data(plink,name);Dlink_Node_t *p plink-phead;while(p-pnext ! NULL){if(p ptmp){p-data.score score;}p p-pnext;}return 0;
}
销毁
void destroy_doulink(Dlink_t *plink)
{while(!is_empty_doulink(plink)){ pop_doulink_head(plink);}free(plink);
}双向链表的优势
双向链表相比于单向链表具有以下几个显著的优势
双向遍历能力 双向链表中的每个节点都包含两个指针一个指向前一个节点ppre另一个指向下一个节点pnext。这种结构使得双向链表可以从链表的任何一个节点开始向前或向后遍历整个链表。相比之下单向链表只能从头节点开始逐个节点向后遍历无法直接向前遍历。高效的插入和删除操作 在双向链表中插入或删除节点时由于可以直接访问要插入或删除节点的前一个和/或后一个节点因此可以更加高效地执行这些操作。例如在双向链表中删除一个节点时只需要修改该节点前后两个节点的指针使其绕过被删除的节点即可而不需要像单向链表那样可能需要遍历整个链表来找到要删除节点的前一个节点。空间效率 虽然双向链表中的每个节点需要额外的空间来存储指向前一个节点的指针但从整体来看它在内存使用上仍然具有较高的灵活性。链表可以在运行时动态地分配和释放内存而不需要像数组那样预先分配固定大小的内存块。这种动态内存分配的特性使得双向链表在处理不确定大小的数据集时更加灵活。
单向链表的优势 内存占用较少 单向链表的每个节点只需要存储一个指向下一个节点的指针以及存储数据所需的空间。而双向链表的每个节点除了存储数据外还需要存储两个指针一个指向前一个节点另一个指向下一个节点。因此在内存使用方面单向链表更为节省。 实现更简单 从实现的角度来看单向链表的结构更为简单直接因为它只涉及到一个方向的链接。这意味着在编写代码时你可能需要处理更少的边界情况和指针操作尤其是在实现一些基本的链表操作时如插入和删除。 特定的应用场景 在某些特定的应用场景中你可能只需要单向遍历链表而不需要反向遍历。例如当实现一个简单的栈后进先出或队列先进先出时单向链表就足够了因为它提供了所需的顺序访问功能而无需额外的反向遍历能力。 兼容性 在某些旧的或资源受限的系统中双向链表的额外内存开销可能是一个问题。在这些情况下单向链表由于其较小的内存占用而成为更合适的选择。 性能优势在某些情况下 虽然这一点可能并不总是成立但在某些特定的情况下单向链表可能具有更好的性能。例如在遍历链表时如果只需要从头到尾遍历一次并且不需要反向遍历那么单向链表可能会由于更简单的结构和更少的指针操作而表现出更好的缓存局部性从而在某些情况下提高性能。