b97902HW 板


LINE

前言 之前在〈陣列簡介 I〉我們說了陣列的基本意義與操作;在〈陣列簡 介 II〉我們說了字串與字串的操作函式。在這一篇文,我們會再去 探討陣列的其他用法諸如二維陣列、陣列大小運算子(sizeof)。 * 二(多)維陣列 先前我們介紹了一維陣列,你可以把一維陣列想像 成一條數線,在 0 的位置上放一個元素,在 1 的 位置上放另一個元素,...,在 n-1 的位置上放一 個元素。可是如果只有一維陣列,在處理一些資料 的時候就不那麼方便了。例如說,如果我們有一個 2列x3行 矩陣,我們要如何方便地表示它呢? ┌ ┐ │ 1 , 3 , 5 │ │ │ │ 3 , 5 , 7 │ └ ┘ * 陣列大小運算 有時候,為了方便,我們不會在宣告時寫死陣列大 小,而是透過陣列的初始值列表(Initialization List) 讓編譯器去決定大小,如果我們的程式又突 然需要陣列的大小,我們該怎麼辦呢? int array[] = {1, 2, 3, 4, 5}; int size = ????; * 陣列的傳遞 有時候我們的程式要對陣列做一些重複而鎖碎的處 理我們會希望把這些程式碼寫成一個函式。可是我 們要怎麼樣讓我們的函式接送一個陣列呢?本文會 大略提到這一個問題,不過真得要詳細地說要在學 指標之後才能完全的解釋。 int array[7] = {1, 2, 3, 4, 5, 6, 7}; int count = count_if_greater(array, 7 /*size*/, 3 /*num*/); 二(多)維陣列 二維陣列的宣告 如果我們要宣告一個二維陣列,和一維陣列不同的部分就僅止於 [] 的數目。左邊的 [] 代表的是我們要有多少列(Row),右邊的 [] 代 表的是我們要有多少欄(Column),例如: int array[2][3]; 這樣就會是一個二維陣列,他會有 2 列 3 欄。這一個陣列會有 6 個元素(Elememt)。這一個陣列的形狀如下: 0 1 2 <= 欄索引 ┌─────┬─────┬─────┐ 0 │12 │893 │985 │ ├─────┼─────┼─────┤ 1 │187690 │346743 │-2222 │ └─────┴─────┴─────┘ ^= 列索引 當然,和一維陣列的時候一樣,索引都是從 0 開始編號,所以欄索 引是 0 到 欄數-1 而列索引是 0 到 列數-1。 另外,值得一提得是在 C/C++ 之中,二維陣列是用「以列為主(Row Major)」對映到線性位址上。所謂的以列為主是指「同一列的會被放 在一起」,同一列的不同欄排完之後,才會開始排下一列。所以上面 的範例在記憶體的 Layout 大約如下: 位址(Address) ┌────┐ 0x00123344 │0C000000│ // array[0][0] ├────┤ 0x00123348 │7D030000│ // array[0][1] ├────┤ 0x00123352 │D9030000│ // array[0][2] ├────┤ 0x00123356 │2ADD0200│ // array[1][0] ├────┤ 0x00123360 │774A0500│ // array[1][1] ├────┤ 0x00123364 │52F7FFFF│ // array[1][2] └────┘ ※ 結果可能因測試環境而異,此例是在 Little Endian 與 32-bit Integer 環境之中的結果。 在正常的程式碼之中,你如果沒有特別注意你應該不會(也不用) 注意這一個細節,不過以後我們有幾會說到指標的時候,會再提到。 初始化列表 我們可以在宣告陣列的時候,加上初始化列表(Initializer List), 讓陣列元素被初始化為我們想要的數值,而非一大堆本來就在記憶體 中毫無意義的數值。我們和一維陣列的時候一樣,不過這一次我們用 了二層的大括號{}int array1[2][3] = {{1, 2, 3}, {4, 5, 6}}; 0 1 2 <= 欄索引 ┌─────┬─────┬─────┐ 0 │1 │2 │3 │ ├─────┼─────┼─────┤ 1 │4 │5 │6 │ └─────┴─────┴─────┘ ^= 列索引 當然,和我們在〈陣列簡介 I〉中所述的一樣,最左邊的維度可以省 略,讓編譯器幫我們算陣列的大小。例如下面的宣告,就可以幫我們 宣告一個 5 列 x 3 欄的二維陣列,每一個元素的值如下所示: int array2[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}, {13, 14, 15}}; 0 1 2 <= 欄索引 ┌─────┬─────┬─────┐ 0 │1 │2 │3 │ ├─────┼─────┼─────┤ 1 │4 │5 │6 │ ├─────┼─────┼─────┤ 2 │7 │8 │9 │ ├─────┼─────┼─────┤ 3 │10 │11 │12 │ ├─────┼─────┼─────┤ 4 │13 │14 │15 │ └─────┴─────┴─────┘ ^= 列索引 存取元素 當然,如果我們要存取特定元素,我們只要用二次下標運算子(Sub- script),就可以了。例如我們要寫一個九九乘法表到陣列之中: int nines[10][10]; int i, j; for (i = 0; i < 10; ++i) { for (j = 0; j < 10; ++j) { nines[i][j] = i * j; } } 我們要印出來就可以用 for (i = 1; i < 10; ++i) { for (j = 1; j < 10; ++j) { printf("%d\t", nines[i][j]); } printf("\n"); } 結果就是: 1 2 3 4 5 6 7 8 9 2 4 6 8 10 12 14 16 18 3 6 9 12 15 18 21 24 27 4 8 12 16 20 24 28 32 36 5 10 15 20 25 30 35 40 45 6 12 18 24 30 36 42 48 54 7 14 21 28 35 42 49 56 63 8 16 24 32 40 48 56 64 72 9 18 27 36 45 54 63 72 81 如果我們要用 scanf 我們就可以用: scanf("%d", &(array[i][j])); 字串陣列 在學過二維陣列之後,我們可以討論一下字串陣列,首先我們看一下: 字串(String)的本質是字元陣列,所以字串陣列可以說是字元的二維 陣列。所以我們可以用下面的方法來宣告一個字串陣列: char weeks[][10] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; 注意:最右邊的維度的大小必需可以容納最長的字串,以上面的例子 來說,最長的字串是 Wednesday 一共需要 10 個字元。 for (i = 0; i < 7; ++i) { printf("%s\n", weeks[i]); } (還有一點就是:這一個方法會浪費若干空間,不過解決的方法我們 就不在本文討論) 多維的陣列 多維陣列和二維陣列唯一的不同就是 [] 的數目,像下面就是一個三 維陣列的例子: int cubearray[2][2][2]; 當然,和上面的規則一樣,最左邊的維度在有初始化列表的時候可以 省略。 int cubearray[][2][2] = { {{1,2}, {3,4}}, {{5,6}, {7,8}}, {{9,10}, {11,12}} }; ==> cubearray[0][0][0] == 1, cubearray[0][0][1] == 2, cubearray[0][1][0] == 3, cubearray[0][1][1] == 4, cubearray[1][0][0] == 5, cubearray[1][0][1] == 6, cubearray[1][1][0] == 7, cubearray[1][1][1] == 8, cubearray[2][0][0] == 9, cubearray[2][0][1] == 10, cubearray[2][1][0] == 11, cubearray[2][1][1] == 12 和二維陣列一樣,多維陣列也是用「以列為主(Row Major)」來表示 的,不過因為在多維陣列之中沒有「列」的觀念,所以在 C99-draft 之中,是以 Row-wise 來定義。所謂的 Row-wise 是指:最右邊的維 度必需能以最快速、簡單的方法來存取,我們可以把 Row-wise 想像 成最右邊的維度會被排在一起。例如上面的,就會是 [0][0][0], [0][0][1], [0][1][0], [0][1][1], [1][0][0], ...。筆者就不再 贅述。 陣列大小的運算 在 C 語言中,有一個運算子專門用來計算一個型別、變數在記憶體 中的大小。這一個運算子叫做 sizeof。他可以有一個引數,它的值 就是引數在記憶體中所佔有的空間(bytes)例如我們可以用它來取得每一個型別的大小(要耗用多少 bytes 的 記憶體)。例如:sizeof(char) 就是 1bytes,其他的值,可能因環 境而異,以下僅供參考: sizeof(int) == 4, sizeof(long) == 4, sizeof(short) == 2, sizeof(double) == 8, sizeof(wchar_t) == 2 我們也可以用 sizeof 來取得一個複合型別(例如:指標、陣列型別) 的大小: sizeof(void *) == 4 sizeof(char *) == 4 sizeof(int *) == 4 sizeof(int [4]) == 16 // (sizeof(int) * 4) 我們也可以用 sizeof 來取得 陣列變數 所佔的空間: int array[5]; sizeof(array) == 5 * sizeof(int) == 20 所以我們可以發現如果反過來做,我們就可以知道陣列有多少元素: int array[] = {1, 2, 3, 4, 5}; int size = sizeof(array) / sizeof(int); /* size == 5 */ 而二維陣列我們可以用: int array[][5] = { {0, 0, 0, 0, 0}, {1, 1, 1, 1, 1}, {2, 2, 2, 2, 2} }; int size = sizeof(array) / sizeof(int [5]); 來找出列數,在這裡,我們可以把 int[5] 視為一個型別,他的大小 是 sizeof(int) * 5。 陣列在函式之間的傳遞 想像一下,有一天我們必需寫一個程式來求出一串數字之中大於給訂 數字的個數。而很不幸的,我們會在程式之中常常用到,此時大家想 必會想到用函式把這一個功能抽取出來,但是我們要怎麼傳遞陣列呢? 預備知識:在這一部分我無可避免得會談到指標,對於不熟悉指標的 同學可能會有些吃力。 指標,你可以想像它是一個路標,用以儲放位址,我們可以透過這一 個位址找到記憶體中的特定變數,不過這已經超出本文的範籌,有機 會再談了。 陣列名字可以被轉型為對應的指標型別。這是 C 語言中定義的合法轉 型之一。不過要注意的是從陣列轉型為指標,會失去一些資訊,如 sizeof 等,我們稍後會再說明。 int array[] = {1, 2, 3, 4, 5}; int *ptr = array; // 轉型為指標 int array2d[][3] = {{1, 2, 3}, {2, 4, 6}}; int (*ptr)[3] = array2d; // 轉型為「指向陣列的某一列」的指標, // 且同一列恰有 3 個元素。這一個我 // 現在沒有辦法說太多,只能說這和線 // 性定址有關,所以不能直接轉為 int* 我們怎麼傳遞陣列? 更正確地來說,在 C 語言之中,為了效率考量,沒有傳遞陣列這一 種事,只有傳遞陣列的指標。說得複雜一點,對於陣列而言,只有 Call-by-Pointer 沒有 Call-by-Value。要在函式之間傳遞陣列, 只能透過指標。我們要透過指標間接地存取在陣列中的元素。 另外,在 C 語言之中,下標運算子(Subscript []),和「計算位址 偏移量並取值」是同一件事。所以我們可以直接對指標做下標運算。 例如我們可以宣告一個函式原型: int count_if_greater(int array[], int size, int number); 第一個參數看起來像是在傳遞陣列,事實上編譯器會轉換成以下的形 式,所以是傳遞指標;第二個參數是在傳遞陣列的大小;第三個參數 是傳遞我們要比較的數值。 int count_if_greater(int *array, int size, int number); 而我們的函式可以這樣實作: int count_if_greater(int *array, int size, int number) { int reuslt = 0; int pos = 0; for (pos = 0; pos < size; ++pos) { if (array[pos] > number) { result++; } } return result; } 從這一個函式,我們可以發現:我們可以直接對指標做下標運算,就 像是在操作陣列一樣,另外就是我們也要傳入陣列的大小。 還有,如果你的函式原型如下,則中括號中的數值可寫可不寫,因為 被轉型之後,都是指標,所以不會有問題。 int count_if_greater(int array[ ], int size, int number); int count_if_greater(int array[9], int size, int number); ==> int count_if_greater(int *array, int size, int number); 而我個人是傾向不要寫,因為多寫並不會保證你輸入的陣列大小一定 會大於該數值。所以為了不要有所誤解,我是建議你不用寫。 如果是另一個例子,我們要處理二維或多維陣列,此時我們 [] 中的 值只有最左邊的可以省,因為轉型成指標時,為了正確的計算位址偏 移量,右邊的維度仍必需相同。 int array[5][2][3][4]; int (*ptr)[2][3][4] = array; void func(int array[5][2][3][4]); void func(int array[ ][2][3][4]); ==> void func(int (*array)[2][3][4]); 還有要注意,因為所有的參數都是指標,所以上面用 sizeof 來計算 陣列大小的方法會有問題,因為 sizeof(array) 是取得指標在記憶體 之中佔用的記憶體量。所以 sizeof(array)/sizeof(int) 不會是陣列 有多少元素,因此我們的函式要傳入陣列的大小。 (錯) 元素個數的計算 int count(int array[]) // 就算是 array[5] 也不行 { int size; size = sizeof(array)/sizeof(int); // 錯! size = sizeof(array)/sizeof(*int); // 這也錯! return size; } 結語 我們的陣列簡介的系列文章,到此就結束了。可能還有一些內容還沒 有講,不過因為個人之水平有限,或者困難度過高不適合當做簡介的 內容,又或者需要更多的預備知識,就不再贅述,有興趣者可以尋找 相關的資料。 從三篇簡介,我們可以學到基本陣列的使用、字串操作與相關函式、 二維陣列、多維陣列、sizeof、陣列(指標)的傳遞。希望能讓各位 對陣列的使用有基本的認識。 ---------------- 想寫得東西太多, 能用的時間太少, 站在機會成本的岔路上, 選擇左邊,我才有機會通古今之變, 選擇右邊,我才有機會成一家之言, 然而我一直停留在原地,究天人之際, 在左右二條道路之間躊躇不定。 LoganChien: 原 Po 真得是瘋了... 11/15 00:56 --



