——藉由實現一個簡單的鏈表效果講解一下二級指針,希望這篇文章能給閱讀的小夥伴一些幫助吧。

看文章的小夥伴應該對指針有一個大概的印象吧,這裡再來講解一下。說到指針那就不得不提另外一個東西了,那就是內存。指針存在的很大一部分意義就是為了給我們提供便捷的操縱內存的工具。對於我們來說,內存中最重要的東西就是「每個位置獨一無二的地址標識符以及內存中每個位置所存的值」。下面看一下內存模型圖:

而對於指針來說就是保存圖中的那個內存地址編號,並通過*操作符來操縱內存中的數據。這裡再補充一點:在我們的高級語言之中,我們平時使用的變數也是一種訪問內存的方式。畢竟如果每次修改數據都要記住那些地址未免有點蠢。那麼 我們平時聲明變數像

int a=1,b=2,c=44,d=12345,e=1111111;

就可以把上圖變為

此時如果我們在之前的基礎上再聲明一個指針的話

int a=1,b=2,c=44,d=12345,e=1111111;
int *p;//聲明一個指向int的指針
p = &a;//a的地址賦值給p

在我們的模型圖中就是:

這裡要明確的一件事是我們的指針p它本身也是一個變數,佔用4個位元組。既然我們現在知道a所代表的內存地址編號,那麼我們便可以通過*號來操縱裡面的數據啦!補充了這些,那麼接下來就步入我們今天的正題啦——二級指針。

首先我們先看下面一段代碼

int a = 3;
int *p = &a;
int **q = &p;

這裡的p很容易理解就是一個指向int型的指針,那我們來分析一下這個q,在這裡我們一定要明確一件事q它本質上還是一個指針,只不過它保存的是指針的地址,我們來看圖。

這下是不是一目瞭然多了呀!嘿嘿,既然它也是一個指針那麼我們就按照分析指針的方法來分析它好了。

int a = 3;
int b = 5;
int *p = &a;
/*
* 對於一級指針來說,首先*p告訴編譯器這是一個指針變數,
* 其次int則告訴編譯器這個指針所指向的內容是int型的。
*/
int **q = &p;
/*
* 二級指針同理,首先*q告訴編譯器這是一個指針變數,
* 其次int*告訴編譯器這個指針所指向的內容是一個int型的指針。
* 其實這個聲明我們可以把他拆開來成(int *)與(*p)理解,前面是保存的類型後面是變數的類型。
* 多級指針同理。
*/

//接下我們來對指針q進行操作來驗證我們的想法

printf("%d
",*p);//此時對p進行解引用操作,列印a的值3。

*q = &b;//結合上圖,我們對q進行解引操作得到的是p的內存的地址,也就是說此時的賦值操作是對p進行的。

printf("%d
",*p);//此時我們再對p進行解應操作,列印的值變為了b的值5。驗證了我們的想法。

**q = 10;//結合上面的分析你肯定知道這步操作的實際操作對象了吧,沒錯就是b。

printf("%d
",b);//列印輸出10

看過上面介紹的一些內容,我想你已經對二級指針有了個大概的認識吧,其實在實際操作中我們一般都只涉及到一級和二級指針。不過,你學會了二級指針,其他高級指針也是一樣分析的,只是多了幾個*而已。那麼接下來我們就來看一個實際中的例子吧。利用二級指針來實現一個鏈表,順帶再介紹一下C語言中的介面概念。

首先我們創建一個Link.h包含我們對鏈表的一些宏定義與對用戶暴露出來的介面。

#define LINK_TYPE int//這裡我們設定節點內保存的值類型為int
#define FALSE 0
#define TRUE 1
/*
* 這裡先定義一個結構體,包含一個值類型,和一個指向下一個節點的指針類型。
*/
typedef struct LINK_NODE {
LINK_TYPE value;
struct LINK_NODE *next;
} Node;
/*
* 暴露給用戶的函數介面。
*/

int insertList(Node **phead, LINK_TYPE);
/*
* 用於向鏈表插入數據,這裡可以看到我們使用了一個二級指針的形參用來保存傳入的節點指針,
* 與一個用來保存插入值的形參。
*/
void printList(Node *phead);
//用於列印鏈表的函數

接下來是包含頭文件中各函數的具體實現的Link.c文件。

#include "Link.h"
//因為要使用頭文件中聲明的Node結構體所以這裡把頭文件包含進來
//這裡解釋一下include後面跟<>與""的區別,<>一般是使用標準庫的頭文件,而""一般是你自已定義的頭文件。
#include<stdio.h>
#include<stdlib.h>

/*
* 這裡我定義了一個初始化Node節點的函數也是為指針開闢一塊堆內存空間,並且將節點內的next指針賦值為空。
* 並將這個指針返回出來。
* 因為這個函數我不打算對用戶暴露,所以可以看到我在頭文件中並沒有聲明這個函數,也即是用戶無法直接使用。
*/
Node* init(Node *pnode) {
pnode = (Node*)malloc(sizeof(Node));
pnode->next = NULL;
return pnode;
}

int insertList(Node **phead, LINK_TYPE value) {
if (*phead == NULL) {
//現在我們對這個二級指針進行解引操作,按照上面所講的內容,這裡實際操作的phead內保存的那個指針
//也就是我們在主函數中聲明的那個指針。
*phead = init(*phead);//如果該指針為空那麼我們就對他進行賦值初始化操作。
}
Node *next = NULL; //聲明一個新的指針來保存我們將要插入的那個節點。
next = init(next);
if (next == NULL)
return FALSE; //如果開闢失敗那麼返回一個FALSE。
next->value = value; //將要插入的值賦值給新節點的value屬性。
/*
* 接下來的操作就很重要了,(*phead)我們在這裡就可以理解為傳入的指針對象
* 如果該指針所指的結構體的next指針為空的話,也是說phead現在所指的是該該鏈表最後一個元素。
* 那麼就結束循環,將我們新創建的節點接到鏈表最後一個元素後面。
* 如果為空的話,那麼讓phead指針指向它後面一個節點,以此類推,知道phead指向最後一個節點。
*/
while ((*phead)->next)
{
*phead = (*phead)->next;
}
(*phead)->next = next;
return TRUE;
}
/*
* 把鏈表中的元素列印出來。
*/
void printList(Node *phead) {
while (phead != NULL)
{
printf("%d
", phead->value);
phead = phead->next;
}
}

最後則是我們的main.c文件

#include"Link.h"
#include<stdio.h>

int main() {
Node *pList = NULL;

insertList(&pList, 1);
insertList(&pList, 2);
printList(pList);

getchar();
return 0;
}

可以看到結果正常輸出。

這裡主要是通過這個例子讓小夥伴們對二級指針有個更好的瞭解,如果文章中有什麼錯誤之處也請各位小夥伴們指出來啦!嘿嘿,感謝你看完了這篇文章哦,希望能對你有幫助。我們下期見。哈哈,See you!

推薦閱讀:

相關文章