【内存对齐】第三篇·显式干预对齐的三种方法

上一篇:【内存对齐】第二篇·结构体内存对齐的规律与原则

C语言的实现相比于其他高级语言显得更底层,给了程序员很大的灵活性。比如,我们可以通过一些方式来设置C语言里面的对齐规则。但C语言本身并没有规定这些使用方法,是由编译器定义的。

方法一 预编译 #pragma pack(n)

参考:百度百科

写一段最简的代码,说明#pragma pack(n)的使用方法:

#pragma pack(1)
struct test_st{
    char hate;
    int like;
};
#pragma pack(4)
struct test_stt{
    char hate;
    int like;
};
struct test_st test1;
struct test_stt test2;

int main(void)
{
    printf("Size: Char[%d], Short[%d], int[%d], long[%d], longlong[%d]\r\n", sizeof(char), sizeof(short), sizeof(int), sizeof(long), sizeof(long long));
    printf("Sizeof struct Test-1 = %d\r\n", sizeof(test1));
    printf("Sizeof struct Test-2 = %d\r\n", sizeof(test2));
    return 0;
}

运行后得到:

Size: Char[1], Short[2], int[4], long[8], longlong[8]
Sizeof struct Test-1 = 5
Sizeof struct Test-2 = 8

也就是通过pack(n)指定了结构体中的成员以n字节对齐,这样可以保证padding member的宽度最大是n,即一次性最多填充n个padding,可以节省空间。

但以上使用并不正确,以上使用方式类似于malloc()没有free(),是不安全的。正确的应该将环境暂存,当pack功能使用完成后,再恢复。有两种方式:

一种是使用完成后来一句 #pragma pack(),恢复为默认对齐方式。

#pragma pack(1)
struct test_st{
    char hate;
    int like;
};
#pragma pack(4)
struct test_stt{
    char hate;
    int like;
};
#pragma pack()		//Restore to default pack value
struct test_st test1;
struct test_stt test2;

另一种是使用前 pack(push) 暂存 pack 设置,使用后 pack(pop) 恢复 pack 设置。

#pragma pack(push)		//Before changing pack value, push the current one
#pragma pack(1)
struct test_st{
    char hate;
    int like;
};
#pragma pack(4)
struct test_stt{
    char hate;
    int like;
};
#pragma pack(pop)		//After using user pack value, restore the original pack value by pop.
struct test_st test1;
struct test_stt test2;

网上还查到一些其他用法:#pragma pack(show)可以用于在编译阶段显示当前的pack设置,正常会显示这样的warning
#pragma pack(show) warning
但是在我的环境GCC下,这一句会出现这样的warning:
GCC warning
在网上找到了相对权威的解答:GCC 不支持 #pragma pack(show) 这个用法。
以及GCC权威的参考,其中确实没有提到show这种用法。
这里给出MicroSoft Visual C中对于这部分的说明
MSDN
也给出GCC中的参考实现,里面确实没有 show 这一 op code 对应的处理逻辑:
GCC
另外,在其实现中注意到 pack(n) 中的 n 必须是 2 的幂,如1,2,4,8,16等。在结构体上使用时以 min(最宽数据类型宽度,n)为pack的值。例如

#pragma pack(4)
struct test_stt{
    short like;
};

相当于 pack(2)。

方法二 __ attribute__ 参数

参考一篇CSDN博主的文章

总结下来是这样的,可以用“__attribute__((aligned(n)))”后缀来设置对齐属性,n即代表结构体的对齐设置,当结构体中有成员的宽度大于n时,则按最大成员长度来对齐,这是GNU C的用法。__attribute__((packed))是“紧凑对齐”的意思,这是GCC特有的语法。

给出一个示例程序:

#include "stdio.h"

char ph;    //Add a char placeholder to make struct start from an odd pointer value.
struct test_st{
    char hate;
    int like;
}__attribute__((packed)) test1;
struct test_stt{
    char Good;
    short boy;
    char like;
    short bad;
    int girl;
}__attribute__((aligned(8))) test2;

struct test_st{
    char hate;
    int like;
}__attribute__((aligned(1))) test3;


int main(void)
{
    printf("Size: Char[%d], Short[%d], int[%d], long[%d], longlong[%d]\r\n", sizeof(char), sizeof(short), sizeof(int), sizeof(long), sizeof(long long));
    printf("Sizeof struct Test-1 = %d\r\n", sizeof(test1));
    printf("Sizeof struct Test-2 = %d\r\n", sizeof(test2));
    printf("Sizeof struct Test-3 = %d\r\n", sizeof(test3));
    return 0;
}

结果是:

Size: Char[1], Short[2], int[4], long[8], longlong[8]
Sizeof struct Test-1 = 5
Sizeof struct Test-2 = 16
Sizeof struct Test-3 = 8

请读者自己总结下用法,并尝试写写吧。

方法三 编译选项 -fpack-struct

我在Telink TLSR8258这个MCU上写嵌入式程序的时候,注意到了自动生成的makefile文件的编译选项中有-fpack-struct,这个是gcc的编译选项,被集成到了Telink IDE中,作为优化的可选项,默认勾选:
-fpack-struct by default
到GCC中实测一下,代码:

struct test_st{
    char meat;
    short a;
    int t;
}test;

int main(void)
{
    printf("Size of Test = %d\r\n", sizeof(test));
    return 0;
}

如果用gcc test.c -o test 编译,运行后,得到

Size of Test = 8

如果用gcc test.c -o test -fpack-struct编译,运行后,得到

Size of Test = 7

可见,这个编译选项,是将被编译的代码中的结构体都pack起来。

下一篇:【内存对齐】第四篇·Array、Union内存对齐的规律与原则

连载中… by 2024/05/28

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值