前言
- 本文用到一個很重要的思想--泛型編程思想;不熟悉泛型的話,請自行搜索相關資料學習(void *,如memcpy,memmove,qsort,memset等庫函數均使用到了泛型思想) 。
- 本文最后會提供一個demo程序附件,該demo程序以c99標准進行編寫的,在Linux-gcc下調試通過,vc6下可能會有錯誤。
- 本文圖示中,紅色實線表示要添加的地方,黑色虛線表示要斷開的地方,黑色實線保持原樣。
- 本文鏈表設計為最簡單的非循環單鏈表。
數組與鏈表比較
數組
|
鏈表
|
|
優點
|
存取速度快
操作方便
|
不限制大小
插入刪除易於實現
空間無需連續
|
缺點
|
插入刪除等操作不易實現
需要連續空間存儲
數組元素需要類型相同
|
存取速度慢
操作復雜
|
通過比較可以發現,數組的優點正好就是鏈表的缺點,而鏈表的缺點正好是數組的優點。
鏈表的分類
單鏈表
每個節點有1個指針域,指向后繼(next)
雙鏈表
每個節點都有2個指針域,一個指向前驅(previous),一個指向后繼(next)
循環鏈表
首尾相接,tail->next = head; 能通過任意一個節點找到其他所有的節點
非循環鏈表
首尾不相接,tail->next = NULL;通過head可以找到所有節點。
鏈表數據結構
typedef struct node{ //節點結構
void *data;
struct node *next;
} node;
typedef struct { //鏈表結構
struct node *head;
struct node *tail;
long len;
} List;
void *data;
struct node *next;
} node;
typedef struct { //鏈表結構
struct node *head;
struct node *tail;
long len;
} List;
常見鏈表算法
初始化
void list_init(List *list);
銷毀
void list_destroy(List *list, void (*destroy)(void *));
插入
void list_insert(List *list, void *data);
頭插法 void list_insert_at_head(List *list, void *data);
隨插法 void list_insert_at_index(List *list, void *data);
尾插法 void list_insert_at_tail(List *list, void *data, long idx);
刪除
void *list_delete(List *list, void *key, int (*compare)(const void *, const void *));
搜索
void *list_search(List *list, void *key, int (*compare)(const void *, const void *));
排序
void *list_sort(List *list, void *key, int (*compare)(const void *, const void *));
遍歷
void list_traverse(List *list, void (*handle)(void *));
逆序 void list_reverse(List *list);
求長度
long get_lenth(List *list);
獲取鏈表節點
void *list_get_element(List *list, long index);
判斷空鏈表
bool is_empty(List *list);
各算法的詳解與實現
已知鏈表結構如下所示
typedef
struct{
//鏈表結構
struct node *head;
struct node *tail;
long len;
} List;
struct node *head;
struct node *tail;
long len;
} List;
該鏈表有具有3個屬性,頭指針head,尾指針tail以及鏈表長度len;
首先,定義一個鏈表,
List list;
此時鏈表結構如下所示:

鏈表的初始化
鏈表剛建立的時候,是不含任何有效數據的,也就是說不含有效節點。因此頭指針head和尾指針tail無指向,即指向NULL,此時鏈表長度為0。
即此時鏈表結構如下所示

程序實現
void list_init(List *list)
{
list->head = NULL;
list->tail = NULL;
list->len = 0;
}
{
list->head = NULL;
list->tail = NULL;
list->len = 0;
}
剛初始化完的鏈表是一個空鏈表,空鏈表的頭指針必定為NULL,該特征可以作為判定空鏈表的依據,由此實現
判斷空鏈表的程序
#include <stdbool.h>
bool is_empty(List *list)
{
return (list->head == NULL);
}
bool is_empty(List *list)
{
return (list->head == NULL);
}
鏈表節點的插入
鏈表初始化完成之后,就可以進行各種操作(如插入刪除節點,求長度,銷毀鏈表等)了,下面來解釋一下鏈表插入的操作方法;
插入節點需要分2種情況討論:
-
鏈表為空時,插入的第一個節點(首節點);
-
在非空鏈表上插入一個節點。
1、第一種情況
該種情況可用下圖表示

