如何在C中实现只读内存?
我在C听说过,如果我这样做的话
char *s = "hello world".
“hello world”实际上存储在只读存储器中。
我不太清楚只读内存。 解释是什么? 这是否像编译器的标志,告诉编译器不写入该部分?
这不是C语言的一个特性,而是编译器/链接器和操作系统协同工作的一个特性。
编译此类代码时,会发生以下情况:
-
编译器会将字符串放入只读数据段。
-
链接器收集此类只读部分中的所有数据,并将它们放入单个段中。 此段驻留在可执行文件中,并标记为“只读”属性。
-
现在是操作系统可执行加载器。 它加载可执行文件(或将其映射到内存中更精确)。 完成此操作后,加载程序将遍历各个部分并为每个段设置访问权限。 对于只读数据段,它很可能会禁用代码执行和写访问。 代码(例如,您的函数)获取执行权限但没有写访问权限。 像静态变量这样的普通数据会获得读写访问等等……
这就是现代操作系统的function。
如上所述,它不是C语言的一个特性。 例如,如果为DOS编译同样的问题,程序将运行但不能进行写保护,因为DOS加载程序不知道只读部分。
可执行文件包含两部分:.data部分,包含全局变量;以及.text部分,包含实际的机器代码。
字符串放在.data部分。 当C语言看到“Hello world”时,它将字符串“Hello world”放入可执行文件本身,并将程序中“Hello world”的实例替换为该字符串最终加载的地址。
话虽如此,我不确定为什么它是只读的 – 理论上程序应该能够修改自己的内存..
真正的只读存储器由OS的存储器子系统实现。 操作系统可以将某些页面标记为只读。
在二进制文件中,编译器可以告诉操作系统可执行文件的哪些部分应该放在只读和读写内存页面中。
有关如何在Linux中执行此操作的示例,请参阅Mark Mitchell,Jeffrey Olham和Alex Samuel 的高级Linux编程的第179页 。
正如其他人所提到的,常量字符串的内容是否存储在只读存储器中是由操作系统,编译器和芯片架构决定的。
更准确地说,C标准规定引用的字符串被认为具有“const char []”类型(或者说这个效果的单词,我手边没有标准)。
尝试修改此类字符串内容的任何代码都在调用未定义的行为。 这意味着在任何时候都可能发生任何事情,甚至不需要编译器的提供者来记录可能发生的事情。
实际上,这意味着想要可移植的C或C ++程序必须避免修改常量字符串。
通常,编译器不允许您修改“const”变量的内容,因此在大多数情况下,您可以将“const”视为“只读”。 不幸的是,char *和const char *有一个特殊的例外,主要是出于历史原因。 这意味着像这样的代码:
char *x = "Hello, World"; *x = 'h';
即使它调用未定义的行为,也会在没有错误或警告的情况下编译。
你可以试试像
s[4] = '0';
当你打电话时看看它是否说“你好w0rld”
puts(s);
如果它导致立即分段错误或数据执行保护exception,那么它可能是只读的。 (如果系统让你逃脱它,那不是一个好主意。)
当你写char s[10]="sneha"
; 您在目标文件中分配10个字节的存储空间(不是内存,只有当您执行程序时内存才会进入图片中)。 这是内存的静态分配(在编译时)。
但是当你写char *s="sneha";
你没有分配任何存储空间来存储"sneha"
。 它将存储在READ ONLY部分。 但是指针s
根据声明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