【新手必看】C語言 char 陣列完整解析|從字串操作基礎到進階應用

1. 前言

C 語言在系統開發與嵌入式開發的現場仍然被廣泛使用。其中「char 陣列」是處理字串最基本且重要的語法要素之一。 C 語言沒有標準的字串型別。相反地,使用字元陣列(char 陣列)來表示字串。這對初學者來說並不直觀,因此對 char 陣列的理解常常成為學習 C 語言的一大障礙。 此外,如果不了解 char 陣列與 char 指標(char*)的區別,以及空字元()的存在等細節規格,會成為意外錯誤的原因。 本文聚焦於「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', ' '};
此陣列為 "Hello" 這個 5 個字元的字串加上空字元(),共佔用 6 個字元的記憶體空間。

空字元(’ ‘)的重要性

在 C 語言中,為了表示字串的結尾,使用空字元 ' '。若缺少此符號,字串操作函式將無法正確執行,可能會產生預期外的行為。
char str[] = "Hello"; // 自動在結尾加入 ' '
如上所示,使用雙引號包住的字串常值時,編譯器會自動在結尾加入 ' '

陣列的大小與注意事項

使用 char 陣列時,基本上要確保 所需字元數 + 1(空字元) 的大小。若將字串指派給大小不足的陣列,會導致 緩衝區溢位,甚至使程式異常終止。

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

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

靜態宣告與初始化

最基本的方法是明確指定陣列大小進行宣告,並逐一字元初始化。
char str[6] = {'H', 'e', 'l', 'l', 'o', ' '};
這樣一來,str 可以作為 "Hello" 這樣的字串來使用。重要的是,必須在末尾包含 ' '

使用字串常值的初始化

在 C 語言中,也可以使用以下方式以字串常值進行簡易初始化。
char str[] = "Hello";
此時,陣列大小會自動變為 "Hello" + ' ' 的 6 個字元。若想修改字串內容,只要先以 char 陣列宣告,即可安全地進行寫入。

指定大小後代入常值

雖然可以在指定陣列大小的同時,以字串常值進行初始化,但 必須注意尺寸不足
char str[5] = "Hello"; // ❌ 錯誤的原因(尺寸不足)
如上所示,"Hello" 需要 5 個字元加上 1 個字元(’ ‘)共計 6 個字元。因此,至少需要 char str[6]

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

若想更彈性地處理字串,可使用 malloc 來動態配置 char 陣列。
#include <stdlib.h>
#include <string.h>

char* str = malloc(6 * sizeof(char));
strcpy(str, "Hello");
此方法會動態配置記憶體,並透過該指標來操作字串。使用完畢後,千萬別忘記使用 free(str); 釋放記憶體。

4. 字串操作

在 C 語言中,使用 char 陣列 進行字串操作時,通常會利用標準函式庫的函式。此處將說明基本的字串操作函式及其用法,並附具體範例。

字串的複製:strcpy

strcpy 是將一個字串複製到另一個 char 陣列的函式。
#include <string.h>

char src[] = "Hello";
char dest[10];
strcpy(dest, src);
注意點:dest 未確保足夠的大小,會導致緩衝區溢位。大小應確保為要複製的字串長度加 1(空字元)。

字串的串接:strcat

strcat 會將兩個字串串接。將第 2 個參數的內容加到第 1 個參數的末端。
#include <string.h>

char str1[20] = "Hello";
char str2[] = " World";
strcat(str1, str2);
結果,str1 會變成 "Hello World"。同樣地,前提是第 1 個參數的陣列必須有足夠的空間容納串接後的整體大小

取得字串長度:strlen

strlen 函式會回傳字串的長度(不含空字元的字元數)。
#include <string.h>

char str[] = "Hello";
int len = strlen(str); // len = 5
空字元 不會被計算,因此不熟悉 C 語言的人需要注意。

字串比較:strcmp

要比較兩個字串是否相同,請使用 strcmp
#include <string.h>

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

if (strcmp(str1, str2) == 0) {
    // 相等
} else {
    // 不相等
}
此函式在相等時回傳 0,否則回傳字元碼的差值。

字串搜尋:strchrstrstr

要搜尋特定字元或子字串,可使用以下函式。
#include <string.h>

char str[] = "Hello World";

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

// 子字串搜尋
char *ptr2 = strstr(str, "World"); // 指向 "World" 起始位置的指標
若找不到,兩個函式皆會回傳 NULL

5. char陣列與指標的差異

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

宣告的差異

首先來看看宣告方式的差異。
char str1[] = "Hello";  // char陣列
char *str2 = "Hello";   // char指標
str1具有實體的陣列,在記憶體上會分配 6 位元組(”Hello” + ‘\0’)。另一方面,str2指向儲存字串常量的記憶體區域的指標

可寫性的差異

作為 char 陣列的 str1,可以自由修改陣列內的字元。
str1[0] = 'h'; // OK
然而,像 char* str2 = "Hello"; 這樣以指標存取字串常量時,修改其內容屬於未定義行為。
str2[0] = 'h'; // ❌ 未定義行為(可能導致執行時錯誤)

記憶體結構的差異

  • char陣列在堆疊上分配的局部實體。
  • char指標可能指向常數區域(唯讀)堆積區域(如 malloc 等),雖較具彈性,但使用記憶體時需特別留意

取得大小的差異

對於陣列,sizeof(str1) 會回傳陣列的整體位元組數
char str1[] = "Hello";
printf("%lu", sizeof(str1)); // → 6(含 '\0')
另一方面,對於指標,sizeof(str2) 會回傳指標本身的大小(通常 4~8 位元組),因此無法用於取得陣列大小
char *str2 = "Hello";
printf("%lu", sizeof(str2)); // → 8(64 位元環境)

