HOWTO · C
在 C 語言中清空字元陣列
本文演示瞭如何在 C 語言中清除字元陣列。
本頁內容
C 的 char 陣列會儲存位元組。請依程式需要的結果選擇操作:寫入已知的位元組模式、取得供日後使用的字串值,或結束配置區塊的生命週期。獨立範例會在容量已知的儲存空間上示範各種操作。
選擇 C char 陣列中「清除」的意義
請依目標選擇操作:使用 memset(array, 0, sizeof array) 或涵蓋全長的迴圈來覆寫每個位元組;寫入第一個空字元終止字元(array[0] = '\0'),讓 C 字串在邏輯上變成空字串;呼叫 free(pointer),釋放動態配置的儲存空間。free 會釋放配置區塊,但不會清除其中的位元組。這些操作對陣列及其生命週期的影響各不相同。
執行此範例:
本節中的程式碼以及顯示的標籤或輸出仍為英文。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char bytes[8] = "secret";
memset(bytes, 0, sizeof bytes);
printf("first byte after memset: %u\n", (unsigned int)(unsigned char)bytes[0]);
char text[16] = "hello";
strcpy(text, "");
printf("string after strcpy: '%s'; next byte: %u\n",
text, (unsigned int)(unsigned char)text[1]);
char items[8] = "Hi";
for (size_t i = 0; i < sizeof items; ++i) {
items[i] = '\0';
}
printf("string after loop: '%s'\n", items);
char *dynamic_text = malloc(16);
if (dynamic_text == NULL) {
return EXIT_FAILURE;
}
strcpy(dynamic_text, "hello");
free(dynamic_text);
dynamic_text = NULL;
return EXIT_SUCCESS;
}
first byte after memset: 0
string after strcpy: ''; next byte: 101
string after loop: ''
程式使用 memset 將 bytes 中的八個位元組全部設為零。稍後讀取 text[1],可看出複製空字串時只會寫入第一個終止字元,後續的陣列位元組仍保持不變。迴圈會將 \0 寫入每個元素。呼叫 free 之後,程式不會讀取已釋放的儲存空間;指定 NULL 只會更新這個區域指標變數。
使用 memset 將每個位元組設為零
memset 宣告於 <string.h>,會將指定數量的位元組設為相同的位元組值。在目前作用範圍內使用陣列時,memset(array, 0, sizeof array) 會涵蓋整個陣列,包括目前字串終止字元之後的元素。如果將陣列作為指標傳入函式,該處的 sizeof 會回報指標大小,因此必須另外傳入陣列長度。
值 0 會寫入值為零的位元組;值 '0' 則會重複寫入數字字元。後者寫入的位元組並非零,雖然可用於填充或顯示,卻不會將原有內容覆寫成零。
使用迴圈清除陣列
如果需要明確指定每個元素,或只處理陣列的一部分,迴圈就很有用。請只在陣列的實際長度範圍內逐一處理,並將 \0 寫入每個元素,以空字元覆寫其內容。在可執行範例中,sizeof items 用法正確,因為 items 在該作用範圍內是陣列。接收指標的函式也應一併接收長度,才能知道容量,而不必猜測。
使用空字元終止字元讓 C 字串變成空字串
對以空字元終止的字串,只要將第一個字元設為 \0,其邏輯長度就會變成零。strcpy(array, "") 也會產生相同的可見效果:將空字串的終止字元複製到開頭。這兩種操作都不會覆寫陣列其餘的位元組。如果目標是將陣列重新用作字串,而且不需要覆寫原有的尾端位元組,就可以採用這種作法。若必須改變每個位元組,請改用 memset 或涵蓋全長的迴圈。
了解 bzero 與 explicit_bzero 的可攜性
bzero 和 explicit_bzero 都不是 ISO C 介面。它們的宣告及可用性取決於作業系統與其標頭檔;使用其中任一介面時,都應遵循目標平台的文件及功能測試要求。在提供這些介面的平台上,兩者都會要求將指定範圍的位元組設為零。explicit_bzero 的設計目的是避免編譯器將這項寫入視為不可觀察的最佳化而移除,但它無法保證清除所有副本、暫存器值、交換空間副本或其他系統層級的殘留資料。若要撰寫可攜式 C 程式碼,一般陣列初始化請優先使用標準的 memset;處理敏感資料時,則應參考平台指引。
釋放動態記憶體不等於清除
free 會釋放由 malloc、calloc 或 realloc 等配置函式取得的區塊。這不代表程式可以在釋放後檢查已清除的陣列:呼叫 free 後,透過指標存取原本的配置區塊是無效的。請勿對 char local[16] 這類自動陣列呼叫 free,也不要對非由配置函式回傳的儲存空間呼叫它。
接收指標參數的函式會取得指標值的副本。將該參數設為 NULL 不會改變呼叫端的指標。呼叫端可以像範例一樣,在呼叫 free 之後,將自己的變數設為 NULL,讓該變數明確不再指向已釋放的區塊。這不會清除配置區塊中的位元組,也不會影響其他別名指標。