Linux C/C++字节对齐

其实字节对齐是程序员与编译器共同完成的,内存对齐跟编译器、系统、CPU都有关系。

字节对齐的方式

  • 字节对齐的关键字及宏
    #pragma pack()
    alignas()
    attribute
    declspce

pragma

  • 解释
    pragma 指令控制编译器的特定于实现的行为,如禁用编译器警告或更改对齐要求。无法识别的会被忽略。
    这里只对对齐进行说明
    paragma pack 控制字节对齐族有以下格式:
    #pragma pack(m) //设置当前以m字节对齐
    #pragma pack() //设置当前对齐为默认值
    #pragma pack(push) //推入当前对齐的值到内部栈
    #pragma pack(push,m) //推入当前对齐的值到内部栈然后设置以m字节对齐
    #pragma pack(pop) //从内部栈弹出顶条目然后设置(恢复)当前对齐为该值
    下面只是针对#pragma pack(m)测试说明
    注意:在我们的程序中经常看到#pragma pack(m),#pragma pack()成对出现,其实这里的成对出现不像#if与#endif,必须成对,#pragma pack(m)可以单独出现,但这里就会引发一个问题所有直接或间接引用该文件的文件都以指定的m字节对齐,直到遇到下一个#pragma pack
  • 默认的对齐
    默认的结构体对齐方式是以结构体中最大的占用字节的类型对齐,下面结构体中double是8字节对齐,所以
#include 
#include "Fish.h"
struct Test2
{
    char   T2a[9];
    int    T2b;
    double T2c;
    char   T2d;
};


int main(int argc,char *argv[])
{
    printf("[%s,%d] size:%d T2aOf:%d T2bOf:%d T2cOf:%d T2dOf:%d \n",
    __func__, __LINE__, sizeof(struct Test2),
    alignof(Test2::T2a), alignof(Test2::T2b),
    alignof(Test2::T2c), alignof(Test2::T2d));
}
$ ./demo 
[main,23] size:32 T2aOf:1 T2bOf:4 T2cOf:8 T2dOf:1//这里32= 9 + (3) + 4 + 8 + 1 + (7)       //这里括号内的是空间占位字节,这里将结构体对象的地址以8字节对齐是编译器保证的,比如他的开始地址为8,而T2a是char类型,是1字节对齐,所以其从8开始而8+9不能被四整除,所以加3可以被4整除,这这里9+(3)的位置就是T2b以4字节对齐的位置,而T2c是以8字节对齐的,9 + (3) +  4的位置刚好是T2c对齐地址,所以9+(3)+4+8,而char又是1字节对齐,所以这里9 + (3) + 4 + 8 + 1,而(7)是为了什么呢,其实7主要是考虑了结构体数族,
例如 struct Test2 TB[2]; 那么T[1]的地址也是8字节对齐,所以就有(7)
  • 16字节对齐
#include 
#include "Fish.h"
#pragma pack(16)

struct Test2
{
    char   T2a[9];
    int    T2b;
    double T2c;
    char   T2d;
};

#pragma pack()

int main(int argc,char *argv[])
{
    printf("[%s,%d] size:%d T2aOf:%d T2bOf:%d T2cOf:%d T2dOf:%d \n",
    __func__, __LINE__, sizeof(struct Test2),
    alignof(Test2::T2a), alignof(Test2::T2b),
    alignof(Test2::T2c), alignof(Test2::T2d));
}

/demo
[main,26] size:32 T2aOf:1 T2bOf:4 T2cOf:8 T2dOf:1
这里设置对齐方式为16字节对齐,但是根默认的是一样的

  • 1字节对齐

struct Test2
{
    char   T2a[9];
    int    T2b;
    double T2c;
    char   T2d;
};

#pragma pack()

