首页 未命名正文

linux编程_C语言之GCC中支持的内存对齐指令

云返利网 未命名 2020-05-26 09:06:39 15 0

1:gcc中支持但不推荐使用的指令

#pragma pack() :作废内存对齐接见
#pragma pack(n) (n=1/2/4/8):按n字节对齐
#pragma pack(2) 
struct mystruct1
{
int a;
char b;
short c;
}
struct mystruct2
{
int a;;
double b;
short c;
}
 #pragma pack()

以上这部分内容就是按2字节对齐了。
剖析:
    (1)#pragma是用来指挥编译器,或者说设置编译器的对齐方式的。编译器的默认对齐方式是4,然则有时候我不希望对齐方式是4,而希望是其余(譬如希望1字节对齐,也可能希望是8,甚至可能希望128字节对齐)。
    (2)常用的设置编译器编译器对齐下令有2种:第一种是#pragma pack(),这种就是设置编译器1字节对齐(有些人喜欢讲:设置编译器不对齐接见,另有些讲:作废编译器对齐接见);第二种是#pragma pack(4),这个括号中的数字就示意我们希望若干字节对齐。
    (3)我们需要#prgama pack(n)开头,以#pragma pack()末端,界说一个区间,这个区间内的对齐参数就是n。
    (4)#prgma pack的方式在许多C环境下都是支持的,然则gcc虽然也可以不外不建议使用。

2:gcc推荐的指令
__attribute__((packed)):作废内存对齐,或者说是1字节对齐
 __attribute__((aligned(n))):设定结构体类型整体按n字节对齐,注重是整体而不是这个结构体变量内的元素按n字节对齐

struct mystruct1
{
int a;
char b;
short c;
}__attribute__((packed));

这样这个结构体类型就按1字节对齐,以是这个结构体类型占7字节
123456 struct mystruct2
{
int a;
char b;
short c;
}__attribute__((aligned(2))) mystr2;

(不能是mystr2 __attribute__((aligned(2))) )
这样mystruct2这个结构体类型就按2字节对齐
    (1)__attribute__((packed))使用时直接放在要举行内存对齐的类型界说的后面,然后它起作用的局限只有加了这个器械的这一个类型。packed的作用就是作废对齐接见。
    (2)__attribute__((aligned(n)))使用时直接放在要举行内存对齐的类型界说的后面,然后它起作用的局限只有加了这个器械的这一个类型(只能是加在类型后面,不能加在变量后面)。它的作用是让整个结构体变量整体举行n字节对齐(注重是结构体变量整体n字节对齐,而不是结构体内各元素也要n字节对齐)

