【給初學者】C語言的 char 陣列完整解說|從字串操作的基礎到應用

1. 前言

C 語言在系統開發與嵌入式開發的現場仍然被廣泛使用。其中「char 陣列」是處理字串時最基本且重要的語法要素之一。

C 語言沒有標準的字串型別。相反地,使用字元陣列(char 陣列)來表示字串。對於初學者而言這並不直觀,因而對 char 陣列的理解常成為學習 C 語言的重大障礙。

此外,若未了解 char 陣列與 char 指標(char*)的區別,以及空字元(\0)的存在等細節規格,將會成為意外錯誤的原因。

本文聚焦於「C 語言 char 陣列」主題,從基本用法到進階技巧,並清楚說明常見錯誤的避免方法。

想要正式學習 C 語言或想複習 char 陣列的讀者,請務必看到最後。下一章將先說明 char 陣列的定義與基本機制。

2. char陣列是什麼?

在 C 語言中,「char 陣列」是用來將多個文字(char 型)一起儲存的陣列。這是處理字串時的基本結構。

char 型是什麼?

在 C 語言中,char 型是用來表示單一字元的資料型別。例如,可以這樣定義單一字元的變數。

char c = 'A';

如此,'A' 這樣以 單引號 包住的單一字元,會被定義為 char 型。

char 陣列的基本語法

若要儲存多個字元,使用 char 型的陣列。可如下定義:

char str[6] = {'H', 'e', 'l', 'l', 'o', '\0'};

此陣列為 "Hello" 這個 5 個字元的字串加上 \0(空字元),共確保了 6 個字元的記憶體空間。

空字元('

陣列的大小與注意事項

'
)的重要性

在 C 語言中,使用 空字元 '\0' 來表示字串的結尾。若沒有此符號,字串操作函數 將無法正確運作,可能會產生未預期的行為。

char str[] = "Hello"; // 自動在末尾加入 '\0'

如上所示,使用以 雙引號 包住的字串常量,編譯器 會自動在末尾加入 '\0'

侍エンジニア塾

3. char 陣列的宣告與初始化

使用 char 陣列時,基本上要確保 必要的文字數 + 1(空字元分) 的大小。若將字串寫入大小不足的陣列,會導致 緩衝區溢出,程式可能異常終止。

靜態宣告與初始化

為了使用 char 陣列,首先需要適當的宣告與初始化。這裡將說明 C 語言中 char 陣列的基本宣告方法、初始化方式,以及動態記憶體的使用。

使用字串常值的初始化

最基本的方法是明確指定陣列大小進行宣告,並逐一字元初始化。

char str[6] = {'H', 'e', 'l', 'l', 'o', '\0'};

這樣一來,str 會被視為 "Hello" 的字串。重要的是,必須在末尾包含 '\0'

指定大小並賦值字串常值

在 C 語言中,也可以使用字串常值進行簡易的初始化,如下所示。

char str[] = "Hello";

在此情況下,陣列的大小會自動成為 "Hello" + '\0' 的 6 個字元。若要修改字串內容,將其宣告為 char 陣列即可安全地重新寫入。

使用動態記憶體配置(malloc)

雖然可以在指定陣列大小的同時,用字串常值初始化,但必須注意大小不足

char str[5] = "Hello"; // ❌ 錯誤的原因(大小不足)

如上所示,"Hello" 需要 5 個字元加上 1 個(’\0’)共計 6 個字元。因此,至少需要宣告為 char str[6]

4. 字串操作

若想更靈活地處理字串,可以使用 malloc 來動態分配 char 陣列。

#include 
#include 

char* str = malloc(6 * sizeof(char));
strcpy(str, "Hello");

此方法會動態分配記憶體,並透過指標操作字串。使用完畢後,務必使用 free(str); 釋放記憶體。

字串複製:strcpy

在 C 語言中,char 陣列進行的字串操作,通常會利用標準函式庫的函數。在這裡,我們將以具體範例,說明基本的字串操作函數及其使用方法。

字串串接:strcat

strcpy是將一個字串複製到另一個 char 陣列的函數。

#include 

char src[] = "Hello";
char dest[10];
strcpy(dest, src);

注意事項: dest如果沒有確保足夠的大小,就會造成緩衝區溢位。大小應確保為複製字串的長度+1(空字元)。

取得字串長度:strlen

strcat是將兩個字串結合。第 2 個引數的內容會附加到第 1 個引數的末尾。

#include 

char str1[20] = "Hello";
char str2[] = " World";
strcat(str1, str2);

結果,str1會變成 "Hello World"。第 1 個引數的陣列也必須有串接後整體大小能容納的餘裕

字串比較:strcmp

strlen函數會傳回字串的長度(不含空字元的字元數)。

#include 

