簡體   English   中英

以下C char數組存儲實現背后的原因是什么?

[英]What is the reason behind the following C char array storage implementation?

以下char數組實現背后的實現原因是什么?

char *ch1 = "Hello"; // Read-only data
/* if we try ch1[1] = ch1[2]; 
we will get **Seg fault** since the value is stored in 
the constant code segment */

char ch2[] = "World"; // Read-write data
/* if we try ch2[1] = ch2[2]; will work. */

根據Head first C (第73,74頁)一書, ch2[]數組既存儲在常量代碼段中,也存儲在函數堆棧中。 在代碼和堆棧內存空間中復制的原因是什么? 如果不是只讀數據,為什么值只能保存在堆棧中?

首先,讓我們澄清一些事情。 字符串字面不一定只讀數據,它只是它是不確定的行為,試圖改變它們。

不一定要崩潰,它可能工作得很好。 但是,如果您希望代碼在另一個實現,同一實現的另一個版本或甚至下周三運行,則不應該依賴它。

這可能源於標准制定之前的時間(最初的ANSI / ISO授權是編纂現有實踐而不是創建新語言)。 在許多實現中,字符串將共享空間以提高效率,例如代碼:

char *good = "successful";
char *bad = "unsuccessful";

導致:

good---------+
bad--+       |
     |       |
     V       V
   | u | n | s | u | c | c | e | s | s | f | u | l | \0 |

因此,如果你改變了其中一個角色good ,這也將改變bad

您可以使用以下內容執行此操作的原因:

char indifferent[] = "meh";

是,雖然goodbad指向一個字符串,該聲明實際上創建了一個字符數組大到足以容納"meh" ,然后將數據復制到它1。 可以自由更改數據的副本。

事實上,C99基本原理文件明確指出這是其中一個原因:

字符串文字不需要是可修改的。 此規范允許實現共享具有相同文本的字符串副本,將字符串文字放在只讀內存中,並執行某些優化。

但無論為什么,標准都很清楚 來自C11 6.4.5 String literals

7 /如果這些數組的元素具有適當的值,則這些數組是否不同是未指定的。 如果程序試圖修改此類數組,則行為未定義。

對於后一種情況,這在6.7.6 Declarators6.7.9 Initialisation有所涉及。


1雖然值得注意的是正常的“似乎”規則適用於此(只要實現的行為就像它遵循標准一樣,它可以做它喜歡的事情)。

換句話說,如果實現可以檢測到您從未嘗試更改數據,則可以非常愉快地繞過副本並使用原始數據。

我們將得到Seg故障,因為值存儲在常量代碼段中

這是錯誤的:您的程序崩潰是因為它收到一個指示段違規的信號( SIGSEGV ),默認情況下會導致程序終止。 但這不是主要原因。 修改字符串文字是未定義的行為 ,無論它是否存儲在只讀段中,這比您想象的要廣泛得多。

數組既存儲在常量代碼段中,也存儲在函數堆棧中。

這是一個實現細節,不應該與您有關:就ISO C而言,這些語句沒有意義。 這也意味着它可以以不同方式實施。

當你

 char ch2[] = "World";

"World" ,一個字符串文字,被復制到ch2 ,如果你使用malloc和指針,你最終會做的事情。 現在,為什么要復制?

其中一個原因可能是你會期待的。 如果你可以修改這樣的字符串文字,如果代碼的另一部分引用它並期望具有該值,該怎么辦? 擁有共享字符串文字是有效的,因為您可以在程序中共享它們並節省空間。

通過復制它,你有自己的字符串副本(你“擁有”它),你可以隨意修改它。

引用“美國信息系統編程語言C國家標准的基本原理”

字符串文字被指定為不可修改的。 此規范允許實現共享具有相同文本的字符串副本,將字符串文字放在只讀內存中,並執行某些優化。 但是,字符串文字沒有const char的類型數組,以避免指針類型檢查的問題,特別是對於庫函數,因為將指向const char的指針指向一個指向char的普通指針是無效的。

對於聲稱字符串文字存儲在只讀存儲器中的反例,這只是部分答案:

int main() {
   char a[]="World";
   printf("%s", a);
}

gcc -O6 -S cc

.LC0:
    .string "%s"                  ;; String literal stored as expected
                                  ;; in read-only area within code
    ...
    movl    $1819438935, (%rsp)   ;; First four bytes in "worl"
    movw    $100, 4(%rsp)         ;; next to bytes in "d\0"
    call    printf
    ...

這里只實現了概念文字的語義; 文字“世界\\ 0”甚至不存在。

實際上,只有當字符串文字足夠長時,優化編譯器才會選擇將文本池中的數據memcpy到堆棧,要求將文字存在為空終止字符串。

char *ch1 = "Hello";的語義char *ch1 = "Hello"; OTOH要求在某處存在線性陣列,其地址可以分配給指針ch1

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM