为什么我可以相信内存分配?

Why Can I Trust Memory Allocation?

我目前正在上一门使用 C 的入门 CS 课程。我的教科书似乎暗示指针变量仍然保留之前分配给它的内存地址,即使在调用 free() 之后也是如此——假设我以前使用过 malloc()。这是否意味着当调用 malloc() 时内存部分变为 "locked" 因此我的指针数据保持不变?是什么阻止了其他进程——比如 google chrome 或某些应用程序——弄乱了我的变量?我可以很容易地为无效索引处的数组赋值,从而导致事情中断。我也可能不正确地访问内存,再次在无效索引处使用数组,给我垃圾,或者,如果我真的很幸运,给我一个对我有意义的值。是什么阻止了计算机陷入混乱!

pointer variable still holds the address for memory previously allocated to it, even after free() was called on it

这是真的。这种情况被称为"dangling pointer"。你的程序不允许使用这些指针;否则,其行为未定义。

Does this mean that portions of memory become "locked" when malloc() is called so the data of my pointer remains constant?

它们被锁定的唯一意义是 malloc 不会 return 再次将分配的范围分配给您的程序,直到您 free 它。但是,没有内置保护:如果您的程序不小心写入已释放的指针,它可能会覆盖合法变量中的数据,从而导致在没有 proper tools.

的情况下极难捕获的错误

What prevents other processes -- say google chrome or some app -- from messing with my variable?

事实上,其他应用 运行 在单独的内存中 space。硬件和 OS 确保其他进程被锁定在程序的内存 space 之外。

What is preventing a computer from spiraling into chaos!

现代处理器有一种称为保护模式的操作模式,带有虚拟内存 - 普通程序(进程)在所谓的用户模式下运行,并且看到内存 space 不同于当前 运行 进程。然后操作系统确保所有此类不当行为都包含在一个这样的进程中 - 虽然这种访问很可能会导致崩溃,但它只会包含在这个进程中。

这在旧版本的 Windows 中并非如此 - 虽然 Windows 3 可以使用 x86 保护模式,但它 运行 所有程序都处于相同的特权级别 - 那里可怕的蓝屏死机可能会导致整个系统瘫痪:


至于

的部分

My textbook seems to imply that a pointer variable still holds the address for memory previously allocated to it, even after free() was called on it -- assume I previously used malloc().

这实际上不是严格意义上的。 C标准明确表示,在指针上调用free后,指针本身的值变为indeterminate。它可能仍指向您的实施中的相同地址,但所有赌注都已关闭。以下程序甚至可能在某些平台上崩溃:

void *ptr = malloc(42);
free(ptr);

// some other code that is between here...

if (ptr) {
    ...
}

正如 C 标准所说 6.2.4p2

The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.

Appendix J.2. Undefined behavior

The value of a pointer to an object whose lifetime has ended is used (6.2.4).

编译器知道 free. 之后不需要该值,因此可能会导致一种行为,因此如果上面代码中的 ptr 是存储在 register 中,编译器可以自由地覆盖它们之间的某些代码的值,随后变量 ptr 可能表现为它是一个未初始化的值它被用在 if.

使用值不确定的指针会导致各种有趣的行为,如 所证明的那样。编译器不需要产生你期望的值,它只需要符合标准。


无论如何,这本书是正确的,因为您不能在 free(ptr) 之后使用 ptr,直到您为它设置了一个新值,只是具体书中的示例具有误导性,因为它只是许多可能结果中的一种,这在 C.未定义行为 中很常见。

早在 70 年代,就有不止一个人使用计算机。当其中一个使系统崩溃时,其他用户讨厌它。于是他们发明了虚拟机。不像你现在听到的那样,或者嗯,是的。

虚拟内存,所有程序都可以同时使用地址 0x4000000。虚拟 CPU 以便许多程序可以 运行 在一个 CPU 上使用时间片。虚拟输入输出设备。

所有这些都是 50 年前在 Multics、VMS、Unix、IBM OS/360 等领域发明的

What prevents other processes -- say google chrome or some app -- from messing with my variable?

所有现代桌面/服务器OSes 确保一个程序的内存space 不能被任何其他程序访问。这主要来自 CPU.

中内存管理单元的配置(由 OS 管理)

然而,并非所有 OS 都这样做。 DOS 没有,主要是因为它打算作为一个进程 OS; TSR程序可以访问整个机器的任何东西,包括前台程序。

一些实时 OSes,例如 VxWorks(特别是较旧的变体)是多任务处理、抢占式 OSes,但不提供任何进程间内存分离。设计人员选择这样做是为了减少上下文切换时间,这在实时方面非常方便OS。

这是一种思考方式。让我们把计算机内存分成 "pages"。让我们假设每个 "page" 是一张实际的 sheet 纸,我们用铅笔在上面写数字。假设我们的程序使用的所有页面都存储在文件柜中,从第 0 页到第 N 页。假设我们有一些简单的方法来跟踪文件柜中的哪些页面正在使用——也许我们折叠顶角或其他东西。最后,让我们假设这张纸有些珍贵:我们实际上从未丢弃过一张纸。当我们需要一些新内存时,找到一个未使用的现有页面并擦除其上的所有内容并再次使用它是值得的。

有了这个类比,我们就可以回答您的问题了。

My textbook seems to imply that a pointer variable still holds the address for memory previously allocated to it.

没错。当我们 "free" 一页内存时,我们只是折叠上角(或其他)。但是我们还没有擦除页面,因为没有必要(那样效率很低)。我们等到其他人稍后分配页面时将其擦除并在其上写入新数字。

What prevents other processes -- say google chrome or some app -- from messing with my variable?

根据我目前开发的类比,什么都没有。事实上,如果您计算机上的所有程序都直接访问内存,那么就没有什么可以阻止它们相互干扰内存,而且会造成严重后果。所以这就是为什么大多数计算机实际上并没有进行装配以使普通程序直接访问内存的原因。

相反,如今几乎所有通用计算机都包含一个 内存管理单元 (通常实现 虚拟内存 )。结果是每个程序都有自己的文件柜。一个程序可以弄乱其文件柜中的所有页面,它可以正确或不正确地使用它们,如果它真的想要的话,它会混淆自己并崩溃,但程序根本没有办法做 任何事情 与任何其他程序文件柜中的页面。它甚至不能偷看他们,更不用说给他们写信了。