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