# 关键字

# 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 分别实现打印和调用 pvPortMallocvPortFree 新的接口,所以在对 mallocfree 引用的时候,是有打印输出并转而执行新的函数接口。

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__ 分别用来显示当前的行号和文件名。

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

夏沫の浅雨 微信支付

微信支付

夏沫の浅雨 支付宝

支付宝