Answers:
一种常见的技术是将字符串文字放在“只读数据”部分中,该部分将以只读方式映射到进程空间中(这就是为什么您不能更改它)。
它的确因平台而异。例如,较简单的芯片体系结构可能不支持只读存储器段,因此数据段将是可写的。
而是尝试找出使字符串文字可更改的技巧(它将高度依赖于您的平台,并且可能随时间而变化),只需使用数组即可:
char foo[] = "...";
编译器将安排从字面量初始化数组,您可以修改数组。
foo = "hello"
在这种情况下)会导致意外的副作用...(假设您不重新用new
或其他方式分配内存)
char *p = "abc";
像@ChrisCooper所说的那样使用可变字符串吗?
对此没有答案。C和C ++标准只是说字符串文字具有静态的存储期限,任何对其进行修改的尝试都会产生未定义的行为,并且具有相同内容的多个字符串文字可能共享也可能不共享同一存储。
根据您要编写的系统及其使用的可执行文件格式的功能,它们可能与程序代码一起存储在文本段中,或者它们可能具有用于初始化数据的单独段。
根据平台的不同,确定细节的方式也有所不同-最有可能包括可以告诉您放置位置的工具。如果需要的话,有些甚至可以让您控制类似的细节(例如,gnu ld允许您提供一个脚本来告诉所有有关如何对数据,代码等进行分组的信息)。
movb $65, 8(%esp); movb $66, 9(%esp); movb $0, 10(%esp)
对于字符串"AB"
,但绝大多数的时候,这将是在非代码段如.data
或.rodata
或类似(取决于是否target支持只读段)。
为什么我不应该尝试更改它?
因为它是未定义的行为。引用C99 N1256草案6.7.8 / 32“初始化”:
例8:声明
char s[] = "abc", t[3] = "abc";
定义“普通” char数组对象
s
,t
其元素用字符串文字初始化。此声明与
char s[] = { 'a', 'b', 'c', '\0' }, t[] = { 'a', 'b', 'c' };
数组的内容是可修改的。另一方面,声明
char *p = "abc";
定义
p
类型为“ char的指针”,并将其初始化为指向长度为4的“ char数组”类型的对象,该对象的元素使用字符串文字进行初始化。如果试图使用它p
来修改数组的内容,则该行为是不确定的。
他们去哪里?
GCC 4.8 x86-64 ELF Ubuntu 14.04:
char s[]
:堆栈char *s
:
.rodata
目标文件的部分.text
转储目标文件部分的同一段,具有读取和执行权限,但没有写入权限程序:
#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
),我们当然可以对其进行修改。
gcc建立了一个.rodata
节,该节被映射到地址空间中的“某处”,并标记为只读,
Visual C ++(cl.exe
).rdata
出于相同目的创建了一个节。
您可以查看dumpbin
或的输出objdump
(在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
printf("some null terminated static string");
而不是printf(*address);
C)
字符串文字常被分配给只读存储器,从而使其不可变。但是,在某些编译器中,可以通过“智能技巧”进行修改。.智能技巧是通过“使用指向内存的字符指针”。记住某些编译器,可能不允许这样做。
char *tabHeader = "Sound";
*tabHeader = 'L';
printf("%s\n",tabHeader); // Displays "Lound"
由于这可能因编译器的不同而不同,所以最好的方法是为搜索的字符串文字过滤对象转储:
objdump -s main.o | grep -B 1 str
其中-s
部队objdump
,以显示所有部分的全部内容,main.o
是目标文件,-B 1
军队grep
的比赛(这样你可以看到部分名称)之前还印一行,并str
为字符串文字你的搜索。
在Windows计算机上使用gcc,并在main
like中声明一个变量
char *c = "whatever";
跑步
objdump -s main.o | grep -B 1 whatever
退货
Contents of section .rdata:
0000 77686174 65766572 00000000 whatever....