天天看點

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

【C語言】C語言——指針進階

🗽初階指針請點選此跳轉

⭐1. 字元指針

⭐2.指針數組

⭐ 3. 數組指針

⭐4. 數組傳參和指針傳參

⭐ 5. 函數指針

⭐ 6. 函數指針數組

⭐7. 指向函數指針數組的指針

⭐8. 回調函數

https://blog.csdn.net/DerrickWestbrook/article/details/117787490.

指針的主題,在上述連結文章中已經解釋過了,我們已經知道了指針的概念:

指針就是個變量,用來存放位址,位址唯一辨別一塊記憶體空間。

指針的大小是固定的4/8個位元組(32位平台/64位平台)。

指針是有類型的,指針的類型決定了指針的±整數的步長,指針解引用操作的時候的權限。

在本篇文章将繼續講解質指針的進階主題

指針裡有一種指針類型為字元指針,字元指針存放一個字元的位址

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

除此之外還有一種使用方式,如:

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

值得注意的是

char* pstr = “Rip Kobe”;

這行代碼的意思是把一個字元串“Rip Kobe”放到pstr指針變量裡了嗎?

其實不然,這行代碼的本質是把字元串 Rip Kobe 的首字元也就是 R 的位址放到了pstr中,當需要用的時候,隻需要找到這個字元串的首位址,就可以根據首位址找到整個字元串的位址

就像這行代碼

printf("%s\n", pstr);

pstr中存的是字元串的首位址,printf函數就可以根據這個首位址把整個字元串列印出來

還有一個點需要注意

看下面這段代碼

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

運作結果是

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

這裡str3和str4指向的是一個同一個常量字元串。C/C++會把常量字元串存儲到單獨的一個記憶體區域當幾個指針,指向同一個字元串的時候,他們實際會指向同一塊記憶體。但是用相同的常量字元串去初始化不同的數組的時候就會開辟出不同的記憶體塊。

是以str1和str2不同,str3和str4不同。

指針數組顧名思義就是一個存放指針的數組。

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

比如 int* arr1[10] 可以分開來看,

arr1[10]是一個有十個元素的數組,裡面存放的元素類型為整型指針

數組指針是指針?還是數組?

答案是:指針。

我們已經熟悉:

整形指針: int * pint; 能夠指向整形資料的指針。

浮點型指針: float * pf; 能夠指向浮點型資料的指針。

那數組指針應該是:能夠指向數組的指針

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

這兩行代碼差別在于

第一行代碼: * 先和int相結合,說明p1數組裡存有十個整型指針,這是個指針數組

第二行代碼: p先和 * 結合 說明p是一個指針變量,然後指針指向的是一個大小為10個整型的數組。

是以p是一個指針,指向一個數組,叫數組指針。

這裡要注意:[ ]的優先級要高于 * 号的,是以必須加上()來保證p先和 * 結合。

&數組名VS數組名

對于下面的數組

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

arr 和 &arr 分别是什麼呢?

我們知道arr是數組名,數組名表示數組首元素的位址。

那 &arr數組名到底是啥?

我們看一段代碼:

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

運作結果如下:

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

可見數組名和&數組名列印的位址是一樣的

為什麼是一樣的呢?

再來看如下代碼

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針
【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

根據上面的代碼我們發現,其實&arr和arr,雖然值是一樣的,但是意義應該不一樣的。

實際上: &arr 表示的是數組的位址,而不是數組首元素的位址,因為記憶體中通路數組,預設從首元素位址開始通路,是以表示整個數組的位址也是首元素的位址,但是意義是不同的。(細細體會一下)

數組的位址+1,跳過整個數組的大小,是以 &arr+1 相對于 &arr 的內插補點是40

接下來說說數組指針的使用

既然數組指針指向的是數組,那數組指針中存放的應該是數組的位址。

看代碼:

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

上邊這種寫法一般很少用,數組指針多用于二維數組傳參

請看如下代碼:

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針
【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

這裡需要注意幾個點:

1.數組名arr,表示首元素的位址

2.但是二維數組的首元素是二維數組的第一行

3.是以這裡傳遞的arr,其實相當于第一行的位址,是一個一維數組的位址

