摘要:需要注意的是用矩陣形式如行列表示二維數組,是邏輯上的概念,能形象地表示出行列關系。再次強調二維數組名如是指向行的。一維數組名如是指向列元素的。
哈嘍!這里是一只派大鑫,不是派大星。本著基礎不牢,地動山搖的學習態度,從基礎的C語言語法講到算法再到更高級的語法及框架的學習。更好地讓同樣熱愛編程(或是應付期末考試 狗頭.jpg)的大家能夠在學習階段找到好的方法、路線,讓天下沒有難學的程序(只有禿頭的程序員 2333),學會程序和算法,走遍天下都不怕!
目錄
本文通過從二維數組的介紹開始,講解什么是二維數組——>怎么定義和使用二維數組——>怎么引用二維數組的元素——>怎么通過指針來引用二維數組(具體包括多種分式)來逐步說明指針+二維數組的配合使用,相信學完本文內容,讀者對于指針和二維數組會有更加深刻的印象和理解。通過學校期末考試和考研C語言內容完全足夠!!!?
怎么定義二維數組呢?其基本概念與方法和一維數組相似。
如: float pay[3][6];
這樣就是定義了一個二維數組,其第一維有3個元素,第二維有6個元素。
每一維的長度分別用一隊方括號括起來。
由此引出 二維數組定義的一般形式為:
類型說明符 數組名[常量表達式][常量表達式]
例如:
float a[3][4],b[5][10];
定義a為3×4(3行4列)的數組,b為5×10(5行10列的數組)。
注意,不能寫成 ??float a[3,4],b[5,10]; ?//在一對方括號內寫兩個下標,錯誤
C語言中,對于二維數組采用以上的定義方式,使得二維數組可被看做一種特殊的一維數組: 它的元素又是一個一維數組。 例如,可以把a看做一個一維數組,它有三個元素:
如圖:? ? ?
?并且它的每一個元素又是包含了4個元素的一維數組:
?因此可以把a[0],a[1],a[2]看做3個一維數組的名字,且是3個行元素,即a[0],a[1],a[2]是一維數組名,這一點對于后面二維數組元素的引用很重要!
?C語言中,二維數組中的元素排列的順序是按行存放的,即在內存中先順序存放第0行的元素,接著再存放第1行的元素。
?假設數組a存放在從2000字節開始的一段內存單元中,一個元素占4個字節,前16個字節(2000~2015)存放序號為0的行中4個元素,接著的16個字節(2016~2031)存放序號為1的行中4個元素,以此類推。
需要注意的是: 用矩陣形式(如3行4列)表示二維數組,是邏輯上的概念,能形象地表示出行列關系。而在內存中,各元素是連續存放的,不是二維的,是線性的。
最通常的方法:
數組名[下標][下標]
例如 a[1][2];
需要注意引用時不能超過下標范圍
方法①? ? 分行給二維數組賦初值。 如:
int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
方法②? ? 寫在一個花括號內,按排列順序賦值。 如:
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
方法③? ? 可以對部分元素賦初值。 如:
1) int a[3][4] = {{1},{5},{9}};
賦值的結果為:
?2)int a[3][4] = {{1},{0,6},{0,0,11}};
初始化后的數組元素如下:
?3)int a[3][4] = {{1},{5,6}};
數組元素為:
?4)int a[3][4] = {{1},{ },{9}};
注:在定義并賦值時,可以省略第一維的大小(實際上不管幾維數組都只能省略第一維大小),但第二維的大小不能省。
?為了讓讀者更加清楚多維數組的地址問題,我們先回顧一下一維數組的地址問題。
在一維數組中,數組名代表的是數組首元素的地址
?例如: int a[4] = {1,2,3,4};
a的值就是第一個元素即a[0]的地址,a+1,往右移一個位置,所以是a[1]的地址,例如如下程序:
#includeint main(){ int a[4] = {1,2,3,4}; printf("%d/n",a); printf("%d/n",a+1); return 0;}
?運行結果為:
?可以看出輸出的是地址,且地址是連續存放的。
那么一維數組中的元素值呢?
我們所熟悉且最常見的就是用[ ]表示法來獲取數組元素值,
所以 a[i] 就是 第 i 個元素的值,既然 a+i 是第 i 個元素的地址,那么我們知道地址引用其值的方法就是在前面加一個“ * ”符號, 所以*(a+i) = a[i] = 第 i 個元素的值。
需要明確, a[i] 和 *(a+i) 是無條件等價的(非常重要)!!!
好了,這里我們主要為了明確:一維數組名 代表的是 一維數組首元素的地址。
解決了這個問題,我們就可以開始思考二維數組的地址問題了。
? 首先 我們定義一個二維數組為: int a[3][4] = {{1,3,5,7},{9,11,13,15},{17,19,21,23}};
a 是二維數組名。
a 數組包含3行,即3個行元素:a[0]、a[1]、a[2]。
而每一個行元素又是一個一維數組,它包含4個元素(即4個列元素)。
(這里有點疑惑的讀者,可以上翻二維數組的初步理解再次細看一下~~~)
一定要非常敏感 行 和 列 的區分。
例如:a[0]所代表的一維數組又包含4個元素:a[0][0],a[0][1],a[0][2],a[0][3],見下圖
?可以認為: 二維數組a是由3個一維數組所組成的,或者看做3個行組成。
從二維數組的角度來看,a代表二維數組首元素的地址,現在的首元素不是一個簡單的整型元素哦,而是由4個整型元素所組成的一維數組。
因此,a代表的是首行(即序號為0的行)的起始地址。
而a+1則是序號為1的行的起始地址。 (a是指向一行的,a+1當然在行方向移動~~~)
如果二維數組的首行起始地址為2000的話,按照一個int類型數據占4個字節來算,則a+1的值應該是2000+4×4 = 2016 (因為一行有4個數組嘛~~~)
又因為a+1指向a[1],也能說a+1的值是a[1](代表序號為1的行)的起始地址
既然a[0],a[1],a[2]是一維數組名,從復習一維數組部分可以知道,代表的是數組首元素的地址,
因此,a[0]代表一維數組a[0]中第0列的地址,即&a[0][0]。 (這里一定要反復琢磨是指向列的)
?同理,a[1]的值是第1列的地址,即&a[1][0]。
請思考,a數組0行1列元素的地址怎么表述(除了&a[0][1],這個太簡單了)?
?a[0]是一維數組名,也是第0行的首元素(或是第0列)的地址,所以同一維數組一樣,
第1列就是第0列+1得到,所以a[0]+1就是a[0][1]的地址。
?此時“a[0]+1”中的1,代表的是1個列元素的字節數,即4個字節,a[0]的值是2000,a[0]+1的值是2004而不是2016。
同理,既然a[0]是a[0][0]的地址,a[1]是a[1][0]的地址,并且a[0]+1是a[0][1]的地址,
所以,a[i]+j 就是 a[i][j]的地址。 注意:是地址,而不是元素的值
看圖理解一下:
? ? ? ? ? ? ? ? ? ?
?前面一維復習部分已經講述了,*(a+i)和a[i]是等價的,
因此,a[0]+1 和 *(a+0)+1 和 *(a)+1 都是 &a[0][1]。
所以 a[i]+j 和 *(a+i)+j 都是 &a[i][j],是一個地址而不是值。
注意不要寫成了 *(a+1+2),這樣就是 *(a+3) 即 a[3] 了。
?進一步分析,若想得到某行某列元素的值呢?
?如果把地址弄明白,那么這就變得簡單了。
& 取地址運算符, * 指針運算符(或稱“間接訪問運算符”),如 *p代表p指向的對象。
得到地址取值的方法就是加一個“ * ”。
因此, *(a[i]+j) 或者 *(*(a+i)+j)是a[i][j]的值,或說 *(a[i]+j) =? *(*(a+i)+j) = a[i][j] 。
總結一下:二維數組 a 的有關指針?
?舉個栗子
在軍訓中,一個排分3個班,每個班站成一行,3個班為3行,相當于一個二維數組。
為方便比較,班和戰士的序號也從0開始。
請思考:班長點名和排長點名的方法有什么不同。
班長從第0個戰士開始逐個檢查本班戰士是否在隊列中,班長每移動一步,走過一個戰士。
而排長點名則是以班為單位,排長先站在第0班的起始位置,檢查該班是否到齊,然后走到第1班的起始位置,檢查該班是否到齊。
班長移動的方向是橫向的,而排長移動的方向是縱向的。排長看起來只走了一步,但實際上他跳過了一個班的10個戰士。這相當于從a移到a+1(見下圖)
班長“指向”的是戰士,排長“指向”的是班,班長相當于列指針,排長相當于行指針。
一定要好好理解,并能分清楚 指向行 和 指向列!!!
?
?為了找到某一班內某一個戰士,必須給兩個參數,即第i班第j個戰士,先找到第i班,然后由該班班長在本班范圍內找第j個戰士。這個戰士的位置就是a[i]+j(這是一個地址)。
開始時班長面對第0個戰士。注意,排長和班長的初始位置是相同的(如圖的a和a[0]都是2000),但他們面對的對象是不同的,班長面向的對象是戰士,排長面向的對象是班。排長“指向”班,在圖上是“縱向管理”,他縱向走一步就跳過一個班,而班長“指向”戰士,在圖上是“橫向管理”,橫向走一步只是指向下一個戰士。
所以 a+1 是在行方向上移動,a[i]+1(或者 *(a+i)+1)是在列的方向上移動!!!
二維數組a相當于排長,而每一行(即一維數組a[o],a[1],a[2])相當于班長,每一行中的元素(如 a[1][2])相當于戰士。
再次強調: 二維數組名( 如a )是指向行的。因此a + 1中的 “1’’ 代表一行中全部元素所占的字節數 。 一維數組名 (如a[0],a[1])是指向列元素的。a[0] + 1 中的1代表一個 a 元素所占的字節數。
在指向行的指針前面加一個 *,就轉換為指向列的指針。 例如,a 和 a+1 是指向行的指針,在它們前面加一個 * 就是 *a 和 *(a+1),它們就成為指向列的指針,分別指向a數組0行0列的元素和1行0列的元素。
反之,在指向列的指針前面加&,就成為指向行的指針。例如 a[0] 是指向0行0列元素的指針,在它前面加一個&,得&a[0],由于a[0] 與 *(a+0)等價,因此 &a[0]與&*a等價,也就是與a等價,它指向二維數組的 0 行。
?不要把&a[i]簡單的理解為a[i]元素的存儲單元的地址,因為二維中并不存在a[i]這樣一個實際的數據存儲單元。 它只是一種地址的計算方法,能得到第 i 行的起始地址。
到這里為止,我們通過二維數組名的不同方式來獲取元素地址、值的內容就這些了,請讀者要反復思考,加深理解。(ps 比較期末、考研的C語言的試題,這可是重點+難點)
?
?啊這,你以為已經結束了嗎??Impossible!!! 還沒有和指針更深入的結合呢~~~
?一看標題那么長有木有,但實際就是普通的指針!
?指向數組元素的指針變量,不就是指針變量嘛
例如: 定義一個 int a = 10;? ? ? int *p = &a;
所以 p=&a, *p = a = 10;
此時用來指向數組的 具體元素 也是一樣的,不管是一維還是二維。
例如這樣一個簡單的程序:
#includeint main(){ int a[4] = {1,2,3,4}; int *p = a; for(int i=0;i<4;i++) printf("%d ",*(p+i)); return 0;}
結果為:
用在二維中效果也是一樣的:
#includeint main(){ int a[3][4] = {{1,3,5,7},{9,11,13,15},{17,19,21,23}}; int *p = a[0]; for(int i=0;i<4;i++) printf("%d ",*(p+i)); return 0;}
?結果為:
?但要注意,p是列指針,a在二維代表行,所以需要把p賦值為a[i]才是指向列的!
?同時,因為在內存中地址是連續的!所以也可以輸出全部的元素,而不只是一行
for(int i=0;i<12;i++) printf("%d ",*(p+i));
?
?指向數組的指針變量,就是 數組指針
定義方式例如:? ? int (*p)[4];
解釋:①別忘了 (),因為[ ]的優先級比 * 高。
? ? ? ? ? ?②4代表的 指向的數組 有4個元素,如果是二維數組,那么就是第二維(即列)的個數。
?第三個標題說的方法是指向具體元素(列)的,我們也可以改用另一種方法,使p不是指向整型變量,而是指向一個包含m個元素的一維數組。
這是,如果p指向a[0](即 p = &a[0] 當然也就是 p = a),則p+1 不是指向a[0][1]了,就是指向a[1],
p的增量是以一行字節為單位的。 這里其實也相當于 第二部分說的 用數組名來引用二維數組一樣,畢竟 p = a 嘛。
看一個簡單的引用例子:
#includeint main(){ int a[3][4] = {{1,3,5,7},{9,11,13,15},{17,19,21,23}}; int (*p)[4] = a; printf("數組指針引用方式1:%d/n",p[1][1]); printf("數組指針引用方式2:%d/n",*(*(p+1)+1)); return 0;}
結果為:
由此看出這樣的確和用 a 來引用方式是一樣的。
?不僅數組指針可以引用二維數組,指針數組也可以哦
?一個數組,若其元素均為指針類型數據,則成為 指針數組
也就是說,指針數組中的每一個元素都是存放一個地址。
定義樣例:? int *p[3];? ? ? //這里的3 我的理解是 如果指向二維數組,則3是代表行數。
注意:這里就不用()了,[ ]優先級比 * 高。因此p先與[3]結合為p[3] ,表示是一個數組。
?既然指針數組是一個數組,所以肯定得用p[0] =? ? p[1]=? ?p[2]=? ? 這樣的形式來賦值~~~
而又因為,數組里面應該放的是一行的地址起始,而且在二維中,a[0],a[1],a[2]不就是行地址嘛
所以就有如下例子:?
#includeint main(){ int a[3][4] = {{1,3,5,7},{9,11,13,15},{17,19,21,23}}; int *p[3]; p[0] = a[0], p[1] = a[1], p[2] = a[2]; return 0;}
那么元素的值呢? 很簡單,我們拿到了地址,所以加一個“ * ”即可得到
printf("%d/n",p[0]); //錯誤寫法printf("%d/n",*(p[0])); //正確寫法
結果為:?
?注意正確寫法,因為我們的p[0]才是地址,不要以為p[0] = *(p+0) 就是值了。
總結一下前面的方法,比較一下對于各種定義,應該怎么賦值以及引用。
#include#includeusing namespace std;int main(){ int a[3][4] = {{1,3,5,7},{9,11,13,15},{17,19,21,23}}; int (*p)[4]; p = a; int *p2[3]; p2[0] = a[0], p2[1] = a[1], p2[2] = a[2]; int *p3 = a[1]; printf("1.數組名本身的引用:%d/n",a[1][1]); printf("2.數組指針引用方式1:%d/n",p[1][1]); printf("3.數組指針引用方式2:%d/n",*(*(p+1)+1)); printf("4.指針數組引用:%d/n",*(p2[1]+1)); printf("5.指針引用1:%d/n",*p3); printf("6.指針引用2:%d/n",*(p3+1)); return 0; }
?
自此,關于指針+二維數組的 地址和值 的引用講解就結束了,指針本就是難點,再加上二維、地址就更懵了,希望讀者能夠好好理解文章的內容。 如果在閱讀時,發現有什么沒有提及或是有錯誤的地方,歡迎廣大讀者留言反映。
最后,寫文不易,如有轉載 請標明出處!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/119106.html
摘要:此專欄文章是對力扣上算法題目各種方法的總結和歸納整理出最重要的思路和知識重點并以思維導圖形式呈現當然也會加上我對導圖的詳解目的是為了更方便快捷的記憶和回憶算法重點不用每次都重復看題解畢竟算法不是做了一遍就能完全記住的所 ...
此專欄文章是對力扣上算法題目各種方法的總結和歸納, 整理出最重要的思路和知識重點并以思維導圖形式呈現, 當然也會加上我對導圖的詳解. 目的是為了更方便快捷的記憶和回憶算法重點(不用每次都重復看題解), 畢竟算法不是做了一遍就能完全記住的. 所以本文適合已經知道解題思路和方法, 想進一步加強理解和記憶的朋友, 并不適合第一次接觸此題的朋友(可以根據題號先去力扣看看官方題解, 然后再看本文內容). 關...
摘要:哪吒社區技能樹打卡打卡貼函數式接口簡介領域優質創作者哪吒公眾號作者架構師奮斗者掃描主頁左側二維碼,加入群聊,一起學習一起進步歡迎點贊收藏留言前情提要無意間聽到領導們的談話,現在公司的現狀是碼農太多,但能獨立帶隊的人太少,簡而言之,不缺干 ? 哪吒社區Java技能樹打卡?【打卡貼 day2...
摘要:所以是數組指針,而是指針數組。因為對一個二維數組,可以不知道有多少行,但是必須知道一行多少元素。當二維數組數組名傳參,形參接收時,數組的行可以省略,列不能省略,如果省略了列,我們就無法知道當指針加減跳過幾個字節。 ...
此專欄文章是對力扣上算法題目各種方法的總結和歸納, 整理出最重要的思路和知識重點并以思維導圖形式呈現, 當然也會加上我對導圖的詳解. 目的是為了更方便快捷的記憶和回憶算法重點(不用每次都重復看題解), 畢竟算法不是做了一遍就能完全記住的. 所以本文適合已經知道解題思路和方法, 想進一步加強理解和記憶的朋友, 并不適合第一次接觸此題的朋友(可以根據題號先去力扣看看官方題解, 然后再看本文內容). 關...
閱讀 1349·2021-09-28 09:43
閱讀 4115·2021-09-04 16:41
閱讀 1917·2019-08-30 15:44
閱讀 3727·2019-08-30 15:43
閱讀 774·2019-08-30 14:21
閱讀 2037·2019-08-30 11:00
閱讀 3318·2019-08-29 16:20
閱讀 1922·2019-08-29 14:21