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,讓該變數明確不再指向已釋放的區塊。這不會清除配置區塊中的位元組,也不會影響其他別名指標。