2.结构体内存对齐问题

发布时间 2023-07-03 21:14:56作者: CodeMagicianT

例子1:

struct S1
{
	char c1;
	int i;
	char c2;
};
#include<stdio.h>
struct S1
{
	char c1;
	int i;
	char c2;
};
int main()
{
	printf("%d\n", sizeof(struct S1));
	return 0;
}

输出:

12

例子2:

struct S2
{
	char c1;
	char c2;
	int i;
};

int main()
{
	printf("%d\n", sizeof(struct S2));
	return 0;
}

输出:

8

对齐规则 :

1.第一个成员在与结构体变量偏移量为0的地址处。

2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。这里有两个点需要说明:
偏移量: 偏移量就是程序的逻辑地址与段首的差值。
对齐数 = 编译器默认的一个对齐数与该成员大小的较小值。
VS中默认的值为8

3.结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。

4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

实际例子1:

struct S1
{
	char c1;
	int i;
	char c2;
};

实际例子2:

struct S2
{
	char c1;
	char c2;
	int i;
};

切记:最后一步一定要检查结构体大小是否为最大对齐数的整数倍!

————————————————
版权声明:本文为CSDN博主「花想云」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/gllll_yu/article/details/127814428



●结构体内成员按照声明顺序存储,第一个成员地址和整个结构体地址相同。

●未特殊说明时,按结构体中size最大的成员对齐(若有double成员,按8字节对齐。)

c++11以后引入两个关键字 alignas (opens new window)alignof (opens new window)。其中alignof可以计算出类型的最大对齐数,alignas可以指定结构体的对齐方式。

但是alignas在某些情况下是不能使用的,具体见下面的例子:

// alignas 生效的情况

struct Info {
  uint8_t a;
  uint16_t b;
  uint8_t c;
};

std::cout << sizeof(Info) << std::endl;   // 6  2 + 2 + 2
std::cout << alignof(Info) << std::endl;  // 2
/*
这段代码输出的6和2+2+2是不同的,因为它们分别表示结构体Info的字节数和对齐方式。
首先,sizeof(Info)返回的是结构体Info占用的字节数,包括成员变量a、b和c所占用的空间。在这个例子中,由于a占用1个字节,b占用2个字节,c占用1个字节,因此Info总共占用了6个字节。
其次,alignof(Info)返回的是结构体Info对齐方式的大小,也就是说,它表示一个整数类型的变量至少需要占用多少个字节才能满足对齐要求。在这个例子中,由于a占用1个字节,b占用2个字节,c占用1个字节,因此它们的对齐方式应该是2个字节对齐。因此,alignof(Info)的值为2。
*/
struct alignas(4) Info2 {
  uint8_t a;
  uint16_t b;
  uint8_t c;
};

std::cout << sizeof(Info2) << std::endl;   // 8  4 + 4
std::cout << alignof(Info2) << std::endl;  // 4

alignas将内存对齐调整为4个字节。所以sizeof(Info2)的值变为了8。

// alignas 失效的情况

struct Info {
  uint8_t a;
  uint32_t b;
  uint8_t c;
};

std::cout << sizeof(Info) << std::endl;   // 12  4 + 4 + 4
std::cout << alignof(Info) << std::endl;  // 4

struct alignas(2) Info2 {
  uint8_t a;
  uint32_t b;
  uint8_t c;
};

std::cout << sizeof(Info2) << std::endl;   // 12  4 + 4 + 4
std::cout << alignof(Info2) << std::endl;  // 4

alignas小于自然对齐的最小单位,则被忽略。

●如果想使用单字节对齐的方式,使用alignas是无效的。应该使用#pragma pack(push,1)或者使用__attribute__((packed))

#if defined(__GNUC__) || defined(__GNUG__)
  #define ONEBYTE_ALIGN __attribute__((packed))
#elif defined(_MSC_VER)
  #define ONEBYTE_ALIGN
  #pragma pack(push,1)
#endif

struct Info {
  uint8_t a;
  uint32_t b;
  uint8_t c;
} ONEBYTE_ALIGN;

#if defined(__GNUC__) || defined(__GNUG__)
  #undef ONEBYTE_ALIGN
#elif defined(_MSC_VER)
  #pragma pack(pop)
  #undef ONEBYTE_ALIGN
#endif

std::cout << sizeof(Info) << std::endl;   // 6 1 + 4 + 1
std::cout << alignof(Info) << std::endl;  // 1

●确定结构体中每个元素大小可以通过下面这种方法:

#if defined(__GNUC__) || defined(__GNUG__)
  #define ONEBYTE_ALIGN __attribute__((packed))
#elif defined(_MSC_VER)
  #define ONEBYTE_ALIGN
  #pragma pack(push,1)
#endif

/**
* 0 1   3     6   8 9            15
* +-+---+-----+---+-+-------------+
* | |   |     |   | |             |
* |a| b |  c  | d |e|     pad     |
* | |   |     |   | |             |
* +-+---+-----+---+-+-------------+
*/
struct Info {
  uint16_t a : 1;
  uint16_t b : 2;
  uint16_t c : 3;
  uint16_t d : 2;
  uint16_t e : 1;
  uint16_t pad : 7;
} ONEBYTE_ALIGN;

#if defined(__GNUC__) || defined(__GNUG__)
  #undef ONEBYTE_ALIGN
#elif defined(_MSC_VER)
  #pragma pack(pop)
  #undef ONEBYTE_ALIGN
#endif

std::cout << sizeof(Info) << std::endl;   // 2
std::cout << alignof(Info) << std::endl;  // 1

这种处理方式是alignas处理不了的

参考资料:

阿秀