# 关键字
# weak 关键字
一般用法: __attribute__((weak))
,用于定义或声明对应的函数是一个 weak 属性。
在 Linux 开发环境中,有强符号和弱符号,符号简单来说就是函数、变量的名字,对于全局(非局部、非 static
)的函数和变量,能不能重名是有一定规矩的,强、弱符号就是针对这些全局函数和变量来说的。
符号类型 | 对象 |
---|---|
强 | 函数名,赋初值的全局变量 |
弱 | 未初始化的全局变量 |
当代码中同时存在多个强或弱的全局变量时,要遵守如下规则:
- 强符号只能定义一次,否则编译错误;
- 强弱符号同时存在,以强符号为准;
- 没有强符号,则从多个弱符号中任选一个,用
–fno-common
编译选项可以在这种情况下输出 warning 提示。
# constructor 和 destructor 关键字
__attribute__((constructor))
与 __attribute__((destructor))
的用法。
__attribute__((constructor))
与 __attribute__((destructor))
是 GCC 中用来修饰函数的,constructor 可以使被修饰的函数在 main () 执行前被调用,destructor 可以使被修饰的函数在 main () 执行结束或 exit () 调用结束后被执行。
__attribute__((constructor)) void constructor_func() { | |
// ... | |
} | |
__attribute__((destructor)) void destructor_func() { | |
// ... | |
} |
一个程序中可以存在多个 constructor 和 destructor,使用优先级区分执行顺序,数字越小表示优先级越高(100 以内的优先级为保留数字不能设置)。constructor 中优先级越高越靠前执行,destructor 中优先级越高越靠后执行。不加优先级参数相当于最低优先级。
__attribute__((constructor(priority)))) void constructor_func() { | |
// ... | |
} | |
__attribute__((destructor(priority)))) void destructor_func() { | |
// ... | |
} |
# unused 关键字
__attribute__((used))
,表示对于这个函数可能不会调用它、可能用不到它,编译器不用进行 warning 提示。
而在嵌入式中 中断函数都是由内部的中断处理机制通过中断向量做跳转调用的,不是开发人员 “显式” 去调用的,因此在一些规则检查比较严格的编译器上编译时,就会出现类似于上面的警告,为了视野干净我们就添加这个属性。
# option
# nano.specs 选项
nano.specs
是一个特殊的 specs 文件,它是为了在资源受限的环境中使用而优化的,比如在嵌入式系统或微控制器中。当你使用 --specs=nano.specs
选项时,编译器会使用 “nano” 版本的 C 库,而这个版本的库被设计为比标准的 GNU C 库 (glibc) 更小,占用更少的 ROM 和 RAM。
主要特点:
- 体积小:nano 版本的库被优化以减小体积,适合那些对存储空间有严格限制的系统。
- 资源使用低:这些库在设计时考虑到了内存使用,以尽可能减少动态内存分配。
- 功能减少:为了减小体积,某些不常用的功能可能被移除或替换为更简单的实现。
在 Makefile
上加上 option :
CLIBS = -specs=nano.specs |
# -wrap 选项
-wrap
选项可以使 GCC 在编译链接的时候,转而调用 __wrap_symbol
的定义,另外还有一个相关函数 __real_symbol
,对于只声明不定义的 symbol,会对其调用到真正的 symbol 符号。
以下是 GCC linker option –wrap
的描述:
Use a wrapper function for symbol. Any undefined reference to symbol will be resolved to | |
__wrap_symbol. Any undefined reference to __real_symbol will be resolved to symbol. This | |
can be used to provide a wrapper for a system function. The wrapper function should be | |
called __wrap_symbol. If it wishes to call the system function, it should call __real_symbol. | |
Here is a trivial example: | |
void * | |
__wrap_malloc (int c) | |
{ | |
printf ("malloc called with %ld\n", c); | |
return __real_malloc (c); | |
} | |
If you link other code with this file using --wrap malloc, then all calls to malloc will | |
call the function __wrap_malloc instead. The call to __real_malloc in __wrap_malloc will | |
call the real malloc function. You may wish to provide a __real_malloc function as well, | |
so that links without the --wrap option will succeed. If you do this, you should not put | |
the definition of __real_malloc in the same file as __wrap_malloc; if you do, the assembler | |
may resolve the call before the linker has a chance to wrap it to malloc. |
像一般对 malloc、free、printf 进行调换:
在 Makefile
上加上 option :
WRAP = -Wl,-wrap,malloc -Wl,-wrap,free | |
WRAP += -Wl,-wrap,printf |
在函数里:
__attribute__((weak)) void *__wrap_malloc(size_t sz) { | |
printf("__wrap_malloc: size = %d\n", sz); | |
return pvPortMalloc(sz); | |
} | |
__attribute__((weak)) void __wrap_free(void *ptr) { | |
printf("__wrap_free: addr = %p\n", ptr); | |
vPortFree(ptr); | |
} | |
int __real_printf(const char *format, ...); | |
int __wrap_printf(const char *format, ...) { | |
return __real_printf(format); | |
} |
上面实现了对 malloc
的引用转换成调用 __wrap_malloc
函数了,同样的 __wrap_free
和 __wrap_printf
也一样;但是有区别的是, __wrap_printf
里面调用了 __real_printf
,因此,可以理解对 printf
的引用其实是不变的;而由于 __wrap_malloc
和 __wrap_free
分别实现打印和调用 pvPortMalloc
及 vPortFree
新的接口,所以在对 malloc
和 free
引用的时候,是有打印输出并转而执行新的函数接口。
note:
在针对所有 “undefined reference to symbol” 中引用 symbol 的源文件里面不能有 symbol 的定义,否则汇编器在处理源文件的时候就会解析这个 symbol,链接器也就没有机会 wrap 这个 symbol 了;嘛简单点说就是需要 wrap 的函数不要跟原函数同时定义在一个源文件。
# -Wunused 选项
在 GCC 中,如果你想要检查程序中未使用的变量,函数或标签,可以使用以下编译参数:
-Wunused-variable
:此选项会让 GCC 发出警告,如果你的代码中定义了一个变量,但从未使用过它。
-Wunused-function
:此选项会让 GCC 发出警告,如果你的代码中定义了一个静态函数,但从未使用过它。
-Wunused-label
:此选项会让 GCC 发出警告,如果你的代码中定义了一个标签,但从未使用过它。
你也可以使用 -Wunused
参数来启用所有未使用相关的警告,这相当于 -Wunused-variable -Wunused-function -Wunused-label
。
note:
如果说 unused
关键字是作用于指定的函数,那么 -Wunused
的这些选项就是作用于整个编译范围。
# 预处理命令
# #error
在编译遇到 #error
指令时,它会停止编译过程,并显示一个错误消息,同时包含该指令的文件名和行号;这通常用于当编译器检查到不满足特定条件时,强制编译失败。
# #warning
在编译遇到 #warning
指令时,它会继续编译,但会在编译输出中显示一个警告消息,同时包含该指令的文件名和行号。
# 预定义宏
__LINE__
和 __FILE__
分别用来显示当前的行号和文件名。