《電子技術(shù)應(yīng)用》
您所在的位置:首頁 > 可編程邏輯 > 其他 > 看完還不會指針,,錘自己,!

看完還不會指針,,錘自己!

2022-11-23
作者: 電子技術(shù)應(yīng)用專欄作家 一口Linux
來源: 電子技術(shù)應(yīng)用專欄作家 一口Linux
關(guān)鍵詞: 指針 C語言

  指針使得 C 語言能夠更高效地實(shí)現(xiàn)對計(jì)算機(jī)底層硬件的操作,,而計(jì)算機(jī)硬件的操作很大程度上依賴地址,,指針便提供了一種對地址操作的方法,在一定意義上,,指針是c語言的精髓,,所以一定要耐心看完。

  指針對于很多c語言初學(xué)者來說可能難以理解,,一不小心可能被指針的指向關(guān)系繞進(jìn)去,,在這里就對指針做一些總結(jié),寫一下自己的理解,。

  一. 指針的介紹

  在程序中,,我們聲明一個(gè)變量(int a = 1),將數(shù)據(jù)1存到變量a中,,計(jì)算機(jī)內(nèi)部會將這個(gè)數(shù)據(jù)存到內(nèi)存(RAM)中,,那么,數(shù)據(jù)存到某個(gè)地方,,就會涉及地址,。就像你買的快遞,快遞到了就要存到某個(gè)驛站里面放著,,你的快遞就是一個(gè)數(shù)據(jù),,驛站就是一個(gè)變量,這個(gè)驛站就要有地址,,不然全國這么多驛站你怎么知道你的快遞在哪個(gè)驛站,。

  到這里,,地址的概念應(yīng)該有了吧,。

  現(xiàn)在想想地址(比如0x0000 0001)不也是一個(gè)數(shù)據(jù)嗎,那么不也可以用一個(gè)變量存地址這個(gè)數(shù)據(jù),?是的,,可以,這個(gè)變量就是指針,,指針?biāo)褪谴鎯α硪粋€(gè)變量的內(nèi)存地址的一種數(shù)據(jù)類型,,即指針的內(nèi)容就是另一個(gè)變量的內(nèi)存地址。

  指針本身也是一個(gè)變量,,所以指針變量也有自己的地址,,只是它有點(diǎn)特殊,它存放的是另一個(gè)變量的地址而已,,理解這句話就行,。

  前面講到過指針?biāo)且环N數(shù)據(jù)類型,,為了方便,我們就規(guī)定在這種類型后面加*號表示該類型指針,,有char型指針(char *),、double型指針(double *)和int型指針(int *)等等。

  試著敲一下下面一段代碼,,可以加深對指針的認(rèn)識:

  int a = 1;       // 定義一個(gè)int型變量

  int *p = &a;     // 定義一個(gè)int型指針p,,&a表示對a取地址,指針p的內(nèi)容是a的地址

  // int  *p;   p = &a;    // 第二行也可以這樣寫,,意思一樣

  printf("%p\n", &a);      // 打印a的地址

  printf("%p\n", p);       // 打印指針p指向的地址

  // %p是打印地址(指針地址),,是十六進(jìn)制的形式

  C/C++ 中規(guī)定了 * 操作符來從對應(yīng)指針類型存放的地址中拿出相應(yīng)數(shù)據(jù),再定義一個(gè)變量int b = *p,,指針p存了a的地址,,*p就是拿出a的值,b的值就變成了1,,*操作也被稱為解引用,。

  二. 指針的相關(guān)操作(運(yùn)算)

  算數(shù)運(yùn)算:+、-,、++,、--、

  指針的運(yùn)算是特別容易搞錯(cuò)的,,千萬不能以為和普通類型(比如int型數(shù)據(jù))的運(yùn)算一樣,。

  指針的加減運(yùn)算:

  1.指針+1/指針-1,加/減的是整個(gè)指針類型的長度,,與其說指針的加減法,,我認(rèn)為不如說成指針的偏移更合適,接下來看為什么是偏移,,舉個(gè)非常明顯的例子:

  char a[5] = {1, 2, 3, 4, 5};  // 定義一個(gè)char型數(shù)組,,這里的a實(shí)質(zhì)上是一個(gè)指針,指向這個(gè)數(shù)組的首元素a[0]的指針

  char *p = a;

  printf("%d\n", *p);        // 輸出1 --> a[0]

  printf("%d\n", *(p + 1));  // 輸出2 --> a[1]

  ......

  看輸出的結(jié)果就很容易看出規(guī)律,,p指針指向a[0],,特別注意p+1指針變成指向a[1],所以*(p+1)  = a[1] = 2,,而不是*(p+1)  = a[0] + 1 = 2,,當(dāng)然這里兩個(gè)答案湊巧一樣,但是把數(shù)組的內(nèi)容換一下就不會是一樣了,,如果是改成(*p) + 1,,那么就是(*p) + 1 = a[0] + 1 = 2,同理可以改成p+2,、p+3......

  還有試著定義其它類型的數(shù)組(比如int型:int a[5] = {1, 2, 3, 4, 5};),,看看是不是這個(gè)規(guī)律,,就可以知道指針加減的是這個(gè)指針類型的長度,也就是指針的偏移,,還可以嘗試定義結(jié)構(gòu)體數(shù)組,,將會有更深的理解。

  減法就不用多說了,,理解了指針p+1/p-1,,那么指針p++/p--其實(shí)是一樣的,都是偏移,。

  三. 多級指針

  說起多級指針這個(gè)東西,,曾經(jīng)大一學(xué)c語言的時(shí)候,學(xué)到二級指針都已經(jīng)把我給繞暈了,,如果當(dāng)時(shí)你給我寫個(gè)int ********p出來,,我估計(jì)直接崩潰到放棄。

  我們先來說說二級指針吧,!前面有講到,,指針也是一種數(shù)據(jù)類型,是一種變量,,也有自己的地址,,所以既然有地址,而指針就是存放另一個(gè)變量的地址的呀,,那為什么不能再用一個(gè)指針存放這個(gè)指針的地址呢,,對吧!所以就有了二級指針,,就是指向指針的指針,。

  ok!來點(diǎn)生活上的東西,,快遞柜大家都用過吧,,快遞小哥給你發(fā)一個(gè)取件碼你就能拿到快遞。

  微信截圖_20221123161725.png

  這里的每一個(gè)柜子就是一塊內(nèi)存,,取件碼就是地址,,柜子里的快遞就是存儲在內(nèi)存的內(nèi)容/數(shù)據(jù),。

  假如快遞小哥把你的快遞放到"058柜子",,給你發(fā)取件碼,那么你輸入取件碼就可以取到快遞,。

  微信截圖_20221123161816.png

  如果快遞小哥逗你一下,,故意給你發(fā)"057柜子"的取件碼,然后在"057柜子"放一張紙條,,上面寫:快遞在058柜子,,這時(shí)候你肯定是按照紙條從"058柜子"里就可以拿到快遞,。

  這里的"057柜子"就是指針,指針里面存放另一個(gè)變量(058柜子)的地址,。

  微信截圖_20221123161828.png

  如果快遞小哥給你發(fā)"056柜子"的取件碼,,在"056柜子"里放一張紙條寫:快遞在"057柜子"里,又在"057柜子"里放一張紙條寫:快遞在"058柜子"里,。

  這里的"056柜子"就是二級指針,,"057柜子"就是指針,"058柜子"就是指針存放的另一個(gè)變量,。

  微信截圖_20221123161846.png

  現(xiàn)在明白了二級指針吧,,那么,N級指針也就那樣,,也就是指向指針的指針的指針的指針的指針........,,是不是非常簡單!

  int a = 1;

  int *p = &a;

  int **pp = &p;     // 二級指針pp存放指針p的地址,,即二級指針pp指向指針p

  int ***ppp = &pp;  // 三級指針ppp存放二級指針pp的地址,,即三級指針ppp指向二級指針pp

  ......

  總之,如果一個(gè)內(nèi)存如果存放的是另一個(gè)變量的地址,,那么就叫指針,。一塊內(nèi)存要么存放實(shí)際內(nèi)容/數(shù)據(jù),要么存放的是另一個(gè)變量的地址,,確實(shí)是剛剛所說的非常簡單,。

  【總結(jié)兩點(diǎn)】:

  1. 指針本身也是一個(gè)變量,也有自己的地址,,需要內(nèi)存存儲,。

  2. 指針存放的是所指向的變量的地址,這個(gè)所指向的變量也可以是一個(gè)指針,。

  【特別注意】:面試可能被問到指針的大小

  1. 指針的大小跟指針是什么類型的沒有任何關(guān)系,。

  2. 在32為系統(tǒng)系統(tǒng)中,所有的指針大小都是4個(gè)字節(jié),,原因是32系統(tǒng)上所有變量的地址都是32位的,,而指針用來存地址的。

  最后,,大家要明白一個(gè)概念,,其實(shí)并沒有什么多級指針這種東西,多級指針就是個(gè)指針,,稱為多級指針是為了我們方便表達(dá)而取的邏輯名稱,。

  四. 多維數(shù)組

  二維數(shù)組其實(shí)和二級指針有著相似的理解方法:

  比如a[3][2],把它理解成一個(gè)一維數(shù)組來看待,這個(gè)一維數(shù)組里面有三個(gè)元素,,只是這個(gè)一維數(shù)組有點(diǎn)特殊,,它的每個(gè)元素又是一個(gè)一維數(shù)組而已。

  懂了上面這段話,,二維數(shù)組就很好理解,。

  前面我們已經(jīng)知道一維數(shù)組a[3]中,a實(shí)質(zhì)上是一個(gè)指針,,指向這個(gè)數(shù)組首元素a[0]:

  int a[3] = {1, 2, 3};

  // a[0]  -->  *a

  printf("%d\n", *a);       // 打印 1  -->  a[0] 的值

  // a[1]  -->  *(a + 1)

  printf("%d\n", *(a + 1)); // 打印 2  -->  a[1] 的值

  // a[2]  -->  *(a + 2)

  printf("%d\n", *(a + 2)); // 打印 3  -->  a[2] 的值

  那么,,二維數(shù)組a[3][2]當(dāng)成一維數(shù)組看是不是可以得出:

  int a[3][2] = {{1, 2}, {3, 4}, {5, 6}};

  // a[0][0]  -->  (*a)[0]

  printf("%d\n", (*a)[0]);       // 打印 1  -->  a[0][0] 的值

  // a[1][0]  -->  (*(a + 1))[0]

  printf("%d\n", (*(a + 1))[0]); // 打印 3  -->  a[1][0] 的值

  // a[2][0]  -->  (*(a + 2))[0]

  printf("%d\n", (*(a + 2))[0]); // 打印 5  -->  a[2][0] 的值

  // a[2][1]  -->  (*(a + 2))[1]

  printf("%d\n", (*(a + 2))[1]); // 打印 6  -->  a[2][1] 的值

  // ..... 二維數(shù)組其它元素類似都可以輸出

  結(jié)論一:a[m][n]  等價(jià)于 (*(a + m)[n]  -->就是一個(gè)數(shù)組指針(后面會提到)

  基于前面兩種指針和數(shù)組的變換,可以繼續(xù)得出:

  int a[3][2] = {{1, 2}, {3, 4}, {5, 6}};

  // a[0][0]  -->  (*a)[0]  -->  *(*a + 0)  -->  把 *a 當(dāng)成整體

  printf("%d\n", *(*a));           // 打印 1  -->  a[0][0] 的值

  // a[1][0]  -->  (*(a + 1))[0]  -->  *(*(a + 1) + 0)

  printf("%d\n", *(*(a + 1)));     // 打印 3  -->  a[1][0] 的值

  // a[2][0]  -->  (*(a + 2))[0]  -->  *(*(a + 2) + 0)

  printf("%d\n", *(*(a + 2)));     // 打印 5  -->  a[2][0] 的值

  // a[2][1]  -->  (*(a + 2))[1]  -->  *(*(a + 2) + 1)

  printf("%d\n", *(*(a + 2) + 1)); // 打印 6  -->  a[2][1] 的值

  // ..... 二維數(shù)組其它元素類似都可以輸出

  結(jié)論二:a[m][n]   等價(jià)于   *(*(a + m) + n)

  五. 數(shù)組指針與指針數(shù)組

  1. 數(shù)組指針:指針在后,,說明它就是個(gè)指針,,所以數(shù)組指針指向的是數(shù)組,相當(dāng)于一次聲明了一個(gè)指針,。從前面就已經(jīng)知道,,二維數(shù)組a[3][2]中,a實(shí)質(zhì)上就是一個(gè)數(shù)組指針,。

  公式:

  指向的那個(gè)數(shù)組的元素類型  (*指針名字)[指向的數(shù)組的元素個(gè)數(shù)]

  2. 指針數(shù)組:數(shù)組在后,,說明它就是個(gè)數(shù)組。字符數(shù)組是什么,?就是存放字符的數(shù)組,,那么指針數(shù)組就是存放指針類型的數(shù)組,相當(dāng)于一次聲明了多個(gè)指針,。

  公式:

  數(shù)組元素的類型  數(shù)組名字[數(shù)組元素個(gè)數(shù)]

  char *a[3] = {"red", "green", "blue"};

  char **pp = a;    //定義二級指針pp, a本質(zhì)上相當(dāng)于二級指針

  printf("%s\n", pp[0]);    // 打印 red

  printf("%s\n", pp[1]);    // 打印 green

  printf("%s\n", pp[2]);    // 打印 blue

  直觀上區(qū)分?jǐn)?shù)組指針和指針數(shù)組的方法:

  由于數(shù)組指針的 [] 比 * 的優(yōu)先級高,,所以數(shù)組指針的指針加括號,所以看看指針有沒有用圓括號括起來,,就可以區(qū)分開,。

  六. 其它

  關(guān)于指針想寫的內(nèi)容還有很多,其實(shí)這只是開了個(gè)頭,,比如:野指針,、函數(shù)指針、函數(shù)參數(shù)傳遞方式,、const 修飾指針,、動態(tài)內(nèi)存分配: malloc 和 free、堆, 棧,、內(nèi)存泄露......,,以后再慢慢補(bǔ)齊。

  指針在鏈表使用的比較多,,多寫一些鏈表的操作會對指針理解很有幫助,,鏈表節(jié)點(diǎn)的增加、刪除,、修改,、查找,單向鏈表,、雙向鏈表,、雙向循環(huán)鏈表、內(nèi)核鏈表等等,。

 更多信息可以來這里獲取==>>電子技術(shù)應(yīng)用-AET<<

微信圖片_20210517164139.jpg

微信圖片_20220701092006.jpg

電子技術(shù)應(yīng)用專欄作家 一口Linux

原文鏈接:https://mp.weixin.qq.com/s/t7vomHGTMJ179XTmuJjDqQ

本站內(nèi)容除特別聲明的原創(chuàng)文章之外,,轉(zhuǎn)載內(nèi)容只為傳遞更多信息,并不代表本網(wǎng)站贊同其觀點(diǎn),。轉(zhuǎn)載的所有的文章,、圖片、音/視頻文件等資料的版權(quán)歸版權(quán)所有權(quán)人所有,。本站采用的非本站原創(chuàng)文章及圖片等內(nèi)容無法一一聯(lián)系確認(rèn)版權(quán)者,。如涉及作品內(nèi)容、版權(quán)和其它問題,,請及時(shí)通過電子郵件或電話通知我們,,以便迅速采取適當(dāng)措施,避免給雙方造成不必要的經(jīng)濟(jì)損失,。聯(lián)系電話:010-82306118,;郵箱:[email protected]