做者 謝恩銘,公衆號「程序員聯盟」(微信號:coderhub)。
轉載請註明出處。
原文: https://www.jianshu.com/p/7fe...《C語言探索之旅》全系列程序員
結束了上一課「指針」的有點艱難的旅程(其實上一課沒有講很深),C語言探索之旅 | 第二部分第二課:進擊的指針,C語言的王牌! ,這一課咱們來學習數組這個 C語言的重點。編程
咱們將繼續「一路向北」,「指」哪打哪。小程序
爲何這麼說呢?由於這一課咱們還要涉及指針的知識。就如上一課說的,指針的使用幾乎是貫穿 C語言的,並且咱們也會步步深刻指針的學習。數組
否則指針怎麼能被稱爲 C語言的精華呢?因此「指針啊,每天見」,你覺得指針會這麼「放過」你麼?Too young, too naive... :P微信
想要如今逃避嗎?那可不是成功者的表現哦。數據結構
不少學 C語言的朋友,都以爲指針和數組貌似有點相似,又好像不一樣。有點撲朔迷離的感受,「情深深,雨濛濛」,糾葛不清,難分難捨。函數
因此這一課咱們就來解惑:到底指針和數組有什麼聯繫和區別呢?學習
學完這一課相信會有些許撥雲見霧的感受。spa
在這一課中,咱們一塊兒學習如何建立數組這種數據類型(或者說是數據結構)。數組在 C語言中也是極爲重要的內容,因此你們不能由於過了指針那一坎,就不「正襟危坐」了。操作系統
咱們會首先解釋一下數組在內存中的機制(配圖),對內存的解釋始終是很重要的,由於理解好了內存的機制,C語言才能學得紮實。
因此推薦你們花些時間去學習王爽老師編寫的《彙編語言》第三版,對於理解 C語言和計算機原理是頗有幫助的。彙編語言可能沒必要學得很深,入門就好。
能夠看我之前寫的文章 學習彙編對編程有什麼幫助?如何學習 。
一個程序員若是能很好地知道本身的程序背後的機理,方能寫出穩定、健壯的程序。
數組是在內存中具備連續地址的一系列相同類型的變量的集合。
好吧,我知道這個定義「學究氣過重,腐儒味更甚」。
簡單地說,數組就是「巨大的變量」(怎麼聽起來那麼變扭,幸好我加了一個「的」字...),其中能夠存儲一系列相同類型的變量(long,double,int,char,等)。
數組在英語中是 array。array 這個詞有這些含義:「數組,陣列;排列,列陣;大批,一系列」。
數組中變量(能夠稱爲數組元素或成員)的數目是固定的(固然也能夠構造動態數組,之後再說),它能夠包含 2 個,3 個,10 個,25 個,2500 個,甚至更多變量,由你決定存放數目。
下圖展現了一個由四個元素組成的數組,首元素的地址是 1700。
當你要建立包含 4 個元素的數組時,實際上是首先向操做系統這個「大管家」發出請求:「可否給我在內存中分配一塊地址,以存放這四個元素」。
操做系統通常都會應聲而起,隨傳隨到,乖乖分配你要的地址。
可是對於數組來講,這四個元素的存放地址是連續的,中間沒有間隔,這也是數組的一個特色。
各個元素之間「親密無間」。如上圖所示,四個元素的地址分別是:1700,1701,1702,1703。
每個地址的區塊上存放相同類型的一個數字(說到底全部數據對於計算機來講都是數字麼)。
若是數組是 int 類型的,那麼每個數組元素的地址塊上就存放了一個 int 類型的數。咱們不能在一個數組裏既存放 int 型又存放 double 型,魚與熊掌不可兼得也~
小結一下,對於數組:
咱們來學習如何定義一個數組。首先定義一個包含 4 個 int 類型數據的數組:
int array[4];
你會說:「原來這麼簡單…」
是啊,就是這麼簡單,只須要在中括號裏寫上你須要的元素個數,一個數組就建立好了。
數組成員的個數通常來講沒有限制,固然這取決於你的內存大小。
接下來,咱們如何訪問每個數組成員呢?
也很簡單,
array[成員編號]
注意:成員編號是從 0,1,2,這樣一直到數組元素個數減一。還記得之前說過電腦數數是從 0 開始的嗎?由於電腦是用二進制的。
因此數組的第一個元素就是 array[0],依此類推。因此上面的包含 4 個成員的數組,它的成員編號是沒有 4 的,而是 0,1,2,3。
若是我要將數組中的成員的值賦爲像上圖中同樣,我能夠這麼作:
int array[4]; array[0] = 10; array[1] = 12; array[2] = 3; array[3] = 7;
你會說:「我可沒看到數組和指針有什麼聯繫啊。」
事實上,若是你只寫 array,那就是一個指針,是指向數組首元素的首地址的一個指針。
例如:
int array[4]; printf("%d", array);
結果輸出:
1700
固然,這裏的 1700 是照應上面圖示中假設的,實際上你會獲得其餘的地址值。
若是你帶着下標來訪問數組,那會獲得數組的對應那個下標的成員:
printf("%d", array[0]);
結果輸出
10
對其餘的下標也是相似。由於咱們知道了單獨用數組名,是表示一個指針,因此咱們也能夠這樣來得到數組的首元素的值:
printf("%d", *array);
結果輸出
10
相似地,咱們也能夠獲得數組的第二個元素的值,經過這樣:
*(array + 1)
因此下面這兩個表達式的結果是同樣的,都是 12 :
array[1] *(array + 1)
C語言有好多個版本,或者稱之爲標準,有 C89(1989 年制定),C99(1999 年制定),C11(2011 年制定),等等。
從版本 C99 開始,容許建立大小可變的數組,也就是元素的個數是一個變量:
int variable = 5; int array[variable];
可是這個新特性可不是全部的 C 編譯器都認識,因此有些版本的編譯器就會在第二行出錯。
咱們的課程參考和基於的 C 語言標準是 C89,因此咱們的課程裏就不容許有大小可變的數組了。
咱們須要達成協議:
數組的元素個數(中括號裏的數)必須是一個常量,不能是變量,連 const 變量也不行。
數組須要有一個固定的大小。
你會問:難道就真的不能建立元素個數可變的數組了嗎?
答案是:能夠建立元素數目一開始不肯定的數組,即便在 C89 裏。
可是要達到這樣的目的,咱們要使用另外一種技術:動態分配。以後的課程會講到。
假如咱們如今要顯示數組中每個成員的值。
我固然能夠一個一個用 printf 輸出,可是這樣的話可能代碼就太多了。最好仍是用一個循環來顯示,好比經常使用的 for 循環:
int main(int argc, char *argv[]) { int array[4], i = 0; array[0] = 10; array[1] = 12; array[2] = 3; array[3] = 7; for (i = 0 ; i < 4 ; i++) { printf("%d\n", array[i]); } return 0; }
程序輸出:
10 12 3 7
咱們的 for 循環藉着一個稱爲 i 的變量來遍歷咱們的數組,其實 i 是很經常使用的變量名,大部分程序員都喜歡將其用於遍歷數組,由於 i 是 index(表示「下標」)的首字母。
你們應該發現了:咱們在定義一個數組時,在中括號 [] 裏不能放一個變量(數組的成員個數須要肯定),可是在遍歷數組時卻能夠在中括號裏放置變量。
注意:不要嘗試訪問 array[4],由於你會獲得任意數據,或者獲得一個錯誤,由於這個地址已經產生了「數組越界」,操做系統就會停止你的程序,由於你的程序嘗試訪問一個沒有權限訪問的地址。
如今既然咱們已經知道如何遍歷一個數組了,那麼咱們應該也能很輕鬆地初始化一個數組了:咱們能夠用 for 循環來將數組的各個成員都初始化爲 0。
int main(int argc, char *argv[]) { int array[4], i = 0; // 數組的初始化 for (i = 0 ; i < 4 ; i++) { array[i] = 0; } // 打印數組各個成員來肯定數值 for (i = 0 ; i < 4 ; i++) { printf("%d\n", array[i]); } return 0; }
輸出:
0 0 0 0
看了上面的初始化方式,以爲仍是不過癮,咱們需要知道還有另外一種初始化的方式,就是這樣寫:
數組名[4] = {數值1, 數值2, 數值3,數值4};
簡單說來,就是把各個成員的數值寫在大括號裏,用逗號隔開,以下:
int main(int argc, char *argv[]) { int array[4] = {0, 0, 0, 0}, i = 0; for (i = 0 ; i < 4 ; i++) { printf("%d\n", array[i]); } return 0; }
輸出也是:
0 0 0 0
實際上,也能夠更簡便。就是寫上前幾個成員的初始值。後面的成員的值,假如你沒給出初值,是會自動初始化爲 0 的:
int array[4] = {10, 23}; // 初始化的值 : 10, 23, 0, 0
第一個成員取到的值是 10,第二個是 23,第三和第四都初始化爲 0 了。
那麼如何簡便地把數組的全部成員都初始化爲 0 呢,只須要這樣寫:
int array[4] = {0};
這樣,全部的成員都初始化爲 0 了。
在咱們寫程序的時候可能會須要把一個數組的全部成員的值顯示出來,那爲何不把這個功能寫成一個函數呢?
藉着這個小程序,咱們也能夠學習如何將一個數組做爲參數傳遞給函數。
咱們須要傳遞兩個參數給函數:數組(實際是數組的地址)和數組的大小。
咱們以前說過,數組名直接用的話實際上是一個指針,指向數組的首元素的首地址!
因此咱們能夠這樣來寫咱們的程序:
void display(int *array, int arraySize); int main(int argc, char *argv[]) { int array[4] = {10, 15, 3}; // 顯示數組內容 display(array, 4); return 0; } void display(int *array, int arraySize) { int i; for (i = 0 ; i < arraySize ; i++) { printf("%d\n", array[i]); } }
程序輸出:
10 15 3 0
上面的函數 display 看上去好像和咱們以前在指針那一課的函數沒什麼區別,這個函數的第一個參數是一個指向 int 型的指針(咱們的數組名 array)。
第二個參數是數組的大小(成員個數),爲了知道咱們的 for 循環何時停止。
也有另外一種方式來代表一個函數接受一個數組做爲參數,這樣寫:
void display(int array[], int arraySize);
此次我用了中括號,來代表參數接受一個數組,但其實傳遞給函數的仍是數組的首元素,沒有傳遞整個數組過去(由於拷貝整個數組是很大的開銷)。
這樣寫的好處是不會讓讀者誤覺得接受的參數是一個普通的指針,固然這一次就不用在中括號裏面寫數組的大小了。
我寫程序時兩種方式都用,但通常爲了避免混淆,仍是用中括號的方式多一些。
學了今天的課,想讓你們本身實現一些和數組有關的函數。
這裏只給出練習題的描述,你們須要本身思考如何實現這些函數。以後能夠在程序員聯盟的 QQ 羣和微信羣裏討論。
求數組的平均值。函數模板:
double arrayAverage(int array[], int arraySize);
寫一個拷貝數組的函數,這個函數有三個參數,第一和第二個參數是數組名,第三個參數是數組大小。將第一個參數(數組)的內容拷貝到第二個參數(數組)裏。函數模板:
void copyArray(int originalArray[], int copyArray[], int arraySize);
寫一個函數,有三個參數,第一個是一個數組,第二個是數組大小,第三個是最大值。若是這個數組裏有成員的值大於最大值,則將此成員的值變爲 0。函數模板:
void arrayMax(int array[], int arraySize, int valueMax);
這道練習題是最難的。寫一個函數,來給數組按成員數值從小到大排序,好比,數組原先是 [15, 81, 22, 13],排序後變爲 [13, 15, 22, 81] 。函數模板:
void orderArray(int array[], int arraySize);
加油吧,歡迎交流討論。
今天的課就到這裏,一塊兒加油吧!
我是 謝恩銘,公衆號「程序員聯盟」(微信號:coderhub)運營者,慕課網精英講師 Oscar 老師,終生學習者。 熱愛生活,喜歡游泳,略懂烹飪。 人生格言:「向着標杆直跑」