关于内存地址的问题

时间:2016-10-17 12:47:40

标签: c++ heap-memory stdvector memory-address

根据以下代码,我几乎没有问题:

  1. 为什么&s->m1&s->m2之间的差异为4个字节,double的大小为8?
  2. 为什么调整m3大小不会更改地址?我希望&s->m4在调整m3
  3. 之后能够进一步远离。{/ 1>
  4. 为什么即使我删除m1所有地址都保持不变?
  5. main.cpp中:

    #include <iostream>
    #include <vector>
    
    struct S
    { 
        int m0;
        int m1;
        double m2;
        std::vector<int> m3;
        std::vector<int> m4;
    };  
    
    int main()
    {   
        S* s = new S();
        s->m3.resize(7);
    
        std::cout << &s->m0 << std::endl;
        std::cout << &s->m1 << std::endl;
        std::cout << &s->m2 << std::endl;
        std::cout << &s->m3 << std::endl;
        std::cout << &s->m4 << std::endl;
    
        return 0;
    }
    

    输出:

    0x1860c20
    0x1860c24
    0x1860c28
    0x1860c30
    0x1860c48
    

2 个答案:

答案 0 :(得分:4)

  1. sizeof(s-> m2)为8字节的事实影响&amp; s-&gt; m2和&amp; s-&gt; m3之间的差异,而不是&amp; s-&gt;之间的差异。 m1和&amp; s-&gt; m2。

  2. 调整m3大小不会改变地址,因为std :: vector使用动态堆内存来存储向量的内容。

  3. 如果删除m1,因为下一个struct element m3在8字节边界上,编译器会添加4个字节的填充以保持对齐。

答案 1 :(得分:1)

喔。

嗯,首先,在访问说明符之间(我相信C ++ 11及更高版本对所有具有相同访问权限的项目),结构中的项目放在严格增加的内存地址,就程序本身而言

然后,给出你的声明

struct S
{ 
    int m0;
    int m1;
    double m2;
    std::vector<int> m3;
    std::vector<int> m4;
};  

...你问

  

1.为什么&s->m1&s->m2之间的差异为4个字节,double的大小为8?

对象的地址是该对象的起始地址,即其第一个字节的地址,即该对象中的最低地址。

因此m1的地址是m1开始的地方。如果类型为m1的{​​{1}}为4个字节,则下一个项int的开始时间至少增加4个字节。所以它与你的编译器一样。然而,编译器可以自由插入填充,因此m2可以放得更高。对于其他一些编译器,m2可以是8个字节,或者甚至只是1个字节(它必须至少为16位,所以后一种可能性意味着int,即C ++内存单元,那么至少有16位,就像德州仪器的一些数字信号处理器那样。)

  

2.为什么调整大小char不会更改地址?我希望m3在调整&s->m4之后能够进一步远离。{/ p>

C ++中的所有对象都是固定大小的,由m3运算符给出。但是它们可以保存指向内存块的指针,这些指针实际上可以在大小上变化。 sizeofm3,它使用这种技术:它包含指向某个内部缓冲区的指针。您可以通过std::vector方法检查该缓冲区的大小。您可以通过.capacity()方法检查当前使用的数量。

  

3.为什么即使我删除.size()所有地址都保持不变?

如果该观察结果是正确的,那么类型m1的大概m2与您的编译器和编译选项的对齐为8个字节。也就是说,double必须放在8的倍数地址上。并且由于double之前仍有m0类型int,因此你的编译器m2是4个字节,编译器插入4个字节的填充

编译器无法移动这些项目以获得更好的空间利用率,因为C ++标准要求它将它们置于递增的地址顺序中,因为没有中间访问说明符。

但是,大多数编译器支持各种int来影响其填充和对齐决策。

相关问题