Chapter0 GCC&编码规范
编程环境 vscode-cpp
Windows:
编辑器:VSCode
gcc编译器:MSYS2
1. 安装msys2,ucrt64窗口安装toolchain
2. pacman -S --needed base-devel mingw-w64-ucrt-x86_64-toolchain
3. 路径 'msys64\ucrt64\bin' 添加到环境变量
4. gcc --version、g++ --version、gdb --version 确保这些都正确添加
MSYS2是一个集成了大量的GNU工具链、工具和库的开源软件包集合。
它提供了一个类似于Linux的shell环境,可以在Windows系统中编译和运行许多Linux应用程序和工具。
Linux:
编辑器:VSCode
gcc编译器:sudo apt install gcc
查询gcc环境:gcc -v
0. GCC编译器
GCC, the GNU Compiler Collection
The GNU Compiler Collection includes front ends for C, C++, Objective-C, Fortran, Ada, Go, D, Modula-2, and COBOL as well as libraries for these languages (libstdc++,...). GCC was originally written as the compiler for the GNU operating system. The GNU system was developed to be 100% free software, free in the sense that it respects the user's freedom.
0.1 交叉编译工具链
-
arm-none-eabi-gcc
(ARM architecture, no vendor, not target an operating system, complies with the ARM EABI)
用于编译 ARM 架构的裸机系统(包括 ARM Linux 的 boot、kernel,不适用编译 Linux 应用 Application),一般适合 ARM7、Cortex-M 和 Cortex-R 内核的芯片使用,所以不支持那些跟操作系统关系密切的函数。
-
arm-none-linux-gnueabi-gcc
(ARM architecture, no vendor, creates binaries that run on the Linux operating system, and use the GNU EABI)
主要用于基于ARM架构的Linux系统,可用于编译 ARM 架构的 u-boot、Linux内核、Linux应用等。arm-none-linux-gnueabi基于GCC,使用Glibc库,经过 Codesourcery 公司优化过推出的编译器。arm-none-linux-gnueabi-xxx 交叉编译工具的浮点运算非常优秀。一般ARM9、ARM11、Cortex-A 内核,带有 Linux 操作系统的会用到。
-
ABI 和 EABI
ABI:二进制应用程序接口(Application Binary Interface (ABI) for the ARM Architecture)。在计算机中,应用二进制接口描述了应用程序(或者其他类型)和操作系统之间或其他应用程序的低级接口。
EABI:嵌入式ABI。嵌入式应用二进制接口指定了文件格式、数据类型、寄存器使用、堆积组织优化和在一个嵌入式软件中的参数的标准约定。开发者使用自己的汇编语言也可以使用 EABI 作为与兼容的编译器生成的汇编语言的接口。
两者主要区别是,ABI是计算机上的,EABI是嵌入式平台上(如ARM,MIPS等)。
-
arm-linux-gnueabi-gcc 和 arm-linux-gnueabihf-gcc
两个交叉编译器分别适用于 armel 和 armhf 两个不同的架构,armel 和 armhf 这两种架构在对待浮点运算采取了不同的策略(有 fpu 的 arm 才能支持这两种浮点运算策略)。
这两个交叉编译器是 gcc 的选项 -mfloat-abi 的默认值不同。gcc 的选项 -mfloat-abi 有三种值 soft、softfp、hard(其中后两者都要求 arm 里有 fpu 浮点运算单元,soft 与后两者是兼容的,但 softfp 和 hard 两种模式互不兼容): soft: 不用fpu进行浮点计算,即使有fpu浮点运算单元也不用,而是使用软件模式。 softfp: armel架构(对应的编译器为 arm-linux-gnueabi-gcc )采用的默认值,用fpu计算,但是传参数用普通寄存器传,这样中断的时候,只需要保存普通寄存器,中断负荷小,但是参数需要转换成浮点的再计算。 hard: armhf架构(对应的编译器 arm-linux-gnueabihf-gcc )采用的默认值,用fpu计算,传参数也用fpu中的浮点寄存器传,省去了转换,性能最好,但是中断负荷高。
0.2 编译过程
编译器的主要任务是将高级编程语言(如C语言)的源代码转换为机器码或中间表示,以便计算机可以执行。在这一过程中,某些编译器确实会生成汇编语言作为中间步骤,但这并不是所有编译过程的必要部分。