int main(int argc,char *argv[])
{
    printf("[%s,%d] size:%d T2aOf:%d T2bOf:%d T2cOf:%d T2dOf:%d \n",
    __func__, __LINE__, sizeof(struct Test2),
    alignof(Test2::T2a), alignof(Test2::T2b),
    alignof(Test2::T2c), alignof(Test2::T2d));
}
./demo 
[main,26] size:22 T2aOf:1 T2bOf:1 T2cOf:1 T2dOf:1
  • pragma pack总结
    从测试可以看出pragma pack() 可以将类型中对齐设置比原来的对齐方式小,但是不能设置比原本的类型对齐大

    alignas()

  • 解释
    指定类型或对象的对齐要求
    alignas 有以下格式:
    alignas( 表达式 ) //表达式 必须是求值为零或合法的对齐或扩展对齐的整型常量表达式
    alignas( 类型标识 ) //等价于 alignas(alignof(类型标识))
    alignas( 包 … ) //等价于对同一说明应用多个 alignas 说明符,逐个对应于形参包的各个成员,形参包可以是类型或非类型形参包
    alignas 说明符可用于:
    类的声明或定义;
    非位域类数据成员的声明;
    变量声明,除了它不能应用于下列内容:
    函数形参;
    catch 子句的异常形参。
    这种声明所声明的对象或类型的对齐要求将等于用于该声明的所有 alignas 说明符中最严格(最大)的非零 表达式,除非这会削弱类型的自然对齐。
    如果某个声明上的最严格(最大)alignas 比当它没有任何 alignas 说明符的情况下本应有的对齐更弱(即弱于其原生对齐,或弱于同一对象或类型的另一声明上的 alignas),那么程序非良构:
    struct alignas(8) S {};
    struct alignas(1) U { S s; }; // 错误:如果没有 alignas(1) 那么 U 的对齐将会是 8
    无效的非零对齐,例如 alignas(3) 非良构。
    同一声明上,比其他 alignas 弱的有效的非零对齐被忽略。
    始终忽略 alignas(0)。
  • 64字节对齐

struct alignas(64) Test2
{
    char   T2a[9];
    int    T2b;
    double T2c;
    char   T2d;
};


int main(int argc,char *argv[])
{
    printf("[%s,%d] size:%d T2aOf:%d T2bOf:%d T2cOf:%d T2dOf:%d \n",
    __func__, __LINE__, sizeof(struct Test2),
    alignof(Test2::T2a), alignof(Test2::T2b),
    alignof(Test2::T2c), alignof(Test2::T2d));
}
$ ./demo 
[main,24] size:64 T2aOf:1 T2bOf:4 T2cOf:8 T2dOf:1
  • 1字节对齐
#include 
#include "Fish.h"

struct alignas(1) Test2
{
    char   T2a[9];
    int    T2b;
    double T2c;
    char   T2d;
};


int main(int argc,char *argv[])
{
    printf("[%s,%d] size:%d T2aOf:%d T2bOf:%d T2cOf:%d T2dOf:%d \n",
    __func__, __LINE__, sizeof(struct Test2),
    alignof(Test2::T2a), alignof(Test2::T2b),
    alignof(Test2::T2c), alignof(Test2::T2d));
}
$ ./demo 
[main,24] size:32 T2aOf:1 T2bOf:4 T2cOf:8 T2dOf:1
  • alignas 总结
  • alignas只能在原来结构体对齐的方式上增加对齐字节,但不能在原来基础上减少,恰好根pragma pack相反

attribute

  • 解释
    attribute__是GNU C的一大特性,主要是设置函数、类型(结构体、共用体、基本类型等)及变量属性。
    大致有六个参数值可以被设定,即:aligned, packed, transparent_union, unused, deprecated 和 may_alias 。
    这里只对对齐进行说明
    attribute 控制字节对齐族有以下格式:
    attribute((aligned(m))) //设置当前以m字节对齐
    attribute((packed()))
    下面只是针对__attribute
    ((aligned(m))) 测试说明
  • attribute(aligned(m))
    设置该变量或类型以m字节对齐
