编译器如何将内存分配给这个结构

How does compiler allocates memory to this struct?

本文关键字:结构 分配 内存 编译器      更新时间:2023-10-16

我试图使用名称空间和structs&遇到问题。

C++
#include<iostream>
using namespace std;
namespace One
{
    struct Data
    {
        int val;
        char character;
    };
}
namespace Two
{
    struct Data
    {
        int val;
        bool boolean;
    };
}
void functionOne(void)
{
    using namespace One;
    cout << "functionOne()" << endl;
    cout << "The size of struct Data : ";
    cout << sizeof(Data) << endl;
}
void functionTwo(void)
{
    using namespace Two;
    cout << "functionTwo()" << endl;
    cout << "The size of struct Data : ";
    cout << sizeof(Data) << endl;
}
int main()
{
    functionOne();
    functionTwo();    
} 
Output
functionOne()
The size of struct Data : 8
functionTwo()
The size of struct Data : 8

当我将"namespace Two"的代码更改为以下代码时:

namespace Two
{
    struct Data
    {
        char val;
        bool boolean;
    };
}
Output :
functionOne()
The size of struct Data : 8
functionTwo()
The size of struct Data : 2

我不知道编译器是如何为结构分配内存的。提前谢谢。

这里的问题很可能是由于对齐要求。如果我没有错的话,结构是根据其成员的最大对齐要求进行对齐的。在结构的第一个版本中,您有int; char;。在您的机器上,int似乎是在4个字节处对齐的,因此编译器在char之后用额外的3个字节填充结构。在第二个版本中,您只有bool; char;,它们的大小都是1个字节,并且与1个字节对齐(在您的机器上),因此编译器不需要填充任何内容,因此大小会降回2。

我指定了"在您的机器上",因为这可能因几个因素而异。

让我们做一个漂亮的图!

// One::Data (version 1)
0              4              5                7
[int (size 4), char (size 1), padding (size 3)][...]
// Because of alignment restrictions on int, this needs a padding of 3 bytes
// Two::Data (version 1)
0              4              5                7
[int (size 4), bool (size 1), padding (size 3)][...]
// Because of alignment restrictions on int, this needs a padding of 3 bytes
// One::Data (version 2), no change
// Two::Data (version 2)
0               1             2
[char (size 1), bool (size 1)][...]
// No alignment restrictions, therefore no padding is required

编译器如何分配内存的官方答案是"不管它想怎样"。有一些限制,但不是许多的然而,在这种情况下,你看到的是逻辑:许多类型具有(或可能具有)对齐限制,并且必须放置在某个价值这些限制会传播到任何类别包含类型的成员,否则无法尊重类成员的对齐方式。显然地在您的计算机上,bool的大小为1(并且char必须具有大小为1),并且int的大小为4,并且还必须对齐在4的地址倍数上。因此在CCD_ 6和CCD_,您有一个int,然后是charbool,然后是足够的填充字节,使结构的总大小4的倍数。(原则上,char/bool和填充可以按任何顺序混合,但在实践中我看到的编译器将填充放在任何声明之后。)

由于boolchar都没有任何对准限制,在一个类中不需要填充每个仅包含一个。

请注意,这取决于机器和编译器。在…上某些计算机(例如Sun Sparc或IBM大型机),访问未对齐的值将导致硬件陷阱,并且编译器几乎需要对齐(并插入衬垫)。在英特尔,在另一方面,未对齐的访问将起作用,但有明显的业绩命中率;编译器通常在此处强制对齐(Windows和Linux二进制API都需要它),但是编译器可以忽略它,而一些非常早期的英特尔编译器做到了,早在内存比现在紧得多的时候现在(实际上,这是一个有趣的问题现代机器上性能最好的。如果你有一个大数组与您的一个结构,额外的内存访问可能会从缓存中解决未对准问题,或者即使从存储器读取流水线以很少的额外成本,而对象的大小越小,缓存就越少失误,从而提高性能。但我没有采取任何措施,所以我只是猜测。)

需要注意的另一点是,该标准要求该类成员按顺序分配。从技术上讲,只有在没有它们之间的访问说明符,但在实践中,所有编译器总是按顺序分配。所以,如果你有一个类,比如:

struct T
{
    double d1;
    char c1;
    double d2;
    char c2;
};

它(通常)的大小为32,其中为:

struct T
{
    double d1;
    double d2;
    char c1;
    char c2;
};

将只有24的尺寸。回到记忆的时代紧张,我们经常关注这样的事情,但现在地方性有时是个问题,也许这样做会有好处再次:按照变量大小的顺序声明变量最大的优先。