关于内存对齐

一:

1.什么是内存对齐

假设我们同时声明两个变量:

char a;

short b;

用&(取地址符号)观察变量a,

b的地址的话,我们会发现(以16位CPU为例):

如果a的地址是0x0000,那么b的地址将会是0x0002或者是0x0004。

那么就出现这样一个问题:0x0001这个地址没有被使用,那它干什么去了?答案就是它确实没被使用。因为CPU每次都是从以2字节(16位CPU)或是4字节(32位CPU)的整数倍的内存地址中读进数据的。如果变量b的地址是0x0001的话,那么CPU就需要先从0x0000中读取一个short,取它的高8位放入b的低8位,然后再从0x0002中读取下一个short,取它的低8位放入b的高8位中,这样的话,为了获得b的值,CPU需要进行了两次读操作。

但是如果b的地址为0x0002,

那么CPU只需一次读操作就可以获得b的值了。所以编译器为了优化代码,往往会根据变量的大小,将其指定到合适的位置,即称为内存对齐(对变量b做内存对齐,a、b之间的内存被浪费,a并未多占内存)。

2.结构体内存对齐规则

结构体所占用的内存与其成员在结构体中的声明顺序有关,其成员的内存对齐规则如下:

(1)每个成员分别按自己的对齐字节数和PPB(指定的对齐字节数,32位机默认为4)两个字节数最小的那个对齐,这样可以最小化长度。

(2)复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在成员是复杂类型时,可以最小化长度。

(3)结构体对齐后的长度必须是成员中最大的对齐参数(PPB)的整数倍,这样在处理数组时可以保证每一项都边界对齐。

(4)计算结构体的内存大小时,应该列出每个成员的偏移地址,则其长度=最后一个成员的偏移地址+最后一个成员数的长度+最后一个成员的调整参数(考虑PPB)。

下面举例说明上述规则:

#include
#pragma pack(2) //指定PPB为2
struct T{
char a; //偏移地址0
int b; //偏移地址2
char c; //偏移地址6
}; #pragma pack() //恢复原来默认PPB,32位下为4 int main(int argc,char * argv[])
{
printf("sizeof(struct T));
return 0;
}

最后输出的结果为:8。语句#pragma pack(2)的作用是指定结构体按2字节对齐,即PPB=2。分析如下:

变量a默认为1字节,PB=2,所以a按1字节对齐,a的偏移地址为0。

变量b默认为4字节(在32位机器中int为4字节),PB=2,所以b按2字节对齐,b的偏移地址为2。

变量c默认为1字节,PB=2,所以c按1字节对齐,偏移地址为6。

此时结构体的计算出的字节数为7个字节。最后按规则3,结构体对齐后的字节数为8。sizeof(T)=6+1+1=8

3.范例

(1)#pragma pack(2) //指定PPB为2

struct T{
char a; //偏移地址0
char b; //偏移地址1
int c; //偏移地址2
};

则sizeof(T)=最后一个成员的偏移地址+最后一个成员数的长度=2+4=6。

(2)

struct T1{
char a; //偏移地址0
char b; //偏移地址1
int c; //偏移地址4
}; struct T2{
char a; //偏移地址0
int b; //偏移地址4
char c; //偏移地址8
};

PPB=4,则sizeof(T1)=4+4=8;sizeof(T2)=8+1=9,9不能整除4,故调整数为3,即sizeof(T2)=8+1+3=12

4.注意的问题

(1)字节对齐取决于编译器;

(2)一定要注意PPB大小,PPB大小由pragam pack(n)指定;

(3)结构体占用的字节数要能被PPB整除。

二:

(1)sizeof也可以对一个函数调用求值,其结果是函数返回类型的大小,函数并不会被调用。

(2)终于搞懂struct结构体内存分配问题了,结构体中各个成员字节对齐遵循以下几个原则: 直接用下面几个原则即可判断结构体的大小

1.结构体每个成员相对于结构体首地址的偏移量(offset)都是(这个)成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internaladding);

例如有以下一个结构体

structex {
int i;
char t;
int n;
}

第1个成员偏移量为0,是int型成员大小4(假设这太机器的整型长度占4个字节)的整数倍。

第2个成员t为char型,他的大小为1,首先假设在成员i和t之间没有填充字节,由于i是整型,占4个字节那么在没有填充之前,第2个成员t相对于结构体的偏移量为4,他是t成员大小1的4倍,符合此条件,所以系统在给结构体第2个成员分配内存时,不会在i和t之间填充字节以到达对齐的目的。

当分配结构体第3个成员n时,首先发现是一个整型数据,大小为4,没有填充之前,n相对于结构体首地址偏移量为:前面2个成员+填充字节=5,所以当系统发现5不是4(成员大小)的整数倍时,会在成员t之后(或者说n之前)填充3个字节,以使n的偏移量到达8而成为4的整数倍。这样这个结构体占用内存情况暂时为4+1+3+4。

2.结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailingpadding)。

上面的结构体内存分配以后还要看是否满足此条件,假设在最末一个成员之后不需填充字节数,那么这个结构体的大小为12。而ex结构体中最宽基本类型成员为int,大小为4,12为4的整数倍,所以无须再在最末一个成员之后加上填充字节了。所以sizeof(ex)=12;

如果一个结构体如下所示

struc tex1{
int i;
char t;
int n;
char add;
}

