#include<stdio.h> #define false 0 #define ok 1 //定義節點的結構 typedef struct node{ int data; struct node *next; }node; typedef struct node *linklist; //初始化鏈表,創建一個鏈表并賦值 void creatlist(linklist *l,int n) { linklist p; (*l) = (linklist)malloc(sizeof(node)); (*l)->next = NULL; int i; for (i = 0; i < n; i++) { p= (linklist)malloc(sizeof(node)); p->data = i; p->next = (*l)->next; (*l)->next = p; } } //鏈表查詢 int getelem(linklist l, int i, int *e) { int k = 1; linklist p=NULL; p = l->next; while (p&&k<i) { p = p->next; k++; } if (!p || k>i) return false; *e = p->data; return ok; } //鏈表特定位置插入元素 int listinsert(linklist *l, int i, int e) { int k = 1; linklist s=NULL, p=NULL; p = *l; int j = 1; while (p&&j<i) { p = p->next; j++; } if (!p || k>i) return false; s = (linklist)malloc(sizeof(node)); s->data = e; s->next = p->next; p->next = s; return ok; } //鏈表特定位置刪除元素 int listdelete(linklist *l, int i, int *e) { int j = 1; linklist p=NULL, q=NULL; p = *l; while (p&&j<i) { p = p->next; j++; } if (!p->next || j>i) return false; q = p->next; p->next = q->next; *e = q->data; free(q); return ok; } //打印鏈表 int seelist(linklist l) { linklist p=NULL; p = l->next; int k = 0; while (p) { printf("%4d", p->data); p = p->next; k++; } if (k == 0) { printf("鏈表為空"); return false; } return ok; } //刪除整個鏈表 int clearlist(linklist *l) { linklist p = NULL,q=NULL; p = (*l)->next; while (p) { q = p->next; free(p); p = q; } (*l)->next = NULL; return ok; } int main(void) { int a; linklist lb; creatlist(&lb, 5); seelist(lb); listinsert(&lb, 2, 0); seelist(lb); listdelete(&lb, 2, &a); seelist(lb); clearlist(&lb); return 0; }
1、剛申請指針就要指向NULL;給指針初始化時,如果不是規定它指向某個地址就要malloc申請空間;不用的指針要free。
2、typedef struct node linklist;這語句的含義是以后linklist就代表struct node 。這里的好處是編程更美觀簡潔。畢竟要用到指向指針的指針,不用這個typedef等下就要出現**。
3、以上代碼頭指針指向了頭結點,這個鏈表擁有頭結點,但是其實這種指向頭結點的頭指針可以不需要指向指針的指針
下面說明為什么可以不要使用指向指針的指針
頭指針是鏈表必須的元素,linklist p,這里定義了頭指針,頭指針通常代表該鏈表的名字,如果頭指針p就是頭結點,含有data、next,直接對調用p做參數,就可以更改p指向的結構的值,不需要指向指針的指針
先看一段簡單代碼:
#include<stdio.h> #include<stdlib.h> typedef struct node { int data; struct node *next; }node,*link; //初始化頭結點,此時頭指針就是頭結點 void init(link p) { p->data = 0; p->next = NULL; } //插入元素 void insrt(link p, int i,int a) { int j = 1; link b= (link)malloc(sizeof(node)); link c = p; while (j < i) { c = c->next; j++; } b->data = a; b->next = c->next; c->next = b; } //顯示鏈表 void seeq(link q) { link b = q->next; while (b != NULL) { printf("%d", b->data); b = b->next; } } void main() { int i; link l1 = (link)malloc(sizeof(node)); init(l1); for (i= 1; i < 5; i++) { insrt(l1, i, i); } seeq(l1); }
對段長的代碼進行改變也無需指向指針的指針,我們把所有的*l改成l,再把主函數里的&去掉
#include<stdio.h> #define false 0 #define ok 1 typedef struct node { int data; struct node *next; }node,*linklist; void creatlist(linklist l, int n) { linklist p; //去掉了l = (linklist)malloc(sizeof(node)); l->next = NULL; int i; for (i = 0; i < n; i++) { p = (linklist)malloc(sizeof(node)); p->data = i; p->next = l->next; l->next = p; } } int getelem(linklist l, int i, int *e) { int k = 1; linklist p = NULL; p = l->next; while (p&&k<i) { p = p->next; k++; } if (!p || k>i) return false; *e = p->data; return ok; } int listinsert(linklist l, int i, int e) { int k = 1; linklist s = NULL, p = NULL; p = l; int j = 1; while (p&&j<i) { p = p->next; j++; } if (!p || k>i) return false; s = (linklist)malloc(sizeof(node)); s->data = e; s->next = p->next; p->next = s; return ok; } int listdelete(linklist l, int i, int *e) { int j = 1; linklist p = NULL, q = NULL; p = l; while (p&&j<i) { p = p->next; j++; } if (!p->next || j>i) return false; q = p->next; p->next = q->next; *e = q->data; free(q); return ok; } int seelist(linklist l) { linklist p = NULL; p = l->next; int k = 0; while (p) { printf("%4d", p->data); p = p->next; k++; } if (k == 0) { printf("鏈表為空"); return false; } return ok; } int clearlist(linklist l) { linklist p = NULL, q = NULL; p = l->next; while (p) { q = p->next; free(p); p = q; } l->next = NULL; return ok; } int main(void) { int a; linklist lb = (linklist)malloc(sizeof(node));//這里有變動! creatlist(lb, 5); seelist(lb); listinsert(lb, 2, 0); seelist(lb); listdelete(lb, 2, &a); seelist(lb); clearlist(lb); return 0; }
可以看到除了&和,還有兩處變動,就是把原本init函數內的為指針申請空間改到了主函數內一開始申明頭指針時就申請空間,否則會報錯:使用了未初始化的局部變量lb。這是因為,剛申明指針時,即linklist lb,編譯器會為lb分配空間(32位系統4字節,64位系統8字節),此時lb的地址是已知的,但是lb的值時不知道的(malloc時才會定下來lb的值)。如果函數調用不知道值的lb,是沒辦法調用的。而如果函數是調用lb的地址,因為lb的地址是已知的,就可以調用了,可以在函數內再為lb申請空間。即函數調用的參數一定是初始化值了的參數*
但是實際上,如果頭指針linklist p,p的值就是一個單純的指針,它的值就是個結點的地址。按照上面的方法,我們想在鏈表頭插入一個結點b,b的地址為xxx,p->next=地址xxx,此時就改變了個結點的next的值,即個結點指向地址xxx,即b變成了第二個結點,沒辦法做到讓b變成個結點!所以需要改變p的值,讓p指向b,既然改變指針本身的值,而不是指針指向的對象的值,就需要用到指向指針的指針了。
想要了解更多的c++應用技術那就加入我們吧!