總結:使用分別的要點

項目char陣列char指標
內容的變更原則不可(字串常量時)
大小取得sizeof() 可精確取得需要使用 strlen()
寫入用途適合不適合(唯讀)
彈性固定大小彈性高,但需注意

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

在 C 語言中,將陣列傳遞給函式時,會使用「指標傳遞」而非「值傳遞」。這是 char 陣列 也同樣的情況,傳遞時會傳遞陣列的起始位址(指標)。 了解這個機制,在函式之間操作字串或修改內容時非常重要。

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

#include <stdio.h>

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

int main() {
    char greeting[] = "Hello";
    printString(greeting);
    return 0;
}
在此範例中,將 printString 函式的參數傳遞為 char[] 型別,但實際上會以 char* 形式接收。也就是說,char str[] 等同於 char *str

修改內容的函式

在函式內修改陣列內容時,也可以透過傳遞的位址直接操作資料。
#include <stdio.h>

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;
}
如此一來,即使想要修改陣列內容,因為會以指標方式處理,函式內的操作也會反映到呼叫端

陣列大小的管理

在 C 語言中,將陣列傳遞給函式時,不會同時傳遞大小資訊。因此,為了安全操作,最好將大小作為參數一起傳遞。
void printChars(char str[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%c ", str[i]);
    }
    printf("
");
}
此外,使用 strlen 函式動態取得字串長度也是常見做法。但請注意,對未以 NUL 結尾的陣列不要使用。

使用 const 的唯讀參數

如果在函式內不修改字串,建議使用 const char* 來明示為唯讀。
void printConstString(const char *str) {
    printf("%s
", str);
}
這樣可以防止未預期的修改,並清楚傳達函式的規格。

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

在此,我們將活用迄今學到的 char 陣列 知識,實際製作一個 將字串逆序顯示的程式

目的

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

範例程式碼

#include <stdio.h>
#include <string.h>

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. 常見錯誤與其對策

在 C 語言中處理 char 陣列 時,從初學者到高手都容易陷入的陷阱有幾個。此處將具體說明 常見錯誤及其防止與解決措施

1. 忘記加入 null 終止字元()的情況

最常見的錯誤之一是忘記在字串末尾加入 null 字元()。
char str[5] = {'H', 'e', 'l', 'l', 'o'}; // ❌ 末尾缺少'�'
printf("%sn", str); // 未定義的行為
對策:
char str[6] = {'H', 'e', 'l', 'l', 'o', '�'}; // ✅ 正確
或者,使用字串常量會自動加入 null 字元。

2. 緩衝區大小不足

strcpystrcat 等函式中,若 目標陣列的大小不足,會導致記憶體破壞(緩衝區溢位)。
char str1[5];
strcpy(str1, "Hello"); // ❌ 陣列大小5卻拷貝6個字元
對策:
  • 在目標端 確保足夠的大小(例如:「字元數 + 1」)
  • 也可考慮使用更安全的 strncpy
char str1[6];
strncpy(str1, "Hello", sizeof(str1) - 1);
str1[5] = '�'; // 為保險起見,明確在末尾加入 null 字元

3. 修改字串常量

char *str = "Hello"; 這樣宣告的指標,可能指向不可寫入的區域。若對其寫入,會在執行時發生錯誤。
char *str = "Hello";
str[0] = 'h'; // ❌ 執行時發生 Segmentation fault
對策:
char str[] = "Hello"; // ✅ 宣告為可寫入的陣列
str[0] = 'h';

4. 忘記處理 fgets 的換行字元

使用 fgets 取得字串時,需要注意末尾會保留換行字元(n)。
fgets(str, sizeof(str), stdin);
// str 會包含類似 "Hellon�" 的內容
對策:
size_t len = strlen(str);
if (len > 0 && str[len - 1] == 'n') {
    str[len - 1] = '�';
}

5. 混淆指標與陣列

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

9. 總結

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

本文學到的內容

  • char 陣列的角色與宣告方式 在 C 語言中沒有字串型別,處理字串需要使用 char 型的陣列。
  • 字串字面值與空字元()的重要性 要表示字串,必須在結尾加上空字元。
  • 使用標準函式庫函式進行字串操作 使用 strcpystrcatstrlenstrcmp 等,即可有效處理字串。
  • char 陣列與 char 指標的差異 陣列與指標看似相似卻不同,在記憶體結構與是否可寫入等方面有明顯差異。
  • 將陣列傳遞給函式時的方式與安全性考量 陣列實際上會以指標形式傳遞,因此在寫入與大小管理上需要注意。
  • 透過實作範例與常見錯誤鞏固理解 透過實際程式碼的應用與錯誤處理方法,學習了更貼近實務的使用方式。

對未來學習的建議

了解 char 陣列的使用方式,是在 C 語言中理解記憶體操作基礎的第一步。在此學到的知識,可應用於以下的下一步。
  • 指標運算:進一步深化對 char 陣列與指標的理解
  • 動態記憶體管理:使用 mallocfree 進行高階字串處理
  • 結構體的結合使用:利用資料結構進行實務應用程式設計
此外,閱讀他人撰寫的程式碼,可吸收自己未曾具備的觀點與寫法。養成閱讀實際專案或 OSS(開放原始碼軟體)的習慣也非常有效。 C 語言因自由度高,若未正確使用會有危險面向,但只要細心累積基礎,就會成為非常強大的武器。對 char 陣列的理解即是第一步。請務必以本文為參考,穩步提升技能。