数据对齐,是指数据所在的内存地址必须是该数据长度的整数倍。DWORD数据的内存起始地址能被4除尽,WORD数据的内存起始地址能被2除尽。X86 CPU能直接访问对齐的数据,当它试图访问一个未对齐的数据时,会在内部进行一系列的调整。这些调整对于程序员来说是透明的,但是会降低运行速度,所以编译器在编译程序时会尽量保证数据对齐。

不同的编译器内存对齐的方式不同。

一个小例子:在32位的机器上,数据是以4字节为对齐单位,这两个类的输出结果为什么不同?(VS2008)

#include <iostream>
using namespace std; class B
{
private:
bool m_bTemp;
int m_nTemp;
bool m_bTemp2;
}; class C {
private:
int m_nTemp;
bool m_bTemp;
bool m_bTemp2;
}; int _tmain(int argc, _TCHAR* argv[]) {
cout<<sizeof(B)<<endl;
cout<<sizeof(C)<<endl;
system("pause");
return 0; }

答案是:3*4=12,2*4=8

分析:在访问内存时,如果地址按4字节对齐,则访问的效率会高很多。

考虑到性能方面,编译器会对结构进行对齐处理,考虑下面的结构:

struct aStruct

{

char cValue;

int iValue;

};

直观地讲,这个结构的尺寸是sizeof(char)+sizeof(int)=6,但是在实际编译下, 这个结构尺寸默认是8,因为第二个域iValue会被对齐到第4个字节。

注意:字节对齐是编译时决定的,一旦决定则不会再改变,因此即使有对齐的因素存在,也不会出现一个结构在运行时尺寸发生变化的情况。

在本题中:第一种类的数据对齐是下面的情况:

bool ---- ---- ----

------- int ---------

bool ----- ---- ----

第二种类的数据对齐是下面的情况:

------- int ----------

bool bool ---------

所以类的大小分别3*4和2*4

一般在VC++中加上#pragma pack(n)设置内存对齐。

我们可以利用#pragma pack()来改变编译器的默认对齐方式。

#pragma pack(n)   //编译器将按照n字节对齐

#pragma pack()     //编译器将取消自定义字节对齐方式

在#pragma pack(n)和#pragma pack()之间的代码按n字节对齐。

但是成员对齐有一个重要的条件,即每个成员按照自己的对齐方式对齐;也就是说虽然指定了按n字节对齐,但并不是所有的成员都以n字节对齐。

对齐的规则是:每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数(这里是n字节)中较小的一个对齐,即min(n,sizeof(item)),并且结构的长度必须为所用过的所有对齐参数的整数倍,不够就补空字节。

#pragma pack(8)
struct TestStruct4
{
char a;
long b;
};
struct TestStruct5
{
char c;
TestStruct4 d;
long long e;
};
int _tmain(int argc, _TCHAR* argv[])
{
cout<<sizeof(TestStruct4)<<endl;
cout<<sizeof(TestStruct5)<<endl;
system("pause");
return 0;
}

运行结果为:8,24

分析:

TestStruct4 中,成员a是1字节,默认按照1字节对齐,指定对齐参数是8,这两个值中取1,a按1字节对齐;

成员b是4字节,默认是按4字节对齐,这时就按4字节对齐,所以sizeof(TestStruct4)应该是8.

TestStruct5 中,c和TestStruct4中的a一样,按1字节对齐;而d是个结构,它是8字节,对于结构来说,它的默认对齐方式就是其所有成员使用的对齐参数中最大的一个,TestStruct4就是4,所以成员d就按照4字节对齐。成员e是8字节,它是默认的8字节对齐,和指定的一样,所以它对齐到8自己的边界上,这时,已经使用了12字节了,所以又添加了4字节的空间,从第16字节开始放置成员e。这时长度为24,已经可以被8整除(成员e按8字节对齐)。这样一共使用了24字节。

内存布局图如下:

TestStruct4 的内存布局:

a      b

1***   1111

TestStruct5 的内存布局:

c     d.a      d.b                e

1***   1***     1111    ****    11111111

注意3点:

(1)每个成员按照自己的方式对齐,并能最小化长度

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

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

补充:对于数组,比如说char a[3],它的对齐方式和分别写3个char是一样的,也就是说它还是按1字节对齐;如果写成typedef char Arrary3[3],Arrary3这种类型的对齐方式还是按1字节对齐,而不是按它的长度。

但是不论类型是什么,对齐的边界一定是1、2、4、8、16、32、64......中的一个。

下面来说下大端模式和小端模式

大端模式:认为第一个字节是最高位字节,也就说按照从低地址到高地址的顺序存放数据的高位字节到低位字节。

小端模式:认为第一个字节是最低位字节,也就是说按照从低地址到高地址的顺序存放数据的低位字节到高位字节。

假设从内存地址0x0000开始有以下数据:

内存地址:0x0000    0x0001    0x0002     0x0003

对应数据:0x12       0x34      0x56       0x78

如果我们去读取一个地址为0x0000的4字节变量

若字节序位为小端模式,读出为:0x78563412

若字节序位为大端模式,读出为:0x12345678

一般来说:X86系列的CPU都是小端字节序,powerPC通常是大端字节序。

int _tmain(int argc, _TCHAR* argv[])  

{
char *sz = "0123456789";
int *p = (int *)sz;
cout<<hex<<*++p<<endl;
}

运行结果为:37363534

分析:这里是小端字节序

地址从0x0000开始,那么sz在内存中的存储为:

内存地址:     0x00  0x01  0x02   0x03   0x04  0x05  0x06   0x07   0x08  0x09