char str[] = "Hello";
int len = strlen(str); // len = 5

由於不計算空字元,不熟悉 C 語言的人需要注意。

字串搜尋:strchrstrstr

要比較兩個字串是否相同,可以使用strcmp

#include 

char str1[] = "Hello";
char str2[] = "World";

if (strcmp(str1, str2) == 0) {
    // 相同
} else {
    // 不同
}

此函數在相同時傳回 0,不同時傳回字元碼的差異。

5. char 陣列與指標的差異

要搜尋特定字元或部分字串,可以使用以下函數。

#include 

char str[] = "Hello World";

// 字元的搜尋
char *ptr1 = strchr(str, 'o'); // 指向第一個 'o' 的指標

// 部分字串的搜尋
char *ptr2 = strstr(str, "World"); // 指向 "World" 起始位置的指標

找不到時,兩個函數都會傳回 NULL

宣告的差異

在 C 語言中處理字串時、char 陣列char 指標(char*)看似相似,實際上卻具有不同的性質的特性。正確理解此差異,可防止記憶體誤用與意外的錯誤。

可寫性差異

首先來看看宣告方式的差異。

char str1[] = "Hello";  // char 陣列
char *str2 = "Hello";   // char 指標

str1具有實體的陣列,在記憶體上會分配 6 位元組(”Hello” + ‘\\0’) 。另一方面,str2指向儲存字串常值的記憶體區域的指標

記憶體結構的差異

陣列的str1可以自由修改陣列內的字元。

str1[0] = 'h'; // OK

然而,char* str2 = "Hello"; 如同以指標存取字串常值的情況下,修改其內容屬於未定義的行為。

str2[0] = 'h'; // ❌ 未定義的行為(可能發生執行時錯誤)

取得大小的差異

  • char陣列堆疊上
  • char指標常數區域(唯讀)堆區域(malloc 等)記憶體的處理需要注意

總結: 使用上的要點

在陣列的情況下,sizeof(str1) 會返回陣列的整體的位元組數

char str1[] = "Hello";
printf("%lu", sizeof(str1)); // → 6(含空字元)

另一方面,指標則 sizeof(str2) 會返回指標本身的大小(通常 4〜8 位元組),因此無法用於取得陣列大小的目的

char *str2 = "Hello";
printf("%lu", sizeof(str2)); // → 8(64 位元環境)

 

6. 函式傳遞 char 陣列的方式

項目char配列char指標
內容更改可能原則不可(字面情況)
取得尺寸sizeof()strlen()
重寫用途適合不適合(只讀)
柔軟性固定尺寸柔軟,但需注意

基本範例:將 char 陣列作為參數傳遞

在 C 語言中,將陣列傳遞給函式時,會使用「指標傳遞」而非「值傳遞」。這對 char 陣列 也是相同的,傳遞給函式時會傳遞陣列的首位位址(指標)

了解這個機制在函式間操作字串或修改內容的情況下非常重要。

修改內容的函式

#include 

