說明:這個C語言專題,是學習iOS開發的前奏。也為了讓有面向對象語言開發經驗的程序員,能夠快速上手C語言。如果你還沒有編程經驗,或者對C語言、iOS開發不感興趣,請忽略
為了讓大家更好地學習和理解數組,我們先來認識一下內存中的"地址"。
地址
1.計算機中的內存是以字節為單位的存儲空間。內存的每一個字節都有一個唯一的編號,這個編號就稱為地址。凡存放在內存中的程序和數據都有一個地址,也就是說,一個函數也有自己的內存地址。
2.當定義一個變量時,系統就分配一個帶有唯一地址的存儲單元來存儲這個變量。比如:
char a = 'A'; // A的ASCII值為65 int b = 66;

int c = 10; // 以16進制形式輸出地址 printf("16進制:%x\n", &c); // 以10進制形式輸出地址 printf("10進制:%d", &c);
輸出結果:
一、一維數組
1.一維數組的定義
* 定義的形式為:類型 數組名[元素個數]
int a[5];
* []只能放在數組名的后面,下面的都是錯誤寫法:
int[5] a; // 錯誤 int[] b; // 錯誤
* []里面的個數必須是一個固定值,可以是常量(比如6、8)、常量表達式(比如3+4、5*7)。絕對不能使用變量或者變量表達式來表示元素個數,大多數情況下不要省略元素個數(當數組作為函數的形參和數組初始化時除外)
下面的都是正確寫法:
int a[5]; // 整型常量 int b['A']; // 字符常量,其實就是65 int c[3*4]; // 整型常量表達式
下面的都是錯誤寫法:
int a[]; // 沒有指定元素個數,錯誤 int i = 9; int a[i]; // 用變量做元素個數,錯誤
2.一維數組的存儲
定義數組時,系統將按照數組類型和個數分配一段連續的存儲空間來存儲數組元素,如int a[3]占據了連續的6字節存儲空間(在16位環境下,一個int類型占用2個字節)。要注意的是,數組名代表着整個數組的地址,也就是數組的起始地址。
注意:其實a不算是變量,是個常量,它代表着數組的地址。上圖把a放到變量一欄是為了方便大家理解數組結構。
數組a的地址是ffc1,a[0]的地址是ffc1,a[1]的地址是ffc3,a[2]的地址是ffc5。因此a == &a[0],即第一個元素的地址就是整個數組的地址
3.一維數組的初始化
* 初始化的一般形式是:類型 數組名[元素個數] = {元素1, 元素2, ...};
int a[2] = {8, 10};
其實相當於:
int a[2]; a[0] = 8; a[1] = 10;
int a[4] = {2, 5};
當數組為整型時,初始化未確定初值的元素,默認為0,所以上面的a[2]、a[3]都為0
* 當對全部數組元素都賦初值時,可以省略元素個數
int a[] = {2, 5, 7};
說明數組a的元素個數是3
* 數組初始化時的賦值方式只能用於數組的定義,定義之后只能一個元素一個元素地賦值
下面的寫法是錯誤的:
1 int a[3]; 2 a[3] = {1, 2, 3}; // 錯誤 3 a = {1, 2, 3}; // 錯誤
其實為什么是錯誤的寫法呢?我們可以簡要分析一下。
1> 第2行的a[3]代表着訪問數組的第4個元素,首先這里已經是數組下標越界了;就算沒有越界,給a[3]賦值時也應該賦一個int類型的整數,不應該是{}。
2> 第3行的a是數組名,代表着數組的地址,它是個常量!給常量賦值,那肯定錯了!
4.一維數組與函數參數
如果忘記了實參和形參的意思,可以回看下《四、函數》這篇文章
* 一維數組的元素作為函數實參,與同類型的簡單變量作為實參一樣,是單向的值傳遞,即數組元素的值傳給形參,形參的改變不影響實參
// b是test函數的形參(形式參數) void test(int b) { b = 9; } int main() { int a[3]; a[0] = 10; printf("函數調用前的a[0]:%d\n", a[0]); test(a[0]); // a[0]是test函數的實參(實際參數) printf("函數調用后的a[0]:%d", a[0]); return 0; }
輸出結果:
* 大家都知道,數組名代表着整個數組的地址,如果一維數組的名字作為函數實參,傳遞的是整個數組,即形參數組和實參數組完全等同,是存放在同一存儲空間的同一個數組。這樣形參數組修改時,實參數組也同時被修改了。形參數組的元素個數可以省略。
// b是test函數的形參(形式參數) void test(int b[]) { // 也可以寫int b[3] b[0] = 9; } int main() { int a[3]; a[0] = 10; printf("函數調用前的a[0]:%d\n", a[0]); test(a); // a是test函數的實參(實際參數) printf("函數調用后的a[0]:%d", a[0]); return 0; }
輸出結果:
二、二維數組
1.二維數組的定義
定義形式:類型 數組名[行數][列數]
int a[2][3]; // 共2行3列,6個元素
2.二維數組的存儲
* C語言把二維數組當作是一維數組的集合,即二維數組是一個特殊的一維數組:它的元素是一維數組。例如int a[2][3]可以看作由一維數組a[0]和一維數組a[1]組成,這兩個一維數組都包含了3個int類型的元素
* 二維數組的存放順序是按行存放的,先存放第一行的元素,再存放第2行的元素。例如int a[2][3]的存放順序是:a[0][0] → a[0][1] → a[0][2] → a[1][0] → a[1][1] → a[1][2]
* 再來看看在內存中的存儲情況,例如int a[2][2]
(注意:a[0]、a[1]也是數組,是一維數組,而且a[0]、a[1]就是數組名,因此a[0]、a[1]就代表着這個一維數組的地址)
1> 數組a的地址是ffc1,數組a[0]的地址也是ffc1,即a = a[0];
2> 元素a[0][0]的地址是ffc1,所以數組a[0]的地址和元素a[0][0]的地址相同,即a[0] = &a[0][0];
3> 最終可以得出結論:a = a[0] = &a[0][0],以此類推,可以得出a[1] = &a[1][0]
3.二維數組的初始化
* 按行進行初始化
int a[2][3] = { {2, 2, 3}, {3, 4, 5} };
* 按存儲順序進行初始化(先存放第1行,再存放第2行)
int a[2][3] = {2, 2, 3, 3, 4, 5};
* 對部分元素進行初始化
int a[2][3] = { {2}, {3, 4} }; int b[3][3] = { { }, { , , 2}, {1, 2, 3}};
* 如果只初始化了部分元素,可以省略行數,但是不可以省略列數
int a[][3] = {1, 2, 3, 4, 5, 6}; int a[][3] = {{1, 2, 3}, {3, 5}, {}};
有些人可能想不明白,為什么可以省略行數,但不可以省略列數。也有人可能會問,可不可以只指定行數,但是省略列數?
其實這個問題很簡單,如果我們這樣寫:
int a[2][] = {1, 2, 3, 4, 5, 6}; // 錯誤寫法
大家都知道,二維數組會先存放第1行的元素,由於不確定列數,也就是不確定第1行要存放多少個元素,所以這里會產生很多種情況,可能1、2是屬於第1行的,也可能1、2、3、4是第一行的,甚至1、2、3、4、5、6全部都是屬於第1行的
三維乃至更多維的數組就不再提及了,大家以此類推。