c语言之内存对齐

概述

        对齐访问就是当我们定义了一个2字节的变量,编译器可能会给我们分配4个字节,另外2个字节被闲置。对齐访问与非对齐访问是编译器对效率与资源权衡后的结果。非对齐访问物理内存是支持的,但是效率低,对齐访问效率高,所以编译器会使用对齐访问。

非对齐访问过程

        第一步:读涉及内存的第一个字节,

        第二步:读涉及内存的第二个字节;

        第三步:清除第一字节的无关位,

        第四部:清除清除第二字节的无关位,

        第5步:将第1字节与第二字节组合。所以效率极低。

对齐访问

        只需要1次读出即可,即使浪费空间也无所谓。如下

struct
{
int         a;//占用4字节,不用考虑填充
char        b; //占用2字节,考虑下面是2字节,所以自己填充1个字节;
short       c;  //占用2字节,考虑下面要8字节,所以填充6个字节
double     e;  //占用8字节
}

对齐方法

  • 每种数据类型都有自己的对齐规则,如char 1字节对齐,起始地址必须是1的整数倍;short 2字节对齐,起始地址必须是2的整数倍;int 4字节对齐,起始地址必须是4的整数倍。
  • 不同类型在一起需要对齐时,首先满足自己的起始地址对齐,如果前面结束的地址没有对齐,不满足本变量的对齐起始地址,编译器会自动在前面填充。

指定对齐

        在变量定义区域的前添加 #pragma pack(x);结束时添加#pragma pack();x是对齐数;结构体内部元素对齐,使用得较少。

取消对齐

        __attribute__ ((packed))取消对齐;注意是双下划线,作用域只针对申明的结构体,其它的不受影响;在定义类型时添加有效,定义变量时添加无效;

struct best
{
    double e; //8
    char table[11];
} __attribute__ ((packed)) x;

整个结构体对齐

__attribute__((aligned( n )))  指定整个结构体对齐位,与成员无关。
struct best

{
    double e; //8
    char table[11];
} __attribute__ ((aligned(1024))) x;  //

offsetof

  • 语法:#define  offsetof(TYEP,MEMBER)  (int) (&((TYEP *)0) -> MEMBER);
  • 作用:计算结构TYEP体成员MEMBER基于TYEP起始地址的偏移量;返回偏移地址。
  • 释义: (TYEP *)0 ;   //将地址0强制转换成TYPE结构体类型的指针;
&((TYEP *)0) ->MEMBER;  //基于0地址,取MEMBER的地址,即MEMBER偏移地址;
(int) (&((type *)0 ->member)); //将取到的地址强制转换成int;
typeof(a):typeof是c语言的关键字;作用是用来提取变量的类型;

container_of宏:

  • 作用:通过给定的结构体成员变量,计算结构体的起始地址;
  • 用法:container(ptr,type,member);  //ptr 指向结构体成员member的指针;返回值是指向结构体的起始地址。
  • 原型:
 #define  container(ptr,type,member)  typeof( ((type *)0)->member)  *__mptr = ptr; \
          (type *) ( (char *)__mptr – offset(type,member) )
  • 释义:

        typeof( (type *)0 ->member )  *__mptr = ptr;  //使用c语言的关键字typeof获得结构体成员member类型,并定义一个该类型的指针__mptr,并且将传参进行赋值。

        (type *) ( char *) __mptr -offset(type,member);   //offset宏返回的是偏移量,是基于8位地址的,因此需要将__mptr先转换成char指针,该指针存的就是地址,使用地址 – 偏移量就能获得起始地址。返回时转换成输入的结构体类型

获取结构体的起始地址

char *add = ((char *)(& x.e)) - offsetof(struct best,e);
  • 用&取地址符号获得成员的地址;
  • offsetof计算成员的偏移地址;
  • 成员地址 – 偏移地址 = 起始地址;


版权声明:本文为m0_60073820原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。