自己写的而且没被调用的函数(注意不包括没有specialized的模板),如果不是inline或static,都会被编译,因为编译分两步,首先每个translation unit都会被编译为o file,然后所有o file会被链接成目标文件。而目标文件不一定是可执行程序,也可以是动态链接库,这个时候那些没被用过的函数当然要留下,因为如果是动态链接库那么它在未来任何时候都有可能被另一个程序链接,所以无法判断一个函数是否真的不会再被使用。如果是inline或者static,不用的就会被删除,毕竟在其他translation unit里都不可见,所以删除了不会有任何副作用
实验表明,gcc无论开O3还是Os都不会在最终的可执行程序中删除没被使用的函数。不过,链接器的确有个选项可以删除可执行文件中没被使用的函数(甚至空的section也可以删除),而这些选项可以通过gcc前端传过去。
另外,全局没初始化的非static变量本身不会占据任意大的空间,只会占据一个descriptor,就是可执行文件里bss段多一个记录说明其大小,然后运行时由操作系统分配足够多的空间给该变量并且清零(说明白了,反正都是0,才不会花空间去储存)。全局static变量则因为外部不可见,所以不用的话会被编译器删除。
inline允许同一个函数在多个translation unit里被定义而不会发生链接错误,具体实现是因为inline把函数名标记为weak symbol,链接时weak symbol可以重名。对用户效果就是inline函数可以定义在h文件里。inline本身在函数调用处插入只是个建议,事实上四大主流编译器目前版本没有一个会遵守,你加不加inline,它们都会自己决定是否真的inline。
一个专注于嵌入式IoT领域的架构师,深耕IoT领域多年,深度掌握IoT领域的相关技术栈,包括但不限于RTOS内核的实现及其移植、硬件驱动移植开发、网络通讯协议开发、编译构建原理及其实现、底层汇编及编译原理、编译优化及代码重构、嵌入式IoT系统的架构设计等。
11-05
文章目录1 问题场景2 需求分析3 需求实现3
.
1 示例代码3
.
2 链接脚本3
.
3
编译
脚本3
.
4 验证测试3
.
4
.
1 验证不启用
编译
回收
优化
的情况3
.
4
.
2 验证启用
编译
回收
优化
的情况4 原理分析4
.
1 实现原理4
.
2 原理验证分析4
.
2
.
1 确认
编译
阶段的
函数
所在的段4
.
2
.
2 确认链接阶段的
函数
所在的段的回收情况5 经验总结6 更多分享
1 问题场景
大家都知道,我们在开发单片机类的嵌入式固件时,一般使用的FLASH存储空间都是比较有限的,小的可能几十KB,大一点的可能也就几百KB,可以说是寸金寸土
编译
选项
-ffunction-sections // 使每个
函数
在
编译
成汇编
文件
时单独成节
-fdata-sections // 使放在全局数据区的变量单独成节
在
编译
C、Ada源
文件
(C++也可以),在gcc/g++
编译
选项中增加-ffunction-sections、-fdata-sections,在
编译
生成的
.
o目标
文件
中,会将每个
函数
或数据段,放在各种单独独立的section中;
-Wl,--gc-sections // 传入链接器参数,只链接使用到的节
在链接生成最终
检查unused的代码
没有
完美的解决方案,介绍比较多的是代码覆盖率检查工具,不能通过直接分析代码得到,需要代码运行起来。
静态代码检查的方式介绍比较少,这里推荐一种,那就是使用cppcheck工具
test
.
h
#ifndef __TEST_H__
#define __TEST_H__
void Test();
int Func();
#endif /*__TEST_H__*/
.
.
.