嵌入式C语言知识点总结
【硬件电路设计零基础入门,4节精品课等着你!!!】
做个awesomeIT大咖
趁这个假期华丽变身,做夏日最靓的仔!
赶紧扫描下方二维码,参与信盈达【硬件电路设计训练营】
打卡一次,即可抱走专属于你的神秘大礼哦!
奔走相告,信盈达学习福利卷起来了


怎么做好嵌入式?相信这个问题无论问谁你都会得到一句学好C语言!今天推荐一篇大佬写的嵌入式C语言知识点总结,非常值得一读。
从语法上来说C语言并不复杂, 但编写优质可靠的嵌入式C程序并非易事,不仅需要熟知硬件特性和缺陷,还需要对编译原理和计算机技术知识有着一定的了解。
本文以嵌入式实践为基础,再结合相关资料, 阐述嵌入式需要了解的C语言知识和重点,希望每个读到这篇文章的人都能有所收获。
1 关键字
关键字是C语言中具有特殊功能的保留标示符,按照功能可分为
1.数据类型(常用char, short, int, long, unsigned, float, double)
2.运算和表达式(=, +, -, *, while, do-while, if, goto, switch-case)
3.数据存储(auto,static ,extern,const,register,volatile,restricted)
4.结构(struct, enum, union,typedef),
5.位操作和逻辑运算(<<, >>, &, |, ~,^, &&),
6.预处理(#define, #include, #error,#if…#elif…#else…#endif等),
7.平台扩展关键字(__asm, __inline,__syscall)
这些关键字共同构成了嵌入式平台的C语法。
嵌入式的应用从逻辑上可以抽象为三个部分:
1). 数据的输入(如传感器,信号,接口输入),
2). 数据的处理(如协议的解码和封包,AD采样值的转换等)
3). 数据的输出(GUI的显示,输出的引脚状态,DA的输出控制电压,PWM波的占空比等),
对于数据的管理就贯穿着整个嵌入式应用的开发,它包含数据类型,存储空间管理,位和逻辑操作,以及数据结构,C语言从语法上支撑上述功能的实现,并提供相应的优化机制,以应对嵌入式下更受限的资源环境。
2 数据类型
C语言支持常用的字符型,整型,浮点型变量,有些编译器如keil还扩展支持bit(位)和sfr(寄存器)等数据类型来满足特殊的地址操作。
C语言只规定了每种基本数据类型的最小取值范围,因此在不同芯片平台上相同类型可能占用不同长度的存储空间,这就需要在代码实现时考虑后续移植的兼容性。
而C语言提供的typedef就是用于处理这种情况的关键字,在大部分支持跨平台的软件项目中被采用,典型的如下:

既然不同平台的基本数据宽度不同,那么如何确定当前平台的基础数据类型如int的宽度,这就需要C语言提供的接口sizeof,实现如下:
1 | printf("int size:%d, short size:%d, char size:%d\n", sizeof(int), sizeof(char), sizeof(short));
这里还有重要的知识点,就是指针的宽度,如:

其实这就和芯片的可寻址宽度有关,如32位MCU的宽度就是4,64位MCU的宽度就是8,在有些时候这也是查看MCU位宽比较简单的方式。
3 内存管理和存储架构
C语言允许程序变量在定义时就确定内存地址,通过作用域,以及关键字extern,static,实现了精细的处理机制,按照在硬件的区域不同,内存分配有三种方式(节选自C++高质量编程):
1). 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。
2). 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中 ,效率很高,但是分配的内存容量有限。
3). 从堆上分配,亦称动态内存分配。程序在运行的时候用 malloc 或 new 申请任意多少的内存,程序员自己负责在何时用 free 或 delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活,但同时遇到问题也最多。
这里先看个简单的C语言实例。

C语言的作用域不仅描述了标识符的可访问的区域,其实也规定了变量的存储区域,在文件作用域的变量st_val和ex_val被分配到静态存储区,其中static关键字主要限定变量能否被其它文件访问。
而代码块作用域中的变量a, ptr和local_st_val则要根据类型的不同,分配到不同的区域,其中a是局部变量,被分配到栈中;ptr作为指针,由malloc分配空间,因此定义在堆中;而local_st_val则被关键字限定,表示分配到静态存储区。
这里就涉及到重要知识点,static在文件作用域和代码块作用域的意义是不同的:在文件作用域用于限定函数和变量的外部链接性(能否被其它文件访问), 在代码块作用域则用于将变量分配到静态存储区。
对于C语言,如果理解上述知识对于内存管理基本就足够,但对于嵌入式C来说,定义一个变量,它不一定在内存(SRAM)中,也有可能在FLASH空间,或直接由寄存器存储(register定义变量或者高优化等级下的部分局部变量),如定义为const的全局变量定义在FLASH中,定义为register的局部变量会被优化到直接放在通用寄存器中,在优化运行速度,或者存储受限时,理解这部分知识对于代码的维护就很有意义。
此外,嵌入式C语言的编译器中会扩展内存管理机制,如支持分散加载机制和attribute((section(“用户定义区域”))),允许指定变量存储在特殊的区域如(SDRAM, SQI FLASH), 这强化了对内存的管理,以适应复杂的应用环境场景和需求。

采用 这种方式,我们就可以将变量指定到需要的区域,这在某些情况下必须的,如做GUI或者网页时因为要存储大量图片和文档,内部FLASH空间可能不足,这时就可以将变量声明到外部区域,另外内存中某些部分的数据比较重要,为了避免被其它内容覆盖,可能需要单独划分SRAM区域,避免被误修改导致致命性的错误。
这些经验在实际的产品开发中是常用且重要,不过因为篇幅原因,这里只简略的提供例子,如果工作中遇到这种需求,建议详细去了解下。
至于堆的使用,对于嵌入式Linux来说,使用起来和标准C语言一致,注意malloc后的检查,释放后记得置空,避免"野指针“,不过对于资源受限的单片机来说,使用malloc的场景一般较少,如果需要频繁申请内存块的场景,都会构建基于静态存储区和内存块分割的一套内存管理机制,一方面效率会更高(用固定大小的块提前分割,在使用时直接查找编号处理),另一方面对于内存块的使用可控,可以有效避免内存碎片的问题,常见的如RTOS和网络LWIP都是采用这种机制,我个人习惯也采用这种方式,所以关于堆的细节不在描述,如果希望了解,可以参考中关于存储相关的说明。

版权声明:本文为CSDN博主「huaijin622」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。如有侵权,请联系删除。
原文链接:https://blog.csdn.net/huaijin520/article/details/108298227