此時,只有一個節點A,此時節點A既是首節點(因此head指向節點A),又是尾節點(因此tail也指向節點A,並且A->next = NULL);同時,鏈表長度要相應的+1(插入程序中,len始終呈++的狀態,下文不再贅述)。
程序實現(假設n為待插入的節點)
struct node *new;
list->head = new;
list->tail = new;
new->next = NULL;
list->len++;
list->head = new;
list->tail = new;
new->next = NULL;
list->len++;
2、第二種情況
第二種情況又分為3種情況,根據新節點的插入位置,分為
1.在頭部插,頭插法:list_insert_at_head(...);
2.在尾部插,尾插法:list_insert_at_tail(...);
3.在第index個節點處插,隨插法:list_insert_at_index(...)。
2.1、頭插法

分析圖例可知,已有鏈表(左側),list->head = A;現在要在head處插入一個節點N,需要斷開head與A的連接,使N->next = A;然后list->head = N(這兩步不可調換順序,可以想想為什么?)。
程序實現如下:
struct node *new;
new->next = list->head->next;
list->head = new;
list->len++;
new->next = list->head->next;
list->head = new;
list->len++;
2.2、尾插法

分析圖例可知,已有鏈表(左側),list->tail = Z;現在要在tail處插入一個節點N,需要斷開tail與Z的連接,使Z->next = N;(即list->tail->next = N;)然后list->tail = N(這兩步可以調換順序嗎?想想為什么?),然后再使N->next = NULL。
程序實現如下:
struct node *new;
list->tail->next = new;
list->tail = new;
new->next = NULL;
list->len++;
list->tail->next = new;
list->tail = new;
new->next = NULL;
list->len++;
2.3、隨插法

分析圖例可知,已有鏈表(左側),A->next= B;現在要在A處插入一個節點N,需要斷開A與B的連接,使N->next = B;(即N->next = A->next;)然后使A->next = N(這兩步可以調換順序嗎?想想為什么?)。
隨插法由於插入位置不確定,所以不一定在圖中的A節點處插入,有可能是B、C、D甚至Z。所以此處需要通過用戶給定的index值配合list->head來找到插入位置。
代碼實現如下:
static struct node *make_node(void *data) //把用戶傳遞過來的數據打包為一個鏈表節點
{
struct node *n;
n = malloc(sizeof(struct node));
assert(n != NULL);
n->next = NULL;
n->data = data;
return n;
}
void list_insert_at_head(List *list, void *data) //頭插法
{
struct node *n;
n = make_node(data);
if(list->head == NULL){ //如果是空鏈表
list->head = n;
list->tail = n;
}
else{ //如果不是非空鏈表
n->next = list->head;
list->head = n;
}
list->len++;
}
void list_insert_at_tail(List *list, void *data) //尾插法
{
struct node *n;
n = make_node(data);
if(list->head == NULL){ //如果是空鏈表
list->head = n;
list->tail = n;
n->next = NULL;
}
else{ //如果不是非空鏈表
list->tail->next = n;
list->tail = n;
n->next = NULL;
}
list->len++;
}
void list_insert_at_index(List *list, void *data, long index)
{
long i = 1; //從1開始算
struct node *p, *n;
p = list->head;
while(p && i < index){
p = p->next;
i++;
}
if(p){ //如果鏈表遍歷完了,計數i還沒到index,說明第index個節點不存在。
n = make_node(data);
n->next = p->next;
p->next = n;
list->len++;
}
}
void list_insert(List *list, void *data) //默認采用尾插法
{
list_insert_at_tail(list, data);
}
{
struct node *n;
n = malloc(sizeof(struct node));
assert(n != NULL);
n->next = NULL;
n->data = data;
return n;
}
void list_insert_at_head(List *list, void *data) //頭插法
{
struct node *n;
n = make_node(data);
if(list->head == NULL){ //如果是空鏈表
list->head = n;
list->tail = n;
}
else{ //如果不是非空鏈表
n->next = list->head;
list->head = n;
}
list->len++;
}
void list_insert_at_tail(List *list, void *data) //尾插法
{
struct node *n;
n = make_node(data);
if(list->head == NULL){ //如果是空鏈表
list->head = n;
list->tail = n;
n->next = NULL;
}
else{ //如果不是非空鏈表
list->tail->next = n;
list->tail = n;
n->next = NULL;
}
list->len++;
}
void list_insert_at_index(List *list, void *data, long index)
{
long i = 1; //從1開始算
struct node *p, *n;
p = list->head;
while(p && i < index){
p = p->next;
i++;
}
if(p){ //如果鏈表遍歷完了,計數i還沒到index,說明第index個節點不存在。
n = make_node(data);
n->next = p->next;
p->next = n;
list->len++;
}
}
void list_insert(List *list, void *data) //默認采用尾插法
{
list_insert_at_tail(list, data);
}
鏈表節點的刪除
刪除鏈表中的一個已有節點,如下圖所示:

分析圖例可知,已有鏈表(左側),A->next= B;B->next = C;現在要刪除節點B,那么需要斷開節點AB和BC之間的關系,然后把AC連接起來。即A->next = A->next->next;然后根據需要f釋放節點B即可。
程序實現:
void * list_delete(List *list, void *key,
int (*compare)(const void *, const void *)) //以key為刪除關鍵詞,compare為節點數據比較機制,由用戶自己編寫
{
void *data;
struct node *n, *t;
n = list->head;
if(!compare(n->data, key)){ //如果要刪除的節點為首節點
printf("list_delete\n");
t = n;
data = n->data;
list->head = n->next;
free(t);
list->len--;
return data;
}
while(n->next != NULL){ //遍歷查找符合條件的節點,刪除之
if(compare(n->next->data, key) == 0){ //只刪除第一個符合條件的節點。
t = n->next;
if(n->next == list->tail){
list->tail = n;
}
n->next = n->next->next;
data = t->data;
free(t);
list->len--;
return data; //把刪除的數據返回給用戶,供用戶后續的處理使用。
}
n = n->next;
}
return NULL; //沒找到匹配的節點,返回NULL
}
int (*compare)(const void *, const void *)) //以key為刪除關鍵詞,compare為節點數據比較機制,由用戶自己編寫
{
void *data;
struct node *n, *t;
n = list->head;
if(!compare(n->data, key)){ //如果要刪除的節點為首節點
printf("list_delete\n");
t = n;
data = n->data;
list->head = n->next;
free(t);
list->len--;
return data;
}
while(n->next != NULL){ //遍歷查找符合條件的節點,刪除之
if(compare(n->next->data, key) == 0){ //只刪除第一個符合條件的節點。
t = n->next;
if(n->next == list->tail){
list->tail = n;
}
n->next = n->next->next;
data = t->data;
free(t);
list->len--;
return data; //把刪除的數據返回給用戶,供用戶后續的處理使用。
}
n = n->next;
}
return NULL; //沒找到匹配的節點,返回NULL
}
鏈表的遍歷
使用node = node->next的方式,依次得到各個節點,對各個節點使用handle方法,即實現了鏈表的遍歷。
void list_traverse(List *list, void (*handle)(void *)) //handle為節點遍歷策略,由用戶自己編寫
{
struct node *p;
p = list->head;
while(p){
handle(p->data);
p = p->next;
}
}
{
struct node *p;
p = list->head;
while(p){
handle(p->data);
p = p->next;
}
}
鏈表的搜索
根據用戶給點的數據遍歷匹配節點,如果找到了,則將該節點的數據域返回給用戶;找不到返回NULL。
void *list_search(List *list, void *key, int (*compare)(const void *, const void *)) //以key為搜索關鍵詞,compare為節點數據比較機制,由用戶自己編寫
{
struct node *n;
n = list->head;
while(n){
if(!compare(n->data, key)){ //找到了,返回找到的數據
return n->data;
}
n = n->next;
}
return NULL; //找不到,返回NULL
}
{
struct node *n;
n = list->head;
while(n){
if(!compare(n->data, key)){ //找到了,返回找到的數據
return n->data;
}
n = n->next;
}
return NULL; //找不到,返回NULL
}
鏈表的排序
排序思想:新建一個鏈表tmp,然后依次取出原鏈表list中的最小節點,以頭插法或者尾插法的機制插入到tmp鏈表中,直到原鏈表list為空。然后再把list指向tmp,此時list即為排序好的鏈表。當然也可以采用選擇排序、冒泡排序等其它方式實現。
static struct node * find_min_node(List *list,
int (*compare)(const void *, const void *)) //找最小節點,鏈表排序用;以compare為比較機制,由用戶自己編寫
{
struct node *min, *n;
n = list->head;
min = list->head;
while(n) {
if(compare(min->data, n->data) > 0) {
min = n;
}
n = n->next;
}
return min;
}
static void delete_node(List *list, struct node *key) //以節點數據key為關鍵詞,刪除匹配的節點,鏈表排序用;
{
struct node *n;
n = list->head;
if(n == key){
list->head = n->next;
return;
}
while(n->next){
if(n->next == key){
if(key == list->tail){
list->tail = n;
}
n->next = n->next->next;
return;
}
n = n->next;
}
}
static void insert_node(List *list, struct node *key)//以節點數據key為關鍵詞,插入匹配的節點,鏈表排序用;
{
if(list->head == NULL){
list->head = key;
list->tail = key;
}else{
list->tail->next = key;
list->tail = key;
}
}
void list_sort(List *list,
int (*compare)(void *, void *))
{
List tmp;
struct node *n;
list_init(&tmp);
while(! is_empty(list)) {
n = find_min_node(list, compare);
delete_node(list, n);
n->next = NULL;
insert_node(&tmp, n);
}
list->head = tmp.head;
list->tail = tmp.tail;
}
int (*compare)(const void *, const void *)) //找最小節點,鏈表排序用;以compare為比較機制,由用戶自己編寫
{
struct node *min, *n;
n = list->head;
min = list->head;
while(n) {
if(compare(min->data, n->data) > 0) {
min = n;
}
n = n->next;
}
return min;
}
static void delete_node(List *list, struct node *key) //以節點數據key為關鍵詞,刪除匹配的節點,鏈表排序用;
{
struct node *n;
n = list->head;
if(n == key){
list->head = n->next;
return;
}
while(n->next){
if(n->next == key){
if(key == list->tail){
list->tail = n;
}
n->next = n->next->next;
return;
}
n = n->next;
}
}
static void insert_node(List *list, struct node *key)//以節點數據key為關鍵詞,插入匹配的節點,鏈表排序用;
{
if(list->head == NULL){
list->head = key;
list->tail = key;
}else{
list->tail->next = key;
list->tail = key;
}
}
void list_sort(List *list,
int (*compare)(void *, void *))
{
List tmp;
struct node *n;
list_init(&tmp);
while(! is_empty(list)) {
n = find_min_node(list, compare);
delete_node(list, n);
n->next = NULL;
insert_node(&tmp, n);
}
list->head = tmp.head;
list->tail = tmp.tail;
}
鏈表逆序
如下圖所示:

