使用 gcc 编译程序时,大小会偏大,本文将介绍一些基本方法来减小程序的大小
1. 使用 -Os 和 LTO
gcc 有很多优化等级,其中
-O0-O1: 只进行必要的优化,方便 gdb 调试
-O2~O3: 重点优化代码执行速度,不考虑代码大小
-Os: 重点优化代码大小
要优化大小,可以选择 -Os 优化,在本插件中,勾选:

某些时候 -Os 效果也不理想,这个时候可以同时开启 gcc 的 -flto (Linker Time Optimization),方法:
在 C Compiler Options 里选择 "Link Time Optimization / 链接时优化" 保存即可

该功能将在链接时,站在全局角度对整个程序进行优化,一般来说可以产生显著的优化效果,该功能的缺点是程序将被合并为一个整体,将难以进行 gdb 调试
原理:https://anatasluo.github.io/lto-livepatch/
尽量使用最新的 gcc, 本文中的一些情况和结论是在使用 gcc 15.2.1 时得出的。
LTO 功能在某些旧的 gcc 版本上可能存在 bug,因此对于满足如下条件的源文件,需要单独禁用 lto,避免在不同版本之间切换时遇到问题:
含有中断向量的源文件 (诸如:xxx_startup.c, xxx_startup.s)。同时还需要在 构建器选项->链接器->Object Order 中将其对应的同名对象文件.o次序调整为 0, 以确保链接时 startup.o 总是在最前面。bug参考:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83967 。虽然该bug已经修复,但保险起见,我们还是这样做。
包含了 libc 中的桩函数实现的源文件(比如:syscalls.c)。桩函数是指libc库导出的一些 weak 函数,可以被用户程序中的同名函数覆盖,比如:_write, _read, _close 等。由于 libc 并没有使用 lto 进行编译,因此包含桩函数的源文件也不要使用 lto, 避免链接时出现 undefined reference to _xxx 这样的错误。
上述不能进行 lto 的文件,需要单独为该源文件添加 -fno-lto 参数以禁用 lto 优化,操作方法如下:

优化效果:
同一程序,优化选项:-Os, 已确认优化前后程序功能正常:

在本例程中,可以看到在 -Os 启用后接着启用 lto, 还可以再优化约 5KB
2. 减少双精度软件浮点的使用,尽可能使用单精度浮点
如果你的 mcu 不带硬件浮点,并且你的程序使用了软件浮点,尽可能使用单精度浮点:
在程序中使用 float32,float 而不是 double
使用 math 库时,使用带 f 的 float 版本
在浮点常量表达式使用 f 后缀,比如:#define PI 3.14f
本例中,仅将程序中唯一一处双精度浮点的语句更正为 单精度,大约节省 3KB 代码空间:


如何判断程序是否使用双精度浮点?以及定位问题语句位置
编译结束后,打开 .map 文件,搜索 __aeabi_d 字样即可查看是否存在双精度浮点符号的使用
如果发现相关符号,打开项目的 Output Files,点击 ELF 文件并打开,然后右键菜单反汇编整个程序:
在其中搜索 __aeabi_d 字样,即可定位问题语句在源程序中的位置

3. 标准库,少用 scanf
scanf 类的库函数(如:scanf(), sscanf() ...)将占用较大空间,若要使用匹配特定的字符串模式,建议手写
本例中,移除仅有的一处 scanf,大约可节省 2KB 空间:


总结
推荐方法一:使用 -Os + -flto,多数情况下效果显著,操作起来也比较简单。
其他方法的话,需要修改程序来进行优化,如果程序比较复杂的话,可能比较麻烦