void printString(char str[]) {
    printf("文字列: %s
", str);
}

int main() {
    char greeting[] = "Hello";
    printString(greeting);
    return 0;
}

在此範例中,printString 函式接收 char[] 型的參數,但實際上是以 char* 的形式接收。也就是說,char str[] 等同於 char *str

陣列大小的管理

即使在函式內更改陣列的內容,也可以透過傳入的位址直接操作資料。

#include 

void toUpperCase(char str[]) {
    for (int i = 0; str[i] != ' '; i++) {
        if ('a' <= str[i] && str[i] <= 'z') {
            str[i] = str[i] - ('a' - 'A');
        }
    }
}

int main() {
    char text[] = "hello world";
    toUpperCase(text);
    printf("%s
", text); // 出力: HELLO WORLD
    return 0;
}

如此一來,即使想要修改陣列內容,因為它被視為指標,函式內的操作會反映到呼叫端

使用 const 的唯讀參數

在 C 語言中,將陣列傳遞給函式時不會同時傳遞大小資訊。因此,為了安全操作,最好將大小也作為參數傳遞,這是最佳實踐。

void printChars(char str[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%c ", str[i]);
    }
    printf("
");
}

此外,使用 strlen 函式動態取得字串長度也是常見做法。但請注意,對未以 NUL 結尾的陣列不要使用此函式。

7. 實作範例: 字串的逆序顯示

如果在函式內不修改字串,建議使用 const char* 來明確表示為唯讀。

void printConstString(const char *str) {
    printf("%s
", str);
}

這樣可以防止意外的寫入,並清楚傳達函式的規格。

目的

在此,我們將利用迄今所學的 char配列 的知識,實際製作字串逆序顯示的程式

範例程式碼

將使用者輸入的字串,從尾端向前端逐字元輸出。這對於陣列操作與字串長度取得、迴圈處理的練習非常有效。

說明

#include 
#include 

void printReversed(char str[]) {
    int len = strlen(str);
    for (int i = len - 1; i >= 0; i--) {
        putchar(str[i]);
    }
    putchar('n');
}

int main() {
    char text[100];

    printf("請輸入字串:");
    fgets(text, sizeof(text), stdin);

    // 移除換行字元(fgets 使用時的對策)
    size_t len = strlen(text);
    if (len > 0 && text[len - 1] == 'n') {
        text[len - 1] = '\0';
    }

    printf("逆序顯示為:");
    printReversed(text);

    return 0;
}

執行範例

  • fgets
  • 輸入的末尾附加的 (換行字元)也不要忘記執行移除處理。
  • strlen()

應用提示

請輸入字串: OpenAI
逆序顯示為: IAnepO

8. 常見錯誤與其對策

此處理可應用於字串的回文判定堆疊結構的理解等,對學習演算法具有應用價值。此外,也可使用指標運算進行改寫,成為更高階的練習題材。

1. 忘記空字元(

2. 緩衝區大小不足

)的遺漏

在 C 語言中處理 char 陣列 時,從初學者到高手都有可能陷入的陷阱有幾個。這裡將具體說明常見錯誤及其防止對策・解決對策

3. 字串常量的寫入

最常見的錯誤之一是忘記在字串末尾加上空字元(\0)。

char str[5] = {'H', 'e', 'l', 'l', 'o'}; // ❌ 缺少結尾的'\0'
printf("%sn", str); // 未定義的行為

對策:

char str[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; // ✅ 正確

或者,使用字串常量會自動加入空字元。

4. fgets 的換行字元處理遺漏

strcpystrcat等函式中,複製先的陣列大小不足會導致記憶體破壞(緩衝區溢位)。

char str1[5];
strcpy(str1, "Hello"); // ❌ 將 6 個字元複製到大小為 5 的陣列

對策:

  • 在複製目的地。 (例:「文字數 + 1」)
  • 更安全的的使用也值得考慮
char str1[6];
strncpy(str1, "Hello", sizeof(str1) - 1);
str1[5] = '\0'; // 為了保險,明確在末尾加上空字元

5. 指標與陣列的混淆

char *str = "Hello"; 這樣宣告的指標,指向不可寫入的區域的可能性。若對其寫入,會在執行時產生錯誤。

char *str = "Hello";
str[0] = 'h'; // ❌ 執行時產生 Segmentation fault

對策:

char str[] = "Hello"; // ✅ 宣告為可寫入的陣列
str[0] = 'h';

9. 總結

fgets 用來取得字串時,需要注意末尾會保留換行字元(n)。

fgets(str, sizeof(str), stdin);
// str 會包含類似 "Hellon\0" 的內容

對策:

size_t len = strlen(str);
if (len > 0 && str[len - 1] == 'n') {
    str[len - 1] = '\0';
}

本篇文章學到的內容

因為外觀相似,char*char[] 混為一談,可能導致未定義的行為。需要注意 大小取得 (sizeof) 的差異 以及 寫入可否的差異

對未來學習的建議

在本篇文章中,我們針對「C語言的 char 陣列」從基礎到應用逐步解說。最後,回顧本篇內容,並介紹未來學習的指引。

本篇文章學到的內容

  • char 陣列的角色與宣告方式 在 C 語言中沒有字串型別,因此處理字串時需要使用 char 型別的陣列。
  • 字串文字列與空字元(\0)的重要性 要表達字串,必須在末尾加入空字元。
  • 使用標準函式庫函式的字串操作 , , , 等函式可使用,能有效處理字串。
  • char 陣列與 char 指標的差異 陣列與指標雖相似但並非相同,且在記憶體結構、可否寫入等方面存在明確差異。
  • 傳遞陣列給函式的方法與安全性考量 陣列實際上是以指標方式傳遞的,因此在寫入或尺寸管理時需要注意。
  • 透過實踐範例與常見錯誤來鞏固理解 透過實際程式碼的應用與錯誤處理方法,學習了更實際的使用方式。

對未來學習的建議

了解 char 陣列的使用方式,是在 C 語言中理解記憶體操作基礎的第一步。在此學到的知識,可應用於以下的下一步。

  • 指標運算
  • 動態記憶體管理mallocfree
  • 與結構體的組合

此外,閱讀他人撰寫的程式碼,可以吸收自己未曾具備的觀點與寫法。養成閱讀實際專案或 OSS(開源軟體)的習慣也非常有效。

C 語言自由度高,若未正確使用會有危險的一面,但只要細心累積基礎,就能成為非常強大的武器。對 char 陣列的理解就是第一步。請務必以本篇文章為參考,穩步提升技能。