# 工欲善其事必先利其器-C语言拓展–嵌入式C语言(六)

简介: # 工欲善其事必先利其器-C语言拓展–嵌入式C语言(六)

工欲善其事必先利其器-C语言拓展–嵌入式C语言(六)

文章内容全部来自–>《嵌入式C语言自我修养——从芯片、编译器到操作系统》 王利涛前辈的,超级推荐

对齐这个事情在内核中可不是个什么小事,内核中涉及到内存方面的都需要非常的谨慎。

上一篇我们知道了可以通过__attribute__来声明属性,也知道了section这个属性,这篇我们来看看关于内存对齐使用的两个属性–>aligned和packed

地址对齐:aligned

GNU C通过__attribute__来声明aligned和packed属性,指定一个变量或类型的对齐方式。

使用这两个变量告诉编译器,按照我让aligned和packed两同事给你传的信息做事情哦。

举个栗子:在内存中以8字节地址对齐

int a __attribute__((aligned(8)))

这个aligned的参数必须是2的幂次方

但是确实总是可以的专业显示指定变量的对齐方式,会因为边界对齐造成一些内存空洞,浪费内存资源。所以这个操作的必要性在哪里呢?

一个主要原因就是:**这种对齐设置可以简化CPU和内存RAM之间的接口和硬件设计。**一个32位的计算机系统,在CPU读取内存时,硬件设计上可能只支持4字节或4字节倍数对齐的地址访问,CPU每次向内存RAM读写数据时,一个周期可以读写4字节。如果我们把一个int型数据放在4字节对齐的地址上,那么CPU一次就可以把数据读写完毕;如果我们把一个int型数据放在一个非4字节对齐的地址上,那么CPU可能就要分两次才能把这个4字节大小的数据读写完毕。

为了配合计算机的硬件设计,编译器在编译程序时,对于一些基本数据类型,如int、char、short、float等,会按照其数据类型的大小进行地址对齐,按照这种地址对齐方式分配的存储地址,CPU一次就可以读写完毕。虽然边界对齐会造成一些内存空洞,浪费一些内存单元,但是在硬件上的设计却大大简化了。

这个对齐要求不只是针对普通变量,对于复合变量也需要满足地址对齐的要求。

结构体对齐

结构体是复合的数据类型,编译器在分配空间时需要的不仅仅是考虑成员变量的对齐,还要考虑整个结构体的。当老大也是很不容易的。

于是: 为了结构体内各个成员地址对齐,编译器可能会在结构体内填充一些空间;

为了结构体整体对齐,编译器可能会在结构体的末尾填充一些空间。

整个栗子吃吃看:

struct data{
  char a;
  int b;
  short c;
}

因为结构体的成员b需要4字节对齐,所以编译器在给成员a分配完1字节的存储空间后,会空出3字节,在满足4字节对齐的0x0028FF34地址处才给成员b分配4字节的存储空间。接着是short类型的成员c占据2字节的存储空间。三个结构体成员一共占据1+3+4+2=10字节的存储空间。

根据结构体的对齐规则,结构体的整体对齐要按结构体所有成员中最大对齐字节数或其整数倍对齐,或者说结构体的整体长度要为其最大成员字节数的整数倍,如果不是整数倍则要补齐。因为结构体最大成员int为4字节,所以结构体要按4字节对齐,或者说结构体的整体长度要是4的整数倍,要在结构体的末尾补充2字节,最后结构体的大小为12字节。

但是我们换个顺序试试:

struct data{
  char a;
  short b;
  int data;
}

char型变量a和short型变量b,被分配在了结构体前4字节的存储空间中,而且都满足各自的地址对齐方式,整个结构体大小是8字节,只造成1字节的内存空洞。

当然这里我们也可以让这个变成12字节

struct data{
  char a;
  short b __attribute__((aligned(4)));
  int c;
}

也可以指定整个结构体

struct data{
char a;
short b;
int c;
}__attribute__((aligned(16)));

整个结构体的对齐只要按最大成员的对齐字节数对齐即可,结构体整体就以4字节对齐,结构体的整体长度为8字节,但是在这里,显式指定结构体整体以16字节对齐,所以编译器就会在这个结构体的末尾填充8字节以满足16字节对齐的要求,最终导致结构体的总长度变为16字节。

想想这个编译器就说什么听什么?会不会它觉得自己更靠谱。

编译器一定会按照aligned指定的方式对齐吗? NONONO

我们通过这个属性声明,其实只是建议编译器按照这种大小地址对齐,但不能超过编译器允许的最大值。一个编译器,对每个基本数据类型都有默认的最大边界对齐字节数。如果超过了,则编译器只能按照它规定的最大对齐字节数来给变量分配地址。(在我的底线内你随便玩)

