数据对齐,是指数据所在的内存地址必须是该数据长度的整数倍。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. MySQL-时间日期类型

    一.MySQL中 日期和时间类型 表示时间值的日期和时间类型为 DATETIME.DATE.TIMESTAMP.TIME和YEAR. 每个时间类型有一个有效值范围和一个"零"值,当 ...

  2. SpringMVC拦截器及多拦截器时的执行顺序

    本文链接:https://blog.csdn.net/itcats_cn/article/details/80371639拦截器的配置步骤 springmvc.xml中配置多个拦截器配置自定义拦截器并 ...

  3. 窗口看门狗 WWDG

    一,窗口看门狗 二,喂狗注意事项 三,程序设计 1.检查复位状态,有助于观察当前工作的可靠性 /* Check if the system has resumed from WWDG reset ,检 ...

  4. yii2.0场景的简单使用

    一.规则中使用场景规则场景的使用模型层public function rules(){ return [ [['name','product_id'],'required','on'=>'add ...

  5. 数据结构实验之查找三:树的种类统计(SDUT 3375)

    C: #include <stdio.h> #include <stdlib.h> #include <string.h> struct node { char d ...

  6. @babel/preset-env useBuiltIns 说明

    推荐阅读:https://blog.hhking.cn/2019/04/02/babel-v7-update/ useBuiltIns false 1 "useBuiltIns": ...

  7. numpy模块-渐入佳境

    1.多维数组降为一维: numpy中的ravel().flatten().squeeze()的用法与区别 2. axis的理解 Python之NumPy(axis=0/1/2...)的透彻理解——通过 ...

  8. fidder监控请求响应时间和请求IP

    1.增加监控请求的详情时间 在CustomRules.js的class Handlers中增加  //添加请求的响应时间 public static BindUIColumn("Time T ...

  9. CentOS 修改固定IP地址

    CentOS 修改固定IP地址 参考地址:https://www.cnblogs.com/technology-huangyan/p/9146699.htmlhttps://blog.csdn.net ...

  10. C++ 中virtual 用法

    一.virtual 修饰基类中的函数,派生类重写该函数: #include using namespace std; class A{ public: virtual void display(){ ...