字符串常量引起的思考


  記得以前看過一道這樣的題目:

  以下程序的執行結果是?

#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的提醒。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM