1. 前言 C 語言中「陣列複製」的重要性 在使用 C 語言進行程式設計時,常會遇到想要 將陣列內容複製到另一個陣列 的情況。例如,想要備份資料,或是為了暫時的處理而需要將值保留在其他變數中等。 然而,與高階語言相比,C 語言在記憶體操作方面的支援較少,陣列的複製也不會自動執行 ,因此必須手動撰寫複製程式。更糟的是,如果方法寫錯,可能會導致「非預期的行為」或「記憶體破壞」等嚴重錯誤。 因此,了解正確的複製方法,安全且有效率地操作陣列 ,是學習 C 語言時極為重要的技能之一。對陣列複製感到困惑的人很多 實際上,以「C 語言 陣列 複製」為關鍵字搜尋的人相當多,足見需求之高。memcpy
就能瞬間完成複製?strcpy
有何不同?for文
逐一複製較安全?如何寫出使用指標的複製? 為了回答這些疑問,本文將清晰說明 C 語言中陣列複製的基礎到應用 。本文可學習的內容 閱讀此頁面,即可獲得以下知識。C 語言中陣列的基本概念 陣列複製的多種方法,以及各自的優點與注意事項 字串(char 型陣列)複製時的技巧與陷阱 以 Q&A 形式說明常見疑問 即使是 C 語言初學者也能輕鬆理解,我們將附上範例程式碼細緻說明。從下一節開始,先來看看陣列的基礎吧。
2. 陣列的基本概念 什麼是陣列? 在 C 語言中,陣列(Array) 是指 用於連續儲存相同資料型別的元素的變數 。例如,為了儲存 5 人的分數,與其定義 5 個 int
型變數,不如使用一個陣列來統一處理。int scores[5];
這樣定義的陣列,可以從第 0 個索引依序指定來存取。scores[0] = 80;
scores[1] = 75;
scores[2] = 90;
scores[3] = 60;
scores[4] = 85;
此處將整數賦值給 scores[0]
~ scores[4]
這 5 個元素。請注意索引是從 0 開始。陣列的初始化方法 陣列也可以在宣告時進行初始化。初始化是指 在建立陣列的同時賦值 。int scores[5] = {80, 75, 90, 60, 85};
這樣寫的話,陣列的每個元素會依序被賦值。另外,也可以省略陣列的大小。int scores[] = {80, 75, 90, 60, 85}; // 元素數會自動判斷為 5
相反地,即使明確指定元素數,若值不足,剩餘的元素會自動以 0
初始化。int scores[5] = {80, 75}; // scores[2]~scores[4] 會變成 0
了解陣列的記憶體結構 在 C 語言中,陣列的元素會在記憶體上連續配置 。此特性使得可以使用 for
迴圈或指標來有效操作。 例如以下程式碼,會依序顯示陣列的所有元素。for (int i = 0; i < 5; i++) {
printf("%d
", scores[i]);
}
這樣了解陣列的基本結構,對於後面提到的「複製處理」 也有很大關係。3. 陣列的複製方法 C 語言中,無法使用賦值運算子(=
)一次性複製陣列 。以下的程式碼會產生編譯錯誤。int a[5] = {1, 2, 3, 4, 5};
int b[5];
b = a; // 錯誤:陣列之間不可賦值
因此,要複製陣列,需要明確地逐一複製元素的方法 或是使用標準函式庫函式的方法 。此處介紹三種代表性的做法。使用 for 迴圈的逐元素複製 最基本且安全的方法是,使用 for
迴圈 逐一複製元素的方法 。#include <stdio.h>
int main() {
int src[5] = {10, 20, 30, 40, 50};
int dest[5];
for (int i = 0; i < 5; i++) {
dest[i] = src[i];
}
// 顯示複製結果
for (int i = 0; i < 5; i++) {
printf("%d ", dest[i]);
}
return 0;
}
此方法的優點是「容易掌握陣列大小,且易於控制 」這點。安全性高,也推薦給初學者。memcpy
函式的高速複製 如果想更有效率地複製陣列,可以使用包含於標準函式庫 <string.h>
的 memcpy
函式 。#include <stdio.h>
#include <string.h>
int main() {
int src[5] = {1, 2, 3, 4, 5};
int dest[5];
memcpy(dest, src, sizeof(src)); // 複製 src 的位元組數
for (int i = 0; i < 5; i++) {
printf("%d ", dest[i]);
}
return 0;
}
memcpy
的使用要點:第1引數 :目的地指標第2引數 :來源指標第3引數 :要複製的位元組數(注意!不是元素數)注意事項: 若來源與目的地的陣列大小不同,可能會導致緩衝區溢位 ,因此務必確認大小。 若記憶體重疊則不可使用(於下一節說明)。 memmove
的差異與使用時機 與 memcpy
類似的函式有memmove
。兩者的差異在於「當來源與目的地重疊時的行為」。memcpy
:用於記憶體不重疊的情況 。若重疊則行為未定義。memmove
:即使重疊也能安全複製 。使用範例: char str[] = "ABCDE";
// 從第2個字元起,覆寫至第1個字元(有重疊)
memmove(str + 1, str, 4);
str[5] = ' '; // null 終止
printf("%s
", str); // 輸出:AABCD
使用時機的基本規則: 使用情況 建議函式 記憶體不重疊 memcpy
可能重疊 memmove
在陣列操作中通常使用 memcpy
已足夠,但在字串操作等需要移動陣列部分的情況下應使用 memmove
。
4. 字串(char 陣列)的複製 在 C 語言中,字串被視為char
型的陣列 ,需要注意它與數值陣列稍有不同。字串複製有專用的函式,memcpy
的二進位複製不同,「包含終止字元一起複製 」的特點。strcpy
函式複製字串 C 語言的標準函式庫 <string.h>
中的 strcpy
函式,是一個會將字串複製至空字元(null 終止)為止 的便利函式。#include <stdio.h>
#include <string.h>
int main() {
char src[] = "Hello, world!";
char dest[50];
strcpy(dest, src);
printf("複製結果:%s
", dest);
return 0;
}
此程式碼將 src
的字串(包含終止字元)複製到 dest
。注意事項: dest
的大小若太小會導致緩衝區溢位 ,因此需要確保足夠的大小。被複製的字元數取決於src
的長度。 strncpy
安全複製 strcpy
的替代函式是strncpy
。此函式的規格是「僅複製指定的字元數量 」,因此安全性較高。#include <stdio.h>
#include <string.h>
int main() {
char src[] = "C language";
char dest[10];
strncpy(dest, src, sizeof(dest) - 1); // 保留最後 1 位元
dest[9] = ' '; // 為了保險明確設定終止字元
printf("複製結果:%s
", dest);
return 0;
}
strncpy
的特性:僅複製指定長度的字元。 若來源較短,剩餘部分會以NULL
填充(視環境而定)。 終止字元不一定會自動附加,需自行明確設定較安全。 處理日文(多位元組字串)時的注意事項 處理日文等多位元組字元 (UTF-8、Shift-JIS 等)時,若使用 strcpy
或 strncpy
截斷中間的位元組,會導致字元亂碼 或顯示錯誤 。 例如只複製「こんにちは」的 3 個位元組,會出現不完整的情況。此時需要考慮使用能以字元為單位處理的函式庫,或使用寬字元(wchar_t
)。字串複製的最佳實踐總結 函式名稱 特性 注意事項 strcpy
複製至終止字元 必須確認緩衝區大小 strncpy
可僅複製指定長度 終止字元可能不保證 memcpy
可複製任意位元組列 二進位複製。非字串用途 strdup
為複製的字串分配新記憶體(非標準) 使用後需free
5. 陣列複製時的注意事項 陣列的複製看似簡單的處理,但如果未正確處理,會產生重大錯誤或安全漏洞的風險 。本節將說明在 C 語言中陣列複製時特別需要注意的要點。注意緩衝區溢位 最常見的錯誤是,將資料寫入超過目標陣列大小 的「緩衝區溢位」。範例:危險的複製處理 char src[] = "This is a long string.";
char dest[10];
strcpy(dest, src); // dest的大小被超過複製 → 可能導致記憶體破壞
此類程式碼會引發對不正確記憶體區域的存取 ,導致程式崩潰或成為漏洞的原因。對策: 使用 strncpy
或 memcpy
,務必限制大小 。 不要忘記手動加入終止字元。 以常數或巨集管理陣列大小。 正確掌握複製目標的大小 使用 memcpy
或 memmove
複製時,需要以「位元組數」而非元素數 指定。安全的大小指定範例: int src[5] = {1, 2, 3, 4, 5};
int dest[5];
memcpy(dest, src, sizeof(src)); // 指定 src 全體的位元組數
如是使用 sizeof(src)
,即可自動取得陣列整體的大小(位元組數),因此安全。 但在函式參數中接收陣列時,sizeof
不會如預期運作(因為陣列會退化為指標),需特別注意。指標操作時的注意事項 在 C 語言中,陣列常被視為指標,錯誤的指標操作可能會破壞記憶體 。常見的錯誤範例: int *src = NULL;
int dest[5];
memcpy(dest, src, sizeof(dest)); // 指定 NULL 指標為來源 → 程式崩潰
要點: 檢查指標是否指向有效位址,執行 NULL 檢查 。 在記憶體配置(如 malloc
)後進行複製時,確認配置大小與複製大小的一致性 。 當來源與目的區域重疊時的對策 如前所述,memcpy
不支援重疊記憶體區域的複製 。若要將陣列的一部份複製到其他位置,應使用 memmove
,這是原則。char buffer[100] = "example";
// 在自身內部移動部分字串
memmove(buffer + 2, buffer, 4); // 安全的重疊複製
陣列大小的定義與管理 為了執行安全的複製處理,統一管理陣列大小 是有效的。如下以巨集定義,可提升程式碼的可維護性與安全性。#define ARRAY_SIZE 10
int arr1[ARRAY_SIZE];
int arr2[ARRAY_SIZE];
memcpy(arr2, arr1, sizeof(arr1));
安全陣列複製的總結 複製時需正確掌握大小(位元組數) 。 選擇 strncpy
、memmove
等安全函式。 始終確認來源與目的的大小一致性 。 特別注意指標操作 ,執行 NULL 檢查與邊界檢查。
6. FAQ(常見問題) C 語言中陣列的複製,從初學者到中級者常常疑惑的要點 以 Q&A 形式說明。正確理解細微差異與規格,亦能預防錯誤與提升程式碼品質 。Q1. memcpy
與 memmove
的差異是什麼? A. 當記憶體區域重疊時,操作的安全性不同。 比較項目 memcpy
memmove
對重疊的安全性 ×(可能產生未定義行為) ◎(內部使用暫存緩衝區處理) 處理速度 高速(開銷少) 稍慢(有安全措施) 用途 陣列的完整複製等 同一陣列內的資料移動等
補足 :如果確定不會重疊,使用 memcpy
即可,但若不確定,選擇 memmove
較安全。 Q2. strcpy
與 strncpy
的差異與使用方式是? A. 因為在安全性與彈性之間有取捨,需要分別使用。 strcpy(dest, src)
→ 複製至 src
的終端(\\0)為止的全部。但若 dest
太小,會有緩衝區溢位的危險 。strncpy(dest, src, n)
→ 只會複製至指定的最大位元組數 n
。安全性較高,但不一定會自動加上終止字元 。 建議的使用方式: 如果確定大小足夠,則使用 strcpy
安全第一、處理不特定字串時,使用 strncpy
並手動加上終止字元 Q3. 傳遞陣列作為函式參數時有什麼注意事項? A. 陣列會退化為指標,因而無法使用 sizeof
取得大小。 void copy_array(int arr[], int size) {
printf("%zu
", sizeof(arr)); // ← 會是指標的大小(例如:8)
}
如此一來,無法取得陣列的實際大小 ,因此在傳遞給函式時,基本上需要同時傳遞大小作為參數 。void copy_array(int arr[], int size) {
for (int i = 0; i < size; i++) {
// 處理內容
}
}
Q4. 使用 memcpy
複製結構體陣列會有問題嗎? A. 基本上是可行的對於「含有指標的結構體」需特別注意。 memcpy
以二進位單位的複製 方式進行,若結構體中含有指標,則指標所指向的實際資料不會被複製 。例(淺層複製): typedef struct {
char *name;
int age;
} Person;
Person a[3];
Person b[3];
memcpy(b, a, sizeof(a)); // 只會複製指標本身(指向的資料會共享)
在此情況下,可能會發生資料的雙重釋放或不一致 。對策是需要對指標部分分別使用 malloc
+ strcpy
等方式進行深層複製 。Q5. 想一次性複製陣列,但每次都寫 for 迴圈很麻煩。能否製作共用函式? A. 可以,將其函式化可提升重用性,程式碼也會更簡潔。 void copy_int_array(int *dest, int *src, int size) {
for (int i = 0; i < size; i++) {
dest[i] = src[i];
}
}
如此一來,針對不同型別與用途事先製作通用的複製函式 ,即可提升開發效率與可讀性。
7. 總結 在本文中,我們對 C 語言中的「陣列複製」進行了從基礎到應用的系統性解說 。最後,回顧重要要點,並針對實務活用進行總結。陣列複製的基本是「安全性與目的的明確化」 在 C 語言中,無法直接以=
來賦值陣列。因此,進行複製需要明確的處理 。for
迴圈 :最基本且易於理解。需要明確指定陣列大小。memcpy
:可在二進位層面高速複製。需注意尺寸錯誤。memmove
:在來源與目的地重疊時使用。strcpy
/ strncpy
:用於字串(char 陣列)的函式。需考慮安全性而加以區分使用。安全的複製離不開「尺寸管理」 陣列尺寸的溢位(超過複製) 會成為程式崩潰或漏洞的原因。 利用 sizeof()
來掌握正確的位元組數 是很重要的。 在函式中處理陣列時,需了解它會退化為指標 ,並且將尺寸也作為參數傳遞。 也要了解常見的陷阱 strncpy
雖然安全,但可能不會自動加上終止字元 ,因此請養成手動補上的習慣。包含結構體指標 的陣列,使用 memcpy
可能無法正確複製。 在處理多位元組字串(如日文等)時,亦需注意字元數與位元組數的差異 。 為了在實務中活用 若在專案中頻繁發生陣列複製,建立專用的工具函式 會很方便。 複製處理是易於測試的單元 ,因此建議透過單元測試等方式確認其安全性。 最後 C 語言因為是低階語言,即使是陣列的複製也相當深奧 。然而,只要掌握本次介紹的基礎知識與技巧,就能在各種情境中靈活運用。 請務必以本文為參考,掌握「正確且安全的複製處理」,製作更具可靠性的 C 語言程式。