c - 字符數組應如何用作字符串?




string c-strings (3)

我知道C中的字符串只是字符數組。 因此,我嘗試了以下代碼,但給出了奇怪的結果,例如垃圾輸出或程序崩潰:

#include <stdio.h>

int main (void)
{
  char str [5] = "hello";
  puts(str);
}

為什麼不起作用?

它可以使用 gcc -std=c17 -pedantic-errors -Wall -Wextra 乾淨地編譯。

注意: 對於在聲明字符串時未能為NUL終止符分配空間而導致的問題,本帖子旨在用作規範的FAQ。


憑直覺...

將數組視為變量(包含事物),將字符串視為值(可以放置在變量中)。

他們當然不是同一回事。 在您的情況下,變量太小而無法容納字符串,因此字符串被切斷。 (C中的“帶引號的字符串”在末尾具有隱式的空字符。)

但是,可以將字符串存儲在比字符串 大得多 的數組中。

請注意,通常的賦值和比較運算符( = == < 等)無法正常工作。 但是,一旦您知道自己在做什麼, strxyz 函數家族就會非常接近。 請參閱有關 stringsarrays C FAQ


是否可以將所有 字符串 視為 字符數組 ),可以將所有 字符數組 視為 字符串 )。

為什麼不? 為何重要?

除了解釋字符串長度不作為字符串的一部分存儲在任何地方以及定義字符串的標準的引用的其他答案之外,另一面是“ C庫函數如何處理字符串?”。

雖然一個字符數組可以容納相同的字符,但它只是一個字符數組,除非最後一個字符後面跟有以 零結尾的 字符。 該 零終止 字符允許將字符數組視為(視為)字符串。

C中所有希望將字符串作為參數的函數都希望字符序列為 nul終止的 為什麼?

它與所有字符串函數的工作方式有關。 由於長度不包含在字符串函數的數組中,因此請在數組中向前掃描,直到找到 nul字符 (例如 '\0' 0'- 等於十進制 0 )為止。 請參閱 ASCII表和說明 。 無論您是否使用 strcpystrchrstrcspn 等。所有字符串函數都依賴於出現的 nul終止 字符來定義該字符串的結尾。

比較 string.h 中兩個相似的函數將強調n 終止 字符的重要性。 舉個例子:

    char *strcpy(char *dest, const char *src);

strcpy 函數只是將字節從 src 複製到 dest 直到找到 nul終止 字符,告訴 strcpy 在哪裡停止複製字符。 現在使用類似的函數 memcpy

    void *memcpy(void *dest, const void *src, size_t n);

該函數執行類似的操作,但不考慮或要求 src 參數為字符串。 由於 memcpy 不能簡單地向前掃描 src 複製字節直到 dest 直到達到 nul終止 字符為止,因此它需要顯式數量的字節作為第三個參數進行複制。 第三個參數為 memcpy 提供了相同的大小信息, strcpy 可以簡單地通過向前掃描直到找到 nul終止 字符來導出。

(這也強調了 strcpy (或任何期望字符串的函數)出了什麼問題,如果您無法為函數提供以 nul終止的 字符串-它不知道從何處停止,並且會在內存的其餘部分中愉快地競爭段調用 未定義的行為, 直到恰好在內存中的某個位置找到 nul字符 -或發生分段錯誤)

這就是 為什麼 必須將以 nul終止的 字符串傳遞給函數的 原因 以及 為什麼它很重要


AC字符串是一個以 空終止符 結尾的字符數組。

所有字符都有符號表值。 空終止符是符號值 0 (零)。 它用於標記字符串的結尾。 這是必需的,因為字符串的大小不會存儲在任何地方。

因此,每次為字符串分配空間時,都必須為空終止符包含足夠的空間。 您的示例未執行此操作,僅為 "hello" 的5個字符分配了空間。 正確的代碼應為:

char str[6] = "hello";

或者等效地,您可以編寫5個字符加1個空終止符的自文檔代碼:

char str[5+1] = "hello";

在運行時為字符串動態分配內存時,還需要為null終止符分配空間:

char input[n] = ... ;
...
char* str = malloc(strlen(input) + 1);

如果不在字符串末尾附加空終止符,則期望字符串的庫函數將無法正常工作,並且會出現“未定義行為”錯誤,例如垃圾輸出或程序崩潰。

用C編寫空終止符的最常見方法是使用所謂的“八進制轉義序列”,如下所示: '\0' 。 這等效於100%等於寫入 0 ,但是 \ 用作自說明代碼,指出零明確表示是空終止符。 諸如 if(str[i] == '\0') 將檢查特定字符是否為空終止符。

請注意,術語空終止符與空指針或 NULL 宏無關! 這可能會令人困惑-名稱非常相似,但含義卻非常不同。 這就是為什麼將空終止符有時稱為帶有一個L的 NUL ,不要與 NULL 或空指針混淆的原因。 有關更多詳細信息,請參見此 SO問題的 答案。

您的代碼中的 "hello" 稱為 字符串文字 。 這將被視為只讀字符串。 "" 語法意味著編譯器將自動在字符串文字的末尾附加一個空終止符。 因此,如果您打印出 sizeof("hello") ,則將得到6,而不是5,因為您將獲得包含空終止符的數組的大小。

用gcc乾淨地編譯

確實,甚至沒有警告。 這是由於C語言中的一個細微的細節/缺陷,它允許使用字符串文字初始化字符數組,該字符串文字包含的字符與數組中的空間一樣多,然後靜默丟棄空終止符(C17 6.7.9 / 15)。 由於歷史原因,該語言的行為方式故意如此,有關詳細信息,請參見 字符串初始化的gcc診斷不一致 。 還要注意,C ++在這裡有所不同,並且不允許使用此技巧/缺陷。





nul