结构体字节对齐

      在用sizeof运算符求算某结构体所占空间时,并不是简朴地将结构体中所有元素各自占的空间相加,这里涉及到内存字节对齐的问题。从理论上讲,对于任何变量的接见都可以从任何地址最先接见,然则事实上不是云云,实际上接见特定类型的变量只能在特定的地址接见,这就需要各个变量在空间上按一定的规则排列,而不是简朴地顺序排列,这就是内存对齐。

      内存对齐的缘故原由:

      1)某些平台只能在特定的地址处接见特定类型的数据;

      2)提高存取数据的速率。好比有的平台每次都是从偶地址处读取数据,对于一个int型的变量,若从偶地址单元处存放,则只需一个读取周期即可读取该变量;然则若从奇地址单元处存放,则需要2个读取周期读取该变量。

  在C99尺度中,对于内存对齐的细节没有作过多的形貌,详细的实现交由编译器去处置,以是在差别的编译环境下,内存对齐可能略有差别,然则对齐的最基本原则是一致的,对于结构体的字节对齐主要有下面两点:

      1)结构体每个成员相对结构体首地址的偏移量(offset)是对齐参数的整数倍,若有需要会在成员之间填充字节。编译器在为结构体成员开拓空间时,首先检查预开拓空间的地址相对于结构体首地址的偏移量是否为对齐参数的整数倍,若是,则存放该成员;若不是,则填充若干字节,以到达整数倍的要求。

      2)结构体变量所占空间的巨细是对齐参数巨细的整数倍。若有需要会在最后一个成员末尾填充若干字节使得所占空间巨细是对齐参数巨细的整数倍。

   注重:在看这两条原则之前,先领会一下对齐参数这个观点。对于每个变量,它自身有对齐参数,这个自身对齐参数在差别编译环境下差别。下面枚举的是两种最常见的编译环境下各种类型变量的自身对齐参数

  从上面可以发现,在windows(32)/VC6.0下各种类型的变量的自身对齐参数就是该类型变量所占字节数的巨细,而在linux(32)/GCC下double类型的变量自身对齐参数是4,是因为linux(32)/GCC下若是该类型变量的长度没有跨越CPU的字长,则以该类型变量的长度作为自身对齐参数,若是该类型变量的长度跨越CPU字长,则自身对齐参数为CPU字长,而32位系统其CPU字长是4,以是linux(32)/GCC下double类型的变量自身对齐参数是4,若是是在Linux(64)下,则double类型的自身对齐参数是8。

  除了变量的自身对齐参数外,另有一个对齐参数,就是每个编译器默认的对齐参数#pragma pack(n),这个值可以通过代码去设定,若是没有设定,则取系统的默认值。在windows(32)/VC6.0下,n的取值可以为1、2、4、8,默认情形下为8。在linux(32)/GCC下,n的取值只能为1、2、4,默认情形下为4。注重像DEV-CPP、MinGW等在windows下n的取值和VC的相同。

  领会了这2个观点之后,可以明白上面2条原则了。对于第一条原则,每个变量相对于结构体的首地址的偏移量必须是对齐参数的整数倍,这句话中的对齐参数是取每个变量自身对齐参数和系统默认对齐参数#pragma pack(n)中较小的一个。举个简朴的例子,好比在结构体A中有变量int a,a的自身对齐参数为4(环境为windows/vc),而VC默认的对齐参数为8,取较小者,则对于a,它相对于结构体A的起始地址的偏移量必须是4的倍数。

  对于第二条原则,结构体变量所占空间的巨细是对齐参数的整数倍。这句话中的对齐参数有点庞大,它是取结构体中所有变量的对齐参数的最大值和系统默认对齐参数#pragma pack(n)对照,较小者作为对齐参数。举个例子假如在结构体A中先后界说了两个变量int a;double b;对于变量a,它的自身对齐参数为4,而#pragma pack(n)值默以为8,则a的对齐参数为4;b的自身对齐参数为8,而#pragma pack(n)的默认值为8,则b的对齐参数为8。由于a的最终对齐参数为4,b的最终对齐参数为8,那么两者较大者是8,然后再拿8和#pragma pack(n)作对照,取较小者作为对齐参数,也就是8,即意味着结构体最终的巨细必须能被8整除。

下面是测试例子:

注重:以下例子的测试效果均在windows(32)/VC下测试的,其默认对齐参数为8

/*测试sizeof运算符  2011.10.1*/
 
#include <iostream>
using namespace std;
//#pragma pack(4)    //设置4字节对齐 
//#pragma pack()     //作废4字节对齐 
 
 
typedef struct node1
{
    int a;
    char b;
    short c;
}S1;
 
typedef struct node2
{
    char a;
    int b;
    short c;
}S2;

typedef struct node3
{
    int a;
    short b;
    static int c;
}S3;

typedef struct node4
{
    bool a;
    S1 s1;
    short b;
}S4;

typedef struct node5
{
    bool a;
    S1 s1;
    double b;
    int c;
}S5;



int main(int argc, char *argv[])
{
    cout<<sizeof(char)<<" "<<sizeof(short)<<" "<<sizeof(int)<<" "<<sizeof(float)<<" "<<sizeof(double)<<endl;
    S1 s1;
    S2 s2;
    S3 s3;
    S4 s4;
    S5 s5;
    cout<<sizeof(s1)<<" "<<sizeof(s2)<<" "<<sizeof(s3)<<" "<<sizeof(s4)<<" "<<sizeof(s5)<<endl;
    return 0;
}

下面解释一下其中的几个结构体字节分配的情形

