结构(或C语言中的“结构”)允许你将几个相关的变量分组,并将它们作为一个单元来处理。它们是一种通过引入用户定义类型来扩展C语言类型系统的机制。在嵌入式开发的嵌入式系统中,结构可以提供一种优雅、直观、高效的访问硬件寄存器的方式,结构的后一个属性是Cortex微控制器软件接口标准(CMSIS) [3]的基础。
去typedef,还是不去typedef?
在C中声明结构(以及联合和枚举)的方式非常独特,因为关键字“struct”后面的标识符是一个标记(例如,struct Foo{…};)。有趣的是,标记(例如,Foo)占用的命名空间与变量、函数和类型不同。此类标记不能单独使用,但必须始终以关键字“struct”开头,以形成详细的类型说明符。这些隐藏标记名的规则可能是C语言设计中的一个错误,我建议遵循Dan Saks的指导原则将标记转换为typedef[1]。对于简单(非自引用)结构,声明根本不需要使用标记:
对于自引用结构(如链接列表或树),标记是必需的,但标记名有意保持与typedef'd名称相同。
两种推荐的结构声明都符合MISRA-C:2012指南,特别是指令2.4[2]。
内存中的结构布局
C编译器将始终遵循结构成员的顺序,正如你在结构中声明的那样。此外,第一个结构成员始终与内存中结构的开头对齐。(这对于模拟C语言中的继承非常有用。)
但是,编译器可以在每个成员(包括最后一个成员)之后插入额外的填充字节。编译器添加填充以实现结构成员的最佳对齐,从而实现更高效的访问。因此,插入的填充字节数取决于CPU及其特定的对齐规则,这意味着结构布局通常不可移植。
在嵌入式开发中,嵌入式编译器通常提供非标准C语言扩展,以允许你避免在结构中填充。例如,视频中的IAR编译器提供了可应用于结构的扩展关键字“__packed”。然而,“打包”结构可能需要更多的CPU指令来访问未对齐的成员,如切换到Cortex-M0 CPU后的视频所示。
最小化填充对内存的浪费的一个很好的经验法则是从最大到最小声明结构成员。
CMSIS中的结构
C中的结构定义了特定的内存布局,可以映射到相关硬件寄存器的块。这允许你方便地访问硬件块中的寄存器作为结构成员,将所有地址计算(从结构开始的偏移量)留给编译器。此外,这种访问硬件的方式可以利用现代CPU的特殊寻址模式(基于寄存器的立即偏移),这可能比硬编码单个硬件地址更有效。
这一思想是Cortex微控制器软件接口标准(CMSIS)[3]的基础,该标准将硬件接口定义为映射到各个硬件块的C结构。
结束注释
“计算机科学中的所有问题都可以通过另一层间接解决”。在嵌入式开发中,但过多的间接性很快就会适得其反,考虑到这一点,CMSIS在提供正确的抽象级别方面取得了正确的平衡,以便结构成员名称可以直接对应于数据表中出现的寄存器。