导言


有些同学刚从Keil或者IAR等开发环境切换到VScode + GCC开发环境时,不知道构建器怎样设置最合适。所谓最合适就是代码体积更小,降低堆栈的要求,提高代码的执行效率等等。
在这里插入图片描述
最佳方案
在这里插入图片描述
在这里插入图片描述

一、全局选项


在这里插入图片描述

1.1、–specs=nano.specs

作用:
使用 newlib-nano 库,这是标准 C 库(newlib)的精简版本

优点:

  1. 大幅减少代码体积: 可节省 20-60 KB Flash
  2. 减少 RAM 使用: 降低堆栈需求
  3. 专为资源受限的嵌入式系统优化

功能差异:
在这里插入图片描述

1.2、–specs=nosys.specs

作用:
提供系统调用(syscalls)的 空实现(stub)
在这里插入图片描述
如上所示,STM32CubeIDE生成了syscalls.c的标准实现。所以,不要添加--specs=nosys.specs,因为已经有自己的syscalls.c

二、C/C++编译器


在这里插入图片描述

2.1、链接时优化(-flto)

作用:
= 在链接阶段进行全局代码优化,而不仅仅是在编译单个文件时优化。

工作原理:
传统编译流程:

file1.c → [优化] → file1.o ──┐
file2.c → [优化] → file2.o ──┼→ [链接] → firmware.elf
file3.c → [优化] → file3.o ──┘
         ↑
    只能看到单个文件内部

启用 LTO 后:

file1.c → file1.o ──┐
file2.c → file2.o ──┼→ [链接 + 全局优化] → firmware.elf
file3.c → file3.o ──┘           ↑
                        可以看到所有文件,进行跨文件优化

何时使用 LTO?
推荐使用:

  • Release 版本 (最终产品固件)
  • Flash 空间紧张 (接近容量上限)
  • 性能要求高 (需要榨取最后的性能)
  • 稳定的代码 (不频繁修改)
    不推荐使用:
  • Debug 版本 (日常开发调试)
  • 频繁编译 (开发阶段)
  • 需要精确调试 (查找 bug)
  • 编译速度优先 (快速迭代)

2.2、Function Sections(-ffunction-sections)

作用:

  • 将每个函数放入独立的代码段(section)中。
    默认行为:
不使用时:
.text 段:
  ├─ function1()
  ├─ function2()      <-- 所有函数在一起
  └─ function3()

启用后:

使用 -ffunction-sections:
.text.function1
.text.function2
.text.function3      <-- 每个函数独立段

2.3、Data Sections(-fdata-sections)

作用:

  • 将每个全局变量/静态变量放入独立的数据段中
    默认行为:
不使用时:
.data 段:
  ├─ global_var1
  ├─ global_var2     <-- 所有变量在一起
  └─ global_var3

启用后:

使用 -fdata-sections:
.data.global_var1
.data.global_var2    <-- 每个变量独立段
.data.global_var3

2.4、Signed Char(-fsigned-char)

作用:

  • 强制将 char 类型解释为有符号字符(signed char)。
    默认行为:
  • 在某些架构(如ARM)上,char 默认是无符号的(unsigned char),范围是 0-255。
    使用场景:
  • 当你需要 char 的范围是 -128 到 127 时启用此选项。
    影响:
  • 确保跨平台代码的一致性,特别是在处理字符串和字节数据时。

不建议勾选,按照让架构来决定。

2.5、禁用运行时类型信息(-fno-rtti)

作用:

  • 禁用C++运行时类型识别。
    优点:
  • 减少代码体积(通常可节省 5-10% 的 Flash 空间)。
  • 减少 RAM 使用。
  • 提高执行效率。
    缺点:
  • 无法使用 dynamic_cast 和 typeid 等特性。

嵌入式MCU推荐:

  • 强烈推荐启用,因为嵌入式MCU系统很少需要 RTTI。

有C++代码才有影响

2.6、禁用异常处理(-fno-exceptions)

作用:
禁用C++异常处理机制。
优点:

  • 显著减少代码体积(可节省 10-20% 的 Flash)。
  • 减少栈使用。
  • 提高代码执行的可预测性。
    缺点:
  • 无法使用 try/catch/throw 语句。
    **嵌入式MCU推荐: **
  • 强烈推荐启用,异常处理在嵌入式系统中开销太大且不确定。

有C++代码才有影响

2.7、Not use __cxa_atexit()(-fno-use-cxa-atexit)

作用:

  • 不使用__cxa_atexit函数来注册析构函数。
    背景:
  • __cxa_atexit是C++标准库用于注册全局对象析构函数的机制。
    优点:
  • 减少代码体积。
  • 避免依赖完整的C++运行库。
    嵌入式推荐:
  • 推荐启用,因为嵌入式程序通常不会"退出",全局对象析构很少被调用。

有C++代码才有影

2.8、禁用本地静态变量的线程安全初始化(-fno-threadsafe-statics)

作用:

  • 禁用局部静态变量的线程安全初始化
    背景:
  • C++11 要求局部静态变量在多线程环境下安全初始化,编译器会生成额外的互斥锁代码
    优点:
  • 减少代码体积
  • 避免隐式的互斥锁开销
  • 提高执行效率
    缺点:
  • 如果多个线程同时访问未初始化的局部静态变量,可能导致竞态条件
    嵌入式推荐:
  • 视情况而定。
  • 如果是单线程或简单RTOS: 推荐启用
  • 如果有复杂多线程: 谨慎使用

裸机勾选,RTOS的话不勾选

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