好比对于node2

typedef struct node2
{
    char a;
    int b;
    short c;
}S2;

 sizeof(S2)=12;

  对于变量a,它的自身对齐参数为1,#pragma pack(n)默认值为8,则最终a的对齐参数为1,为其分配1字节的空间,它相对于结构体起始地址的偏移量为0,能被4整除;

  对于变量b,它的自身对齐参数为4,#pragma pack(n)默认值为8,则最终b的对齐参数为4,接下来的地址相对于结构体的起始地址的偏移量为1,1不能够整除4,以是需要在a后面填充3字节使得偏移量到达4,然后再为b分配4字节的空间;

  对于变量c,它的自身对齐参数为2,#pragma pack(n)默认值为8,则最终c的对齐参数为2,而接下来的地址相对于结构体的起始地址的偏移量为8,能整除2,以是直接为c分配2字节的空间。

  此时结构体所占的字节数为1+3+4+2=10字节

  最后由于a,b,c的最终对齐参数分别为1,4,2,最大为4,#pragma pack(n)的默认值为8,则结构体变量最后的巨细必须能被4整除。而10不能够整除4,以是需要在后面填充2字节到达12字节。其存储如下:

  |char|----|----|----|  4字节

    |--------int--------|  4字节

    |--short--|----|----|  4字节

  总共占12个字节

对于node3,含有静态数据成员 

typedef struct node3
{
    int a;
    short b;
    static int c;
}S3;

 

  则sizeof(S3)=8.这里结构体中包罗静态数据成员,而静态数据成员的存放位置与结构体实例的存储地址无关(注重只有在C++中结构体中才气含有静态数据成员,而C中结构体中是不允许含有静态数据成员的)。其在内存中存储方式如下:

  |--------int--------|  4字节

  |--short-|----|----|    4字节

  而变量c是单独存放在静态数据区的,因此用siezof盘算其巨细时没有将c所占的空间盘算进来。

而对于node5,内里含有结构体变量

typedef struct node5
{
    bool a;
    S1 s1;
    double b;
    int c;
}S5; 

sizeof(S5)=32。

  对于变量a,其自身对齐参数为1,#pragma pack(n)为8,则a的最终对齐参数为1,为它分配1字节的空间,它相对于结构体起始地址的偏移量为0,能被1整除;

  对于s1,它的自身对齐参数为4(对于结构体变量,它的自身对齐参数为它内里各个变量最终对齐参数的最大值),#pragma pack(n)为8,以是s1的最终对齐参数为4,接下来的地址相对于结构体起始地址的偏移量为1,不能被4整除,以是需要在a后面填充3字节到达4,为其分配8字节的空间;

  对于变量b,它的自身对齐参数为8,#pragma pack(n)的默认值为8,则b的最终对齐参数为8,接下来的地址相对于结构体起始地址的偏移量为12,不能被8整除,以是需要在s1后面填充4字节到达16,再为b分配8字节的空间;

  对于变量c,它的自身对齐参数为4,#pragma pack(n)的默认值为8,则c的最终对齐参数为4,接下来相对于结构体实在地址的偏移量为24,能够被4整除,以是直接为c分配4字节的空间。

  此时结构体所占字节数为1+3+8+4+8+4=28字节。

  对于整个结构体来说,各个变量的最终对齐参数为1,4,8,4,最大值为8,#pragma pack(n)默认值为8,以是最终结构体的巨细必须是8的倍数,因此需要在最后面填充4字节到达32字节。其存储如下:

   |--------bool--------|    4字节

   |---------s1---------|    8字节

   |--------------------|    4字节

   |--------double------|    8字节

   |----int----|---------|    8字节 

【关于云返利网】

云返利网是阿里云、腾讯云、华为云产品推广返利平台,在各个品牌云产品官网优惠活动之外,云返利网还提供返利。您可以无门槛获得阿里云、华为云、腾讯云所有产品返利,在官网下单后就可以领取,无论是自己用、公司用还是帮客户采购,您个人都可以获得返利。云返利网的目标是让返利更多、更快、更简单!详情咨询13121395187(微信同号)