2010-04-07 57 views
130

我對字符串文字在哪裏分配/存儲感興趣。字符串文字:他們去哪裏?

我沒有找到一個有趣的答案here,他說:

定義字符串內聯實際上在程序本身嵌入的數據,並不能改變(一些編譯器允許這個由智能把戲,不要打擾)。

但是,它必須與C++,更不用說它說不打擾。

我很煩。 = D

所以我的問題是我的字符串文字在哪裏以及如何保存?爲什麼我不應該嘗試改變它?實施是否因平臺而異?有沒有人關心「巧妙的把戲」?

+1

http://stackoverflow.com/questions/ 164194 | http://stackoverflow.com/questions/1704407 – 2015-06-05 10:50:12

回答

101

一種常見的技術是將字符串文字放在「只讀數據」部分,該部分以只讀方式映射到進程空間(這就是爲什麼您無法更改它)。

它因平臺而異。例如,更簡單的芯片架構可能不支持只讀存儲器段,因此數據段將是可寫的。

而是然後試圖找出一個技巧,使字符串文字多變的(這將在很大程度上取決於你的平臺上,並可能會隨時間而改變),只需使用數組:

char foo[] = "..."; 

編譯器會安排數組從文字中初始化,您可以修改數組。

+4

是的,我想要有可變字符串時使用數組。我只是好奇而已。謝謝。 – 2010-04-07 06:07:05

+2

雖然使用數組作爲可變字符串時,必須小心緩衝區溢出,但只要寫入比字符串長度更長的字符串(例如'foo =「hello」'在這種情況下)就會導致意想不到的副作用...... (假設你沒有用'new'或其他東西重新分配內存) – johnny 2011-09-26 17:52:09

+1

使用數組字符串進入堆棧還是其他地方? – 2016-12-26 11:32:47

4

這取決於您的executableformat。想一想的一種方法是,如果您是彙編程序設計,那麼您可能會將字符串文字放入彙編程序的數據段中。你的C編譯器就是這樣做的,但這一切都取決於你編譯的二進制系統。

43

沒有人回答這個問題。 C和C++標準只是說字符串文字具有靜態存儲持續時間,任何修改它們的嘗試都會導致未定義的行爲,並且具有相同內容的多個字符串文字可能共享或不共享相同的存儲。

根據您要寫入的系統以及它使用的可執行文件格式的功能,它們可能隨文本段中的程序代碼一起存儲,或者它們可能具有用於初始化數據的單獨段。

確定細節也會因平臺而異 - 最有可能包括的工具可以告訴你它在哪裏放置它。有些甚至會給你控制這樣的細節,如果你想要的話(例如,gnu ld允許你提供一個腳本來告訴它如何分組數據,代碼等等)

+0

我發現字符串數據不太可能直接存儲在.text段中。對於非常短的文字,我可以看到編譯器生成代碼,例如'movb $ 65,8(%esp); movb $ 66,9(%esp); movb $ 0,10(%esp)'爲字符串'「AB」',但絕大多數時候,它將處於非代碼段,如'.data'或'.rodata'等取決於目標是否支持只讀段)。 – 2012-10-02 20:00:10

+0

如果字符串文字在程序的整個持續時間內都是有效的,即使在破壞靜態對象的過程中,那麼它是否有效將const引用返回給字符串?爲什麼這個程序顯示運行時錯誤請參閱http://ideone.com/FTs1Ig – Destructor 2015-11-21 14:48:31

13

gcc使.rodata節被映射到地址空間的某個地方並被標記爲只讀,

Visual C++(cl.exe)爲了同樣的目的而製作了一個.rdata部分。

您可以查看dumpbinobjdump(在Linux上)的輸出以查看可執行文件的各個部分。

E.g.

>dumpbin vec1.exe 
Microsoft (R) COFF/PE Dumper Version 8.00.50727.762 
Copyright (C) Microsoft Corporation. All rights reserved. 


Dump of file vec1.exe 

File Type: EXECUTABLE IMAGE 

    Summary 

     4000 .data 
     5000 .rdata <-- here are strings and other read-only stuff. 
     14000 .text 
+1

我看不到如何用objdump反彙編rdata部分。 – user2284570 2015-11-30 10:26:34

+0

@ user2284570,這是因爲該部分不包含程序集。它包含數據。 – 2015-11-30 11:21:15

+1