4.是以可以用數組指針來接收

但是接收的時候需要注意的是 int(*arr)[5] 和 int arr[3][5] 這兩種寫法都是可以的

指針數組和數組指針的總結:

int arr[5]; arr是一個整型數組,有5個int型元素

int *parr1[10]; parr1是一個數組,裡面有10個元素,每個元素是一個指向int型的指針

int (*parr2)[10]; parr2是一個數組指針,該指針指向的數組有10個元素,每個元素是int型

int (*parr3[10])[5]; parr3是一個數組,裡面有10個元素 ,每個元素是一個數組指針,該指針指向的數組有5個元素,每個元素的類型是int型

一維數組傳參

可以通過數組,也可以通過指針來接收,幾種方法方式如下:

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

①用數組來接收

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

②形參[]裡的值可以省略,因為在這裡沒有意義因為傳入的實際上是一個位址

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

③用整型指針接收一個整型數組的首元素位址

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

④用指針數組來接收指針,同理[ ]裡的值可以省略,因為在這裡沒有意義

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

⑤這裡的意思是arr2[20]裡每個元素是int*

然後又把arr2的首元素位址傳入了函數,是以要用 **arr 來接收;

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

二維數組傳參

同理也可以通過數組或者指針來接收:

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

①用數組

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

②這種方式不可以,行可以省略但是列不可以省略,和初始化二維數組是一樣的

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

③可以像這樣省略行

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

總結:二維數組傳參,函數形參的設計隻能省略第一個[ ]的數字。

因為對一個二維數組,可以不知道有多少行,但是必須知道一行多少元素。

這樣才友善運算。

④這種方式錯誤,二維數組的首元素是第一行,傳入的其實是一個一維數組的位址,不能用一個整型指針來接收

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

⑤也不能這樣,這是個整型指針數組

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

⑥二維數組的首元素是第一行,傳入的其實是一個一維數組的位址,一個一維數組的位址要用數組指針來接收,表示傳入一個位址,這個位址指向一個有5個元素數組,每個元素是int型

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

⑦這種方式錯誤,不能在此處用二級指針

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

一級指針傳參

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

思考:

當一個函數的參數部分為一級指針的時候,函數能接收什麼參數?

比如:

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

答案如下:

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

二級指針傳參

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

當函數的參數為二級指針的時候,可以接收什麼參數?

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

函數指針變量——存放函數的位址

了解函數指針之前先來了解下函數的位址:

如圖所示,列印函數 test 和 &test 得到的結果一模一樣

這裡需要注意一個問題:

函數名 和 &函數名 不同于 數組名 和 &數組名

數組名 和 &數組名,結果一樣,但是意義不同

函數名 和 &函數名,結果和意義都一樣,表示的都是函數的位址

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

函數名——函數的位址

&函數名——函數的位址

兩種表示方式完全一樣,都表示函數的位址

———————————————————————————————

函數指針的文法

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

這裡的pf就是一個函數指針變量

這個函數指針類型是int(*)(int,int)

表示指向參數是兩個int,整個函數傳回值類型為int的函數

函數指針的使用:

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

列印結果如下:

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

注意:

(*pf) (4,5) 也可以省略 * 号,寫成 pf (4,5) 也是正确的,解引用符号隻是為了友善了解指針,其實在此處解引用符号沒有意義

接下來看一行代碼加深了解:

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

這行代碼要如何了解?

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

函數指針數組——存放函數指針的數組

文法如下:

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

函數指針數組用途:轉移表

舉例:電腦

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

指向函數指針數組的指針是一個指針 指針指向一個 數組 ,數組的元素都是函數指針

定義文法如下:

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

回調函數就是一個通過函數指針調用的函數。如果你把函數的指針(位址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。回調函數不是由該函數的實作方直接調用,而是在特定的事或條件發生時由另外的一方調用的,用于對該事件或條件進行響應。

舉個栗子:

使用回調函數,模拟實作qsort(采用冒泡的方式)

注意:這裡第一次使用 void * 的指針,講解 void * 的作用

【C語言】C語言之深入指針進階(建議收藏以備不時之需)⭐ 6. 函數指針數組⭐7. 指向函數指針數組的指針

繼續閱讀