使用特殊的编译器命令可以将符号声明为weak。根据维基百科:
弱符号是目标文件或动态库中的符号定义,可能会被其他符号定义覆盖
在哪些场景或哪些应用中需要弱符号?什么是典型的用例?
在嵌入式开发中,例如,当您有一个中断指针向量时,能够使用弱链接为您不感兴趣的中断获取默认处理程序非常方便。
这通过定义一个空处理程序(一次),然后为您需要的每个中断指针引入一个新的正确命名的符号,该符号与默认处理程序弱链接。
然后用这些符号填充向量,这些符号都指向相同的实际代码,直到您决定使用相同(正确)名称实现其中一个,然后您的代码“压倒”弱链接,导致指向您的指针要安装在中断表中的代码。
这通常在 C 和汇编的某种混合中实现,但使用 C 伪代码,我们可能会有类似的东西:
static void placeholder_isr(void)
{
}
/* Introduce properly-named function pointers, with weak linking.
* NOTE: This syntax is completely fictional as far as I know.
*/
void (*timer1_isr)() = placeholder_isr __attribute("weak linking");
void (*timer2_isr)() = placeholder_isr __attribute("weak linking");
void (*usart1_isr)() = placeholder_isr __attribute("weak linking");
void (*usart2_isr)() = placeholder_isr __attribute("weak linking");
void (*dma1_isr)() = placeholder_isr __attribute("weak linking");
void (*dma1_isr)() = placeholder_isr __attribute("weak linking");
/* Declare the table of interrupt handlers. */
static void (*isr_table)[] = {
timer1_isr,
timer2_isr,
usart1_isr,
usart2_isr,
dma1_isr,
dma2_isr,
} __attribute("isr vector"); /* Attribute to place it where it needs to go. */
然后您可以在需要时实现自己的功能:
void timer1_isr(void)
{
/* Handler ISR from timer1. */
}
无需更改任何其他内容,它“就可以工作”。当然,只要您的名字是上述“支持代码”所期望的名字。
弱链接的一种用途是实现 C++ 标准中的可替换函数。即:
void *operator new(std::size_t);
void *operator new(std::size_t, std::nothrow_t const &) noexcept;
void *operator new[](std::size_t);
void *operator new[](std::size_t, const std::nothrow_t&) noexcept;
void operator delete(void *) noexcept;
void operator delete(void *, std::nothrow_t const &) noexcept;
void operator delete[](void *) noexcept;
void operator delete[](void *, std::nothrow_t const &) noexcept;
这些是实现必须提供的功能,但是如果程序实现了它们,那么程序的实现将替换或覆盖实现的版本。这很容易通过弱链接实现。
典型的日常用例是内联和模板函数。
例如,使用以下代码编译时的这部分代码g++ -shared -fPIC
:
extern void a();
inline void foo() { a(); }
void bar() { foo(); }
void baz() { foo(); }
将符号 bar 和 baz 标记为 T(正常),nm
而 foo 将标记为 W - 弱。
(即mn -C ./a.out
)
理由:
内联函数和模板可以在标题中定义,并且通常在源的不同部分定义多次,最后只有一个保持活动状态。
如果它没有被标记为弱,它将是多个“foo”符号的冲突,或者编译器将无法禁用内联
weak属性导致声明作为弱符号而不是全局符号发出。这主要用于定义可以在用户代码中覆盖的库函数,尽管它也可以与非函数声明一起使用。ELF 目标支持弱符号,使用 GNU 汇编器和链接器时也支持 a.out 目标。
弱.c
extern void foo() __attribute__((weak));
int main() {
if (foo) foo();
}
foo.c
void foo() {
printf("in foo.\n");
}
强.c
extern void foo() ;
int main() {
if (foo) foo();
}
编译
$ cc weak.c // Compiles OK
$ cc strong.c // undefined reference to `foo'
当“foo”被声明为弱时,它的定义可以省略,或者被不同的库替换,具有一种“链接时绑定”的特点。链接器将为未定义的弱符号填充 0。
当您希望能够覆盖代码的另一部分中的函数定义时,通常会使用弱链接。这在指定例如默认错误处理程序的库中通常是这种情况,如果您使用该库,您可以使用自定义函数覆盖该处理程序。