目录

attribute-aligned 编译属性-地址对齐

目录

简介

对于一些硬件控制器或者是操作系统堆栈,要求数据的起始地址必须是处理器位长的倍数关系,如果不是倍数关系则会影响运行速率甚至硬件会运行出错。对于这类需求,GCC 的编译属性__attribute__((aligned(n)))为我们提供了解决方案。
更多的 attribute 编译属性
attribute-section 编译属性-数据拼接
attribute-aligned 编译属性-地址对齐
attribute-packed 编译属性-字节对齐
attribute-weak 编译属性-弱符号
attribute-un/used 编译属性-未用警告
attribute-at 编译属性-地址指定

使用

1、作用对象

__attribute__((aligned(n)))作用对象
1、单个标准数据类型变量的起始地址。
2、整个标准数据类型数组的起始地址。
3、结构体的起始地址,还可以通过特定语法影响其大小(此特性可以影响结构体数组的组员起始地址,注意是组员非成员!)。
总结:主要作用是影响变量的起始地址,对于结构体还可以通过特定语法影响其大小。

2、编程语法
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <stdio.h>
#include <stdint.h>
int main(int arg1, char *arg2[])
{
	//----------------------------------------------------------------------------------------------------
	//1、[单个]标准数据类型[变量]的【起始地址】对齐:
	//----------------------------------------------------------------------------------------------------
	typedef uint8_t my_uint16addr64_t __attribute__((aligned(64)));   //自定义标准数据类型的【起始地址】对齐
	typedef uint8_t __attribute__((aligned(64))) my_uint16addr64_t;   //同上
	my_uint16addr64_t a;
//	my_uint16addr64_t a5[5]; 非法语句!!!!!!!!!!
//	uint8_t a5[5] __attribute__((aligned(64)));

	printf("&a=%u\n", &a);                         //&a=4294953984 --> 起始地址整除[64]
	printf("sizeof(a)=%u\n", sizeof(a));           //sizeof(a)=1   --> 还是原标准数据类型的大小!!
	printf("----------------\n");                  //----------------

	//----------------------------------------------------------------------------------------------------
	//2、[整个]标准数据类型[数组]的【起始地址】对齐:
	//----------------------------------------------------------------------------------------------------
	uint8_t a5[5] __attribute__((aligned(64)));                       //作用对象:整个数组
	uint8_t __attribute__((aligned(64))) a5[5];                       //作用对象:整个数组(同上)
	__attribute__((aligned(64))) uint8_t a5[5];                       //作用对象:整个数组(同上)
	//__attribute__((aligned(64))) (uint8_t a5[5]);                   //作用对象:非法!!!!
	//(__attribute__((aligned(64))) uint8_t) a5[5];                   //作用对象:非法!!!!
	//uint8_t (__attribute__((aligned(64))) a5[5]);                   //作用对象:非法!!!!
	//在数组声明中,只要括号括住两个语段,__attribute__((aligned(n)))的作用对象都为非法!!!

	printf("&a5=%u\n", &a5);                       //&a5=4294953856 --> 起始地址整除[64]
	printf("sizeof(a5)=%u\n", sizeof(a5));         //sizeof(a5)=5   --> 还是原标准数据类型的大小!!
	printf("----------------\n");                  //----------------

	//----------------------------------------------------------------------------------------------------
	//3、[结构体]的【起始地址】对齐(通过特定语法可影响结构体【大小】)
	//----------------------------------------------------------------------------------------------------
	typedef struct objx_
	{
		uint16_t a;
		uint8_t  b;
	}__attribute__((aligned(64))) objx_t; //此写法可影响结构体类型的【起始地址】和【大小】,或者是结构体数组的组员(结构体)
	typedef struct objy_
	{
		uint16_t a;
		uint8_t  b;
	}objy_t __attribute__((aligned(64))); //此写法只影响结构体类型的【起始地址】,但不影响其大小,不能用于结构体数组->解决方法请看[z5]
	objx_t x5[5];
	objx_t x;
	objy_t y;
//	objx_t y5[5]; 非法语句!!!!!!!!!!
	__attribute__((aligned(64))) struct objy_ z5[5]; //实际就是第【2】的只影响数组的【起始地址】对齐

	printf("&x5=%u\n", &x5);                       //&x5=4294953408   --> 起始地址整除[64]
	printf("&x=%u\n", &x);                         //&x=4294953728    --> 起始地址整除[64]
	printf("&y=%u\n", &y);                         //&y=4294953920    --> 起始地址整除[64]
	printf("&z5=%u\n", &z5);                       //&z5=4294953792   --> 起始地址整除[64]
	printf("sizeof(z5[0])=%u\n", sizeof(z5[0]));   //sizeof(z5[0])=4  --> 单个结构体大小为[4]字节
	printf("sizeof(x5[0])=%u\n", sizeof(x5[0]));   //sizeof(x5[0])=64 --> 单个结构体大小为[64]字节
	printf("sizeof(x5)=%u\n", sizeof(x5));         //sizeof(x5)=320   --> 结构体数组总大小[320]字节
	printf("sizeof(x)=%u\n", sizeof(x));           //sizeof(x)=64     --> 此个结构体大小为[64]字节
	printf("sizeof(y)=%u\n", sizeof(y));           //sizeof(y)=4      --> 此个结构体大小为[4]字节
	printf("----------------\n");                  //----------------
}

3、语法总结

一、关于__attribute__(())的参数名称,为了防止与其它对象出现同名影响,强烈建议在参数的前后都加上__两个下划线。

1
2
3
4
5
6
7
8
__attribute__((section(x))) 改为  __attribute__((__section__(x)))
__attribute__((at(a)))      改为  __attribute__((__at__(a)))
__attribute__((packed))     改为  __attribute__((__packed__))
__attribute__((aligned(n))) 改为  __attribute__((__aligned__(n)))
__attribute__((unused))     改为  __attribute__((__unused__))
__attribute__((used))       改为  __attribute__((__used__))
__attribute__((weak))       改为  __attribute__((__weak__))

二、关于__attribute__(())语句书写位置总体原则:书写到修饰对象名称的后面(★修饰其左边的单元体(非每个元素),放到最前面即是修饰整体★),但考虑要跨编译平台使用,强烈建议使用宏定义并且按下面规则使用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
//【总体原则】+++++++++++++++++++
#define O2O_SECTION(x)   __attribute__((__section__(x)))    //数据拼接 (对象名称后明声明)
#define O2O_AT(a)        __attribute__((__at__(a)))         //地址指定 (对象名称后明声明)
#define O2O_PACKED       __attribute__((__packed__))        //字节对齐 (对象名称后明声明,强烈建议改用 #pragma pack(push, 1) ... #pragma pack(pop) 的兼容性更好)
#define O2O_ALIGN(n)     __attribute__((__aligned__(n)))    //地址对齐 (对象整体最前声明)
#define O2O_UNUSED       __attribute__((__unused__))        //未用不警告(对象整体最前声明)
#define O2O_USED         __attribute__((__used__))          //未用不优化(对象整体最前声明)
#define O2O_WEAK         __attribute__((__weak__))          //弱化对象 (对象整体最前声明)
#define O2O_INLINE       static __inline                    //内联函数 (对象整体最前声明,c/h文件中直接编写函数(体),不能外部声明)

三、关于__attribute__(())__aligned__(n)参数对【结构体类型】修饰的特殊表现(只是唯一的特殊个案):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
typedef struct obj_1_
{
	uint16_t a;
	uint8_t  b;

}obj_1_t __attribute__((__aligned__(64))); //1.用于[单体]结构体类型的【起始地址】

typedef struct obj_n_
{
	uint16_t a;
	uint8_t  b;

}__attribute__((__aligned__(64))) obj_n_t; //2.用于结构体[组员]类型的【起始地址】和【大小】,也可用于单体结构体!备注:是组员非成员!
                //                                                -┬-
obj_1_t aaaaaa; //1.影响[单体结构体]的起始地址对齐,不能用于数组                      ├→ ●只是唯一的特殊个案●
obj_n_t bbb[6]; //2.影响结构体[每个组员]的起始地址对齐和大小限制----------------------┘
                //3.影响[数组整体]起始地址,但不影响[其它组员]起始地址和大小!
__attribute__((__aligned__(64))) struct obj_1_ ccc[8]; //←┤
__attribute__((__aligned__(64))) struct obj_n_ ddd[8]; //←┘

四、关于__attribute__(())__section__(x)参数被【编译器】与【链接器】优化的问题:

【section】修饰的数据段会被【编译器】和【链接器】这两道关卡优化掉,需要增加特别语句加以防止!【used、unused】只针对【编译器】而言,但对于【链接器】无效,也就是说链接器的【-Wl,–gc-sections】参数还是会优化掉没使用的段,除非在【链接脚本】中使用【KEEP】语句特别指出的段才不会被优化掉!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
//1.变量声明
__attribute__((__used__)) const uint8_t aaa __attribute__((__section__(".init.001"))); //增加__used__防止被[编译器]优化
__attribute__((__used__)) const uint8_t bbb __attribute__((__section__(".init.002"))); //增加__used__防止被[编译器]优化

//2.编译链接
gcc -Wl,--gc-sections -o hello hello.c /* -Wl,--gc-sections 会强制优化掉没使用的__section__(函数段/数据段)*/

//3.链接脚本
.text :
{
  .............
  
  . = ALIGN(4);
  KEEP(*(SORT(.init.*)))               /* 使用 KEEP(*(SORT())) 防止被[链接器]优化 */

  . = ALIGN(4);
  _etext = .;
} >FLASH