最近闲来无事,翻阅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) = ,alignof(MyStruct) =

通过计算可以看出,结构体的对齐值为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()   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())  struct   MyStruct
{
char member;
int a;
}mystruct;
sizeof(mystruct) = ;alignof(MyStruct) = struct alignas() MyStruct
{
char member;
int a; }mystruct;

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

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

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

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

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

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

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

  • #pragma pack(16)

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

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

aligna()和#pragma(1)
#pragma pack(1)
struct alignas() MyStruct
{
char member;
int a;
}mystruct; sizeof(mystruct) = ;alignof(MyStruct) = 设置了预编译指令和alignas(),遵循alignas()

4、总结

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

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

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

    17.1 Introduction 这一章主要讲了UNIX Domain Sockets这样的进程间通讯方式,并列举了具体的几个例子. 17.2 UNIX Domain Sockets 这是一种特殊s ...

  2. C语言中结构体内存存储方式

    C语言中结构体内存存储方式 结构体的默认存储方式采用以最大字节元素字节数对其方式进行对齐,例如一个结构体中定义有char.int类型元素,则结构体存储空间按照int类型占用字节,如果还有double类 ...

  3. C++ struct结构体内存对齐

    •小试牛刀 我们自定义两个结构体 A 和 B: struct A { char c1; char c2; int i; double d; }; struct B { char c1; int i; ...

  4. [C/C++] 结构体内存对齐用法

    一.为什么要内存对齐 经过内存对齐之后,CPU的内存访问速度大大提升; 内存空间按照byte划分,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内 ...

  5. [C/C++] 结构体内存对齐:alignas alignof pack

    简述: alignas(x):指定结构体内某个成员的对齐字节数,指定的对齐字节数不能小于它原本的字节数,且为2^n; #pragma pack(x):指定结构体的对齐方式,只能缩小结构体的对齐数,且为 ...

  6. C语言-结构体内存对齐

    C语言结构体对齐也是老生常谈的话题了.基本上是面试题的必考题.内容虽然很基础,但一不小心就会弄错.写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的 ...

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

    内存对齐是指的是编译器在编译的时候总是会将结构体的元素的地址放在一些合适的位置使得CPU访问这些数据的效率变得更高.首先来看下面这个例子: struct A{ char a; char b; int ...

  8. c 结构体内存对齐详解

    0x00简介 首先要知道结构体的对齐规制 1.第一个成员在结构体变量偏移量为0的地址处 2.其他成员变量对齐到某个数字的整数倍的地址处 对齐数=编辑器默认的一个对齐数与该成员大小的较小值 vs中默认的 ...

  9. go语言结构体内存对齐

    cpu要想从内存读取数据,需要通过地址总线,把地址传输给内存,内存准备好数据,输出到数据总线,交给cpu,如果地址总线只有8根,那这个地址就只有8位可以表示[0,255]256个地址,因为表示不了更多 ...

随机推荐

  1. java匿名内部类的使用注意事项

    1.首先匿名内部类要继承自抽象基类或者实现基类接口 like this abstract class Seed{ int cnt; public Seed(int x){ cnt=x; } abstr ...

  2. 设置Webdriver启动chrome为默认用户的配置信息

    Webdriver 启动Chrome浏览器时,默认是打开一个新用户,而非默认用户.即新用户没有我们安装扩展程序.但在实际应用中,我们会须要 默认用户安装的一些扩展程序,比方对于某些js或者css样式. ...

  3. jsp页面中自定义标签的小演示

    在实习期遇到公司的pg自定义标签了,同事要我自己自学一下 自定义标签是用户定义的JSP语言元素.当JSP页面包含一个自定义标签时将被转化为servlet.JSP标签扩展可以让你创建新的标签并且可以直接 ...

  4. 利用Matlab自带的深度学习工具进行车辆区域检测与车型识别【Github更新!!!】(三)

    前言 对前面的东西更新了一下.地方包括: 1.GUI的更新,更友好的用户界面 2.支持用手直接画车辆区域,并且识别出来 3.将proposal.detect.fine-grained classifi ...

  5. django 笔记7 多对多

    多对多 方法一 :双外键关联 自定义关系表 自定义 class Host(models.Model): nid = models.AutoField(primary_key=True) hostnam ...

  6. [洛谷P1169] [ZJOI2007] 棋盘制作 解题报告(悬线法+最大正方形)

    题目描述 国际象棋是世界上最古老的博弈游戏之一,和中国的围棋.象棋以及日本的将棋同享盛名.据说国际象棋起源于易经的思想,棋盘是一个 8×8 大小的黑白相间的方阵,对应八八六十四卦,黑白对应阴阳. 而我 ...

  7. kafka集群安装配置

    1.下载安装包 2.解压安装包 3.进入到kafka的config目录修改server.properties文件 进入后显示如下: 修改log.dirs,基本上大部分都是默认配置 kafka依赖zoo ...

  8. .net Web获取域用户账号

    HttpContext.Current.Request.LogonUserIdentity.Name //可以获取出域账号 HttpContext.Current.Request.LogonUserI ...

  9. Debian9.5 系统配置持久化iptables规则

    RedHat和SUSE系列下有比较好用的iptables管理工具,可以像控制服务进程一样来对防火墙进行管理及控制,Debian系发行版默认不开启iptables,当然也没有与之相关的能直接管理的工具了 ...

  10. python2中打印列表与字典内的中文字符

    在开发过程中,我们经常需要打印一些变量的值,便于调试.这个时候就会发现如果在列表与字典这些容器中,如果包含中文字符,不管是str类型,还是unicode类型,都打印不出来.如下: >>&g ...