zoukankan      html  css  js  c++  java
  • C语言字节对齐

    什么是字节对齐?

    现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特 定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。

    为什么要字节对齐?

    字节是否对齐关系到CPU访问数据时的效率问题,假设一个CPU每次总是从内存中取出4个字节,从内存编号为0的地方开始,现在我定义一个char a,定义一个int b,让他们按顺排列在内存中,就是这样的:

    char a占用1个字节,int a占用4个字节,CPU每次总是取4个字节,这时我想要取b时,需要先取出0-3,再取出4-7,然后将1-4拼在一起,这样就需要取两次,但是,如果我让char aint b按照特定的顺序排列:

    这样我只需要取一次就能将b取出,提升了CPU的工作效率。

    字节对齐的概念和规则

    概念:

    • 数据类型自身的对齐值:对于char型数据,其自身对齐值为1,对于short型为2,对于int,float类型,其自身对齐值为4,对于double型,其自身对齐值为8(单位字节)。
    • 结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。
    • 指定对齐值:#pragma pack (value)时的指定对齐值value。
    • 数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值。

    规则:
    有效对齐值N是最终用来决定数据存放地址方式的值,最重要。
    有效对齐N,就是表示“对齐在N上”,也就是说该数据的"存放起始地址%N=0"。而数据结构中的数据变量都是按定义的先后顺序来排放的。第一个数据变量的起始地址就是数据结构的起始地址。结构体的成员变量要对齐排放,结构体本身也要根据自身的有效对齐值圆整。

    举例说明

    代码如下

    #include <stdio.h>
    
    struct Test
    {
    	char a;
    	int b;
    	short c;
    };
    int main(void)
    {
    	struct Test t = {'a',1,2};
    	printf("%d 
    ",sizeof(t));
    	getchar();
    	return 0;
    }
    

    如果我们不知道字节对齐规则,那么一定会认为这个结构体的大小是这样的,char类型1个字节,int类型4个字节,short类型2个字节一共7个字节,执行一下看看结果:

    执行之后的结果是12,我们先来看一下反汇编代码:

    根据反汇编代码我们可以看到a,b,c中的值分别存放在ebp-0ch,ebp-8,ebp-4这三个地方,我们到内存中看一下他们是怎么排列的:

    可以看到char占用1个字节,int占用4个字节,short占用2个字节,但是并没有我们想象的那样紧挨着排放,而是有一定的排放规则。这里就体现出了字节对齐,因为我这里是32位的机器,默认是4字节对齐,下面来详细的说一下是怎么排列的:
    假设基址为0012FF3C,从偏移地址为0的位置开始存放

    • 根据上面的字节对齐规则,a是char类型,自身对齐值就是1字节,b是int类型,自身对齐值是4字节,c是short类型,自身对齐值是2字节
    • 结构体的自身对齐值:结构体中自身对齐值最大的那个,根据上面的分析这个结构体的自身对齐值为4字节
    • 指定对齐值:因为我们没有指定对齐值,使用的是默认的4字节对齐
    • 数据成员有效对齐值:自身对齐值和指定对齐值中小的那个值,a的自身对齐值是1,指定对齐值是4,较小值是1,那么a的对齐值就是1;b的自身对齐值是4,指定对齐值是4,较小值是4,那么a的对齐值就是4;c的自身对齐值是12,指定对齐值是4,较小值是2,那么c的对齐值就是2,假设有一个变量是long类型,那么这个变量的对齐值应该为4.

    这样,a占用1个字节,存放在偏移地址为0的内存,0%1=0,没有问题。

    b占用4个字节,如果将他挨着变量a存放,也就是存放在偏移地址为1的位置,1%4=1,这样就存在问题了,所以b存放到偏移地址为4的位置,4%4=0。

    最后,c占用2个字节,如果将他挨着b存放,也就是存放到偏移地址为8的位置,8%2=0,没有问题,所以c存放到 偏移地址为8的位置,占两个字节。

    这样就排列好了,这时,发现a,b,c一共才占用了10个字节,因为结构体还没有根据自身有效对齐值圆整,根据上面的分析,这个结构体的自身对齐值是4,12%4=0,所以结构体会再占用两个字节,10和11,也就是A和B,这样就得出这个结构体的实际大小是12。

    当然,除了使用默认的对齐值,我们还可以自己使用#pragma pack (value)指定对齐值:

    #include <stdio.h>
    #pragma pack (2)
    
    struct Test
    {
    	char a;
    	int b;
    	short c;
    };
    int main(void)
    {
    	struct Test t = {'a',1,2};
    	printf("%d 
    ",sizeof(t));
    	getchar();
    	return 0;
    }
    

    这将对齐值设置成2,那么a,b,c在内存中的排列方式应该是这样的:

    要注意,这里的int b的自身对齐值是4字节,有效对齐值是2字节。

  • 相关阅读:
    Redis源码解析:07压缩列表
    Redis源码解析:06整数集合
    Redis源码解析:05跳跃表
    Laravel中的路由管理
    jquery登录的异步验证
    ThinkPHP3.2.3中,配置文件里配置项的读取
    ThinkPHP3.2.3中,查询语句中in的使用方法。
    Fatal error: Maximum function nesting level of '100' reached, aborting!
    无限级分类的收缩与展开功能
    mysql添加DATETIME类型字段导致Invalid default value错误的问题
  • 原文地址:https://www.cnblogs.com/Timesi/p/12248059.html
Copyright © 2011-2022 走看看