对应的值:      0      1     2      3      4     5     6      7     8      9

对应的值:      48     49    50     51     52    53    54     55    56     57

对应的16进制:0x30   0x31  0x32   0x33   0x34  0x35  0x36   0x37  0x38   0x39

sz                         ++p

所以读取为:0x37363534

参考资料:《程序员面试宝典》第三版 电子工业出版社  P50-P45

C语言深度解剖》 北京航空航天大学出版社  P39-P40,P71-P75

【C/C++开发】内存对齐(内存中的数据对齐)、大端模式及小端模式的更多相关文章

  1. Chrome扩展开发之三——Chrome扩展中的数据本地存储和下载

    目录: 0.Chrome扩展开发(Gmail附件管理助手)系列之〇——概述 1.Chrome扩展开发之一——Chrome扩展的文件结构 2.Chrome扩展开发之二——Chrome扩展中脚本的运行机制 ...

  2. C# 中大端序与小端序

    C# 中大端序与小端序 static void Main(string[] args) { uint value = 0x12345678; Console.WriteLine("原始字节序 ...

  3. gpu显存(全局内存)在使用时数据对齐的问题

    全局存储器,即普通的显存,整个网格中的随意线程都能读写全局存储器的任何位置. 存取延时为400-600 clock cycles  很easy成为性能瓶颈. 訪问显存时,读取和存储必须对齐,宽度为4B ...

  4. 谈谈C++中的数据对齐

    对于C/C++程序员来说,掌握数据对齐是很有必要的,因为只有了解了这个概念,才能知道编译器在什么时候会偷偷的塞入一些字节(padding)到我们的结构体(struct/class),也唯有这样我们才能 ...

  5. Ultra Edit中的数据对齐

    有时会用到Ultra Edit的数据对齐功能.比如,要求64个符号一组,从低位开始对齐.这时,如果数据长度不是一行长度的整数, 就会产生高位对齐.低位不足的问题.为了调整,往往需要逐行调整,很不方便. ...

  6. 关于 linux中TCP数据包(SKB)序列号的小笔记

    关于  SKB序列号的小笔记 为了修改TCP协议,现在遇到了要改动tcp分组的序列号,但是只是在tcp_sendmsg函数中找到了SKB的end_seq  一直没有找到seq 不清楚在那里初始化了,就 ...

  7. C语言深入学习系列 - 字节对齐&内存管理

    用C语言写程序时需要知道是大端模式还是小端模式. 所谓的大端模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中:所谓的小端模式,是指数据的低位保存在内存的低地址中,而数据的高 ...

  8. C语言内存:大端小端及判别方式

    大端和小端是指数据在内存中的存储模式,它由 CPU 决定:1) 大端模式(Big-endian)是指将数据的低位(比如 1234 中的 34 就是低位)放在内存的高地址上,而数据的高位(比如 1234 ...

  9. 计算机中的大小端模式及C语言中如何鉴别他们

    我的博客:www.while0.com 参考http://blog.csdn.net/ce123_zhouwei/article/details/6971544 写的很详细. 大小端主要是对数字类型来 ...

随机推荐

  1. Greenplum table 之 appendonly表

    一.压缩表 1.appendonly压缩表的数据视图为pg_appendonly 2.appendonly在Greenplum后也可更新与删除

  2. 对深层嵌套对象进行取值&赋值

    需求如下: let obj = { foo: { bar: { name: 'biz' } } }; // 输出 'biz' this.getObj(obj, 'foo.bar.name'); obj ...

  3. P3066 [USACO12DEC] 逃跑的Barn 左偏树

    P3066 逃跑的Barn 左偏树 题面 题意:给出以1号点为根的一棵有根树,问每个点的子树中与它距离小于等于l的点有多少个. 注意到答案的两个性质: 一个点的所有答案一定包含在其所有儿子的答案中 如 ...

  4. Cogs 2221. [SDOI2016 Round1] 数字配对(二分图)

    [SDOI2016 Round1] 数字配对 ★★★ 输入文件:menci_pair.in 输出文件:menci_pair.out 简单对比 时间限制:1 s 内存限制:128 MB [题目描述] 有 ...

  5. TensorFlow(十五):使用inception-v3实现各种图像识别

    上代码: import tensorflow as tf import os import numpy as np import re from PIL import Image import mat ...

  6. 微信小程序的分享功能-css文字超过两行隐藏

    .info{ width:100px; word-break:break-all; display:-webkit-box; -webkit-box-orient:vertical; -webkit- ...

  7. ubuntu ukylin wineqq 登录时提示:您的号码暂时不能使用低版本的qq

    ubuntu ukylin wineqq 登录时提示:您的号码暂时不能使用低版本的qq,而有的qq号登录没有问题. 优麒麟官网上下载的qqwine安装包,解压后安装三个deb包. 郁闷了一下午,都想装 ...

  8. P2597 [ZJOI2012]灾难——拓扑,倍增,LCA

    最近想学支配树,但是基础还是要打好了的: P2597 [ZJOI2012]灾难 这道题是根据食物链链接出一个有向图的关系,求一个物种的灭绝会连带几种物种的灭绝: 求得就是一个点能支配几个点: 如果一个 ...

  9. 20191214数组之四:数字不相同的完全平方数(关于数位上数字判断与sprintf)

    sprintf用法参见之前的随笔;(以解决):

  10. RK3399 删除开机锁屏界面

    CPU:RK3399 系统:Android 7.1 删除开机锁屏界面 diff --git a/frameworks/base/packages/SettingsProvider/res/values ...