string文字:他们去哪里?

我感兴趣的是在哪里string文字分配/存储。

我在这里find了一个有趣的答案,他说:

定义一个内联string实际上是embedded在程序本身的数据,不能改变(一些编译器允许这个聪明的把戏,不要打扰)。

但是,这与C ++有关,更不用说它不说了。

我很烦。 = d

所以我的问题是我的string文字保存在哪里以及如何? 为什么我不应该试图改变它? 实施是否因平台而异? 有没有人关心“巧妙的把戏”?

一个常见的技术是将string文字放在“只读数据”部分,该部分被映射到进程空间中为只读(这就是为什么您不能更改它)。

它确实因平台而异。 例如,更简单的芯片架构可能不支持只读存储器段,因此数据段将是可写的。

相反,然后尝试找出一个技巧,使string文字可变(这将高度依赖于您的平台,并可能随时间而改变),只是使用数组:

char foo[] = "..."; 

编译器将安排数组从文字初始化,您可以修改数组。

没有人回答这个问题。 C和C ++标准只是说,string文字具有静态存储持续时间,任何修改它们的尝试都会给出未定义的行为,并且具有相同内容的多个string文字可能共享或不共享相同的存储。

根据您要编写的系统以及它使用的可执行文件格式的function,可能会将它们与程序代码一起存储在文本段中,也可能有一个用于初始化数据的独立段。

确定细节也会根据平台而变化 – 最有可能包括可以告诉你它放在哪里的工具。 有些甚至会让你控制这样的细节,如果你想要的话(例如gnu ld允许你提供一个脚本来告诉它如何分组数据,代码等)

为什么我不应该试图改变它?

因为它是未定义的行为。 来自C99的报价N1256草稿6.7.8 / 32“初始化”

例8:声明

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

定义“简单的”字符数组对象st其元素用string文字初始化。

这个声明是一样的

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

数组的内容是可修改的。 另一方面,声明

 char *p = "abc"; 

定义p的types为“指向char的指针”并将其初始化为指向长度为4的types为“char的数组”的对象,其元素用string文字初始化。 如果尝试使用p来修改数组的内容,则行为是不确定的。

他们去哪里?

GCC 4.8 x86-64 ELF Ubuntu 14.04:

  • char s[] :堆栈
  • char *s
    • .rodata节的对象文件
    • 对象文件的.text部分被转储的同一个段,具有Read和Exec权限,但不包含Write

程序:

 #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 

所以string存储在.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 RE 200000 Section to Segment mapping: Segment Sections... 02 .text .rodata 

这意味着默认链接脚本将.text.rodata转储到可以执行但不能修改的段( Flags = RE )。 尝试修改这样的段会导致Linux中发生段错误。

如果我们对char[]做同样的处理:

  char s[] = "abc"; 

我们获得:

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

所以它被存储在堆栈中(相对于%rbp ),我们当然可以修改它。

仅供参考,只是备份其他答案:

标准: ISO / IEC 14882:2003说:

2.13。 string文字

  1. […]一个普通的string文字的types为“ n const char数组”和静态存储时间(3.7)

  2. 是否所有string文字都是不同的(即,是否存储在非重叠对象中)是由实现定义的。 尝试修改string文字的效果是未定义的。

gcc使得一个.rodata节被映射到地址空间的某个地方并被标记为只读,

Visual C ++( cl.exe )为同一目的制作.rdata节。

您可以查看dumpbinobjdump (在Linux上)的输出以查看可执行文件的各个部分。

例如

 >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 

这取决于您的可执行文件的格式 。 一种考虑的方法是,如果你是汇编编程,你可能会把string文字放在汇编程序的数据段中。 你的C编译器做类似的事情,但这一切都取决于你正在编译的二进制文件系统。

string文字经常分配给只读存储器,使它们不可变。 然而,在一些编译器修改是可能的一个“聪明的把戏”。聪明的把戏是“使用字符指针指向内存”..记住一些编译器,可能不允许这..这里是演示

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

由于编译器和编译器可能有所不同,最好的方法是为search到的string文本过滤对象转储:

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

其中-s强制objdump显示所有段的全部内容, main.o是对象文件, main.o强制grep在匹配之前也打印一行(这样就可以看到段名), str是string你正在寻找的文字。

在Windows机器上使用gcc,并在main声明一个variables

 char *c = "whatever"; 

赛跑

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

回报

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