新四季網

c語言指針數組講解(零基礎學C語言知識總結十)

2023-04-21 11:25:40 4

二級指針 (多級指針)

指針變量作為一個變量也有自己的存儲地址,而指向指針變量的存儲地址就被稱為指針的指針,即二級指針。依次疊加,就形成了多級指針。指針可以指向一份普通類型的數據,例如 int、double、char 等,也可以指向一份指針類型的數據,例如 int *、double *、char * 等。如果一個指針指向的是另外一個指針,我們就稱它為二級指針,或者指向指針的指針。,我們先看看二級指針,它們關係如下:

int a =100;//一個普通變量

int *p1 = //一個一級指針p1指向a變量的地址

int **p2 = &p1;//一個二級指針p2指向p1指針的地址

// p2 -> p1 -> a

// &p1 &a 100

/*規律:

一級指針 指向變量的地址

二級指針 指向一級指針的地址

三級指針 指向二級指針的地址

依次類推....

指針變量也是一種變量,也會佔用存儲空間,也可以使用&獲取它的地址。C語言不限制指針的級數,每增加一級指針,在定義指針變量時就得增加一個星號*。p1 是一級指針,指向普通類型的數據,定義時有一個*;p2 是二級指針,指向一級指針 p1,定義時有兩個*。

多級指針的話就是:

int ***p3 = &p2;//三級指針

int ****p4 = &p3;//四級指針

int *****p5 = &p4;//五級指針

//實際開發中會經常使用一級指針和二級指針,幾乎用不到高級指針。

想要獲取指針指向的數據時,一級指針加一個*,二級指針加兩個*,三級指針加三個*關係如下:

#include

int main {

int a = 100;

int *p1 =

int **p2 = &p1;

int ***p3 = &p2;

printf("%d, %d, %d, %d\n", a, *p1, **p2, ***p3);//他們的值都是一樣的

printf("&p2 = %#X, p3 = %#X\n", &p2, p3);//所指向的地址也是一樣的

printf("&p1 = %#X, p2 = %#X, *p3 = %#X\n", &p1, p2, *p3);

printf(" &a = %#X, p1 = %#X, *p2 = %#X, **p3 = %#X\n", &a, p1, *p2, **p3);

return 0;

}

//以三級指針 p3 為例來分析上面的代碼。***p3等價於*(*(*p3))。*p3 得到的是 p2 的值,

也即 p1 的地址;*(*p3) 得到的是 p1 的值,也即 a 的地址;經過三次「取值」操作後,*(*(*p3))

得到的才是 a 的值。

指針數組、指向函數的指針、指向二維數組的指針指針數組:

指針變量和普通變量一樣,也能組成數組,如果一個數組中的所有元素保存的都是指針,那麼我們就稱它為指針數組。指針數組的定義形式一般為:

數據類型 * 名字 [數組長度];

這裡注意 [ ]的優先級比 * 來得高

int *a [10];

這裡說明a是一個數組,包含了10個元素,每個元素的類型為int *。

除了每個元素的數據類型不同,指針數組和普通數組在其他方面都是一樣的,下面是一個簡單的例子:

#include

int main(void) {

int a = 1;

int b = 2;

int c = 3;

//定義一個指針的數組

int *an[3] = { &a,&b,&c };//由於裡邊每一個元素都是指針,所以利用取地址符&,指向abc三個變量

//這裡定義一個指向指針數組的指針,由於數組已經是指針了,所以要用到二級指針

int **p = an;//由於數組本身就是表示一個地址所以不用取地址符&

printf("%d %d %d\n", *an[0], *an[1], *an[2]);

printf("%d %d %d\n", **(p 0) , **(p 1), **(p 2));

return 0;

}

//arr 是一個指針數組,它包含了 3 個元素,每個元素都是一個指針,在定義 arr 的同時,

我們使用變量 a、b、c 的地址對它進行了初始化,這和普通數組是多麼地類似。

parr 是指向數組 arr 的指針,確切地說是指向 arr 第 0 個元素的指針,

它的定義形式應該理解為int *(*parr),括號中的*表示 parr 是一個指針,

括號外面的int *表示 parr 指向的數據的類型。arr 第 0 個元素的類型為 int *,

所以在定義 parr 時要加兩個 *。

指針數組還可以和字符串數組結合使用:

#include

int main{

char *str[3] = { //定義一個字符串數組 長度為3

"c.biancheng.net",

"C語言中文網",

"C Language"

};

printf("%s\n%s\n%s\n", str[0], str[1], str[2]);//依次輸出每個字符串

return 0;

}

指向函數的指針:

一個函數總是佔用一段連續的內存區域,函數名在表達式中有時也會被轉換為該函數所在內存區域的首地址,這和數組名非常類似。我們可以把函數的這個首地址(或稱入口地址)賦予一個指針變量,使指針變量指向函數所在的內存區域,然後通過指針變量就可以找到並調用該函數。這種指針就是函數指針。

數據類型 *指針名 (數據類型 參數);

數據為函數返回值類型,指針名稱,括號裡邊為函數參數列表。參數列表中可以同時給出參數的類型和名稱,

也可以只給出參數的類型,省略參數的名稱,這一點和函數原型非常類似。

注意( )的優先級高於*,第一個括號不能省略,如果寫作returnType *pointerName(param list);就成了函數原型,它表明函數的返回值類型為returnType *

用指針來實現對函數的調用:

#include

//返回兩個數中較大的一個

int max(int a, int b){

return a>b ? a : b;

}

int main(void){

int x, y, maxval;

//定義指向函數指針*pmax

int (*pmax)(int, int) = max; //也可以寫作int (*pmax)(int a, int b)

//要注意的是定義必須和函數形式一致

printf("Input two numbers:");

scanf("%d %d", &x, &y);

maxval = (*pmax)(x, y);//將函數調用並指針賦值

printf("Max value: %d\n", maxval);

return 0;

}

// maxval 對函數進行了調用。pmax 是一個函數指針,在前面加 * 就表示對它指向的函數進行調用。

注意( )的優先級高於*,第一個括號不能省略。

指向二維數組的指針:

(復盤一下二維數組的知識)二維數組在概念上是二維的,有行和列,但在內存中所有的數組元素都是連續排列的,它們之間沒有「縫隙」。以下面的二維數組 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

但在內存中,a 的分布是一維線性的,整個數組佔用一塊連續的內存:

【0】【1】【2】【3】【4】【5】【6】【7】【8】【9】【10】【11】

C語言中的二維數組是按行排列的,也就是先存放 a[0] 行,再存放 a[1] 行,最後存放 a[2] 行;每行中的 4 個元素也是依次存放。數組 a 為 int 類型,每個元素佔用 4 個字節,整個數組共佔用 4×(3×4) = 48 個字節。

C語言把一個二維數組分解成多個一維數組來處理。對於數組 a,它可以分解成三個一維數組,即 a[0]、a[1]、a[2]。每一個一維數組又包含了 4 個元素,例如 a[0] 包含`a[0][0] 、a[0][1]、a[0][2]、a[0][3]。

為了更好的理解指針和二維數組的關係,我們先來定義一個指向 a 的指針變量 p:

int (*p)[4] = a;

//括號中的*表明 p 是一個指針,它指向一個數組,數組的類型為int [4],

這正是 a 所包含的每個一維數組的類型。

[ ]的優先級高於*,( )是必須要加的,如果赤裸裸地寫作int *p[4],那麼應該理解為int *(p[4]),p 就成了一個指針數組,而不是二維數組指針。

對指針進行加法(減法)運算時,它前進(後退)的步長與它指向的數據類型有關,p 指向的數據類型是int [4],那麼p 1就前進 4×4 = 16 個字節,p-1就後退 16 個字節,那麼這正好是數組 a 所包含的每個一維數組的長度。也就是說,p 1會使得指針指向二維數組的下一行,p-1會使得指針指向數組的上一行。

按照上面的定義,我們來看看代碼:

#include

int main(void){

int a[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11} };

int (*p)[4] = a;

printf ( "%d\n", sizeof(*(p 1)) );//這裡輸出是16

return 0;

}

*(p 1) 1表示第 1 行第 1 個元素的地址:*(p 1)單獨使用時表示的是第 1 行數據,放在表達式中會被轉換為第 1 行數據的首地址,也就是第 1 行第 0 個元素的地址,因為使用整行數據沒有實際的含義,編譯器遇到這種情況都會轉換為指向該行第 0 個元素的指針;就像一維數組的名字,在定義時或者和 sizeof、& 一起使用時才表示整個數組,出現在表達式中就會被轉換為指向數組第 0 個元素的指針。

*(*(p 1) 1)表示第 1 行第 1 個元素的值。很明顯,增加一個 * 表示取地址上的數據:**

規律:

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)

#include

int 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;//定義一個指向二維數組的指針p

for (i = 0; i < 3; i ) {

for (j = 0; j < 4; j ) {

printf("%d ", *(*(p i) j));//利用二級指針就可以訪問到i行j列的元素

} //*(p i):一維數組

printf("\n"); //*(*(p i) j) 二維數組

}

return 0;

}

/*輸出:

1 2 3 4

5 6 7 8

9 10 11 12

數組名 a 在表達式中也會被轉換為和 p 等價的指針!

指針數組和二維數組指針在定義時非常相似,但是括號的位置不同所表示的意思也就天壤之別:

int *(p1[5]); //指針數組,可以去掉括號直接寫作 int *p1[5];

int (*p2)[5]; //二維數組指針,不能去掉括號

指針數組和二維數組指針有著本質上的區別:

指針數組是一個數組,只是每個元素保存的都是指針,以上面的 p1 為例,在32位環境下它佔用 4×5 = 20 個字節的內存。二維數組指針是一個指針,它指向一個二維數組,以上面的 p2 為例,它佔用 4 個字節的內存。

至於多維數組和二維數組沒有本質的區別,但是複雜度倒是高了許多。一般不常用。

結束語:

程序在運行過程中需要的是數據和指令的地址,變量名、函數名、字符串名和數組名在本質上是一樣的,它們都是地址的助記符:在編寫代碼的過程中,我們認為變量名表示的是數據本身,而函數名、字符串名和數組名表示的是代碼塊或數據塊的首地址;程序被編譯和連結後,這些名字都會消失,取而代之的是它們對應的地址。指針就是存放地址的一種變量。

常見的的指針:

1、 指針變量可以進行四則運算。指針變量的加減運算並不是簡單的加上或減去一個整數,而是跟指針指向的數據類型與地址有關。

2、給指針變量賦值時,要將一份數據的地址賦給它,不能直接賦給一個整數,例如int *p = 1000;是沒有意義的,使用過程中一般會導致程序崩潰。

3、使用指針變量之前一定要初始化,否則就不能確定指針指向哪裡,如果它指向的內存沒有使用權限,程序就崩潰了。對於暫時沒有指向的指針,直接賦值NULL讓它變為空指針。

4、數組也是有類型的,數組名的本意是表示一組類型相同的數據。在定義數組時,或者和 sizeof、& 運算符一起使用時數組名才表示整個數組,表達式中的數組名會被轉換為一個指向數組的指針。

指針的用法暫時就這些,C指針大法這些才是入門!繼續加油咯~

作者:Mr_Li_

對啦對啦!另外的話為了幫助大家,輕鬆,高效學習C語言/C ,我給大家分享我收集的資源,從最零基礎開始的教程到C語言項目案例,幫助大家在學習C語言的道路上披荊斬棘!可以來我粉絲群領取哦~

編程學習書籍分享:

編程學習視頻分享:

整理分享(多年學習的源碼、項目實戰視頻、項目筆記,基礎入門教程)最重要的是你可以在群裡面交流提問編程問題哦!

對於C/C 感興趣可以關注小編在後臺私信我:【編程交流】一起來學習哦!可以領取一些C/C 的項目學習視頻資料哦!已經設置好了關鍵詞自動回復,自動領取就好了!

,
同类文章
葬禮的夢想

葬禮的夢想

夢見葬禮,我得到了這個夢想,五個要素的五個要素,水火只好,主要名字在外面,職業生涯良好,一切都應該對待他人治療誠意,由於小,吉利的冬天夢想,秋天的夢是不吉利的
找到手機是什麼意思?

找到手機是什麼意思?

找到手機是什麼意思?五次選舉的五個要素是兩名士兵的跡象。與他溝通很好。這是非常財富,它擅長運作,職業是仙人的標誌。單身男人有這個夢想,主要生活可以有人幫忙
我不怎麼想?

我不怎麼想?

我做了什麼意味著看到米飯烹飪?我得到了這個夢想,五線的主要土壤,但是Tu Ke水是錢的跡象,職業生涯更加真誠。他真誠地誠實。這是豐富的,這是夏瑞的巨星
夢想你的意思是什麼?

夢想你的意思是什麼?

你是什​​麼意思夢想的夢想?夢想,主要木材的五個要素,水的跡象,主營業務,主營業務,案子應該抓住魅力,不能疏忽,春天夢想的吉利夢想夏天的夢想不幸。詢問學者夢想
拯救夢想

拯救夢想

拯救夢想什麼意思?你夢想著拯救人嗎?拯救人們的夢想有一個現實,也有夢想的主觀想像力,請參閱週宮官方網站拯救人民夢想的詳細解釋。夢想著敵人被拯救出來
2022愛方向和生日是在[質量個性]中

2022愛方向和生日是在[質量個性]中

[救生員]有人說,在出生88天之前,胎兒已經知道哪天的出生,如何有優質的個性,將走在什麼樣的愛情之旅,將與生活生活有什么生活。今天
夢想切割剪裁

夢想切割剪裁

夢想切割剪裁什麼意思?你夢想切你的手是好的嗎?夢想切割手工切割手有一個真正的影響和反應,也有夢想的主觀想像力。請參閱官方網站夢想的細節,以削減手
夢想著親人死了

夢想著親人死了

夢想著親人死了什麼意思?你夢想夢想你的親人死嗎?夢想有一個現實的影響和反應,還有夢想的主觀想像力,請參閱夢想世界夢想死亡的親屬的詳細解釋
夢想搶劫

夢想搶劫

夢想搶劫什麼意思?你夢想搶劫嗎?夢想著搶劫有一個現實的影響和反應,也有夢想的主觀想像力,請參閱週恭吉夢官方網站的詳細解釋。夢想搶劫
夢想缺乏缺乏紊亂

夢想缺乏缺乏紊亂

夢想缺乏缺乏紊亂什麼意思?你夢想缺乏異常藥物嗎?夢想缺乏現實世界的影響和現實,還有夢想的主觀想像,請看官方網站的夢想組織缺乏異常藥物。我覺得有些東西缺失了