那么sizeof(ex1)=16;原因就是在最后一个成员之后填充了3个字节。

3.还有一个额外的条件:结构体变量的首地址能够被其最宽基本类型成员的大小所整除;

4.对于结构体成员属性中包含结构体变量的复合型结构体再确定最宽基本类型成员时,应当包括复合类型成员的子成员。但在确定复合类型成员的偏移位置时则是将复合类型作为整体看待。

5总结出一个公式:结构体的大小等于最后一个成员的偏移量加上其大小再加上末尾的填充字节数目,即:

sizeof( struct ) = offsetof( last item ) + sizeof( last item ) +sizeof( trailing padding )

【C/C++】【VS开发】结构体存储空间数据对齐说明的更多相关文章

  1. [c/c++] programming之路(28)、结构体存储和内存对齐+枚举类型+typedef+深拷贝和浅拷贝

    一.结构体存储 #include<stdio.h> #include<stdlib.h> struct info{ char c; //1 2 4 8 double num; ...

  2. c语言学习笔记之结构体存储

    今天讲讲结构体存储问题 首先,结构体简单说是对不同类型的封装,一开始我们可能会想结构体在内存中的存储的大小是直接元素的和 例如 我们可能会觉得是 结构体大小=int(4个字节)+ short(2个字节 ...

  3. C语言结构体的内存对齐问题

    在C语言开发当中会遇到这样的情况: #include <stdio.h> struct test { int a; char b; }; int main(int argc, const ...

  4. C语言结构体的字节对齐原则

    为什么要对齐? 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特 定的内存地址访问,这就需要各种类型数据 ...

  5. C语言 结构体的内存对齐问题与位域

    http://blog.csdn.net/xing_hao/article/details/6678048 一.内存对齐 许多计算机系统对基本类型数据在内存中存放的位置有限制,它们会要求这些数据的首地 ...

  6. sizeof(结构体)和内存对齐以及位域

    Win32平台下的微软C编译器的对齐策略: 1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除: 备注:编译器在给结构体开辟空间时,首先找到结构体中最宽的基本数据类型,然后寻找内存地址能被该 ...

  7. [转]C++结构体|类 内存对齐详解

    内存地址对齐,是一种在计算机内存中排列数据(表现为变量的地址).访问数据(表现为CPU读取数据)的一种方式,包含了两种相互独立又相互关联的部分:基本数据对齐和结构体数据对齐 . 为什么需要内存对齐?对 ...

  8. C语言结构体变量字节对齐问题总结

    结构体字节对齐 在用sizeof运算符求算某结构体所占空间时,并不是简单地将结构体中所有元素各自占的空间相加,这里涉及到内存字节对齐的问题.从理论上讲,对于任何 变量的访问都可以从任何地址开始访问,但 ...

  9. 结构体的数据对齐 #pragma浅谈

    之前若是有人拿个结构体或者联合体问我这个结构占用了多少字节的内存,我一定觉得这个人有点low, 直到某某公司的一个实习招聘模拟题的出现,让我不得不重新审视这个问题, 该问题大致如下: typedef ...

随机推荐

  1. PHP流程控制之if语句多种嵌套

    王思总同学我们在最开始的故事中讲到了他有两个秘书:一个生活秘书.一个工作秘书. 王思总同学在出行和项目中也是极度有计划性.他给自己的生活秘书和工作秘书分别指派了出差的行程:大理石平台支架 生活上: 先 ...

  2. SQLServer常见查询问题

     http://bbs.csdn.net/topics/340078327 1.生成若干行记录 --自然数表1-1M CREATE TABLE Nums(n int NOT NULL PRIMAR ...

  3. AS400遇到的一些问题和解决办法

    1.没有权限进入distribution directory wrklnk 'QDLS\'    >User not enrolled in system distribution direct ...

  4. [HNOI2002]营业额统计 II

    https://www.luogu.org/problemnew/show/2234 将权值离散化,以权值为下标建立权值线段树 #include <bits/stdc++.h> using ...

  5. 在其他博客里看到的比较好的map用法,进行储存啦啦~ x

    1.map简介 map是一类关联式容器.它的特点是增加和删除节点对迭代器的影响很小,除了那个操作节点,对其他的节点都没有什么影响.对于迭代器来说,可以修改实值,而不能修改key. 2.map的功能 自 ...

  6. 【csp模拟赛九】--dfs

    思路: 这道题可以宽搜,深搜,最短路 代码: #include<cstdio> #include<cstring> #include<iostream> #incl ...

  7. TensorFlow(十):卷积神经网络实现手写数字识别以及可视化

    上代码: import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data mnist = inpu ...

  8. Android工程的合并

    http://www.xuanyusong.com/archives/3395 1.游戏包名( 类似 com.xx.xxx ) Android应用程序只能有一个包名,如果两个游戏包名一样,那么后者安装 ...

  9. JavaScript--自定义事件Event

    在开发过程中,js原生事件不足以满意开发需求,需要开发者自定义事件. 一.Event Event()构造函数创建一个新的Event. event = new Event(typeArg,eventIn ...

  10. Tkinter 之Canvas画布

    一.参数说明 参数 作用 background(bg) 指定 Canvas 的背景颜色 borderwidth(bd) 指定 Canvas 的边框宽度 closeenough 指定一个距离,当鼠标与画 ...