【问题标题】:Memory allocation of identical structs相同结构的内存分配
【发布时间】:2010-07-14 23:26:06
【问题描述】:

所以我在教我的朋友指针。这样做时,我注意到两个相同结构的地址完全是背靠背的。

struct Foo
{
    int a;
};

struct Bar
{
    int b;
};

这让我可以这样做:

Foo foo; foo.a = 100;
Bar bar; bar.b = 100;

Foo *pFoo = &foo;
Bar *pBar = &bar;

(pFoo+1)->a = 200;

这会覆盖 bar.b 中的值并将其设置为 200。

现在,我并没有质疑这样做的好处——它可能永远不会在真正的程序中出现。我只是想知道,操作系统是否总是背靠背分配相同的结构?前提是给定区域中有足够的可用内存空间。

【问题讨论】:

    标签: c++ pointers struct


    【解决方案1】:

    不,不一定。某些架构/编译器可能会在边界上对齐结构,以便数组中的结构与调用堆栈上的结构之间存在不同的间距。您在调用堆栈上进行分配并将它们视为连续数组,这是一个不可靠的假设。

    【讨论】:

    • +1。如果他使用大小不等于对齐的类型,则此代码将失败,因为它应该。
    【解决方案2】:

    操作系统不会为局部变量分配堆栈空间,编译器会。 C++ 编译器不保证分配的顺序。

    【讨论】:

    • @SoulBeaver - 程序向操作系统请求内存,但它们没有说明内存中发生了什么。所以操作系统不知道是否在堆栈上分配了两个相同的结构。实际上,程序只能请求页面大小的块空间,而一个页面通常是 4096 字节长,因此两个 struct 很可能同时分配,程序决定内存如何与 struct 对应。跨度>
    • @DeadMG:将“C”更改为“C++”是否会实质性地改变答案的内容?
    • @Omnifarious:实际上,在 Windows 上,程序可以要求尽可能多的内存,因为堆管理器是由操作系统提供的,而不是由编译器提供的。这是 Doug Lea 在 Unix 机器上使用的 malloc 实现与在 Windows 机器上使用的 RtlHeap 之间最显着的区别之一。
    • @Billy:Windows 操作系统提供了一个堆管理器(实际上有几个,一个称为堆管理器,其他的简称为分配器,很可能也使用堆)。这个问题中的代码没有使用它,而且可能 99% 的代码也没有。 C++ 编译器带有自己的堆管理器(在运行时库中),它与 Windows 分配器完全不同,并可能在顶部添加各种调试数据(顺便保证数据不是“操作系统决定的地方” ")。
    • @Ben:通常C/C++函数是按照Win32函数来实现的。见:labri.fr/perso/betrema/winnt/images/virtmm_1.gif
    【解决方案3】:

    不 - 编译器/链接器通常会将变量放置在与它们在源文件中的位置无关的完全不同的位置。

    我非常高兴能够使用从汇编翻译而来的 C 代码,这些代码在整个地方都做出了这种假设(在汇编中,当您在同一段/部分中有 2 个变量彼此相邻时,这就是你将在图像中得到的)。

    那不好玩...

    请注意,您可以使用一些工具(例如,链接器配置文件或编译器编译指示)来控制变量在内存中的位置。但是到那时,您显然处于“特定于实现”的领域,您可能仍然需要处理对齐问题等等(尽管如果结构的大小相同,这可能不会成为问题)。

    让 2 个结构彼此相邻的可移植方法是将它们包装在另一个结构中(同样,指针算术和别名可能仍然存在问题)。

    【讨论】:

    • 背靠背我不是指它们在源代码中的距离,而是在内存中。如果它依赖于源代码距离会很有趣。
    • @SoulBeaver:我知道您想知道这些结构是否最终会在内存中相邻,但我想您是在问这是否可能由源中的位置控制文件。您认为还有哪些其他因素可能会控制展示位置?
    • 大小,内存碎片,两种大小的划分(也许它们是内存中连续块的机会取决于它们是否是彼此的倍数),操作系统和编译器优化,那种东西。然而,我的问题更笼统,看看它是否总是这样做,或者这是否只是一个危险的假设。它似乎是。不过感谢您的编辑!结构思想中的结构非常好。
    【解决方案4】:

    不,这不能保证有效。

    首先,不能保证无关对象在内存中的放置。对象被保证连续放置在内存中的唯一时间是

    • 如果您自己将它们彼此相邻放置(例如,您手动分配一定数量的字节并将它们一个接一个地放置)或
    • 如果它们在一个数组中(数组元素总是连续的

    所以,以下是有效的:

    int ints[2];
    *(&ints[0] + 1) = 42;
    

    问题中的代码无效,因为foobar 对象完全不相关,可以放在内存中的任何位置。

    有一个相关的问题是,在一个结构中也可能有未命名的填充字节

    • 结构成员之间(以便成员正确对齐)和
    • 在结构的末尾(以便该结构类型的对象可以连续放置在数组中)

    这意味着以下内容无效,因为Foo 的末尾可能有未命名的填充:

    Foo foos[2];
    *(&foos[0].a + 1) = 42;
    

    通常,除非两个对象位于数组中,否则您永远不应依赖于两个对象的相对位置。无论对象在源代码中是否相邻声明,它们具有相同的类型,或者它们具有相同的大小:根本无法保证。

    【讨论】:

      【解决方案5】:

      我记得在 gcc 上使用整个程序优化时遇到过这样一种情况,它在优化过程中完全重新排序了所有全局变量。也就是说,对于 C++ 中单独变量的这种布局,并没有这样的保证。

      【讨论】:

        【解决方案6】:

        这不仅仅发生在结构体上。单个变量通常是连续的,但正如大家所说,这并不能保证(实际上,在我的框中,以下示例的顺序是颠倒的)

        int a,b;
        int *pa=&a;
        int *pb=&b;
        a=1;
        b=2;
        *(pa)=3;
        *(pb+1)=4;
        printf("a value: %i \tb value: %i\n", *pa, *pb);
        
        

        结果是

        a 值:4 b 值:2

        【讨论】:

          猜你喜欢
          • 2010-11-20
          • 2022-08-20
          • 1970-01-01
          • 1970-01-01
          • 2011-06-01
          • 2016-01-27
          • 1970-01-01
          • 2020-03-29
          相关资源
          最近更新 更多