基本思想:遍歷一遍鏈表,依次處理每個節點的next指針指向;遍歷完一遍鏈表,鏈表的順序就倒置過來了。見下列程序實現:
void list_reverse(List *list)
{
struct node *pre = NULL, *next, *p = list->head;
list->tail = list->head; //tail指向head;第一次head到tail的倒置。
while(p){
next = p->next;
if(!next){ //當p->next為最后一個節點時,讓head指向p->next;最后一次tail到head的倒置。
list->head = p;
}
//備份當前節點為pre,作為其下一個節點的next(第一個節點為NULL,初始化時已定義);中間部分節點的倒置。
p->next = pre;
pre = p;
p = next;
}
}
{
struct node *pre = NULL, *next, *p = list->head;
list->tail = list->head; //tail指向head;第一次head到tail的倒置。
while(p){
next = p->next;
if(!next){ //當p->next為最后一個節點時,讓head指向p->next;最后一次tail到head的倒置。
list->head = p;
}
//備份當前節點為pre,作為其下一個節點的next(第一個節點為NULL,初始化時已定義);中間部分節點的倒置。
p->next = pre;
pre = p;
p = next;
}
}
求鏈表長度
由於鏈表結構中添加了len屬性,因此獲取鏈表長度,直接讀取len即可。如果鏈表結構中不實用len屬性,則需要遍歷一遍鏈表,統計循環次數。
程序實現:
long get_lenth(List *list)
{
return (list->len);
}
{
return (list->len);
}
獲取鏈表某節點的數據
遍歷鏈表到第idx個節點,將該節點處的數據返回給用戶即可。
程序實現:
void *list_get_element(List *list, int idx)
{
int i = 0;
struct node *n;
n = list->head;
while(n && i < idx){
i ++;
n = n->next;
}
if(n){
return n->data;
}
return NULL;
}
{
int i = 0;
struct node *n;
n = list->head;
while(n && i < idx){
i ++;
n = n->next;
}
if(n){
return n->data;
}
return NULL;
}
鏈表的注銷
使用完鏈表之后,需要銷毀鏈表來釋放資源;秉着誰的數據誰負責的原則,需要傳遞一個函數指針到list_destroy中,用於銷毀節點的數據;
void list_destroy(List *list, void (*destroy)(void *)) //destroy為銷毀鏈表時對節點數據的處理函數,由用戶自己編寫。傳遞NULL時表示不做處理
{
list->len = 0;
struct node *n, *tmp;
n = list->head;
while(n){
tmp = n->next; //tmp只起一個記錄n->next的功能,否則后面把n free掉之后,就找不到n->next了。
if(destroy){ //傳遞用戶自定義的數據處理函數,為0時不執行
destroy(n->data); //使用用戶提供的destroy函數來釋放用戶的數據。
}
free(n);
n = tmp; //把n free掉之后,再把tmp給n,相當於把n->next給n,如此循環遍歷鏈表,挨個刪除。
}
}
{
list->len = 0;
struct node *n, *tmp;
n = list->head;
while(n){
tmp = n->next; //tmp只起一個記錄n->next的功能,否則后面把n free掉之后,就找不到n->next了。
if(destroy){ //傳遞用戶自定義的數據處理函數,為0時不執行
destroy(n->data); //使用用戶提供的destroy函數來釋放用戶的數據。
}
free(n);
n = tmp; //把n free掉之后,再把tmp給n,相當於把n->next給n,如此循環遍歷鏈表,挨個刪除。
}
}
Sample1
簡單的功能測試函數,程序無實際意義。
#include <stdio.h>
#include "list.h"
typedef struct test{
int val1;
float val2;
}test_t;
void handle(void *data)
{
test_t *test = (test_t *)data;
printf("val1:%d, val2:%f\n", test->val1, test->val2);
}
int compare_int(const void *s1, const void *s2)
{
test_t *data1 = (test_t *)s1;
int *data2 =(int *)s2;
return (data1->val1 - *data2);
}
int compare_int_sort(const void *s1, const void *s2)
{
test_t *data1 = (test_t *)s1;
test_t *data2 = (test_t *)s2;
return (data1->val1 - data2->val1);
}
void print(List *list)
{
printf("list len = %ld\n", list_get_lenth(list));
if(!is_empty(list)){
//test list_reverse
list_traverse(list, handle);
}
else{
printf("\tthe list is empty\n");
}
}
int main(void)
{
List list;
list_init(&list);
test_t test1 = {10, 10.5};
test_t test2 = {20, 20.5};
test_t test3 = {30, 30.5};
test_t test4 = {40, 40.5};
test_t test5 = {50, 50.5};
//test list_insert
printf("------insert(_at_tail)----\n");
list_insert(&list, &test1);
list_insert(&list, &test2);
list_insert(&list, &test3);
print(&list);
//test list_delete
printf("------delete----\n");
list_delete(&list, &test1.val1, compare_int);
print(&list);
//test list_insert_at_head
printf("------insert_at_head----\n");
list_insert_at_head(&list, &test4);
print(&list);
//test list_insert_at_index
printf("------insert_at_index(2)----\n");
list_insert_at_index(&list, &test5, 2);
print(&list);
//test list_reverse
printf("------reverse----\n");
list_reverse(&list);
print(&list);
//test list_search
int key = 20;
test_t *ret;
printf("------search----\n");
ret = list_search(&list, &key, compare_int);
printf("%d:%f\n", ret->val1, ret->val2);
key = 50;
ret = list_search(&list, &key, compare_int);
printf("%d:%f\n", ret->val1, ret->val2);
//test list_get_element
printf("------list_get_element----\n");
ret = list_get_element(&list, 2);
printf("%d:%f\n", ret->val1, ret->val2);
ret = list_get_element(&list, 3);
printf("%d:%f\n", ret->val1, ret->val2);
//test list_sort
printf("-----sort----\n");
list_sort(&list, compare_int_sort);
print(&list);
//test list_destroy
printf("-----destroy----\n");
list_destroy(&list, NULL);
return 0;
}
#include "list.h"
typedef struct test{
int val1;
float val2;
}test_t;
void handle(void *data)
{
test_t *test = (test_t *)data;
printf("val1:%d, val2:%f\n", test->val1, test->val2);
}
int compare_int(const void *s1, const void *s2)
{
test_t *data1 = (test_t *)s1;
int *data2 =(int *)s2;
return (data1->val1 - *data2);
}
int compare_int_sort(const void *s1, const void *s2)
{
test_t *data1 = (test_t *)s1;
test_t *data2 = (test_t *)s2;
return (data1->val1 - data2->val1);
}
void print(List *list)
{
printf("list len = %ld\n", list_get_lenth(list));
if(!is_empty(list)){
//test list_reverse
list_traverse(list, handle);
}
else{
printf("\tthe list is empty\n");
}
}
int main(void)
{
List list;
list_init(&list);
test_t test1 = {10, 10.5};
test_t test2 = {20, 20.5};
test_t test3 = {30, 30.5};
test_t test4 = {40, 40.5};
test_t test5 = {50, 50.5};
//test list_insert
printf("------insert(_at_tail)----\n");
list_insert(&list, &test1);
list_insert(&list, &test2);
list_insert(&list, &test3);
print(&list);
//test list_delete
printf("------delete----\n");
list_delete(&list, &test1.val1, compare_int);
print(&list);
//test list_insert_at_head
printf("------insert_at_head----\n");
list_insert_at_head(&list, &test4);
print(&list);
//test list_insert_at_index
printf("------insert_at_index(2)----\n");
list_insert_at_index(&list, &test5, 2);
print(&list);
//test list_reverse
printf("------reverse----\n");
list_reverse(&list);
print(&list);
//test list_search
int key = 20;
test_t *ret;
printf("------search----\n");
ret = list_search(&list, &key, compare_int);
printf("%d:%f\n", ret->val1, ret->val2);
key = 50;
ret = list_search(&list, &key, compare_int);
printf("%d:%f\n", ret->val1, ret->val2);
//test list_get_element
printf("------list_get_element----\n");
ret = list_get_element(&list, 2);
printf("%d:%f\n", ret->val1, ret->val2);
ret = list_get_element(&list, 3);
printf("%d:%f\n", ret->val1, ret->val2);
//test list_sort
printf("-----sort----\n");
list_sort(&list, compare_int_sort);
print(&list);
//test list_destroy
printf("-----destroy----\n");
list_destroy(&list, NULL);
return 0;
}
輸出結果如下所示:
--
--
--insert(_at_tail)
--
--
list len = 3
val1 : 10, val2 : 10. 500000
val1 : 20, val2 : 20. 500000
val1 : 30, val2 : 30. 500000
-- -- --delete -- --
list len = 2
val1 : 20, val2 : 20. 500000
val1 : 30, val2 : 30. 500000
-- -- --insert_at_head -- --
list len = 3
val1 : 40, val2 : 40. 500000
val1 : 20, val2 : 20. 500000
val1 : 30, val2 : 30. 500000
-- -- --insert_at_index( 2) -- --
list len = 4
val1 : 40, val2 : 40. 500000
val1 : 20, val2 : 20. 500000
val1 : 50, val2 : 50. 500000
val1 : 30, val2 : 30. 500000
-- -- --reverse -- --
list len = 4
val1 : 30, val2 : 30. 500000
val1 : 50, val2 : 50. 500000
val1 : 20, val2 : 20. 500000
val1 : 40, val2 : 40. 500000
-- -- --search -- --
20 : 20. 500000
50 : 50. 500000
-- -- --list_get_element -- --
50 : 50. 500000
20 : 20. 500000
-- -- -sort -- --
list len = 4
val1 : 20, val2 : 20. 500000
val1 : 30, val2 : 30. 500000
val1 : 40, val2 : 40. 500000
val1 : 50, val2 : 50. 500000
-- -- -destroy -- --
list len = 3
val1 : 10, val2 : 10. 500000
val1 : 20, val2 : 20. 500000
val1 : 30, val2 : 30. 500000
-- -- --delete -- --
list len = 2
val1 : 20, val2 : 20. 500000
val1 : 30, val2 : 30. 500000
-- -- --insert_at_head -- --
list len = 3
val1 : 40, val2 : 40. 500000
val1 : 20, val2 : 20. 500000
val1 : 30, val2 : 30. 500000
-- -- --insert_at_index( 2) -- --
list len = 4
val1 : 40, val2 : 40. 500000
val1 : 20, val2 : 20. 500000
val1 : 50, val2 : 50. 500000
val1 : 30, val2 : 30. 500000
-- -- --reverse -- --
list len = 4
val1 : 30, val2 : 30. 500000
val1 : 50, val2 : 50. 500000
val1 : 20, val2 : 20. 500000
val1 : 40, val2 : 40. 500000
-- -- --search -- --
20 : 20. 500000
50 : 50. 500000
-- -- --list_get_element -- --
50 : 50. 500000
20 : 20. 500000
-- -- -sort -- --
list len = 4
val1 : 20, val2 : 20. 500000
val1 : 30, val2 : 30. 500000
val1 : 40, val2 : 40. 500000
val1 : 50, val2 : 50. 500000
-- -- -destroy -- --
獲取Sample1
源碼
Sample2
預計是一個學生信息管理系統,待續.......
后記
通過
valgrind這個工具可以檢測內存泄漏,ubuntu下使用
sudo apt-get install valgrind
即可安裝;使用方法:
valgrind --tool=memcheck ./app
Sample1的檢查結果如下所示:
$ valgrind
--tool
=memcheck .
/app
==4298== Memcheck, a memory error detector
==4298== Copyright (C) 2002-2009, and GNU GPL'd , by Julian Seward et al.
==4298== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info
==4298== Command: ./app
==4298==
....
....
....
==4298==
==4298== HEAP SUMMARY:
==4298== in use at exit: 0 bytes in 0 blocks
==4298== total heap usage: 5 allocs, 5 frees, 40 bytes allocated
==4298==
==4298== All heap blocks were freed -- no leaks are possible
==4298==
==4298== Memcheck, a memory error detector
==4298== Copyright (C) 2002-2009, and GNU GPL'd , by Julian Seward et al.
==4298== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info
==4298== Command: ./app
==4298==
....
....
....
==4298==
==4298== HEAP SUMMARY:
==4298== in use at exit: 0 bytes in 0 blocks
==4298== total heap usage: 5 allocs, 5 frees, 40 bytes allocated
==4298==
==4298== All heap blocks were freed -- no leaks are possible
==4298==
通過valgrind的檢測,我們發現,程序退出時,仍有0個內存塊上的0字節未釋放,即程序所申請的內存已經全部釋放;堆內存一共申請了5次,釋放了5次,共申請了40個字節;所有堆內存都釋放了,沒有內存泄漏。推薦你也使用一下,與之功能相同的軟件有很多,不再一一贅述。
附件列表