数据对齐,是指数据所在的内存地址必须是该数据长度的整数倍。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. SQL server 中rowcount与@@rowcount 的使用

    rowcount的用法: rowcount的作用就是用来限定后面的sql在返回指定的行数之后便停止处理,比如下面的示例,set rowcount 10select * from 表A 这样的查询只会返 ...

  2. 5-STM32物联网开发WIFI(ESP8266)+GPRS(Air202)系统方案升级篇(,远程升级GPRS内部程序)

    https://www.cnblogs.com/yangfengwu/p/10410202.html 与升级WIFI相同介绍的不再叙述  先看WIFI升级的: ↑ 演示视频: https://www. ...

  3. linux系列(七):mv命令

    1.命令格式: mv [选项] 源文件或目录 目标文件或目录 2.命令功能: Linux mv命令用来为文件或目录改名.或将文件或目录移入其它位置. 3.命令参数: -b :若需覆盖文件,则覆盖前先行 ...

  4. docker笔记--docker 各系统安装

    在线安装 Docker 在 CentOS/RHEL 中安装 Docker 在终端中运行下面的命令安装 Docker. sudo yum install -y yum-utils sudo yum-co ...

  5. Bsgs模板

    模板最主要的是自己看得舒服,不会给自己留隐患,调起来比较简单,板子有得是,最主要的是改造出适合你的那一套.                  ——mzz #include<bits/stdc++ ...

  6. SpringCloud介绍及入门一

    springcloud是什么 基于spring boot实现的服务治理工具包,管理和协微服务 把别人的东西拿来组合在一起,形成各种组件 微服务协调者[service registtry注册中心 Eur ...

  7. 《Glibc内存管理》笔记DAY4

    目录 分箱式内存管理 Small bins Large bins 内容来源 分箱式内存管理   对于空闲的 chunk,ptmalloc 采用分箱式内存管理方式,根据空闲 chunk 的大小和处于的状 ...

  8. 微信小程序侧边栏滑动特效(左右滑动)

    侧边栏滑动是很常见的功能,但是小程序出来不久,很多特效还没有成熟案例,只能原生重写,所以今天为大家带来4个漂亮的侧边栏特效~~ 侧边栏特效一 先看效果: wxml: <!--page/one/i ...

  9. 本周JavaScript学习小结

    应组长杨老师号召,写个js阶段性学习小结. emmm这周学了Linux进程通讯,学正则表达式尝试完成第一次编程作业,中秋还去平潭露营(所以...js学得很少hhh). 现在还处于感性认识阶段,浏览了一 ...

  10. macbook配置flutter环境变量

    打开命令窗口,如果没有文件的,可以手动创建文件 code ~/.bash_profile 打开的文件内容如下,如果新增的空文件,肯定是空白的 如果将flutter存放到了应用中,可以如下操作,如果不是 ...