記得以前看過一道這樣的題目:
以下程序的執行結果是?
#include <stdio.h> int main() { char* p="Hello World"; *(p+1)='a'; printf("%s\n",p); return 0; }
應該不難吧,不知道大家的答案是什么。
以下是我的一些解答:
對於指針p,他的大小是sizeof(p),一般為4,至於他指向的對象的大小是sizeof(char),那么在哪里存放字符串“Hello World”呢?
我們知道,程序編譯時,編譯器將代碼翻譯成匯編代碼,然后匯編器將匯編代碼翻譯成機器代碼(得到目標文件),最后鏈接器將目標文件鏈接成可執行文件。而目標文件和可執行文件的格式一般是類似的,由一個個section(段)組成,一般來說有代碼段、數據段、bss段等,有些平台還會有.rodata段(只讀數據段),用來放置只讀變量(const變量)和字符串常量,這樣不僅可以在語義上支持C++的const,而且操作系統還可以在加載的時候將.rodata映射為只讀,這樣對於這個段的修改會作為非法處理,保證了程序的安全。
(1)在Linux下可以用objdump查看目標文件或可執行文件中的內容:
objdump -x -s -d testchar
得到的結果如下:

(左邊是十六進制內容,右邊是ASCII形式的內容)
可以看到字符串Hello World就在.rodata段中。
(2)接下來我們使用readelf來查看.rodata段的屬性:
得到:

其中.rodata段的標志(Flg)為A(alloc),表示該段在進程中要分配空間,如果一個段可寫的話,應該要有W(write)標志,如數據段(.data):
也就是說,現在字符串“Hello World”在不可寫的.rodata中,如果我們用指針直接修改的話,就會引發段錯誤。
所以程序的運行結果是:Segmentation fault 應該說在Linux 下用gcc編譯后,程序運行結果是:Segmentation fault
感謝@garbageMan的回復,確實,我說只是在gcc下的情況
@garbageMan:
很難說String literal 放在哪里
C語言並沒有具體規定
放在哪里是編譯器自己確定的
所以從一個編譯結果推廣到所有就是錯誤的
此外,C語言規定,修改String literal是一種UB
任何可能結果都有
不一定引發“段錯誤”
(3)數組中的情況
如果程序是這樣呢:
#include <stdio.h> int main() { char p[]="Hello World"; *(p+1)='a'; printf("%s\n",p); return 0; }
程序是可以正常運行的。
同樣用objdump查看.rodata的內容:
![]()
發現字符串已經不在.rodata中了。
對於數組,我們可以直接分配空間,像上面的數組p就可以直接在棧中分配空間來存放字符串“Hello World”,這樣就可以修改字符串了。
以下內容我有點不肯定:
查看text段(代碼段)中的內容,發現字符串在text段中。

所以我的猜想是程序執行時,先將text段中的字符串復制到數組p在棧中的分配的內存里,這樣就可以對字符串修改同時又不影響代碼段中的內容(因為代碼段一般是只讀 的)不知我這個猜想對不對,希望各位解答一下……同時這個問題也感謝 @buzzerrookie的提醒。
