c/c++中内存对齐完全理解
一,什么是内存对齐?内存对齐用来做什么?
所谓内存对齐,是为了让内存存取更有效率而采用的一种编译阶段优化内存存取的手段。
比如对于int x;(这里假设sizeof(int)==4),因为cpu对内存的读取操作是对齐的,如果x的地址不是4的倍数,那么读取这个x,需要读取两次共8个字节,然后还要将其拼接成一个int,这比存取对齐过的x要麻烦很多。
二,怎么算内存对齐大小(理论)?
对于简单类型,如int,char,float等,其对齐大小为其本身大小,即align(int) == sizeof(int),align(char)==sizeof(char),等等。
对于复合类型,如struct,class,其本身并无所谓对齐,因为CPU没有直接存取一个struct的指令。对于struct而言,它的对齐指的是它里面的所有成员变量都是对齐的,class同理。
下面就讲讲struct对齐是怎么回事。
首先要明白三个点:
1,内存对齐是指首地址对齐,而不是说每个变量大小对齐;
2,结构体内存对齐要求结构体内每一个成员变量都是内存对齐的;
3,结构体对齐除了第2点之外还要求结构体数组也必须是对齐的,也就是说每个相邻的结构体内部都是对齐的。
OK,先知道上面这3点之后,开始接触怎么算对齐大小。
程序员可自己指定某些数据的对齐大小,通过使用下面的预处理指令,指定对齐大小为x。(这里需要注意:只能指定2的n次方作为对齐大小,对于指定对齐大小为6,9,10这样的编译器可能会不予理会)
#pragma pack(x)
//...
#pragma pack()
那到现在,可能大家有个疑问了,那对于int(这里假设sizeof(int)==4),手动指定对齐大小为8,那align(int)是等于sizeof(int)还是等于8呢 ?
这里大家可以记住,align(x) = min ( sizeof(x) , packalign) , 即sizeof(x)和指定对齐大小哪个小,对齐大小就为哪个。
因此,上面的疑问答案是align(int)=sizeof(int)=4 。
三,怎么算内存对齐大小(示范)?
#include <cassert> int main(int argc, char* argv[])
{
//此处指定对齐大小为1
//对于a,实际对齐大小为min(sizeof(int),1)=min(4,1)=1
//对于b,实际对齐大小为min(sizeof(char),1)=min(1,1)=1
//编译器会确保TEST_A首地址即a的地首址是1字节对齐的,此时a对齐
//对于b,由于b要求首地址1字节对齐,这显然对于任何地址都合适,所以a,b都是对齐的
//对于TEST_A数组,第一个TEST_A是对齐的(假设其地址为0),则第二个TEST_A的首地址为(0+5=5),对于第二个TEST_A的两个变量a,b均对齐
//OK,对齐合理。因此整个结构体的大小为5
#pragma pack(1)
struct TEST_A
{
int a;
char b;
};
#pragma pack()
assert(sizeof(TEST_A) == ); //此处指定对齐大小为2
//对于a,实际对齐大小为min(sizeof(int),2)=min(4,2)=2
//对于b,实际对齐大小为min(sizeof(char),2)=min(1,2)=1
//编译器会确保TEST_A首地址即a的地首址是2字节对齐的,此时a对齐
//对于b,由于b要求首地址1字节对齐,这显然对于任何地址都合适,所以a,b都是对齐的
//对于TEST_B数组,第一个TEST_B是对齐的(假设其地址为0),则第二个TEST_B的首地址为(0+5=5),对于第二个TEST_B的变量a,显然地址5是不对齐于2字节的
//因此,需要在TEST_B的变量b后面填充1字节,此时连续相连的TEST_B数组才会对齐
//OK,对齐合理。因此整个结构体的大小为5+1=6
#pragma pack(2)
struct TEST_B
{
int a;
char b;
};
#pragma pack()
assert(sizeof(TEST_B) == ); //此处指定对齐大小为4
//对于a,实际对齐大小为min(sizeof(int),2)=min(4,4)=4
//对于b,实际对齐大小为min(sizeof(char),2)=min(1,4)=1
//编译器会确保TEST_A首地址即a的地首址是4字节对齐的,此时a对齐
//对于b,由于b要求首地址1字节对齐,这显然对于任何地址都合适,所以a,b都是对齐的
//对于TEST_C数组,第一个TEST_C是对齐的(假设其地址为0),则第二个TEST_C的首地址为(0+5=5),对于第二个TEST_C的变量a,显然地址5是不对齐于4字节的
//因此,需要在TEST_C的变量b后面填充3字节,此时连续相连的TEST_C数组才会对齐
//OK,对齐合理。因此整个结构体的大小为5+3=8
#pragma pack(4)
struct TEST_C
{
int a;
char b;
};
#pragma pack()
assert(sizeof(TEST_C) == ); //此处指定对齐大小为8
//对于a,实际对齐大小为min(sizeof(int),8)=min(4,8)=4
//对于b,实际对齐大小为min(sizeof(char),8)=min(1,8)=1
//编译器会确保TEST_A首地址即a的地首址是4字节对齐的,此时a对齐
//对于b,由于b要求首地址1字节对齐,这显然对于任何地址都合适,所以a,b都是对齐的
//对于TEST_D数组,第一个TEST_D是对齐的(假设其地址为0),则第二个TEST_D的首地址为(0+5=5),对于第二个TEST_D的变量a,显然地址5是不对齐于4字节的
//因此,需要在TEST_D的变量b后面填充3字节,此时连续相连的TEST_D数组才会对齐
//OK,对齐合理。因此整个结构体的大小为5+3=8
#pragma pack(8)
struct TEST_D
{
int a;
char b;
};
#pragma pack()
assert(sizeof(TEST_D) == ); //此处指定对齐大小为8
//对于a,实际对齐大小为min(sizeof(int),8)=min(4,8)=4
//对于b,实际对齐大小为min(sizeof(char),8)=min(1,8)=1
//对于c,这是一个数组,数组的对齐大小与其单元一致,因而align(c)=align(double)=min(sizeof(double),8)=min(8,8)=8
//对于d,实际对齐大小为min(sizeof(char),8)=min(1,8)=1
//编译器会确保TEST_A首地址即a的地首址是4字节对齐的,此时a对齐
//对于b,由于b要求首地址1字节对齐,这显然对于任何地址都合适,所以a,b都是对齐的
//对于c,由于c要求首地址8字节对齐,因此前面的a+b=5,还要在c后面补上3个字节才能对齐
//对于d,显而易见,任何地址均对齐,此时结构体大小为4+1+3+10*8+1=89
//对于TEST_E数组,第一个TEST_E是对齐的(假设其地址为0),则第二个TEST_E的首地址为(0+89=89),对于第二个TEST_E的变量a,显然地址89是不对齐于4字节的
//因此,需要在TEST_E的变量d后面填充7字节,此时连续相连的TEST_E数组才会对齐
//(注意:此处不仅要确保下一个TEST_E的a,b变量对齐,还要确保c也对齐,所以这里不是填充3字节,而是填充7字节)
//OK,对齐合理。因此整个结构体的大小为(4)+(1+3)+(10*8)+(1+7)=96
#pragma pack(8)
struct TEST_E
{
int a;
char b;
double c[];
char d;
};
#pragma pack()
assert(sizeof(TEST_E) == ); return ;
}
四,内存对齐相关
使用msvc未公开编译选项可以查看c++类的内存布局。使用方法:启动vs命令行,输入cl 【source.cpp】 /d1reportSingleClassLayout【CBaseClass1】以查看单个class的内存布局,输入cl 【source.cpp】 /d1reportAllClassLayout以查看所有类的内存布局。注意:/d1reportSingleClassLayout【CBaseClass1】没有空格 !!
大家可以用这个来对照我上面讲的例子来看编译器是怎么安排对齐的。
这个东东是神器,类似于宏展开时的选项(输出与处理过之后的源文件),一切内部布局方面的真相全都展现在你眼前,包括坑脑细胞的虚函数、虚函数表、虚基类表、虚继承等一系列坑爹。
五,参考资料:
1,http://blog.csdn.net/arethe/article/details/2548867
2,http://msdn.microsoft.com/en-us/library/83ythb65.aspx
3,http://msdn.microsoft.com/en-us/library/9dbwhz68.aspx
4,http://msdn.microsoft.com/en-us/library/71kf49f1.aspx
5,http://blog.sina.com.cn/s/blog_67c294ca01012qbu.html
c/c++中内存对齐完全理解的更多相关文章
- C语言中内存对齐规则讨论(struct)
C语言中内存对齐规则讨论(struct) 对齐: 现代计算机中内存空间都是按着byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地 ...
- C/C++中内存对齐问题的一些理解(转)
内存对齐指令 一般来说,内存对齐过程对coding者来说是透明的,是由编译器控制完成的 如对内存对齐有明确要求,可用#pragma pack(n)指定,以n和结构体中最长数据成员长度中较小者为有效值 ...
- C语言中内存对齐
今天一考研同学问我一个问题,一个结构体有一个int类型成员和一个char类型成员,问我这个结构体类型占多少个字节,我直接编个程序给他看结果.这个结构体占八个字节,咦,当时我蛮纳闷的,一个int类型四个 ...
- C语言中内存对齐方式
一.什么是对齐,以及为什么要对齐: 1. 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问, ...
- VC++中内存对齐
我们经常看到求 sizeof(A) 的值的问题,其中A是一个结构体,类,或者联合体. 为了优化CPU访问和优化内存,减少内存碎片,编译器对内存对齐制定了一些规则.但是,不同的编译器可能有不同的实现,本 ...
- c语言中内存对齐问题
在最近的项目中,我们涉及到了“内存对齐”技术.对于大部分程序员来说,“内存对齐”对他们来说都应该是“透明的”.“内存对齐”应该是编译器的“管辖范围”.编译器为程序中的每个“数据单元”安排在适当的位置上 ...
- 什么是内存对齐,go中内存对齐分析
内存对齐 什么是内存对齐 为什么需要内存对齐 减少次数 保障原子性 对齐系数 对齐规则 总结 参考 内存对齐 什么是内存对齐 弄明白什么是内存对齐的时候,先来看一个demo type s struct ...
- C语言中内存对齐与结构体
结构体 结构体是一种新的数据类型,对C语言的数据类型进行了极大的扩充. struct STU{ int age; char name[15]; }; struct STU a; //结构体实例 str ...
- C++内存对齐的理解
程序编译器对结构的存储的特殊处理确实提高CPU存储变量的速度,但是有时候也带来了一些麻烦,我们也屏蔽掉变量默认的对齐方式,自己可以设定变量的对齐方式. 编译器中提供了#pragma pack(n)来设 ...
随机推荐
- php使用curl请求数据(采集数据)
<?php $url = "http://www.baidu.com/s?wd=刘俊涛的博客"; $header = array( 'User-Agent: Mozilla/ ...
- 最长公共子序列(LCS)问题 Longest Common Subsequence 与最长公告字串 longest common substr
问题描述:字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列.令给定的字符序列X=“x0,x1,…,xm-1”,序列Y=“y0,y1,…,yk ...
- 【Linux】VMware上安装Linux操作系统
Vmware上安装Linux系统 1. 文件菜单选择新建虚拟机 2. 选择经典类型安装,下一步. 3. 选择稍后安装操作系统,下一步. 4. 选择Linux系统,版本选择CentOS 64位. 给虚拟 ...
- SCRUM敏捷开发规则一栏
敏捷.敏捷开发这类词近期非常火!敏捷开发,就是指可以在需求迅速变化的情况下高速开发软件.我们接触最多的和敏捷相关的名词是:极限编程(XP).结对编程.測试驱动开发(TDD)等. 敏捷建模(Agile ...
- 关于ie,火狐,谷歌浏览器滚动条的隐藏以及自定义样式
最近做了一个项目,要求各个浏览器统一滚动条的样式,不显示滚动条,但是不影响鼠标的滑动事件. 查了很多资料,ie和谷歌都是可以自定义滚动条样式的,但是ie只能改变颜色,并不能修改宽度,圆角之类的.谷歌就 ...
- JavaScript中逻辑运算符
一.JavaScript“逻辑”运算符 很多学习 JavaScript的人,容易被 JavaScript 的逻辑运算符的运算规则搞晕.为什么呢?因为JavaScript的逻辑运算符和其他语言(比如:j ...
- java基础讲解12-----Swing
package com.swing; import java.awt.*; import javax.swing.*; public class Swing01 extends JFrame{ /* ...
- python--文件处理范例
import os,os.path,string dir="D:\\curl\\data" if (os.path.exists(dir)==False): print " ...
- 程序包javax.servlet.annotation不存在
1.错误描写叙述 [INFO] Scanning for projects... [INFO] [INFO] --------------------------------------------- ...
- CentOS安装自动补全安装包
CentOS7标准版有这个功能,但是CentOS6却没有,其实很简单: 1.安装bash-completion yum install bash-completion 2.保存一下最新的缓存 yum ...