在一个小型微型嵌入式项目中,我发现我的编译代码大小比预期大得多。事实证明这是因为我包含了使用assert()的代码。在包含的代码中使用assert是合适的,但导致我的编译代码大小几乎翻倍。
问题不在于是否应该使用assert,而是编译器/链接器如何决定包含断言所需的所有开销。
我在另一篇文章中的原始问题:
如果有人能够向我解释当调用assert时gcc如何决定包含库函数会有所帮助?我看到assert.h声明了一个外部函数__assert_func。链接器如何知道从库中引用它而不是仅仅说“未定义引用__asert_func”?
答案 0 :(得分:6)
配置工具链时,作者会决定默认情况下应链接到哪些库。
这通常包括运行时启动/初始化代码和名为libc
的库,其中包含C标准的实现,以及作者认为相关的任何其他代码(例如libc也可能实现Posix,任何特定的自定义板函数等)对于嵌入式目标,链接到为目标实现RTOS的库并不罕见。
您可以使用-nodefaultlibs
标志来gcc在链接阶段省略这些默认库。
在assert()的情况下,它是标准的C宏/函数,通常在libc中实现。如果失败,assert()可能会打印到stdout
,因此使用assert()可以引入实现FILE *处理/缓冲,printf等的整个stdio工具,这些都是在libc中实现的。
如果您为链接阶段运行gcc -v
,则默认情况下可以看到gcc链接到的库。
答案 1 :(得分:4)
gcc
(或g++
)命令只是一个驱动程序。它运行其他程序,包括编译器本身(C代码为cc1
,C ++代码为cc1plus
)以及汇编程序和链接器。
运行的程序由spec file决定(有一个隐含程序,请参阅-dumpspecs
developer option)。顺便说一句,使用gcc
选项运行-v
会显示所涉及的实际程序。
assert
宏已定义(请参阅文件/usr/include/assert.h
),仅在<assert.h>
不是已定义的预处理器符号时才在NDEBUG
中进行一些检查。在我的Linux / Glibc系统上,它可以从C标准库中调用__assert_failed
内部函数。引用assert(3)文档:
如果在
NDEBUG
最后一次定义宏<assert.h>
包含,宏assert()
不生成代码,因此 什么都没有。
有些项目在生产模式下使用-DNDEBUG
代码进行编译。
您应该阅读文档的Invoking GCC章节。
也许您希望compile with -ffreestanding
避免使用任何额外的库,即使是标准的库?
答案 2 :(得分:2)
在您的嵌入式系统上,链接是静态的。静态链接的工作原理如下。
静态库是目标文件的存档。链接器分别考虑每个对象。
在静态库中找到的引用函数或变量包含在生成的可执行文件中,以及包含引用符号的整个目标文件。不会引入不包含引用符号的目标文件。
这绝不是gcc特有的。林克斯从一开始就以这种方式工作。