二級指針 (多級指針)
指針變量作為一個變量也有自己的存儲地址,而指向指針變量的存儲地址就被稱為指針的指針,即二級指針。依次疊加,就形成了多級指針。指針可以指向一份普通類型的數(shù)據(jù),例如 int、double、char 等,也可以指向一份指針類型的數(shù)據(jù),例如 int *、double *、char * 等。如果一個指針指向的是另外一個指針,我們就稱它為二級指針,或者指向指針的指針。,我們先看看二級指針,它們關(guān)系如下:
int a =100;//一個普通變量 int *p1 = &a;//一個一級指針p1指向a變量的地址 int **p2 = &p1;//一個二級指針p2指向p1指針的地址 // p2 -> p1 -> a // &p1 &a 100 /*規(guī)律: 一級指針 指向變量的地址 二級指針 指向一級指針的地址 三級指針 指向二級指針的地址 依次類推....
指針變量也是一種變量,也會占用存儲空間,也可以使用&獲取它的地址。C語言不限制指針的級數(shù),每增加一級指針,在定義指針變量時(shí)就得增加一個星號*。p1 是一級指針,指向普通類型的數(shù)據(jù),定義時(shí)有一個*;p2 是二級指針,指向一級指針 p1,定義時(shí)有兩個*。
多級指針的話就是:
int ***p3 = &p2;//三級指針 int ****p4 = &p3;//四級指針 int *****p5 = &p4;//五級指針 //實(shí)際開發(fā)中會經(jīng)常使用一級指針和二級指針,幾乎用不到高級指針。
想要獲取指針指向的數(shù)據(jù)時(shí),一級指針加一個*,二級指針加兩個*,三級指針加三個*關(guān)系如下:
#includeint main() { int a = 100; int *p1 = &a; int **p2 = &p1; int ***p3 = &p2; printf("%d, %d, %d, %d ", a, *p1, **p2, ***p3);//他們的值都是一樣的 printf("&p2 = %#X, p3 = %#X ", &p2, p3);//所指向的地址也是一樣的 printf("&p1 = %#X, p2 = %#X, *p3 = %#X ", &p1, p2, *p3); printf(" &a = %#X, p1 = %#X, *p2 = %#X, **p3 = %#X ", &a, p1, *p2, **p3); return 0; } //以三級指針 p3 為例來分析上面的代碼。***p3等價(jià)于*(*(*p3))。*p3 得到的是 p2 的值, 也即 p1 的地址;*(*p3) 得到的是 p1 的值,也即 a 的地址;經(jīng)過三次“取值”操作后,*(*(*p3)) 得到的才是 a 的值。
指針數(shù)組、指向函數(shù)的指針、指向二維數(shù)組的指針
指針數(shù)組:
指針變量和普通變量一樣,也能組成數(shù)組,如果一個數(shù)組中的所有元素保存的都是指針,那么我們就稱它為指針數(shù)組。指針數(shù)組的定義形式一般為:
數(shù)據(jù)類型 * 名字 [數(shù)組長度]; 這里注意 [ ]的優(yōu)先級比 * 來得高 int *a [10]; 這里說明a是一個數(shù)組,包含了10個元素,每個元素的類型為int *。
除了每個元素的數(shù)據(jù)類型不同,指針數(shù)組和普通數(shù)組在其他方面都是一樣的,下面是一個簡單的例子:
#includeint main(void) { int a = 1; int b = 2; int c = 3; //定義一個指針的數(shù)組 int *an[3] = { &a,&b,&c };//由于里邊每一個元素都是指針,所以利用取地址符&,指向abc三個變量 //這里定義一個指向指針數(shù)組的指針,由于數(shù)組已經(jīng)是指針了,所以要用到二級指針 int **p = an;//由于數(shù)組本身就是表示一個地址所以不用取地址符& printf("%d %d %d ", *an[0], *an[1], *an[2]); printf("%d %d %d ", **(p + 0) , **(p + 1), **(p + 2)); return 0; } //arr 是一個指針數(shù)組,它包含了 3 個元素,每個元素都是一個指針,在定義 arr 的同時(shí), 我們使用變量 a、b、c 的地址對它進(jìn)行了初始化,這和普通數(shù)組是多么地類似。 parr 是指向數(shù)組 arr 的指針,確切地說是指向 arr 第 0 個元素的指針, 它的定義形式應(yīng)該理解為int *(*parr),括號中的*表示 parr 是一個指針, 括號外面的int *表示 parr 指向的數(shù)據(jù)的類型。arr 第 0 個元素的類型為 int *, 所以在定義 parr 時(shí)要加兩個 *。
指針數(shù)組還可以和字符串?dāng)?shù)組結(jié)合使用:
#includeint main(){ char *str[3] = { //定義一個字符串?dāng)?shù)組 長度為3 "c.biancheng.net", "C語言中文網(wǎng)", "C Language" }; printf("%s %s %s ", str[0], str[1], str[2]);//依次輸出每個字符串 return 0; }
指向函數(shù)的指針:
一個函數(shù)總是占用一段連續(xù)的內(nèi)存區(qū)域,函數(shù)名在表達(dá)式中有時(shí)也會被轉(zhuǎn)換為該函數(shù)所在內(nèi)存區(qū)域的首地址,這和數(shù)組名非常類似。我們可以把函數(shù)的這個首地址(或稱入口地址)賦予一個指針變量,使指針變量指向函數(shù)所在的內(nèi)存區(qū)域,然后通過指針變量就可以找到并調(diào)用該函數(shù)。這種指針就是函數(shù)指針。
數(shù)據(jù)類型 *指針名 (數(shù)據(jù)類型 參數(shù)); 數(shù)據(jù)為函數(shù)返回值類型,指針名稱,括號里邊為函數(shù)參數(shù)列表。參數(shù)列表中可以同時(shí)給出參數(shù)的類型和名稱, 也可以只給出參數(shù)的類型,省略參數(shù)的名稱,這一點(diǎn)和函數(shù)原型非常類似。
注意( )的優(yōu)先級高于*,第一個括號不能省略,如果寫作returnType *pointerName(param list);就成了函數(shù)原型,它表明函數(shù)的返回值類型為returnType *
用指針來實(shí)現(xiàn)對函數(shù)的調(diào)用:
#include//返回兩個數(shù)中較大的一個 int max(int a, int b){ return a>b ? a : b; } int main(void){ int x, y, maxval; //定義指向函數(shù)指針*pmax int (*pmax)(int, int) = max; //也可以寫作int (*pmax)(int a, int b) //要注意的是定義必須和函數(shù)形式一致 printf("Input two numbers:"); scanf("%d %d", &x, &y); maxval = (*pmax)(x, y);//將函數(shù)調(diào)用并指針賦值 printf("Max value: %d ", maxval); return 0; } // maxval 對函數(shù)進(jìn)行了調(diào)用。pmax 是一個函數(shù)指針,在前面加 * 就表示對它指向的函數(shù)進(jìn)行調(diào)用。 注意( )的優(yōu)先級高于*,第一個括號不能省略。
指向二維數(shù)組的指針:
(復(fù)盤一下二維數(shù)組的知識)二維數(shù)組在概念上是二維的,有行和列,但在內(nèi)存中所有的數(shù)組元素都是連續(xù)排列的,它們之間沒有“縫隙”。以下面的二維數(shù)組 a 為例:
int a[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11} }; a就是像一個矩陣: 0 1 2 3 4 5 6 7 8 9 10 11 但在內(nèi)存中,a 的分布是一維線性的,整個數(shù)組占用一塊連續(xù)的內(nèi)存: 【0】【1】【2】【3】【4】【5】【6】【7】【8】【9】【10】【11】
C語言中的二維數(shù)組是按行排列的,也就是先存放 a[0] 行,再存放 a[1] 行,最后存放 a[2] 行;每行中的 4 個元素也是依次存放。數(shù)組 a 為 int 類型,每個元素占用 4 個字節(jié),整個數(shù)組共占用 4×(3×4) = 48 個字節(jié)。
C語言把一個二維數(shù)組分解成多個一維數(shù)組來處理。對于數(shù)組 a,它可以分解成三個一維數(shù)組,即 a[0]、a[1]、a[2]。每一個一維數(shù)組又包含了 4 個元素,例如 a[0] 包含`a[0][0] 、a[0][1]、a[0][2]、a[0][3]。
為了更好的理解指針和二維數(shù)組的關(guān)系,我們先來定義一個指向 a 的指針變量 p:
int (*p)[4] = a; //括號中的*表明 p 是一個指針,它指向一個數(shù)組,數(shù)組的類型為int [4], 這正是 a 所包含的每個一維數(shù)組的類型。
[ ]的優(yōu)先級高于*,( )是必須要加的,如果赤裸裸地寫作int *p[4],那么應(yīng)該理解為int *(p[4]),p 就成了一個指針數(shù)組,而不是二維數(shù)組指針。
對指針進(jìn)行加法(減法)運(yùn)算時(shí),它前進(jìn)(后退)的步長與它指向的數(shù)據(jù)類型有關(guān),p 指向的數(shù)據(jù)類型是int [4],那么p+1就前進(jìn) 4×4 = 16 個字節(jié),p-1就后退 16 個字節(jié),那么這正好是數(shù)組 a 所包含的每個一維數(shù)組的長度。也就是說,p+1會使得指針指向二維數(shù)組的下一行,p-1會使得指針指向數(shù)組的上一行。
按照上面的定義,我們來看看代碼:
#includeint main(void){ int a[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11} }; int (*p)[4] = a; printf ( "%d ", sizeof(*(p+1)) );//這里輸出是16 return 0; }
*(p+1)+1表示第 1 行第 1 個元素的地址:*(p+1)單獨(dú)使用時(shí)表示的是第 1 行數(shù)據(jù),放在表達(dá)式中會被轉(zhuǎn)換為第 1 行數(shù)據(jù)的首地址,也就是第 1 行第 0 個元素的地址,因?yàn)槭褂谜袛?shù)據(jù)沒有實(shí)際的含義,編譯器遇到這種情況都會轉(zhuǎn)換為指向該行第 0 個元素的指針;就像一維數(shù)組的名字,在定義時(shí)或者和 sizeof、& 一起使用時(shí)才表示整個數(shù)組,出現(xiàn)在表達(dá)式中就會被轉(zhuǎn)換為指向數(shù)組第 0 個元素的指針。
*(*(p+1)+1)表示第 1 行第 1 個元素的值。很明顯,增加一個 * 表示取地址上的數(shù)據(jù):**
規(guī)律: a+i == p+i a[i] == p[i] == *(a+i) == *(p+i) a[i][j] == p[i][j] == *(a[i]+j) == *(p[i]+j) == *(*(a+i)+j) == *(*(p+i)+j) #includeint main(void) { int a[3][4] = { {1,2,3,4},{5,6,7,8},{9,10,11,12} }; int i, j; int(*p)[4] = a;//定義一個指向二維數(shù)組的指針p for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { printf("%d ", *(*(p+i)+j));//利用二級指針就可以訪問到i行j列的元素 } //*(p+i):一維數(shù)組 printf("n"); //*(*(p+i)+j) 二維數(shù)組 } return 0; } /*輸出: 1 2 3 4 5 6 7 8 9 10 11 12 數(shù)組名 a 在表達(dá)式中也會被轉(zhuǎn)換為和 p 等價(jià)的指針!
指針數(shù)組和二維數(shù)組指針在定義時(shí)非常相似,但是括號的位置不同所表示的意思也就天壤之別:
int *(p1[5]); //指針數(shù)組,可以去掉括號直接寫作 int *p1[5]; int (*p2)[5]; //二維數(shù)組指針,不能去掉括號
指針數(shù)組和二維數(shù)組指針有著本質(zhì)上的區(qū)別:
指針數(shù)組是一個數(shù)組,只是每個元素保存的都是指針,以上面的 p1 為例,在32位環(huán)境下它占用 4×5 = 20 個字節(jié)的內(nèi)存。二維數(shù)組指針是一個指針,它指向一個二維數(shù)組,以上面的 p2 為例,它占用 4 個字節(jié)的內(nèi)存。
至于多維數(shù)組和二維數(shù)組沒有本質(zhì)的區(qū)別,但是復(fù)雜度倒是高了許多。一般不常用。
結(jié)束語:
程序在運(yùn)行過程中需要的是數(shù)據(jù)和指令的地址,變量名、函數(shù)名、字符串名和數(shù)組名在本質(zhì)上是一樣的,它們都是地址的助記符:在編寫代碼的過程中,我們認(rèn)為變量名表示的是數(shù)據(jù)本身,而函數(shù)名、字符串名和數(shù)組名表示的是代碼塊或數(shù)據(jù)塊的首地址;程序被編譯和鏈接后,這些名字都會消失,取而代之的是它們對應(yīng)的地址。指針就是存放地址的一種變量。
常見的的指針:
1、 指針變量可以進(jìn)行四則運(yùn)算。指針變量的加減運(yùn)算并不是簡單的加上或減去一個整數(shù),而是跟指針指向的數(shù)據(jù)類型與地址有關(guān)。
2、給指針變量賦值時(shí),要將一份數(shù)據(jù)的地址賦給它,不能直接賦給一個整數(shù),例如int *p = 1000;是沒有意義的,使用過程中一般會導(dǎo)致程序崩潰。
3、使用指針變量之前一定要初始化,否則就不能確定指針指向哪里,如果它指向的內(nèi)存沒有使用權(quán)限,程序就崩潰了。對于暫時(shí)沒有指向的指針,直接賦值NULL讓它變?yōu)榭罩羔槨?/p>
4、數(shù)組也是有類型的,數(shù)組名的本意是表示一組類型相同的數(shù)據(jù)。在定義數(shù)組時(shí),或者和 sizeof、& 運(yùn)算符一起使用時(shí)數(shù)組名才表示整個數(shù)組,表達(dá)式中的數(shù)組名會被轉(zhuǎn)換為一個指向數(shù)組的指針。
指針的用法暫時(shí)就這些,C指針大法這些才是入門!繼續(xù)加油咯~
-
存儲
+關(guān)注
關(guān)注
13文章
4328瀏覽量
85944 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4338瀏覽量
62739 -
指針
+關(guān)注
關(guān)注
1文章
480瀏覽量
70581 -
數(shù)組
+關(guān)注
關(guān)注
1文章
417瀏覽量
25978
原文標(biāo)題:【零基礎(chǔ)學(xué)C語言】知識總結(jié)十:二級指針、指針數(shù)組和指向函數(shù)的指針
文章出處:【微信號:cyuyanxuexi,微信公眾號:C語言編程學(xué)習(xí)基地】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論