#include 
#include "Fish.h"

struct  Test2
{
    char   T2a[9];
    int    T2b;
    double T2c;
    char   T2d;
}__attribute__((aligned(1)));

int main(int argc,char *argv[])
{
    printf("[%s,%d] size:%d T2aOf:%d T2bOf:%d T2cOf:%d T2dOf:%d \n",
    __func__, __LINE__, sizeof(struct Test2),
    alignof(Test2::T2a), alignof(Test2::T2b),
    alignof(Test2::T2c), alignof(Test2::T2d));
}
$ ./demo 
[main,23] size:32 T2aOf:1 T2bOf:4 T2cOf:8 T2dOf:1

设置的值未能生效

#include 
#include "Fish.h"

struct  Test2
{
    char   T2a[9];
    int    T2b;
    double T2c;
    char   T2d;
}__attribute__((aligned(64)));

int main(int argc,char *argv[])
{
    printf("[%s,%d] size:%d T2aOf:%d T2bOf:%d T2cOf:%d T2dOf:%d \n",
    __func__, __LINE__, sizeof(struct Test2),
    alignof(Test2::T2a), alignof(Test2::T2b),
    alignof(Test2::T2c), alignof(Test2::T2d));
}
$ ./demo 
[main,23] size:64 T2aOf:1 T2bOf:4 T2cOf:8 T2dOf:1

以64位对齐,这里可以看出__attribute__(aligned(m))与alignas一样,可以将其对应的属相对齐在原来对齐基础上增大,但是不能减小

  • attribute((packed(m)))
    使用该属性对struct 或者union 类型进行定义,设定其类型的每一个变量的内存约束。当用在enum 类型 定义时,暗示了应该使用最小完整的类型
#include 
#include "Fish.h"

struct  Test2
{
    char   T2a[9];
    int    T2b;
    double T2c;
    char   T2d;
}__attribute__((packed));

int main(int argc,char *argv[])
{
    printf("[%s,%d] size:%d T2aOf:%d T2bOf:%d T2cOf:%d T2dOf:%d \n",
    __func__, __LINE__, sizeof(struct Test2),
    alignof(Test2::T2a), alignof(Test2::T2b),
    alignof(Test2::T2c), alignof(Test2::T2d));
}
$ ./demo 
[main,23] size:22 T2aOf:1 T2bOf:1 T2cOf:1 T2dOf:1 

对齐方式以1字节对齐了,其实就是前面所说的,以对应结构体属性中的最小的对齐为对齐依据。

paramga pack 与 alignas同时出现

#include 
#include "Fish.h"

#pragma pack(1)

struct alignas(64) Test1
{
    char   T1a[9];
    int    T1b;
    double T1c;
    char   T1d;
};

struct Test2
{
    char   T2a[9];
    int    T2b;
    double T2c;
    char   T2d;
};

#pragma pack()

int main(int argc,char *argv[])
{
    printf("[%s,%d] size:%d T1aOf:%d T1bOf:%d T1cOf:%d T1dOf:%d \n",
    __func__, __LINE__, sizeof(struct Test1),
    alignof(Test1::T1a), alignof(Test1::T1b),
    alignof(Test1::T1c), alignof(Test1::T1d));

    printf("[%s,%d] size:%d T2aOf:%d T2bOf:%d T2cOf:%d T2dOf:%d \n",
    __func__, __LINE__, sizeof(struct Test2),
    alignof(Test2::T2a), alignof(Test2::T2b),
    alignof(Test2::T2c), alignof(Test2::T2d));
}
$ ./demo 
[main,35] size:64 T1aOf:1 T1bOf:1 T1cOf:1 T1dOf:1 
[main,40] size:22 T2aOf:1 T2bOf:1 T2cOf:1 T2dOf:1

declspce

这个没有见过使用的,只是看网上说有,没有深究,有了解的留言分享下,这里先谢谢了

你可能感兴趣的:(linux,c语言,c++)