1. 前言
程式語言「C語言」因其簡潔性與高效能,被廣泛應用於系統開發、嵌入式系統等多個領域。其中,「陣列」是一種用來集中管理資料的重要資料結構,在許多程式中被頻繁使用。
本文將詳細解說在 C 語言中「取得陣列長度的方法」。特別針對初學者容易遇到的難點,從基礎到應用進行細緻說明,幫助你扎實掌握正確取得陣列長度的技巧。
2. 陣列的基本概念
什麼是陣列?
陣列是一種可以將相同資料型態的值集中管理的資料結構。在處理多筆相同型態的資料時非常方便,並會在記憶體中連續配置空間。
陣列的用途
- 資料批次處理 – 適用於需要集中管理同類型資料的情況,例如學生分數、感測器資料等。
- 重複處理的應用 – 因為可透過迴圈依序存取,因此適合在相同處理需重複執行時使用。
- 記憶體管理 – 使用連續的記憶體空間,存取速度快且效率高。
陣列的運作原理
陣列透過索引(index,下標)來存取各個元素。在 C 語言中,索引從 0 開始,最後一個元素可透過「陣列大小 – 1」來存取。
範例:
int numbers[5] = {10, 20, 30, 40, 50};
printf("%d\n", numbers[0]); // 輸出 10
printf("%d\n", numbers[4]); // 輸出 50
在此範例中,陣列 numbers
存放了 5 個整數,並可透過索引存取各元素。
3. 陣列的宣告與初始化
宣告陣列的方法
在 C 語言中,陣列的宣告方式如下:
型別 陣列名稱[大小];
具體範例:
int scores[10]; // 整數型陣列,元素數量為 10
此範例中,宣告了一個整數型(int)的陣列 scores
,並配置了 10 個元素的記憶體空間。
陣列的初始化
宣告陣列時可以同時進行初始化。
- 明確初始化的例子
int values[5] = {1, 2, 3, 4, 5};
- 僅初始化部分元素的例子
int data[5] = {10, 20}; // 其他元素自動初始化為 0
- 省略大小的初始化例子
int numbers[] = {10, 20, 30}; // 自動計算元素數量為 3
未初始化的注意事項
若在使用陣列前未初始化,可能會存有不可預測的值(隨機記憶體資料)。因此建議在需要時進行初始化。
4. 取得陣列長度(元素數)的方法
在 C 語言中,正確取得陣列的長度(元素數)非常重要。尤其在進行迴圈處理或資料管理時,需要掌握陣列的大小。本節將詳細說明具體的取得方法與注意事項。
4.1 使用 sizeof
運算子取得長度
最常見的方法是使用 sizeof
運算子。sizeof
會回傳資料型態或變數的記憶體大小(位元組數)。
基本程式範例
#include <stdio.h>
int main() {
int array[5] = {10, 20, 30, 40, 50};
int length = sizeof(array) / sizeof(array[0]); // 計算陣列元素數
printf("陣列長度: %d\n", length); // 輸出: 5
return 0;
}
重點說明:
sizeof(array)
– 取得整個陣列的位元組大小。sizeof(array[0])
– 取得第一個元素的位元組大小。- 用陣列總大小除以單一元素大小,即可計算出元素數量。
4.2 將陣列傳入函式時的注意事項
當陣列作為引數傳入函式時,會轉換成「指標」。因此,sizeof
運算子此時回傳的是指標的大小(多數情況下為 4 或 8 位元組),而不是整個陣列的大小。
錯誤範例
#include <stdio.h>
void printArrayLength(int arr[]) {
printf("大小: %ld\n", sizeof(arr) / sizeof(arr[0])); // 無法正確運作
}
int main() {
int array[5] = {1, 2, 3, 4, 5};
printArrayLength(array);
return 0;
}
解決方法
在傳遞陣列時,同時傳遞長度作為另一個引數。
修正版範例
#include <stdio.h>
void printArrayLength(int arr[], int length) {
printf("陣列長度: %d\n", length);
}
int main() {
int array[5] = {1, 2, 3, 4, 5};
int length = sizeof(array) / sizeof(array[0]); // 計算元素數
printArrayLength(array, length);
return 0;
}
5. 取得字串陣列的長度
在 C 語言中,字串也是以陣列形式儲存。但與一般數值陣列不同,字串是 char
型態的陣列,且在結尾會自動加上特殊的終止字元 '\0'
。本節將解說取得字串陣列長度的方法與注意事項。
5.1 字串與陣列的關係
字串是由多個字元組成的 char
型態陣列。範例如下:
char str[] = "Hello";
此時 str
在記憶體中的配置如下:
H | e | l | l | o | ‘\0’ |
---|
重點說明:
'\0'
是終止字元,表示字串的結尾。- 陣列的大小會包含這個終止字元,因此此例中大小為 6 位元組。
5.2 使用 strlen
函式取得字串長度
要取得字串長度(字元數),可使用標準函式庫的 strlen
函式。
程式範例:
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "Hello";
printf("字串長度: %ld\n", strlen(str)); // 輸出: 5
return 0;
}
注意事項:
strlen
計算時不包含終止字元'\0'
。
5.3 與 sizeof
的差異
取得字串長度時,雖然也可以使用 sizeof
,但結果與 strlen
不同。
程式範例:
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "Hello";
printf("sizeof: %ld\n", sizeof(str)); // 輸出: 6(包含終止字元)
printf("strlen: %ld\n", strlen(str)); // 輸出: 5(不含終止字元)
return 0;
}
差異重點:
sizeof
回傳的是整個陣列的大小(含終止字元)。strlen
回傳的是實際字元數(不含終止字元)。
6. 可變長陣列(VLA)的使用
在 C 語言中,自 C99 標準開始引入了可變長陣列(VLA: Variable Length Array)。使用此功能可以在執行時決定陣列的大小,而不必在編譯時固定。本節將詳細解說 VLA 的特點、使用方法與注意事項。
6.1 什麼是可變長陣列(VLA)?
一般的陣列屬於靜態陣列,大小必須在編譯時決定。而 VLA 則是一種可在執行時依輸入或計算結果動態決定大小的陣列。
傳統靜態陣列範例:
int arr[10]; // 大小在編譯時固定
可變長陣列(VLA)範例:
int size;
scanf("%d", &size); // 執行時決定大小
int arr[size]; // 執行時配置的陣列
6.2 VLA 的基本用法
以下範例會依使用者輸入的大小建立陣列並輸出內容:
程式範例:
#include <stdio.h>
int main() {
int n;
printf("請輸入陣列大小: ");
scanf("%d", &n); // 執行時決定大小
int arr[n]; // 宣告 VLA
// 輸入資料
for (int i = 0; i < n; i++) {
arr[i] = i + 1;
}
// 輸出陣列內容
printf("陣列元素: ");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
輸出範例:
請輸入陣列大小: 5
陣列元素: 1 2 3 4 5
重點說明:
- VLA 可以在執行時決定大小,資料管理更靈活。
- 即使程式開始時未知大小,也可以在中途依輸入決定。
7. 關於陣列長度的注意事項
在 C 語言中,管理陣列的大小至關重要。如果陣列長度處理錯誤,不僅會造成程式執行異常,甚至可能帶來嚴重的安全風險。本節將介紹重要注意事項與安全編碼方法。
7.1 防止陣列越界存取
由於陣列是固定大小的資料結構,如果超過索引範圍存取,可能導致不可預期的行為或程式崩潰。
錯誤範例(越界存取)
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
for (int i = 0; i <= 5; i++) { // 條件錯誤,會越界
printf("%d\n", arr[i]);
}
return 0;
}
正確作法:
- 設定正確的迴圈條件
for (int i = 0; i < 5; i++) { // 正確條件
printf("%d\n", arr[i]);
}
- 使用動態計算的長度
int length = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < length; i++) {
printf("%d\n", arr[i]);
}
7.2 緩衝區溢位(Buffer Overflow)的風險
管理陣列大小不當最常見的問題之一就是緩衝區溢位,即寫入超過陣列範圍的資料,導致覆寫其他記憶體區域。
錯誤範例:
#include <stdio.h>
#include <string.h>
int main() {
char buffer[10]; // 陣列大小 10
strcpy(buffer, "This string is too long!"); // 超過大小
printf("%s\n", buffer);
return 0;
}
安全作法:
- 使用安全版本的函式
strncpy(buffer, "This string is too long!", sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0'; // 確保結尾有終止字元
8. 總結
本文詳細解說了 C 語言中關於「陣列長度」的基礎到進階知識。陣列雖然好用,但若操作不當,可能引發錯誤或安全漏洞。以下是重點回顧與後續建議。
8.1 本文回顧
- 陣列的基本概念與宣告、初始化
- 陣列會連續儲存相同型態的元素,宣告時需指定大小。
- 初始化時可省略大小,由編譯器自動計算。
- 取得陣列長度的方法
- 靜態陣列可用
sizeof
計算長度。 - 函式傳參時需額外傳遞長度。
- 取得字串陣列長度的方法
- 字串用
strlen
取得字元數,理解與sizeof
的差異。 - 多維字串需分別管理每列長度。
- 可變長陣列(VLA)的使用
- 可在執行時決定大小,但要注意堆疊溢位與編譯器支援性。
- 必要時可考慮使用
malloc
動態配置。
- 安全編碼與長度管理
- 避免越界與緩衝區溢位,使用安全函式。
- 動態記憶體需搭配釋放使用。
8.2 後續建議
- 動手實作範例
- 編譯並執行文中範例,觀察結果。
- 挑戰進階題
- 嘗試撰寫多維陣列與指標的應用程式。
- 持續實踐安全編碼
- 養成考慮安全性與錯誤處理的習慣。
8.3 最後
陣列是 C 語言的基礎,同時具有廣泛應用範圍。請善用本文內容,編寫出安全且高效的程式。
常見問題(FAQ)
Q1: 為什麼使用 sizeof
在函式內取得陣列長度會失敗?
A:sizeof
在陣列直接定義於作用域內時,會回傳整個陣列的大小。但當陣列作為引數傳入函式時,會退化成指標,只保留陣列首位址。因此 sizeof
取得的是指標大小(4 或 8 位元組),無法得到正確元素數。
解決方法:
在呼叫函式時一併傳遞陣列長度。
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
printf("%d\n", arr[i]);
}
}
int main() {
int array[5] = {1, 2, 3, 4, 5};
printArray(array, sizeof(array) / sizeof(array[0])); // 傳遞長度
return 0;
}
Q2: 取得字串長度時該用 sizeof
還是 strlen
?
A:
依用途而定:
sizeof
: 回傳整個陣列大小(含終止字元'\0'
),適合檢查緩衝區大小。strlen
: 回傳實際字元數(不含'\0'
),適合取得字串長度。
範例:
char str[] = "Hello";
printf("%ld\n", sizeof(str)); // 輸出: 6(含 '\0')
printf("%ld\n", strlen(str)); // 輸出: 5
Q3: VLA 和 malloc
動態配置哪個比較好?
A:
取決於需求:
- VLA 特點: 語法簡單,執行時可決定大小,但使用堆疊記憶體,過大會導致堆疊溢位,且 C11 之後部分編譯器不支援。
malloc
特點: 使用堆記憶體,可處理大型資料,需自行釋放記憶體,但可移植性高。
Q4: 如果忘記釋放動態記憶體會怎樣?
A:
會發生記憶體洩漏(Memory Leak),累積後可能造成系統變慢或程式崩潰。
解決方法:
動態配置後,使用完必須呼叫 free()
釋放。
int *arr = malloc(10 * sizeof(int));
if (arr == NULL) {
printf("配置失敗\n");
return 1;
}
free(arr); // 釋放記憶體
Q5: 如何防止緩衝區溢位?
A:
請注意以下事項:
- 檢查輸入長度 – 限制輸入資料不超過陣列大小。
- 使用安全函式 – 例如用
strncpy
取代strcpy
。 - 預留終止字元空間 – 陣列大小應包含
'\0'
。 - 邊界測試 – 執行測試檢查極端情況。
總結
本 FAQ 針對陣列長度取得的常見疑問與注意事項提供了具體解答,請搭配本文內容一起參考,幫助你撰寫安全且高效的程式。