Fork me on GitHub

C语言结构体之内存对齐

C语言结构体之内存对齐

1、什么是内存对齐

首先看一个例子,下面有一个结构体:

struct structTest1
{
    char  c1;
    short s;
    char  c2;
    int   i;    
};

假设这个结构体成员在内存中是紧凑排列的,那么c1的存储地址就是0,s的存储地址是1-2,c2的存储地址是3,i的存储地址是4-7,c1的地址是0000000000000000,s的地址是0000000000000001,c2的地址是0000000000000003,i的地址是0000000000000004,整个结构体所占内存是8。但是写一个程序输出一下,则会得到不同的结果。

#include <stdio.h>

struct structTest1
{
    char  c1;
    short s;
    char  c2;
    int   i;    
};

int main()
{
    struct structTest1 s1;
    printf("sizeof(s1): %d\n", sizeof(s1));
    printf("c1:%p\ns :%p\nc2:%p\ni :%p\n",\
    (unsigned int)(void*)&s1.c1 - (unsigned int)(void*)&s1,\
    (unsigned int)(void*)&s1.s - (unsigned int)(void*)&s1,\
    (unsigned int)(void*)&s1.c2 - (unsigned int)(void*)&s1,\
    (unsigned int)(void*)&s1.i - (unsigned int)(void*)&s1);
    
    
    return 0;
 } 

 

 程序的输出结果与我们预测的结果不一致,这就是因为内存对齐导致的。

内存对齐就是,按照成员的声明顺序,依次安排内存,其偏移量为最大成员大小的整数倍,最后结构体的大小为最大成员的整数倍。

2、为什么会有内存对齐

因为访问未对齐的内存,处理器需要做两次内存访问,然而对齐的内存访问仅需要一次访问。缺省情况下,编译器默认将结构、栈中的成员数据进行内存对齐。编译器将未对齐的成员往后移,将每一个成员都对齐到自然边界上,从而也导致了整个结构体的尺寸变大。尽管会牺牲一点空间(成员间有部分内存空闲),但提高了性能。

3、如何避免内存对齐的影响

一个小技巧就是将上面的结构体写成下面的形式:

struct structTest1
{
    char  c1;
    short s;
    char  c2;
    int   i;    
};
这样一来,每个成员都对齐在其自然边界上,从而避免了编译器自动对齐。
#include <stdio.h>

struct structTest1
{
    char  c1;
    char  c2;
    short s;
    int   i;    
};

int main()
{
    struct structTest1 s1;
    printf("sizeof(s1): %d\n", sizeof(s1));
    printf("c1:%p\nc2:%p\ns :%p\ni :%p\n",\
    (unsigned int)(void*)&s1.c1 - (unsigned int)(void*)&s1,\
    (unsigned int)(void*)&s1.c2 - (unsigned int)(void*)&s1,\
    (unsigned int)(void*)&s1.s - (unsigned int)(void*)&s1,\
    (unsigned int)(void*)&s1.i - (unsigned int)(void*)&s1);
    
    
    return 0;
 }

 

除此之外我们还可以利用#pragma pack()来改变编译器的默认对齐方式
使用指令#pragma pack (n),编译器将按照 n 个字节对齐。
使用指令#pragma pack (),编译器将取消自定义字节对齐方式。
在#pragma pack (n)和#pragma pack ()之间的代码按 n 个字节对齐。
当n=1时:
#include <stdio.h>

#pragma pack(1)
struct TestStruct1
{
char a;
long b;
};

struct TestStruct2
{
char c;
struct TestStruct1 d;
long long e;
};
#pragma pack()

int main()
{
    printf("sizef(TestStruct2): %d\n", sizeof(struct TestStruct2));
    
    return 0;
}

 

 当n=2时

#include <stdio.h>

#pragma pack(2)
struct TestStruct1
{
char a;
long b;
};

struct TestStruct2
{
char c;
struct TestStruct1 d;
long long e;
};
#pragma pack()

int main()
{
    printf("sizef(TestStruct2): %d\n", sizeof(struct TestStruct2));
    
    return 0;
}

 

 当n=4时

#include <stdio.h>

#pragma pack(4)
struct TestStruct1
{
char a;
long b;
};

struct TestStruct2
{
char c;
struct TestStruct1 d;
long long e;
};
#pragma pack()

int main()
{
    printf("sizef(TestStruct2): %d\n", sizeof(struct TestStruct2));
    
    return 0;
}

 

 

当n=8时

#include <stdio.h>

#pragma pack(8)
struct TestStruct1
{
char a;
long b;
};

struct TestStruct2
{
char c;
struct TestStruct1 d;
long long e;
};
#pragma pack()

int main()
{
    printf("sizef(TestStruct2): %d\n", sizeof(struct TestStruct2));
    
    return 0;
}

 

 

 

 

 

 

posted @ 2020-08-21 23:23  小黑子杜  阅读(523)  评论(0编辑  收藏  举报