-
预处理阶段:在这个阶段,预处理器处理所有的预处理指令(例如 #include 和 #define),并将宏替换为实际的代码。
-
编译阶段:接下来,编译器将预处理后的代码翻译成低级的汇编语言代码。对于一些编译器(如GCC),你可以通过指定特定的选项(例如 -S)来让编译器只进行到这一步并输出汇编文件,而不继续生成机器码。
-
汇编阶段:如果编译器生成了汇编代码,那么接下来的步骤就是使用汇编器将这些汇编代码转换成机器码,即目标代码(object code)。这个阶段产生的文件通常具有 .o 或 .obj 扩展名,并且包含可以直接由操作系统加载和执行的二进制数据。
-
链接阶段:最后,链接器将一个或多个目标文件与库文件组合在一起,解决符号引用问题,创建最终的可执行文件,例如.elf/.obj/.out/.axf。
0.3 编译器特性
__attribute__实际上是GCC的一种编译器命令,用来指示编译器执行实现某些高级操作。__attribute__可以设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)。
1. 命名规范
| 类型 | 模板 | 示例 |
|---|---|---|
| 文件 | 小写+下划线(功能_模块) | disp_driver.c |
| 函数 | 小写+下划线(模块_动词_名词) | disp_draw_line() |
| 变量 | 小写+下划线 | irq_count |
| 全局变量 | 前缀g_+小写 | g_systick |
| 指针变量 | 小写+后缀_ptr | buffer_ptr |
| typedef | 小写+后缀_t | system_param_t |
| 枚举类型 | 小写+后缀_e | fsm_state_e |
| 宏定义 | 大写+下划线 | QUEUE_BUF_NUM |
-
state与status命名
在程序代码中似乎很好区分:因为状态机(state machine)、状态迁移图 (state transition diagram)都是明确的state,所以如果「状态」的有效值之间可以儿搞出类似状态迁移图之类的东西,就命名为state;否则就用status。
比如TCP状态之间是有迁移关系的,所以是TCPstate;HHTTP状态码+由于没有互相迁移的关系,所以是HTTPstatuscode。
1.1 常用缩写
2. 优秀编码准则
为什么需要优雅地写代码?
合理的,逻辑清晰的软件架构可以节省开发时间,节省调试时间,以及减少程序的bug。
代码运行正确性,可维护性都会提高,而且个人编程思维长进也藏在其中
- 维护性:一般需要解耦合度,降低各功能代码块间的耦合度
- 复用性:需要将各功能代码块进行封装,用到时直接调用
- 扩展性:需要应用类的可继承性。或者配合使用工厂模式,让工厂根据不同的情形实例化不同功能的对象。
- 灵活性:需要满足以上三个特性,然后考虑实现跨平台ARM x86,可移植性等。
- 健壮性:考虑各异常情况,尽量使代码任何时候都能工作,否则抛出异常
- 可读性:例如if else 嵌套不要超过三层。语法不要复杂嵌套,例如多级指针
2.1 变量管理规范
-
限制变量可见域:
1)将extern 变量,尽量修改为static变量。如果提供接口给外部使用,谨慎提供写操作接口,并规范写全局变量的方式和时机。
2)如果没在别的函数访问的变量,就改为函数局部变量。
-
封装成一个结构体:
1)多个相关或者同类全局变量,封装成结构体成员放到各自模块,按相同方式初始、读、写,方便统一管理,避免杂乱无章的访问方式;
2)结构体传递(参数传递、函数返回),在安全的情况下,使用指针传递;
-
用函数/inline函数/宏函数等 封装对全局变量的操作,减少全局变量的编写次数
(等你要修改某几个全局变量的时候,就知道痛苦了)
-
安全访问全局变量
1)中断服务程序访问的全局变量,加上volatile强制到内存读取数据;
2)多个执行流(如中断程序和普通程序同时)访问的全局变量,要考虑并发问题,比如使用原子类型,原子操作,设置临界区等;
-
统一命名风格,方便阅读和搜索
2.1 注释要点
注释多不一定好,太多了会阻碍阅读的流畅性,首要(写优雅的代码,可读性强的代码,结构好的代码),次要(特殊行的注释,快速引导的注释,例如函数引导)