-4

我在一次采访中被问到这些问题。

  1. 为什么二进制代码和数据完全分开,即为什么他们计划数据段,为什么不是代码段内的所有内容?

2.

class A
{
private :
    int i;
public:
    void show()
    {
        printf("hello");
    }
};
int main()
{
    A* a = NULL; (what happens in object table?)
        A* aa =  new A();  (what happens in object table?)

        a->show();
    aa->show();

    delete aa;
    return 0;
}

aa 和 a 到底有什么不同以及对象在内存中的行为如何。

4

3 回答 3

2

代码段与数据段

代码段是只读的,而数据段是读/写的,如果将这两个部分混合在一起,在保持代码安全的同时更新数据成为一个挑战:一个例子是 Lol4t0 指出的:

对于操作系统的内存管理,代码段将被换出到文件系统上的原始可执行文件,而被认为发生变化的数据段被换出到页面文件。如果将它们混合在一起,您可能会失去将可执行文件作为分页文件重用的优势。

而且,代码段通常加载到只读的内存页面(VirtualAlloc(PAGE_READONLY)

a(空)与 aa(非空)

a 和 aa 本身只是 A* 类型的堆栈变量,但 a 指向 NULL,而 aa 指向在堆中分配的对象。

a->show() 被翻译成:

A_show(a)
//which is:
A_show(NULL)

因为在 show() 中没有引用成员变量,所以应该可以正常工作。

aa->show() 被翻译成:

A_show(aa)

这里 aa 是一个有效的地址,所以即使你在 show() 中引用成员变量,它也可以工作。

请注意,与在运行时解析的虚函数不同,因此在每个对象中都需要一个 vptr,成员函数只是普通函数,它以this作为第一个参数,并在编译时由编译器解析。

于 2012-08-17T17:13:17.420 回答
1

分离代码和数据的另一个原因是内存管理。

当您的物理内存不足时,您可以“忘记”代码页并在需要时从磁盘重新读取它。但是你不能对数据做同样的事情。在这种情况下,您应该将页面移动到交换/页面文件。现在,如果您有混合的代码和数据页面,则必须将它们保存在交换/页面中,因此这种策略可以节省资源。

好吧,我不假装完整。

于 2012-08-17T17:44:14.750 回答
1

我想第二部分是一个有点技巧的问题。你会认为会发生分段错误,但由于A::show不引用类的任何数据部分,它将被优化为二进制代码的一部分(几乎就像一个静态方法),你会得到“hellohello”。只需将 printf 更改为printf("hello %d ", i);,您就会得到分段错误。

于 2012-08-17T17:52:17.910 回答