27

我在 C 中听说过,如果我这样做了

char *s = "hello world". 

“hello world”实际上存储在只读存储器中。

我对只读内存不太清楚。解释是什么?这是否像编译器的标志,告诉编译器不要写入该部分?

4

7 回答 7

37

这不是C 语言的特性,而是编译器/链接器和操作系统协同工作的特性。

当您编译此类代码时,会发生以下情况:

  • 编译器会将字符串放入只读数据部分。

  • 链接器收集此类只读部分中的所有数据并将它们放入单个段中。该段驻留在可执行文件中,并用“只读”属性标记。

  • 现在是操作系统可执行加载程序。它加载可执行文件(或者更准确地说,将其映射到内存中)。完成此操作后,加载程序会遍历这些部分并为每个段设置访问权限。对于只读数据段,它很可能会禁用代码执行和写入访问。代码(例如,您的函数)获得执行权限,但没有写入权限。像静态变量这样的普通数据获得读写访问权限等等......

现代操作系统就是这样做的。

如前所述,它不是 C 语言的特性。例如,如果您为 DOS 编译相同的问题,程序将运行但不可能有写保护,因为 DOS 加载器不知道只读部分。

于 2009-11-10T00:34:08.037 回答
6

可执行文件包含两部分:包含全局变量的 .data 部分和包含实际机器代码的 .text 部分。

字符串被放入 .data 部分。当 C 看到“Hello world”时,它会将字符串“Hello world”放入可执行文件本身,并将程序中的“Hello world”实例替换为该字符串最终被加载的地址。

话虽如此,我不确定为什么它是只读的-理论上一个程序应该能够修改自己的内存..

于 2009-11-10T00:32:23.570 回答
4

真正的只读内存是由操作系统的内存子系统实现的。操作系统可以将某些页面标记为只读。

在二进制文件中,编译器可以告诉操作系统可执行文件的哪些部分应该放在只读内存页面和读写内存页面中。

于 2009-11-10T00:32:04.600 回答
3

如何在 Linux 中执行此操作的一个示例是Mark Mitchell、Jeffrey Olham 和 Alex Samuel 撰写的Advanced Linux Programming第 179 页。

于 2009-11-10T00:37:55.553 回答
3

当你写char s[10]="sneha"; 您在目标文件中分配了 10 个字节的存储空间(不是内存,内存仅在您执行程序时才会出现)。这是内存的静态分配(在编译时)。

但是当你写的时候char *s="sneha";你并没有分配任何存储空间来存储"sneha"。它将存储在只读部分。但是指针s根据声明的位置存储在不同的部分中。但它指向 READ ONLY DATA "sneha"。因此,如果您尝试在其上书写,则会出现分段错误。

例如:

char *s = "sneha";
s[1] = 'N'; 
printf("%s",s);  // you expecting output sNeha, 
                 // but you get a seg fault since it is READ ONLY DATA 
于 2016-07-08T20:43:28.587 回答
1

你可以尝试类似的东西

s[4] = '0';

看看你打电话时是否说“hello w0rld”

puts(s);

如果它立即导致分段错误或数据执行保护异常,那么它可能是只读的。(如果系统让你侥幸逃脱,那就不是一个好主意。)

于 2009-11-10T00:38:43.537 回答
1

正如其他人提到的,常量字符串的内容是否存储在只读存储器中是由操作系统、编译器和芯片架构决定的。

更准确地说,C 标准指定引用的字符串被认为具有“const char[]”类型(或类似的词,我手头没有标准)。

任何试图修改此类字符串内容的代码都在调用未定义的行为。这意味着实际上任何事情都可能在那时发生,并且编译器的提供者甚至不需要记录可能发生的事情。

实际上,这意味着想要可移植的 C 或 C++ 程序必须避免修改常量字符串。

一般来说,编译器不允许你修改“const”变量的内容,所以在大多数情况下,你可以认为“const”意味着“只读”。不幸的是,char * 和 const char * 有一个特殊的例外,主要是出于历史原因。这意味着这样的代码:

char *x = "Hello, World";
*x = 'h';

即使它调用未定义的行为,也将在没有错误或警告的情况下编译。

于 2009-11-10T01:19:26.713 回答