2

我发现一个常量文字 get 放置在程序的数据段中(来自 SO)并且是只读的,因此“s[0] = 'a'”行会导致错误,这实际上发生在我取消注释该行并运行。但是,当我查看 MS VS 中的内存窗口时,变量都放在内存中。我很好奇他们(编译器)如何强制对“s”进行只读访问?

#include <iostream>

int main(void)
{
      char *s = "1023";
      char s_arr[] = "4237";
      char *d = "5067";
      char s_arr_1[] = "9999";
      char *e = "6789";
      printf("%c\n", s[0]);
//      s[0] = 'a'; This line would error out since s should point to data segment of the program
      printf("%s\n", s);
      system ("pause");
}

0x002E54F4  31 30 32 33 00 00 00 00 34 32 33 37 00 00 00 00  1023....4237....
0x002E5504  35 30 36 37 00 00 00 00 39 39 39 39 00 00 00 00  5067....9999....
0x002E5514  36 37 38 39 00 00 00 00 25 63 0a 00 25 73 0a 00  6789....%c..%s..
0x002E5524  70 61 75 73 65 00 00 00 00 00 00 00 43 00 3a 00  pause.......C.:.

编辑 1:更新存储在 s_arr 中的值(应放置在堆栈空间中),以明确它与字符串常量相邻放置。

编辑 2:由于我看到有关基于页面的 ro/rw 访问的答案,这里地址 .. 0x...4f4 是 rw 0x...4fc 是ro并且 0x...504 是 rw。他们如何实现这种粒度?此外,由于每个页面可能至少为 4kb,因此有人可能会争辩说 0x4fb 可能是前一个 ro 页面的最后地址。但是我现在添加了一些变量,以表明它们都连续放置在内存中,并且粒度是每 8 个字节。您可以说,由于您提到的页面处于 4k 级别,

4

3 回答 3

7

我不知道是什么让您认为您的示例在不可修改内存旁边显示了可修改内存。你在说什么“粒度”?您的内存转储没有显示类似的内容。

"4237"您在内存转储中看到的字符串不是您的s_arr. 你"4237"看到有一个只读字符串s_arr文字,它被用作. 该初始化程序被复制s_arr. 同时,实际s_arr存在于其他地方(在堆栈中)并且是完全可修改的。它也包含"4237"(作为它的初始值),但这是一个完全不同的"4237",您在内存转储中看不到。让您的程序打印地址,s_arr您会发现它与您转储的内存范围相去甚远。

同样,您关于“0x...4f4 是 rw 0x...4fc 是 ro 而 0x...504 是 rw”的说法完全不正确。所有这些地址都是只读的。它们都不是读写的。那里没有任何“粒度”。

记住这样的声明

char s_arr[] = "4237";

真的相当于

const char *unnamed = "4237";
char s_arr[5];
memcpy(s_arr, unnamed, 5);

在您的内存转储中,您正在查看unnamed我上面示例中的那个地址。该内存区域是只读的。您s_arr驻留在完全不同的内存区域中,即读写。

于 2012-07-02T05:59:07.993 回答
2

由于引入了 32 位平台,所有内容都放在同一个段中(这并不完全如此,但更容易认为是这样。有一些需要几页来解释的小警告,它们适用于操作系统设计) .

32 位地址空间被分成几页。Intell 允许使用页粒度分配 RO 位。调试器仅显示 32 位(64 位)地址,从技术上讲,它是段中的偏移量。可以简单地将此偏移量称为地址。这一点不会有错。

然而,链接器将不同的内存区域称为段。这些段与 Intel 内存段无关。链接器段(代码、数据、堆栈等)被加载到不同的页面中。这些页面获得不同的属性(RO/RW、执行权限等)。

于 2012-07-02T05:33:24.347 回答
0

您显示的内存块是存储字符串常量的区域(您可以看到所有 4 个值都一个接一个地直接在那里)。该区域被标记为只读。在 Windows 上,每个 4Kb 内存块(页面)都可以有自己的属性(读/写/执行),因此即使是 2 个相邻的位置也可以有不同的访问标志。

变量所在的区域位于不同的位置(在您的情况下为堆栈)。您可以通过检查&s即时窗口(或监视窗口)的值来查看它。

于 2012-07-02T05:34:36.070 回答