指针学习2
主要是指針數組、數組指針、函數指針的學習,以及二重指針、二維數組的學習。
一、指針數組與數組指針
1、概念
- 指針數組的實質是一個數組,這個數組中存儲的內容全部是指針變量。
- 數組指針的實質是一個指針,這個指針指向的是一個數組。
2、分析指針數組與數組指針的表達式:int *p[5]; ?int (*p)[5]; ?int *(p[5]);
(1)一般規律:int *p;(p是一個指針); int p[5];(p是一個數組);
(2)定義符號時,首先要搞清楚定義的符號是誰(第一步:找核心),接著看誰跟核心結合(第二步:找結合),然后繼續向外擴展。
- 如果核心和*結合,表示核心是指針;如果核心和[]結合,表示核心是數組;如果核心和()結合,表示核心是函數。
- 第一個:int *p[5]; 核心是p,p是一個數組,數組有5個元素大,數組中的元素都是指針,指針指向的元素類型是int類型的;整個符號是一個指針數組。
- 第二個,int (*p)[5];核心是p,p是一個指針,指針指向一個數組,數組有5個元素,數組中存的元素是int類型; 總結一下整個符號的意義就是數組指針。
- 第三個,int *(p[5]); 解析方法和結論和第一個相同,()在這里是可有可無的。
二、函數指針與typedef
1、函數指針的實質
- 函數指針的實質還是指針,還是指針變量。本身占4字節(在32位系統中,所有的指針都是4字節)。函數指針、數組指針、普通指針之間并沒有本質區別,區別在于指針指向的東西是個什么玩意。
- 函數的實質是一段代碼,這一段代碼在內存中是連續分布的(一個函數的大括號括起來的所有語句將來編譯出來生成的可執行程序是連續的),所以對于函數來說很關鍵的就是函數中的第一句代碼的地址,這個地址就是所謂的函數地址,在C語言中用函數名這個符號來表示。
- 函數指針其實就是一個普通變量,這個普通變量的類型是函數指針變量類型,它的值就是某個函數的地址(也就是它的函數名這個符號在編譯器中對應的值)。
2、函數指針的書寫和分析方法
- C語言本身是強類型語言(每一個變量都有自己的變量類型),編譯器可以幫我們做嚴格的類型檢查。
- 假設函數是:void func(void); 對應的函數指針:void (*p)(void); 類型是:void (*)(void);
- 譬如函數是strcpy函數(char *strcpy(char *dest, const char *src);),對應的函數指針是:char *(*pFunc)(char *dest, const char *src);
- 函數名做右值時加不加&效果和意義都是一樣的。
3、typedef關鍵字的用法
- typedef是C語言中一個關鍵字,作用是用來定義(或者叫重命名類型);
- C語言中的類型一共有2種:一種是編譯器定義的原生類型(基礎數據類型,如int、double之類的);第二種是用戶自定義類型,是程序員自己定義的(譬如數組類型、結構體類型、函數類型·····)。
- 數組指針、指針數組、函數指針等都屬于用戶自定義類型。
- 有時候自定義類型太長了,用起來不方便,所以用typedef給它重命名一個短點的名字。
- 注意typedef是給類型重命名,也就是說typedef加工出來的都是類型,而不是變量。
三、函數指針實戰
1、用函數指針調用執行函數
- linux中命令行默認是行緩沖的。
- 即程序printf輸出的時候,linux不會一個字一個字的輸出內容,而是將其緩沖起來放在緩沖區等一行準備完了再一次性把一行全部輸出出來(為了效率)。
- linux判斷一行有沒有完的依據就是換行符'\n'(windows中換行符是\r\n, linux中是\n,iOS中是\r)。
- 也就是說你printf再多,只要沒有遇到\n(或者程序終止,或者緩沖區滿)都不會輸出而會不斷緩沖,這時候你是看不到內容輸出的。
- 因此,在每個printf打印語句(尤其是用來做調試的printf語句)后面一定要加\n,否則可能導致誤判。
- 用戶在輸入內容時結尾都會以\n結尾,但是程序中scanf的時候都不會去接收最后的\n,導致這個回車符還存留在標準輸入中。下次再scanf時就會先被拿出來,這就導致你真正想拿的那個數反而沒機會拿,導致錯誤。
2、結構體內嵌函數指針實現分層
(1)程序為什么要分層?
- 因為復雜程序東西太多一個人搞不定,需要更多人協同工作,于是乎就要分工。要分工先分層,分層之后各個層次由不同的人完成,然后再彼此調用組合共同工作。
- 分層寫代碼的思路是:有多個層次結合來完成任務,每個層次專注各自不同的領域和任務;不同層次之間用頭文件來交互。
- 分層之后上層為下層提供服務,上層寫的代碼是為了在下層中被調用。
- 上層注重業務邏輯,與我們最終的目標相直接關聯,而沒有具體干活的函數。
- 下層注重實際干活的函數,注重為上層填充變量,并且將變量傳遞給上層中的函數(其實就是調用上層提供的接口函數)來完成任務。
四、再論typedef
1、C語言的2種數據類型
- 內建類型ADT、用戶自定義類型UDT
2、typedef定義類型而不是變量
- 類型是一個數據模板,變量是一個實在的數據。類型是不占內存的,而變量是占內存的。
- 面向對象的語言中:類型就是類class,變量就是對象。
3、typedef與#define宏的區別
- typedef char *pChar;
- #define pChar char *
4、typedef與結構體
typedef struct teacher {char name[20];int age;int mager; }teacher, *pTeacher;5、typedef與const
(1)typedef int *PINT; const PINT p2; 相當于是int *const p2;
(2)typedef int *PINT; PINT const p2; 相當于是int *const p2;
(3)如果確實想得到const int *p;這種效果,只能typedef const int *CPINT; CPINT p1;
6、使用typedef的重要意義
(1)簡化類型的描述
- char *(*)(char *, char *); typedef char *(*pFunc)(char *, char *);
(2)創造與平臺無關的類型。
- 很多編程體系下,人們傾向于不使用int、double等C語言內建類型,因為這些類型本身和平臺是相關的(譬如int在16位機器上是16位的,在32位機器上就是32位的)。為了解決這個問題,很多程序使用自定義的中間類型來做緩沖。譬如linux內核中大量使用了這種技術。
- 內核中先定義:typedef int size_t; 然后在特定的編碼需要下用size_t來替代int(譬如可能還有typedef int len_t)。
- 比如STM32的庫中全部使用了自定義類型,譬如typedef volatile unsigned int vu32;
五、二重指針
1、二重指針與普通一重指針的區別
- 二重指針和一重指針的本質都是指針變量,指針變量的本質就是變量。一重指針變量和二重指針變量本身都占4字節內存空間,
2、二重指針的本質
(1)二重指針本質上也是指針變量,和普通指針的差別就是它指向的變量類型必須是個一重指針。
- 二重指針其實也是一種數據類型,編譯器在編譯時會根據二重指針的數據類型來做靜態類型檢查,一旦發現運算時數據類型不匹配編譯器就會報錯。
(2)為什么C語言需要發明二重指針?
- 之所以要發明二重指針(函數指針、數組指針),就是為了讓編譯器了解這個指針被定義時定義它的程序員希望這個指針被用來指向什么東西(定義指針時用數據類型來標記,譬如int *p,就表示p要指向int型數據),編譯器知道指針類型之后可以幫我們做靜態類型檢查。編譯器的這種靜態類型檢查可以輔助程序員發現一些隱含性的編程錯誤,這是C語言給程序員提供的一種編譯時的查錯機制。
3、二重指針的用法
(1)二重指針指向一重指針的地址;
(2)二重指針指向指針數組;
(3)實踐編程中二重指針用的比較少,大部分時候就是和指針數組糾結起來用的。
(4)實踐編程中有時在函數傳參時,為了通過函數內部改變外部的一個指針變量,會傳這個指針變量的地址(也就是二重指針)進去
4、二重指針與數組指針
- 二重指針、數組指針、結構體指針、一重指針、普通變量的本質都是相同的,都是變量。
- 所有的指針變量本質都是相同的,都是4個字節,都是用來指向別的東西的,不同類型的指針變量只是可以指向的(編譯器允許你指向的)變量類型不同。
- 二重指針就是(指針數組)指針
六、二維數組
1、二維數組的內存映像
- 一維數組在內存中是連續分布的多個內存單元組成的,而二維數組在內存中也是連續分布的多個內存單元組成的。
- 從內存角度來看,一維數組和二維數組沒有本質差別。
- 二維數組和一維數組在內存使用效率、訪問效率上是完全一樣的(或者說差異是忽略不計的)。
- 在某種情況下用二維數組而不用一維數組,原因在于二維數組好理解、代碼好寫、利于組織。
2、哪個是第一(二)維?
- 二維數組int a[2][5]中,2是第一維,5是第二維。
- 首先第一維是最外面一層的數組,所以int a[2][5]這個數組有2個元素;其中每一個元素又是一個含有5個元素的一維數組(這個數組就是第二維)。
- 二維數組的第一維是最外部的那一層,第一維本身是個數組,這個數組中存儲的元素也是個一維數組;二維數組的第二維是里面的那一層,第二維本身是個一維數組,數組中存的元素是普通元素,第二維(這個一維數組)本身作為元素存儲在第一維的數組中。
3、二維數組的下標式訪問和指針式訪問
- 二維數組的兩種訪問方式:以int a[2][5]為例,(合適類型的)p = a; ?則a[0][0]等同于*(*(p+0)+0); a[i][j]等同于 *(*(p+i)+j)
七、二維數組的運算和指針
1、指針指向二維數組的數組名
(1)二維數組的數組名表示二維數組的第一維數組中首元素(也就是第二維的數組)的首地址;
(2)二維數組的數組名a等同于&a[0],這個和一維數組的符號含義是相符的。
(3)用數組指針來指向二維數組的數組名是類型匹配的。
2、指針指向二維數組的第一維
(1)用int *p來指向二維數組的第一維a[i]
3、指針指向二維數組的第二維
(1)二維數組的第二維元素其實就是普通變量了(a[1][1]其實就是int類型的7),已經不能用指針類型和它相互賦值了。
(2)除非int *p = &a[i][j];,類似于指針指向二維數組的第一維。
4、二維數組和指針的關鍵點
- 數組中各個符號的含義。
- 數組的指針式訪問,尤其是二維數組的指針式訪問。
總結
- 上一篇: 3.ZooKeeper客户端Curato
- 下一篇: Unity开发手游的实用插件