※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 140.112.241.166
1F:推 rohan21:推微甲爆了心情不好(/‵Д′)/~ ╧╧ 11/15 04:10
2F:推 xflash96:推 11/15 09:03







like.gif 您可能會有興趣的文章
icon.png[問題/行為] 貓晚上進房間會不會有憋尿問題
icon.pngRe: [閒聊] 選了錯誤的女孩成為魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一張
icon.png[心得] EMS高領長版毛衣.墨小樓MC1002
icon.png[分享] 丹龍隔熱紙GE55+33+22
icon.png[問題] 清洗洗衣機
icon.png[尋物] 窗台下的空間
icon.png[閒聊] 双極の女神1 木魔爵
icon.png[售車] 新竹 1997 march 1297cc 白色 四門
icon.png[討論] 能從照片感受到攝影者心情嗎
icon.png[狂賀] 賀賀賀賀 賀!島村卯月!總選舉NO.1
icon.png[難過] 羨慕白皮膚的女生
icon.png閱讀文章
icon.png[黑特]
icon.png[問題] SBK S1安裝於安全帽位置
icon.png[分享] 舊woo100絕版開箱!!
icon.pngRe: [無言] 關於小包衛生紙
icon.png[開箱] E5-2683V3 RX480Strix 快睿C1 簡單測試
icon.png[心得] 蒼の海賊龍 地獄 執行者16PT
icon.png[售車] 1999年Virage iO 1.8EXi
icon.png[心得] 挑戰33 LV10 獅子座pt solo
icon.png[閒聊] 手把手教你不被桶之新手主購教學
icon.png[分享] Civic Type R 量產版官方照無預警流出
icon.png[售車] Golf 4 2.0 銀色 自排
icon.png[出售] Graco提籃汽座(有底座)2000元誠可議
icon.png[問題] 請問補牙材質掉了還能再補嗎?(台中半年內
icon.png[問題] 44th 單曲 生寫竟然都給重複的啊啊!
icon.png[心得] 華南紅卡/icash 核卡
icon.png[問題] 拔牙矯正這樣正常嗎
icon.png[贈送] 老莫高業 初業 102年版
icon.png[情報] 三大行動支付 本季掀戰火
icon.png[寶寶] 博客來Amos水蠟筆5/1特價五折
icon.pngRe: [心得] 新鮮人一些面試分享
icon.png[心得] 蒼の海賊龍 地獄 麒麟25PT
icon.pngRe: [閒聊] (君の名は。雷慎入) 君名二創漫畫翻譯
icon.pngRe: [閒聊] OGN中場影片:失蹤人口局 (英文字幕)
icon.png[問題] 台灣大哥大4G訊號差
icon.png[出售] [全國]全新千尋侘草LED燈, 水草

請輸入看板名稱,例如:iOS站內搜尋

TOP