指標與字串


在〈字元陣列與字串〉談過 C 風格字串,本質上就是個字元陣列,而陣列名稱具有指標性質,那可以如下建立字串嗎?

char *text = "hello";

gcc 沒提出任何警訊,然而 text 儲存了字串常量的位址值,字串常量建立的內容是唯讀的,如果試圖透過 text 改變字元,會發生不可預期的結果:

char *text = "hello";
text[0] = 'H';        // 不可預期

因此對於字面常量,建議加上 const

const char *text = "hello";

如此一來,試圖透過 text 改變字元,編譯器會失敗,從而避免了執行時期的錯誤。

上述方式中,text 只是個型態為 const char* 的指標,是與以下不同的,底下建立的 text 內容並不是唯讀的,因為 text 是個陣列,text 是將 "hello" 複製至各索引處:

char text[] = "hello";

對於 wchar_t 等其他為了支援 Unicode 的型態,都有這類特性。

然而,無論是哪個形式,都可以傳遞位址,例如:

char text1[] = "hello";
const char *text2 = "hello";

const char *text = text1; // OK
text = text2;             // OK

不過,底下不行:

char text1[] = "hello";
const char *text2 = "hello";

char *text = text1; // OK
text = text2;       // error: invalid conversion from 'const char*' to 'char*'

錯誤該行如果真的想通過編譯,就必須明確告訴編譯器,你要去除 const 修飾:

char text1[] = "hello";
const char *text2 = "hello";

char *text = text1;   // OK
text = (char*) text2; // 強制去除 const

會需要這麼做的情況,可能是在使用一些舊的函式,它們在參數上宣告的是 char*,而不是 const char*

那麼,如何建立字串陣列呢?

#include <stdio.h>

int main(void) {
    const char *names[] = {"Justin", "Monica", "Irene"};

    for(int i = 0; i < 3; i++) {
        const char *name = names[i];
        printf("%s\n", name);
    }

    return 0;
}

留意一下底下的不同:

const char *names1[] = {"Justin", "Monica", "Irene"};
char names2[][10] = {"Justin", "Monica", "Irene"}; 

name1 的每個元素,儲存了各個字串常量的位址值;然而,name2 是有三個長度為 10 的 char 陣列,並複製了各個字串常量的 char

可以透過 typedefconst char* 建立別名,令字串陣列的建立易讀、易寫一些:

#include <stdio.h>

typedef const char* String;

int main(void) {
    String names[] = {"Justin", "Monica", "Irene"};

    for(int i = 0; i < 3; i++) {
        String name = names[i];
        printf("%s\n", name);
    }

    return 0;
}