数据对齐,是指数据所在的内存地址必须是该数据长度的整数倍。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. 没有向ZK写offset数据

    两天公司要学习kafka,结合之前的storm,做了一个简单的集成,之前也参考了网上的例子一些例子,发现或多或少都有一些问题.所以自己做了一个. 这个是网上其他人遇到的问题,给摘录一下,防止以后自己和 ...

  2. LeetCode 246. Strobogrammatic Number

    原题链接在这里:https://leetcode.com/problems/strobogrammatic-number/ 题目: A strobogrammatic number is a numb ...

  3. 【安卓周记】笔记复习记录:No.1

    [安卓] 1. 安装APK记得考虑兼容7.0,Uri不能直接从Uri.parse()中构建,要使用FileProvider构建Uri. <provider android:name=" ...

  4. E:nth-of-type(n)

    E:nth-of-type(n) 语法: E:nth-of-type(n) { sRules } 说明: 匹配同类型中的第n个同级兄弟元素E.深圳dd马达 要使该属性生效,E元素必须是某个元素的子元素 ...

  5. redis登录及设置密码

    redis服务开启 : ./redis-server /opt/redisConf/redis.conf 1,查询默认密码 127.0.0.1:6379> config get requirep ...

  6. 普通页面引入React(使用和不使用JSX)

    1. 不使用JSX 优点: 不用配置有关JSX的编译. 依赖语法: React.createElement(component/type, props, ...chilidren); //第一个参数可 ...

  7. SQL动态标签

    MyBatis的动态SQL详解MyBatis 的强大特性之一便是它的动态 SQL.如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么痛苦.拼接的时候要确保不 ...

  8. MongoDB Wiredtiger存储引擎实现原理

    Mongodb-3.2已经WiredTiger设置为了默认的存储引擎,最近通过阅读wiredtiger源代码(在不了解其内部实现的情况下,读代码难度相当大,代码量太大,强烈建议官方多出些介绍文章),理 ...

  9. web+页面支持批量下载吗

    一.此方法火狐有些版本是不支持的 window.location.href = 'https://*****.oss-cn-**.aliyuncs.com/*********';二.为了解决火狐有些版 ...

  10. 洛谷P1006 传纸条(多维DP)

    小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题.一次素质拓展活动中,班上同学安排做成一个mm行nn列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了.幸运的是,他们 ...