如何在C中实现只读内存?

时间:2009-11-10 00:26:41

标签: c memory readonly

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

char *s = "hello world". 

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

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

7 个答案:

答案 0 :(得分:31)

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

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

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

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

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

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

如上所述,它不是C语言的一个特征。例如,如果为DOS编译相同的问题,程序将运行但不能进行写保护,因为DOS加载程序不知道只读部分。

答案 1 :(得分:6)

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

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

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

答案 2 :(得分:4)

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

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

答案 3 :(得分:2)

如何在Linux中执行此操作的示例由Mark Mitchell,Jeffrey Olham和Alex Samuel撰写page 179 of Advanced Linux Programming

答案 4 :(得分:1)

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

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

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

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

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

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

将编译时没有错误或警告,即使它调用未定义的行为。

答案 5 :(得分:1)

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

但是当您编写char *s="sneha";时,您没有分配任何存储空间来存储"sneha"。它将存储在READ ONLY部分中。但是指针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 

答案 6 :(得分:0)

您可以尝试类似

的内容
s[4] = '0';

当你打电话

时看看它是否说“你好w0rld”
puts(s);

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