0

我试图使用命名空间和结构并遇到问题。

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
{
    struct Data
    {
        char val;
        bool boolean;
    };
}

Output :

functionOne()
The size of struct Data : 8
functionTwo()
The size of struct Data : 2

我无法弄清楚编译器如何为结构分配内存。提前致谢。

4

2 回答 2

5

这里的问题很可能是由于对齐要求。如果我没记错的话,结构是根据其成员的最大对齐要求对齐的。在您的结构的第一个版本中,您拥有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
于 2013-07-21T16:17:11.200 回答
2

关于编译器如何分配内存的官方答案是“无论如何”。有一些限制,但不是很多。但是,在这种情况下,您所看到的非常合乎逻辑:许多类型具有(或可能具有)对齐限制,并且必须放置在某个值的倍数的地址处。并且这些限制会传播到包含该类型成员的任何类,否则,您将无法尊重类成员的对齐方式。显然,在您的机器上,bool大小为 1(并且char必须大小为 1),int大小为 4,并且还必须与 4 的地址倍数对齐。所以在One::Dataand中Two::Data,您有一个int, 后跟一个char或一个bool, 然后是足够的填充字节,使结构的总大小成为 4 的倍数。(原则上,char/bool和填充可以按任何顺序混合,但实际上,我见过的每个编译器都将填充放在后面任何声明。)

由于 abool和 achar都没有任何对齐限制,因此不需要在仅包含其中一个的类中进行填充。

请注意,这取决于机器和编译器。在某些机器上(例如 Sun Sparc 或 IBM 大型机),访问未对齐的值会导致硬件陷阱,并且编译器几乎需要对齐(并插入填充)。另一方面,在 Intel 上,未对齐的访问会起作用,但性能会受到明显影响;编译器通常会在此处强制对齐(并且 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 的大小。在记忆紧张的时候,我们经常关注这些事情,但是现在局部性有时是一个问题,也许再次这样做是值得的:按照它们的顺序声明变量尺寸,最大的在前。

于 2013-07-21T16:22:43.713 回答