简体   繁体   English

C / C ++中的对象/结构对齐

[英]Object/Struct Alignment in C/C++

#include <iostream>

using namespace std;

struct test
{
    int i;
    double h;
    int j;
};

int main()
{
    test te;
    te.i = 5;
    te.h = 6.5;
    te.j = 10;

    cout << "size of an int: " << sizeof(int) << endl; // Should be 4
    cout << "size of a double: " << sizeof(double) << endl; //Should be 8
    cout << "size of test: " << sizeof(test) << endl; // Should be 24 (word size of 8 for double)

    //These two should be the same
    cout << "start address of the object: " << &te << endl; 
    cout << "address of i member: " << &te.i << endl;

    //These two should be the same
    cout << "start address of the double field: " << &te.h << endl;
    cout << "calculate the offset of the double field: " << (&te + sizeof(double)) << endl; //NOT THE SAME

    return 0;    
}

Output: 输出:

size of an int: 4
size of a double: 8
size of test: 24
start address of the object: 0x7fffb9fd44e0
address of i member: 0x7fffb9fd44e0
start address of the double field: 0x7fffb9fd44e8
calculate the offset of the double field: 0x7fffb9fd45a0

Why do the last two lines produce different values? 为什么最后两行产生不同的值? Something I am doing wrong with pointer arithmetic? 指针算术我做错了什么?

(&te + sizeof(double))

This is the same as: 这与:

&((&te)[sizeof(double)])

You should do: 你应该做:

(char*)(&te) + sizeof(int)

Firstly, your code is wrong, you'd want to add the size of the fields before h (ie an int ), there's no reason to assume double . 首先,您的代码是错误的,您想 h 之前添加字段的大小(即int ),没有理由假设double Second, you need to normalise everything to char * first (pointer arithmetic is done in units of the thing being pointed to). 其次,首先需要将所有内容标准化为char * (指针算术以所指向的事物为单位进行)。

More generally, you can't rely on code like this to work. 一般来说,您不能依靠这样的代码来工作。 The compiler is free to insert padding between fields to align things to word boundaries and so on. 编译器可以自由地在字段之间插入填充,以使内容与单词边界对齐,依此类推。 If you really want to know the offset of a particular field, there's an offsetof macro that you can use. 如果您真的想知道特定字段的偏移量,可以使用一个offsetof宏。 It's defined in <stddef.h> in C, <cstddef> in C++. 它在C中的<stddef.h>中定义,在C ++中的<cstddef>中定义。

Most compilers offer an option to remove all padding (eg GCC's __attribute__ ((packed)) ). 大多数编译器都提供了删除所有填充的选项(例如,GCC的__attribute__ ((packed)) )。


I believe it's only well-defined to use offsetof on POD types. 我相信在POD类型上使用offsetof只有明确定义。

You are correct -- the problem is with pointer arithmetic. 您是正确的-问题出在指针算法上。

When you add to a pointer, you increment the pointer by a multiple of that pointer's type 添加到指针时,将指针增加该指针类型的倍数

Therefore, &te + 1 will be 24 bytes after &te . 因此, &te + 1&te之后的24个字节。

Your code &te + sizeof(double) will add 24 * sizeof(double) or 192 bytes. 您的代码&te + sizeof(double)将增加24 * sizeof(double)或192个字节。

struct test
{
    int i;
    int j;
    double h;
};

Since your largest data type is 8 bytes, the struct adds padding around your ints, either put the largest data type first, or think about the padding on your end! 由于最大的数据类型是8个字节,因此该结构会在int周围添加填充,或者将最大的数据类型放在第一位,或者考虑最后的填充! Hope this helps! 希望这可以帮助!

&te + sizeof(double) is equivalent to &te + 8 , which is equivalent to &((&te)[8]) . &te + sizeof(double)等效于&te + 8 ,等效于&((&te)[8]) That is — since &te has type test * , &te + 8 adds eight times the size of a test . 也就是说,因为&te具有类型test * ,所以&te + 8的大小是test八倍。

Compilers are free to space out struct s however they want past the first member, and usually use padding to align to word boundaries for speed. 编译器可以自由地struct但是他们希望经过第一个成员,并且通常使用填充对齐字边界以提高速度。

See these: 看到这些:
C struct sizes inconsistence 结构体大小不一致
Struct varies in memory size? 结构在内存大小上有所不同?
et. 等。 al.

You can see what's going on more clearly using the offsetof() macro: 您可以使用offsetof()宏更清楚地了解正在发生的事情:

#include <iostream>
#include <cstddef>

using namespace std;

struct test
{
    int i;
    double h;
    int j;
};

int main()
{
    test te;
    te.i = 5;
    te.h = 6.5;
    te.j = 10;

    cout << "size of an int:   " << sizeof(int)    << endl; // Should be 4
    cout << "size of a double: " << sizeof(double) << endl; // Should be 8
    cout << "size of test:     " << sizeof(test)   << endl; // Should be 24 (word size of 8 for double)

    cout << "i: size = " << sizeof te.i << ", offset = " << offsetof(test, i) << endl;
    cout << "h: size = " << sizeof te.h << ", offset = " << offsetof(test, h) << endl;
    cout << "j: size = " << sizeof te.j << ", offset = " << offsetof(test, j) << endl;

    return 0;
}

On my system (x86), I get the following output: 在我的系统(x86)上,得到以下输出:

size of an int:   4
size of a double: 8
size of test:     16
i: size = 4, offset = 0
h: size = 8, offset = 4
j: size = 4, offset = 12

On another system (SPARC), I get: 在另一个系统(SPARC)上,我得到:

size of an int:   4
size of a double: 8
size of test:     24
i: size = 4, offset = 0
h: size = 8, offset = 8
j: size = 4, offset = 16

The compiler will insert padding bytes between struct members to ensure that each member is aligned properly. 编译器将在结构成员之间插入填充字节,以确保每个成员正确对齐。 As you can see, alignment requirements vary from system to system; 如您所见,对齐要求因系统而异。 on one system (x86), double is 8 bytes but only requires 4-byte alignment, and on another system (SPARC), double is 8 bytes and requires 8-byte alignment. 在一个系统(x86)上, double是8字节,但只需要4字节对齐;在另一个系统(SPARC)上, double是8字节,并且需要8字节对齐。

Padding can also be added at the end of a struct to ensure that everything is aligned properly when you have an array of the struct type. 也可以在结构的末尾添加填充,以确保在具有结构类型的数组时,所有内容均正确对齐。 On SPARC, for example, the compile adds 4 bytes pf padding at the end of the struct. 例如,在SPARC上,编译会在结构末尾添加4个字节的pf填充。

The language guarantees that the first declared member will be at an offset of 0, and that members are laid out in the order in which they're declared. 该语言保证第一个声明的成员的偏移量为0,并且按声明顺序排列成员。 (At least that's true for simple structs; C++ metadata might complicate things.) (至少对于简单的结构是这样; C ++元数据可能会使事情复杂化。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM