[英]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";
是,雖然good
和bad
指向一個字符串,該聲明實際上創建了一個字符數組大到足以容納"meh"
,然后將數據復制到它1。 可以自由更改數據的副本。
事實上,C99基本原理文件明確指出這是其中一個原因:
字符串文字不需要是可修改的。 此規范允許實現共享具有相同文本的字符串副本,將字符串文字放在只讀內存中,並執行某些優化。
但無論為什么,標准都很清楚。 來自C11 6.4.5 String literals
:
7 /如果這些數組的元素具有適當的值,則這些數組是否不同是未指定的。 如果程序試圖修改此類數組,則行為未定義。
對於后一種情況,這在6.7.6 Declarators
和6.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.