char c2 __attribute__((aligned(16))) = 4;    //能执行
char c2 __attribute__((aligned(32))) = 4;    //nonono

这是因为32字节的对齐方式超过了编译器允许的最大值。

编译器还挺霸道总裁

到这里,关于aligned七七八八了,具体的还需要去内核的源码中遨游学习,在属性中,还有个packed,在上篇文章的例子中有出现过。下面来一起看看。

属性声明:packed

aligned属性一般用来增大变量的地址对齐,元素之间因为地址对齐会造成一定的内存空洞。

packed属性则与之相反,一般用来减少地址对齐,指定变量或类型使用最可能小的地址对齐方式。

话不多说,给大爷们摆上栗子:

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

结构体内各个成员地址的分配,使用最小1字节的对齐方式,没有任何内存空间的浪费,导致整个结构体的大小只有7字节。

上面刚刚说了内存对齐方便读写,这感觉有点矛盾?不是的哈,各个使用的场景不同。

packed这个特性在底层开发驱动还是非常有用:

例如,你想定义一个结构体,封装一个IP控制器的各种寄存器,在ARM芯片中,每一个控制器的寄存器地址空间一般都是连续存在的。如果考虑数据对齐,则结构体内就可能有空洞,就和实际连续的寄存器地址不一致。使用packed可以避免这个问题,结构体的每个成员都紧挨着,依次分配存储地址,这样就避免了各个成员因地址对齐而造成的内存空洞。

于是我们可以对结构体加packed属性,保证每个结构体的成员是连续的。

配合使用packed和aligned

我们可以在结构体内部保持 连续,然后让结构体去对齐,这样既避免了结构体内各成员因地址对齐产生内存空洞,又指定了整个结构体的对齐方式。

struct data{
  char a;
  short b;
  int c;
}__attribute__((packed,aligned(8)));

结构体data虽然使用了packed属性声明,结构体内所有成员所占的存储空间为7字节,但是我们同时使用了aligned(8)指定结构体按8字节地址对齐,所以编译器要在结构体后面填充1字节,这样整个结构体的大小就变为8字节,按8字节地址对齐。

目录
相关文章
|
21天前
|
编译器 Linux C语言
嵌入式C语言(八)
嵌入式C语言(八)
22 0
|
21天前
|
编译器 C语言 芯片
嵌入式C语言(九)
嵌入式C语言(九)
19 0
|
21天前
|
缓存 小程序 编译器
嵌入式C语言(十)
嵌入式C语言(十)
30 0
|
1天前
|
安全 Linux 编译器
嵌入式C语言(十二)
嵌入式C语言(十二)
9 1
|
1天前
|
存储 算法 Linux
嵌入式C语言(十三)
嵌入式C语言(十三)
4 0
|
1天前
|
安全 Unix Linux
嵌入式C语言(十四)
嵌入式C语言(十四)
9 0
|
7天前
|
数据处理 调度 C语言
C语言:嵌入式硬件利器
C语言:嵌入式硬件利器
|
12天前
|
人工智能 物联网 数据处理
C语言在嵌入式系统中的应用
该文探讨了C语言在嵌入式系统中的应用,强调其优势,如可移植性、高效性、灵活性及社区支持,并列举了在RTOS开发、驱动程序、通信协议实现和简单GUI开发中的应用场景。文中通过LED闪烁程序示例展示了C语言如何控制硬件。结论指出,C语言在嵌入式系统中扮演重要角色,随着技术发展,开发者需不断学习以适应新需求。
|
21天前
|
安全 算法 开发工具
【C 言专栏】基于 C 语言的嵌入式系统开发
【5月更文挑战第1天】本文探讨了C语言在嵌入式系统开发中的核心作用。嵌入式系统作为专用计算机系统广泛应用于家电、汽车、医疗等领域,具备实时性、低功耗等特点。C语言因其高效性、可移植性和灵活性成为开发首选。文章介绍了开发流程,包括需求分析、硬件选型、软件设计至部署维护,并强调中断处理、内存管理等关键技术。C语言在智能家居、汽车电子和医疗设备等领域的应用实例展示了其广泛影响力。面对硬件限制、实时性要求和安全挑战,开发者需不断优化和适应新技术趋势,以推动嵌入式系统创新发展。
【C 言专栏】基于 C 语言的嵌入式系统开发
|
21天前
|
传感器 算法 C语言
C语言在嵌入式系统开发中的优化策略与代码实现
C语言在嵌入式系统开发中的优化策略与代码实现
33 1