導言
筆者最近學習嚴薇敏教授的《數據結構》,同時也參考B站上王道考研的數據結構視頻,發現單鏈表傳入函數參數這一方面教材與視頻,與網絡上的一些實現寫法各有不同,有點迷惑。經過查了一些資料以后,終於撥雲見日。寫下這篇文章,希望為后來的同學們有所幫助。水平不足,還請多多指正!
指針
指針是指向內存中一定大小內存區域的變量。定義一個指針如:
int *p;
但這樣只是聲明了一個指針變量p,p指向哪里我們不清楚,需要指定這個指針變量指向的內存區域
int x = 2; int *p = NULL; //初始化,避免p是野指針 p = &x; //讓p指向x
指針變量實際上存放的是目標區域的內存地址。
我們要注意區分*p = 和 p =這兩種情況
*p = 2; //讓p指向的內存區域中的值變成2 p = &a; //讓指針p指向內存中的區域a,也就是讓p的值為a的地址
注意:指針變量的值是地址,取內容符號與指針變量搭配,改變的不是指針的值,而是指針指向的內存區域的值
函數中參數傳遞問題
C語言中,參數傳遞有兩種,一個是值傳遞,一個是地址傳遞。值傳遞情況下,C程序只是獲得了值,也就是變量的拷貝,在C程序內對變量的任何操作都不會引起內存中原本的變量的變化。想要改變傳遞進來的參數,就需要進行地址傳遞。
void test(int *a){ *a = 2; } int main(){ int x = 0; test(&a); //傳入變量的地址 printf("%d\n",x); //輸出結果為2 return 0; }
鏈表中指針的使用(帶頭結點為例)
我們在單鏈表中,用到了指針來實現單鏈表
typedef struct LNode{ int data; struct LNode *next; }LNode, *LinkList; LinkList L;
怎么理解這個L?
我們要知道,LinkList本質上是一個指針,它指向的是內存中大小為的LNode類型區域。那么,L這個指針,存儲的應該是目標區域的地址。
一開始我們只是聲明了這么一個指針L,還沒有將它指向一片內存區域。為了將它指向一片內存區域,我們需要使用malloc函數來分配內存空間
L = (LNode *)malloc(sizeof(LNode)); //注意要用(LNode *)或者(LinkList)
//來對malloc函數返回的地址進行類型轉換 if (L == NULL) return false;
這個時候我們就要注意,這個對單鏈表進行初始化的函數initList,接收的參數應該是怎么樣的?
經過驗證,我們得知,如果直接傳入指針L,那么我們將不能更改內存中L的指向區域。為了改變指針L,我們有兩種方案可以選擇:
-
通過return語句來返回地址
LinkList initList(LinkList L){ L = (LinkList)malloc(sizeof(LNode)); if (L == NULL) return NULL; else{ L->next = NULL; //設置L指向的區域(頭結點)的next域的值 return L; } } int main(){ LinkList L; L = initList(L); return 0; }
-
使用二級指針,來對一級指針L的值(指向的內存區域是哪一片)進行改變。
int initList(LinkList *L){ //此處的L是二級指針,指向的是:指向頭結點的頭指針 *L = (LinkList)malloc(sizeof(LNode));
//*L就是二級指針的內容,也就是一級指針存儲的值,也就是目標區域地址。所以,可以直接把*L當頭指針用 if (*L == NULL) return 0; else{ (*L)->next = NULL; return 1; } } int main(){ LinkList L; initList(&L); return 0; }
必須使用二級指針的操作
根據單鏈表的基本操作,我們總結一下必須要用二級指針的情況,實際上也就是對單鏈表內容進行更改的情況:
-
initList 鏈表初始化
-
listInsert 向指定位置插入
-
listDelete 刪除指定位置元素
-
-
getElem 獲取具體某個元素的結點(這個看情況,如果只是找到鏈表中第幾個元素的值,那么就沒必要用二級指針)