关于结构体内存对齐方式的总结(#pragma pack()和alignas())

最近闲来无事,翻阅msdn,在预编译指令中,翻阅到#pragma pack这个预处理指令,这个预处理指令为结构体内存对齐指令,偶然发现还有另外的内存对齐指令aligns(C++11),__declspec(align(#))(Microsoft专用),遂去探究两者之间的不同点。

1、#pragma pack

这个指令为预处理指令,所谓与处理指令执行在程序的预处理阶段,该指令对应着编译选项/Zp,可以在vs的工程属性中设置编译选项的内存对齐,也可以利用预处理指令来设置。

#pragma pack( [ show ] | [ push | pop ] [, identifier ] , n )

预处理指令的用法如上,其中必选参数n为内存对齐值,有效值为1、4、8、16,默认值为8。可选参数中,show代表右警告消息显示当前的内存对齐值,push | pop 二者选其一,代表将当前的内存对齐值入、出栈。identifier跟随push | pop一同出入栈,作为表示使用。

2、aligns、__declspec(align(#))

aligns和alignof为c++标准的类型说明和运算符,其中aligns为内存对齐类型符,alignof为返回内存对齐值得运算符。对应还有Microsoft的msvc专用的__alignof()和__declspec(align(#))也可以达到同样的的效果。

__declspec(align(#))和aligns(#)中#为对齐值,可选的对齐值为2、4、8、16、32、64

3、区别

同样是对齐操作,两种不同的对齐操作有什么区别呢。

字面上,一个是预处理指令,另一个是类型说明符。

在相应的结构体内存对齐上,也存在差别。

所谓的内存对齐指的是变量分配到要求的内存地址上,这个地址必须是对齐值的整数倍,例如int型变量,默认的对齐值为数据长度,也就是4,分配到内存地址0x0001103F,该地址模上内存对齐值,也就是4。1103F%4=3,所以将int型变量分配到0x00011042上,该地址为对齐值得整数倍。

定义一个结构体,结构体内存对齐值为内存存储变量的最大默认对齐值

 struct   MyStruct
    {
        char member;
        int a;

    }mystruct;

    sizeof(mystruct) = 8,alignof(MyStruct) = 4

通过计算可以看出,结构体的对齐值为4,内存占用为8。结构体遵循内存对齐规则,结构体内部变量也遵循对齐规则,其中char偏移量0,int偏移量为4,两变量之间填充3字节数据。

  • #pragma pack(1),再次计算sizeof和alignof

sizeof(mystruct) = 5;alignof(MyStruct) = 1

可以看出,预处理指令要求的内存对齐为1设置生效了。

  • __declspec(align(2)),最小对齐值,再次计算sizeof和alignof

__declspec(align(2))
     struct   MyStruct
     {
         char member;
         int a;

}mystruct;

sizeof(mystruct) = 8;alignof(MyStruct) = 4

设置内存对齐失败了。

    • 用c++11标准再试试,align(2)
struct  alignas(2)   MyStruct
     {
         char member;
         int a;

     }mystruct;

这下vs直接报错,‘MyStruct‘: Alignment specifier is less than actual alignment (4), and will be ignored

通过这个报错,可以看出,类型说明符形式的内存对齐要求最小的对齐值为结构体的对齐默认值,也就是内存变量的最大对齐  值。

  • __declspec(align(8)),计算sizeof和alignof
  __declspec(align(8))  struct   MyStruct
     {
         char member;
         int a;
     }mystruct;
     sizeof(mystruct) = 8;alignof(MyStruct) = 8

     struct alignas(8)  MyStruct
     {
         char member;
         int a;

     }mystruct;

sizeof(mystruct) = 8;alignof(MyStruct) = 8

其中char偏移为0,int 的偏移为4,中间填充3字节数据

  • 将aligns(8)添加到结构体内部变量,设置内部变量的对齐值
struct  MyStruct
     {
        alignas(8) char member;
        alignas(8) int a;
     }mystruct;

    sizeof(mystruct) = 16;alignof(MyStruct) = 8

此时可以看出,结构体对齐值为内部变量最大对齐值8,其中char变量对齐值为8,偏移量0,int对齐值为8,偏移量为8,中间   填充7字节数据,结尾填充4字节数据。可见 alignas()对于修饰的变量有效

  • aligna(16)
 struct alignas(16) MyStruct
     {
        char member;
        int a;
     }mystruct;

     sizeof(mystruct) = 16;alignof(MyStruct) = 16

sizeof(mystruct) = 16;alignof(MyStruct) = 16

其中char偏移量0,int偏移量为4,中间填充3字节数据,结尾填充8字节数据

  • #pragma pack(16)

sizeof(mystruct) = 8;alignof(MyStruct) = 4

可以看出通过预编译指令设置对齐失效了。

aligna(16)和#pragma(1)
    #pragma pack(1)
     struct alignas(8) MyStruct
     {
        char member;
        int a;
     }mystruct;

     sizeof(mystruct) = 8;alignof(MyStruct) = 8

    设置了预编译指令和alignas(),遵循alignas()

4、总结

我们可以通过两种方式来设置对齐,分别是#pragam pack()和alignas()/declspce(align(#)) 
对于#pragma pack()我们可以设置全局对齐方式,结构体和结构体内部变量都将遵循设置的对齐参数;而alignas()对修饰的单个变量是,对齐参数有效,例:alignas()修饰结构体变量,那么结构体变量遵循该对齐参数,内部变量遵循默认的对齐参数,以上对齐方式均是对结构体变量起作用,对于数据区的其他变量不管如何设置对齐方式,还会按照默认的字节来对齐。
#pragma pack()这种对齐参数设置有最大上限,最大设置为其默认对齐参数;alignas()/declspce(align(#))这种对齐方式存在最小下限,最小下限为默认对齐参数。
对于设置两种对齐方式,优先遵循alignas()这种对齐方式。
结构体的默认对齐方式为4byte,最终的结构体对齐方式与结构体内所占最大空间的项的对齐方式一致
结构体所占空间一定为对齐参数的整数倍,也就意味着结构内部可能会存在填充的字节

原文地址:https://www.cnblogs.com/ye-ming/p/9305788.html

时间: 2024-10-22 14:52:14

关于结构体内存对齐方式的总结(#pragma pack()和alignas())的相关文章

【APUE】Chapter17 Advanced IPC & sign extension & 结构体内存对齐

17.1 Introduction 这一章主要讲了UNIX Domain Sockets这样的进程间通讯方式,并列举了具体的几个例子. 17.2 UNIX Domain Sockets 这是一种特殊socket类型,主要用于高效的IPC,特点主要在于高效(因为省去了很多与数据无关的格式的要求). int socketpair(int domain, int type, int protocol, int sockfd[2]) 这个函数用于构建一对unix domain sockets:并且与之前

C++ Primer 学习笔记_15_从C到C++(1)--bool类型、const限定符、const与#define、结构体内存对齐

欢迎大家阅读参考,如有错误或疑问请留言纠正,谢谢 一.bool类型(C语言没有) 1.逻辑型也称布尔型,其取值为true(逻辑真)和false(逻辑假),存储字节数在不同编译系统中可能有所不同,VC++中为1个字节. 2.声明方式:bool result; result=true; 3.可以当作整数用(true一般为1,false为0) 4.把其它类型的值转换为布尔值时,非零值转换为true,零值转换为false 5.示例 #include <iostream> using namespace

结构体内存对齐规则

结构体内存对齐规则 1.第一个成员在与结构体变量偏移量为0的地址处: 2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处: 对齐数=编译器默认的对齐数 与 该成员大小的较小值: vc中默认为对齐数为8 linux中默认对齐数为4 3.结构体总大小为最大对齐数的整数倍: 4.如果嵌套了结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体 的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍 如下面代码 struct A { int a;//4  8   ->4 char c

关于结构体内存对齐

对齐规则如下: 1.  结构体的大小等于结构体内最大成员大小的整数倍2.  结构体内的成员的首地址相对于结构体首地址的偏移量是其类型大小的整数倍,比如说double型成员相对于结构体的首地址的地址偏移量应该是8的倍数.3.  为了满足规则1和2编译器会在结构体成员之后进行字节填充! 注: 1. 内存对齐只是一种用空间来换时间的方法. 2. 实际上不同的编译器他的优化规则并不完全相同.

跟我一起学C++之从C到C++(结构体内存对齐)

1.什么是内存对齐 (1)      编译器为每个"数据单元"按排在某个合适的位置上. (2)      C.C++语言非常灵活,它允许你干涉"内存对齐".也就是可以人为的设置编译器的对齐方式. 2.为什么要对齐 性能原因:在对齐的地址上访问数据快.如果是字节对齐方式存储的话,CPU读取的时候只需要进行一个总线周期即可全部读取完毕,如果不对齐的话,对于32位的系统,CPU读取的时候一般架构都是从偶地址开启的,一次只能读取4个字节,因此需要两个总线周期才能完成,并且还

C/C++结构体内存对齐

ZC: 注意点:ZC: (1).不同的编译器 对结构体("struct"和"typedef struct")的内存对齐 可能不同:ZC: (2).关键词"__packed",网上查到 可以使用这个,但是我没有尝试成功,不知 如何使用... 测试环境:Win7x64,cn_visual_studio_2010_ultimate_x86_dvd_532347.iso,qt-opensource-windows-x86-msvc2010_opengl-

结构体内存对齐——

什么是内存对齐 编译器为每个“数据单元”安排在某个合适的位置上: C,C++语言非常灵活,它允许你干涉“内存对齐” 为什么要对齐 性能原因:在对齐的地址上访问数据快 如何对齐 第一个数据成员放在offset为0的位置 其它成员对齐至min(sizeof(member),#pragma pack()所指定的值)的整数倍. 整个结构体也要对齐,结构体的总大小对齐至各个成员中最大对齐数的整数倍. #include<iostream> #include <stdio.h> using na

结构体内存对齐

1.原因:为什么需要内存对齐.1.平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的:某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常. 2.性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐.原因在于,为了访问未对齐的内存,处理器需要作两次内存访问:而对齐的内存访问仅需要一次访问. 2.内存对齐的规则和范例讲述内存对齐之前先看下各种类型的大小,和编译器以及字长有关具体在此不多叙述.成员的内存分配规律:从结构体的首地址开始向后依次为每个成员寻找第一个

C++结构体内存对齐跨平台测试

测试1,不规则对齐数据. Code: #include <stdio.h> #pragma pack(push) #pragma pack(8) struct Test8 { char a; long long b; char d; }; #pragma pack(pop) #pragma pack(push) #pragma pack(4) struct Test4 { char a; long long b; char d; }; #pragma pack(pop) #pragma pac