只是爲了獲得更多可讀的輸出。我的意思是我想用反彙編內聯字符串而不是這些部分的地址。 *(你知道'printf(「一些null終止靜態字符串」);'而不是'printf(*地址);'在C)* – user2284570 2015-11-30 11:23:53

21

僅供參考,只是備份其他答案:

標準:ISO/IEC 14882:2003說:

2.13。字符串文字

  1. [...]一個普通的字符串文字的類型是「的n const char陣列」和 靜態存儲持續時間(3.7)

  2. 是否所有字符串文字是不同的(即,是存儲在 不重疊的對象)是 實現定義。試圖修改字符串文字 的 的效果未定義。

+1

有用的信息,但通知鏈接是用於C++,而問題被拼成[標籤:C] – 2013-07-10 19:30:15

+1

在2.13中證實了#2。使用-Os選項(針對大小進行優化),gcc與.rodata中的字符串文字重疊。 – 2015-02-20 07:15:03

2

字符串字面經常分配給只讀存儲器,使他們不變。然而,在一些編譯器中,通過「智能技巧」進行修改是可能的。聰明的技巧是通過「使用字符指針指向內存」。記住一些編譯器,可能不允許這樣..這裏是演示

char *tabHeader = "Sound"; 
*tabHeader = 'L'; 
printf("%s\n",tabHeader); // Displays "Lound" 
+0

它爲什麼工作? – 2016-12-26 11:51:50

28

爲什麼我不應該嘗試改變它?

因爲它是未定義的行爲。從C99 N1256 draft6.7.8/32 「初始化」引用:

實施例8:聲明

char s[] = "abc", t[3] = "abc"; 

定義的 「普通」 char對象數組st其元素與字符串文字被初始化。

此聲明是相同的

char s[] = { 'a', 'b', 'c', '\0' }, 
t[] = { 'a', 'b', 'c' }; 

陣列的內容是可修改的。在另一方面,聲明

char *p = "abc"; 

限定p,類型爲「字符指針」並對其進行初始化,以指向與類型「char數組」將對象與長度爲4,其元素具有字符串文字被初始化。如果嘗試使用p來修改數組的內容,則行爲是未定義的。

他們去哪裏?

GCC 4.8 x86-64 ELF Ubuntu 14。04:

  • char s[]:堆
  • char *s:對象文件
  • 其中對象文件的.text部被轉儲的相同段,這已經閱讀和exec權限
    • .rodata部,但不寫

計劃:

#include <stdio.h> 

int main() { 
    char *s = "abc"; 
    printf("%s\n", s); 
    return 0; 
} 

編譯和反編譯:

gcc -ggdb -std=c99 -c main.c 
objdump -Sr main.o 

輸出包含:

char *s = "abc"; 
8: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp) 
f: 00 
     c: R_X86_64_32S .rodata 

因此字符串存儲在.rodata部分。

然後:

readelf -l a.out 

包含(簡化):

Program Headers: 
    Type   Offset    VirtAddr   PhysAddr 
       FileSiz   MemSiz    Flags Align 
     [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] 
    LOAD   0x0000000000000000 0x0000000000400000 0x0000000000400000 
       0x0000000000000704 0x0000000000000704 R E 200000 

Section to Segment mapping: 
    Segment Sections... 
    02  .text .rodata 

這意味着缺省鏈接腳本轉儲既.text.rodata成可以執行,但是不能修改的段(Flags = R E) 。嘗試修改這樣的細分會導致在Linux中出現段錯誤。

如果我們做同樣char[]

char s[] = "abc"; 

我們得到:

17: c7 45 f0 61 62 63 00 movl $0x636261,-0x10(%rbp) 

所以它被保存在棧(相對於%rbp),當然我們可以修改它。

0

由於這可能因編譯器不同,最好的辦法就是過濾的對象轉儲搜索字符串文字:

objdump -s main.o | grep -B 1 str 

其中-s力量objdump顯示所有部分的全部內容,main.o是目標文件-B 1強制grep也要在匹配之前打印一行(以便您可以看到節名稱)並且str是您正在搜索的字符串文字。

隨着Windows機器上的gcc,和一個可變的main聲明如下

char *c = "whatever"; 

運行

objdump -s main.o | grep -B 1 whatever 

回報

Contents of section .rdata: 
0000 77686174 65766572 00000000   whatever....