精华内容
下载资源
问答
  • 结构体填充与数据对齐 结构变量的大小等于它包含所有变量的总大小。 结构体填充:是编译器用来对齐内存偏移数据。...为什么要字节对齐? 现代计算机中内存的存储理论上都是按照 byte 大小来存储的,但实际上是

    结构体填充与数据对齐

    • 结构变量的大小等于它包含所有变量的总大小。
    • 结构体填充:是编译器用来对齐内存偏移数据。
    • 字段填充:为了提高性能,编译器在结构体中利用 结构体填充 方法进行数据对齐。
    • 数据对齐:当CPU读写内存时,它都在小块内(字长或4个字节)进行。这种安排增加了系统的性能,有效地将数据存放在字长整数倍的偏移地址。
      • 结构体中每个数据类型都要对齐
      • 联合体中按照最大长度的数据类型对齐
      • 按照基本数据类型对齐

    为什么要字节对齐?

    现代计算机中内存的存储理论上都是按照 byte 大小来存储的,但实际上是按照 字长(word size) 为单位存储的。这样做是减少CPU访问内存的次数,加大CPU访问内存的吞吐量。比如同样读取8个字节的数据,一次读取4个字节那么只需要读取2次。
    在这里插入图片描述

    字节对齐方法

    • C编译器中采用#pragma指令#pragma pack(n) 编译器将按照 n 个字节对齐
    • _packed: 按照一字节对齐。packed一般会以降低运行性能为代价,由于大多数cpu处理数据在合适的字节边界数的情况下会更有效,packed的使用会破坏这种自然的边界数。
    • GCC编译器中采用 attribute((aligned (n)))方式对齐。

      让所作用的结构成员对齐在 n 字节自然边界上。如果结构体中有成员的长度大于 n,则按照最大成员的长度来对齐。

    • GCC编译器中采用attribute ((packed)) 取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。
    • 结构体字节对齐的细节和具体编译器实现相关,但一般而言满足三个准则
      • 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
      • 结构体每个成员相对结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节 internal adding
      • 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节 trailing padding

    为什么要用传递结构体指针变量?

    结构体中数据成员变量的数据非常大,采用 结构体指针变量传递值 的效率要高,花费的时间少。

    参考

    展开全文
  • 字节对齐

    2013-12-21 21:54:26
    为什么要写有关字节对齐的东西呢?字节对齐应该是最基础的问题了,我原以为对字节对齐比较了解了。但是最近遇到了字节对齐的麻烦事。说麻烦其实也不麻烦,总而言之是自己对字节对齐没有理解透彻。所以就查查了字节...

    为什么要写有关字节对齐的东西呢?字节对齐应该是最基础的问题了,我原以为对字节对齐比较了解了。但是最近遇到了字节对齐的麻烦事。说麻烦其实也不麻烦,总而言之是自己对字节对齐没有理解透彻。所以就查查了字节对齐的具体定义和使用,发现自己对字节对齐的了解是很肤浅的。

    那么为什么要字节对齐呢?因为各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些构架的cpu在访问某种没有字节对齐的数据类型时会出错。字节对齐还关乎cpu的存取效率。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那 么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据。

    在不同平台下字节对齐后的存储空间不同,比如windows和linux。windows默认的字节对齐的有效值为8个字节,而linux默认的字节对齐的有效值为4个字节,并且如果设置#pragma pack (8), linux下的字节对齐的有效值也是4,因为它不支持大于4的有效对齐值。因此在不同平台下操作的时候,得到的size是不同的(我遇到过类似的问题)。

    在说明字节对齐的时候,我们先引入几个概念,这样能更方便我们理解字节对齐的概念:

    1. 数据类型本身的对齐值:顾名思义,很好理解的,比如 32位系统下,int的对齐值为4, short 为 2, long 为 4, double 为8;

    2. 结构体或类的自身的对齐值: 即类或结构体中的基本数据类型自身对齐值中的最大值。比如

    struct test
    {
    char a;
    double b;
    int c;
    }
    该结构体自身的对齐值为double的对齐值,应该为8;

    3. 设置的对齐值:可以通过#pragma pack (n)来设置字节的对齐值,其中n就是指定的字节对齐值。也可以利用__attribute__((aligned(n)))来指定字节对齐值(Linux下有效,其他平台不清楚);

    4. 结构体或类的有效对齐值:这个是通过比较设定的对齐值和结构体或类自身的对齐值来确定的,有效对齐值就是它们中较小的一个对齐值。比如对于test结构体,如果我们设置了#pragma pack(4),那么在计算sizeof(test)的时候,我们要按照4字节的对齐值来结算,结果就是20;


    定义了上面四个概念后,我们在来说说如何计算结构体或类的字节数(计算基本类型的字节大小是没有意义的):

    还是计算上面的test结构体,在vc下默认的字节对齐有效值为8byte。结构体中包含一个char类型(占1字节),那么结构体的首地址(设为0)是满足char类型对齐的(0是1的整数倍);第二个数据为double(占8字节),那么地址1是不符合double数据类型的(1不是8的整数倍),也就是我们需要在double类型之前填充7个字节,此时double类型数据的地址是8(8是8的整数倍);第三个数据是int类型(占4个字节),它的地址可以是16(16是4的整数倍),满足字节对齐。也就是此时test结构体所占字节数为20。但是我们需要考虑整个结构体的字节对齐情况,这时结构体的有效对齐值登场了。由于结构体的有效对齐值是8,那么整个结构体所占的字节数必须是8的整数倍,这可以通过填充4个字节来实现。也就是test结构体所占的字节数应该是24。


    如果我们将设置的对齐值设置为4,那么结构体test的有效值就应该是4(4 < 8),此时test所占的字节数可以是20了,因为20是4的整数倍。


    其实字节对齐就是要保证任何类型数据所占的字节数必须是他有效对齐值的整数倍,而且该数据类型的地址也必须他有效对齐值的整数倍。


    展开全文
  • 内存字节对齐

    2020-03-03 16:30:51
    写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?...那么为什么要使用对齐呢? ​ 体系结构的对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设一个体系结构的字长为w,那么它同...
    写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的变量总长度要大,这是怎么回事呢?讲讲字节对齐吧.
    

    ​ 如果体系结构是不对齐的,A中的成员将会一个挨一个存储,从而sizeof(a)为11。显然对齐更浪费了空间。那么为什么要使用对齐呢?
    ​ 体系结构的对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设一个体系结构的字长为w,那么它同时就假设了在这种体系结构上对宽度为w的数据的处理最频繁也是最重要的。它的设计也是从优先提高对w位数据操作的效率来考虑的。比如说读写时…此处省略50万字

    一般的内存字节对齐

    上面是你随便 google一下,人家就可以跟你解释的,一大堆的道理,我们没怎么多时间,讨论为何要对齐.直入主题,怎么判断内存对齐规则,sizeof的结果怎么来的,请牢记以下3条原则:(在没有#pragma pack宏的情况下,务必看完最后一行)

    1:数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。

    2:结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储.(struct a里存有struct b,b里有char,int ,double等元素,那b应该从8的整数倍开始存储.)

    3:收尾工作:结构体的总大小,也就是sizeof的结果,.必须是其内部最大成员的整数倍.不足的要补齐.

    typedef struct bb
    {
     int id;             //[0]....[3]
     double weight;      //[8].....[15]      原则1
     float height;      //[16]..[19],总长要为8的整数倍,补齐[20]...[23]     原则3
    }BB;
    
    typedef struct aa
    {
     char name[2];     //[0],[1]
     int  id;         //[4]...[7]          原则1
    
     double score;     //[8]....[15]    
     short grade;    //[16],[17]        
     BB b;             //[24]......[47]          原则2
    }AA;
    
    int main()
    {
      AA a;
      cout<<sizeof(a)<<" "<<sizeof(BB)<<endl;
      return 0;
    }
    

    结果是

    48 24
    

    ok,上面的全看明白了,内存对齐基本过关.

    自定义内存字节对齐#pragma pack()

    再讲讲#pragma pack().

    在代码前加一句#pragma pack(1),你会很高兴的发现,上面的代码输出为

    32 16
    

    bb是4+8+4=16,aa是2+4+8+2+16=32;

    这不是理想中的没有内存对齐的世界吗.没错,#pragma pack(1),告诉编译器,所有的对齐都按照1的整数倍对齐,换句话说就是没有对齐规则.

    明白了不?

    那#pragma pack(2)的结果又是多少呢?对不起,5分钟到了,自己去测试吧.

    Vc,Vs等编译器默认是#pragma pack(8),所以测试我们的规则会正常;注意gcc默认是#pragma pack(4),并且gcc只支持1,2,4对齐。套用三原则里计算的对齐值是不能大于#pragma pack指定的n值。

    展开全文
  • 目录什么是内存对齐为什么要内存对齐怎么内存对齐示例字节对齐算法ios获取内存大小sizeofclass_getInstanceSizemalloc_size 什么是内存对齐 先看下面这个结构体 struct Test{ int a; char b; }; 如果没有内存对齐...

    什么是内存对齐

    先看下面这个结构体

    	struct Test{
        	int a;
        	char b;
    	};
    

    如果没有内存对齐,在64位机器上(以后不作说明,均是64位)字节内存大小应是: 4 + 1 = 5

    而实际使用sizeof(struct Test)输出却得到的是8

    由5到8这个过程便是内存对齐

    为什么要内存对齐

    本来5字节就够用了,那为什么需要占用8个字节的存储空间呢?

    1. 速度

    首先了解下概念:

    memory access granularity(内存访问粒度): CPU把内存当成是一块一块的,块的大小可以是2,4,8,16字节大小,因此CPU在读取内存时是一块一块进行读取的,块的大小便是内存访问粒度,此值与数据总线的位数有关。
    对于现代计算机来说,数据总线为64位,所以cpu读取块的大小为8字节

    内存访问的首地址必定是内存访问粒度的倍数

    对齐系数:每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。gcc中默认#pragma pack(8),可以通过预编译命令#pragma pack(n),n = 1,2,4,8,16来改变这一系数。

    这两个概念之间有什么关系呢?
    对齐系数是对编译器来讲的,内存访问是对硬件来讲的

    最优:对齐系数 等于 运行此代码硬件的内存访问粒度


    下面实际看下cpu是怎么读取数据的

    在这里插入图片描述
    这里内存访问粒度为4

    • 左为内存对齐情况:数据从地址0开始,cpu从地址0开始读取,只需要读取一次,便得到[0 , 1, 2, 3]
    • 右为内存未对齐情况:数据从地址1开始,而cpu会依旧从地址0开始读取,如果想要获取想要的数据,应该怎么做呢?

      在这里插入图片描述
      可以看到,如未内存对齐,不仅需要增加内存的访问次数,还需要额外操作剔除不需要的数据。

    2. 硬件原因

    如果内存未对齐可能产生崩溃等问题,主要与cpu内存读取时原子性等有关

    怎么内存对齐

    对齐规则:

    • struct 或者 union 的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如数据、结构体等)的整数倍开始(例如int在32位机中是4字节,则要从4的整数倍地址开始存储)
    • 数据成员为结构体:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储(例如:struct a里面存有struct b,b里面有char、int、double等元素,则b应该从8的整数倍开始存储)
    • 结构体的整体对齐规则:结构体的总大小,即sizeof的结果,必须是其内部做大成员的整数倍,不足的要补齐

    示例

    以下分析中,如1 2 3 (4 5),内存地址1,2, 3为空穴,内存地址4,5为实际使用的内存

    	struct A{
            double a; // (0 1 2 3 4 5 6 7)
            char b;   // (8)
            //每个数据成员存储的起始位置要从该成员大小的整数倍开始,这里int类型占用4字节,故应从12开始
            int c;    // 9 10 11 (12 13 14 15)  
            short d;  // (16 17)
            short e;  // (18 19)
        };
    

    最大成员为4字节,字节对齐后是24字节

    struct Test{
        char a;
    };
    // 最大成员为1字节, sizeof(struct Test)为1字节
    
    struct Test{
    //    short a;
    };
    sizeof(struct Test)也为1字节
    

    结构体嵌套的情形呢

    	struct A{
            double a; // (0 1 2 3 4 5 6 7)
            char b;   // (8)
            //每个数据成员存储的起始位置要从该成员大小的整数倍开始,这里int类型占用4字节,故应从12开始
            int c;    //  9 10 11 (12 13 14 15)  
            short d;  // (16 17)
            short e;  // (18 19)
        };
        
       struct B{
        	double a;
        	int c;
        	char b;
        	short d;
        	char e;
        	struct A s;
    	};
    

    可以转化为

    	struct B1{
        	double a; // (0 1 2 3 4 5 6 7)
        	int c;    // (8 9 10 11)
        	char b;   // (12)
        	short d;  // 13 (14 15)
        	char e;  // (16)
        	//如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储
        	//在下面的结构体中最大的是double,所以成员最大字节为8,应从8字节的倍数开始
        	struct {
            	char b; //17 18 19 20 21 22 23 (24)
            	int c; //25 26 27 (28 29 30 31)
            	double a; //(32 33 34 35 36 37 38 39)
            	short d; //(40 41)
        	} s;
    };
    

    8字节对齐后,sizeof(struct B1)为48字节

    另一种情况

    struct A{
        double a; // (0 1 2 3 4 5 6 7)
        char b;   // (8)
        //每个数据成员存储的起始位置要从该成员大小的整数倍开始,这里int类型占用4字节,故应从12开始
        int c;    // 9 10 11 (12 13 14 15)
        short d;  // (16 17)
        short e;  // (18 19)
    };
    
    //24字节
    
    struct B{
        double a; (0 1 2 3 4 5 6 7)
        int c;
        char b;
        short d;
        char e;
        struct A s;
        short f;
    };
    

    转化为

    struct B1{
            double a; // (0 1 2 3 4 5 6 7)
            int c;    // (8 9 10 11)
            char b;   // (12)
            short d;  // 13 (14 15)
            char e;   // (16)
            //如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储
            //在下面的结构体中最大的是double,所以成员最大字节为8,应从8字节的倍数开始
            struct {
                char b;     // 17 18 19 20 21 22 23 (24)
                int c;      //25 26 27 (28 29 30 31)
                double a;   //(32 33 34 35 36 37 38 39)
                short d;    //(40 41)
                short e;    //(42 43)
            } s;
            //先将内部结构体先8字节对齐,B占用24字节,起始地址为24,所以终止地址为48
            short f;  // 48 49
    };
    

    8字节对齐后,sizeof(struct B1)为56字节

    总结:如内部包含结构体成员,则一定考虑结构体成员对齐

    字节对齐算法

    	static inline size_t align16(size_t x) {
        	return (x + size_t(15)) & ~size_t(15);
    	}
    
    	static inline size_t align16(size_t x) {
        	return (x + size_t(15)) >> 4 << 4
    	}
    

    内存优化

    	struct A{
        	char a;
        	double b;
        	char c;
    	};
    	
    	struct B{
        	double b;
       		char a;
        	char c;
    	};
    	
    	NSLog(@"%ld --- %ld", sizeof(struct A), sizeof(struct B));
    
    24 --- 16
    

    我们可以看到,虽然A和B仅成员变量的位置顺序不同,但结构体的大小却发生了变化。由此,可以通过改变成员变量的顺序,优化结构体内存大小

    ios获取内存大小

    • sizeof

      C 语言提供了一个编译时(compile•time)一元运算符 sizeof,它可用来计算任一对象的长度

      表达式 sizeof(类型名/对象)

      “将返回一个整型值,它等于指定对象或类型占用的存储空间字节数。(严格地说,sizeof 的 返回值是无符号整型值,其类型为 size_t,该类型在头文件<stddef.h>中定义。)其中, 对象可以是变量、数组或结构;类型可以是基本类型,如 int、double,也可以是派生类型, 如结构类型或指针类型。” 摘录来自: (美)Brian W. Kernighan / (美)Dennis M. Ritchie. “C程序设计语言(中文第二版·新版)。”

      	struct Test t;
          NSURLSession *obj;
          //对象
          NSLog(@"%ld", sizeof(t));               // 16
          NSLog(@"%ld", sizeof(obj));             // 8
          NSLog(@"%ld", sizeof(1));               // 4
          //类型
          NSLog(@"%ld", sizeof(struct Test));     // 16
          NSLog(@"%ld", sizeof(NSURLSession *));  // 8
          NSLog(@"%ld", sizeof(int));             // 4
      

      若传入对象类型,输出结果为8,为指针类型占用内存大小。因此不能通过该运算符计算对象占用内存大小

    • class_getInstanceSize

      对象实际所需内存大小(8字节对齐)

    • malloc_size

      实际分配内存大小(16字节对齐)

      class_getInstanceSize <= malloc_size

    附录各类型大小

    展开全文
  • 字节对齐大全

    2018-04-23 15:39:00
    写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的变量总长度要大,这是怎么回事呢?...那么为什么要使用对齐呢?体系结构的对齐和不对齐,是在时间和空间上的一个...
  • 以下为自己在原博主上修改做笔记用文末已经附上原文链接: 自定义一个struct变量,然后sizeof,你会不会经常对...那么为什么要使用对齐呢? 体系结构的对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设
  • C-字节对齐

    2014-05-08 17:14:40
    写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的变量总长度要大,这是怎么回事呢?讲讲字节对齐吧. ...那么为什么要使用对齐呢? 体系结构的对齐和不对齐,是在时间和空间上
  • 为什么要字节对齐 三. 有哪些对齐形式 1. 结构体对齐 对齐值: 对齐准则: 对齐的隐患: 更改对齐方式 2. 栈内存对齐 3. 位域对齐 位域说明 使用场景 对齐规则 注意事项 四. linux内存分配对齐 一....
  • 转:内存字节对齐

    2014-09-23 00:52:00
    参考:5分钟搞定内存字节对齐 写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的变量总长度要大,这是怎么回事呢?讲讲字节对齐吧. ...那么为什么要使用对齐呢?体系结...
  • 写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的变量总长度要大,这是怎么回事呢?讲讲字节对齐吧. ...那么为什么要使用对齐呢?体 系结构的对齐和不对齐,...
  • 那么为什么要使用对齐呢?体系结构的对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设一个体系结构的字长为w,那么它同时就假设了在这种体系结构上对宽度为w的数据的处理最频繁也是最重要的。它的...
  • 为什么要内存对齐? 访问未对齐的内存,处理器要访问两次(数据先读高位,再度地位),访问对齐的内存,处理器只要访问一次,为了提高处理器读取数据的效率,我们使用内存对齐。Windows 默认对齐数为8字节,Linux...

空空如也

空空如也

1 2 3 4 5 ... 8
收藏数 156
精华内容 62
关键字:

为什么要使用字节对齐