_weak 弱函数

一、修饰函数

两个重要点:
1、__weak__attribute__((weak)) 在声明和定义的时候,其所处的位置有不同。
2、__weak 仅在函数定义中使用时才会生成弱函数。而在任何情况下(声明和定义) __attribute__((weak)) 都会生成弱函数,无论是用于函数定义还是用于函数声明中!

        用户可以在用户文件中重新定义一个同名函数,最终编译器编译的时候,会选择用户定义的函数,如果用户没有重新定义这个函数,那么编译器就会执行__weak声明的函数,并且编译器不会报错。所以我们可以在别的地方定义一个相同名字的函数,而不必也尽量不要修改之前的函数。

        表示弱声明,若外部文件没有声明EXTI0_IRQHandler函数,则在编译链接的阶段,链接本汇编起始startup_stm32f40_41xxx.s文件即启动代码中的EXTI0_IRQHandler函数。反之,链接外部文件中的EXTI0_IRQHandler函数。

__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(GPIO_Pin);

  /* NOTE: This function should not be modified, when the callback is needed,
            the HAL_GPIO_EXTI_Callback could be implemented in the user file
   */ 
}

从这个函数中的语句和注释来看,这个函数其实什么都没有做。
我们需要在用户文件中,自己再定义一个一模一样的函数,只是我们自己定义的函数,不需要指明是弱函数。

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  if (GPIO_Pin == GPIO_PIN_13)
  {
    /* Toggle LED2 */
    HAL_GPIO_TogglePin( GPIOA,  GPIO_PIN_5);
   
  }
}

        程序在编译的时候,如果发现有两个相同名称的函数,而且其中一个是弱函数,就会忽略弱函数,使用正常的函数进行编译;如果发现只有一个弱函数,那还是会使用弱函数参与编译。

/* test1.c */
#include <stdio.h>

void HelloFun(void)
{
    printf("my hello.");
}

/* test2.c */
#include <stdio.h>

#define __weak __attribute__((weak))

__weak void HelloFun(void)
{
    printf("default hello.");
}

void main(void)
{
    HelloFun();
}

        ​__weak void HelloFun(void)函数和void HelloFun(void)函数不能在同一个.c文件中(当然,如果在同一个文件中,就没有了什么意义。因为弱函数一般用于几个模块之间的交互接口,哪有把几个模块写一个文件中的)。上面的程序中,如果我们在test1.c中没有定义HelloFun()函数,则编译器会使用test2.c中的HelloFun()函数。因此程序会打印default hello.如果在test1.c中定义了HelloFun()函数。则打印my hello.

        c99并没有__weak关键字。此关键字是编译器外扩的。所以不同的编译器可能不一样。比如gcc编译链中并没有这个关键字。而是使用__attribute__((weak))代替。为了方便移植,我们可以宏定义,如下:

#ifndef __weak
#define __weak __attribute__((weak))
#endif

        原理:连接器发现同时存在弱符号和强符号,有限选择强符号,如果发现不存在强符号,只存在弱符号,则选择弱符号。如果都不存在:静态链接,编译时会报错,动态链接:系统无法启动。

        综上所述:如果我们没有在工程中其他地方重新定义 HAL_GPIO_EXTI_Callback()函数,那么 HAL_Init 初始化函数执行的时候, 会默认执行 stm32f4xx_hal.c 文件中定义的 HAL_GPIO_EXTI_Callback函数,而这个函数没有任何控制逻辑。
如果用户在工程中重新定义函数 HAL_GPIO_EXTI_Callback,那么调用 HAL_Init 之后,会执行用户自己定义的 HAL_GPIO_EXTI_Callback函数而不会执行 stm32f4xx_hal.c 默认定义的函数。也就是说,表面上我们看到函数 HAL_GPIO_EXTI_Callback被定义了两次,但是因为有一次定义是弱函数,使用了__weak修饰符,所以编译器不会报错。

weak属性只会在静态库(.o .a )中生效,动态库(.so)中不会生效。

从上面的外部中断函数看出,_weak弱函数声明经常会出现在回调函数当中。

1、回调函数(钩子函数)

概念:函数实现方,不方便直接调用该函数, 而是有函数接口提供方简介调用该函数,称为回调函数
示例: 系统中的信号处理函数,就是一个比较典型的回调函数
1

二、修饰变量(了解)

不持有对象,所以在超出其变量作用域时,对象即被释放
使用方式

__weak int i
__attribute__((weak)) 可以声明弱变量,并且其声明方式与 __weak 相比更加灵活。
extern int Variable_Attributes_weak_1 __attribute__((weak));

        修饰变量在代码编写用的比较少。
        循环引用容易发生内存泄漏。所谓的内存泄漏就是应当废弃的对象在其作用域之外继续存在。
此代码的本意是赋予变量 test0 的对象 A 和赋予变量 test1 的对象 B 在超出其变量作用域时被释放,即在对象不被任何变量持有的状态下予以废弃。但是,循环引用使得对象不能被再次废弃。
        以下情况,虽然只有一个对象,也会出现循环引用:

id test = [[Test alloc] init];

[test setObject:test];

        因为带 __weak 修饰符的变量(即弱引用)不持有对象,所以在超出其变量作用域时,对象即被释放。如果像以下内容将可能发生循环引用的类成员变量改成附有 __weak 修饰符的成员变量的话,该现象是可以避免的。
原文:_weak修饰符详解

@inerface Test : NSObject

{

id __weak obj_;

}

  • (void)setObject:(id __strong)obj;

@end

### 关于 `__weak` 的概念及其在 C++ 和 Python 中的应用 #### 什么是弱符号 (`__weak`)? 在编译器层面,符号可以分为强符号和弱符号。弱符号允许程序中存在多个同名的定义,而链接器会选择其中一个作为最终使用的版本。这种机制通常用于实现可选功能或者提供默认行为。 在嵌入式开发领域(如 U-Boot 和 Linux Kernel),`__weak` 是一种常用的修饰符,用来标记函数或变量为弱符号[^2]。这意味着如果其他地方提供了相同名称的强符号,则该强符号会被优先使用;如果没有,则回退到弱符号。 --- #### 在 C++ 中如何定义和使用弱函数? 以下是基于 GCC 编译器的一个简单例子: ```cpp // weak_function.cpp #include <iostream> // 声明一个弱函数 extern "C" __attribute__((weak)) void my_weak_function() { std::cout << "Default implementation of weak function." << std::endl; } void call_my_function() { if (my_weak_function) { // 检查是否存在强符号覆盖 my_weak_function(); } else { std::cout << "No strong symbol found, skipping..." << std::endl; } } ``` 在这个例子中: - 如果外部模块重新实现了 `my_weak_function()` 并将其声明为强符号,则此版本将被调用。 - 若没有任何重写发生,默认的弱实现就会生效。 需要注意的是,在标准 C++ 中并没有直接支持类似的特性,因此它依赖特定平台扩展(比如 GNU 的 `__attribute__((weak))` 或 MSVC 下的不同方法)。上述代码适用于 GCC/Clang 环境下运行。 --- #### 对于 Python 来说是否有类似的概念呢? 严格意义上讲,Python 不具备像 C/C++ 那样的静态绑定阶段以及相应的“弱符号”概念。然而,我们可以通过动态导入或其他设计模式模拟某种形式的行为: 假设有一个插件系统希望让用户能够替换某些默认处理逻辑而不强制修改核心库文件本身的话,可以采用如下方式之一——利用 try-except 结构尝试加载用户自定义模块并设置回调路径: ```python try: from user_defined import custom_handler as handler except ImportError: def handler(): print("Using default handler.") handler() ``` 这里展示了即使不存在名为 'user_defined' 的包时也不会中断执行流程而是转而应用内置方案的情况。这类似于 C 当中的弱引用效果。 另外值得注意的一点是在多线程环境下操作 python 解析器对象(Python Interpreter State),由于全局解释锁(Global Interpreter Lock,GIL)的存在使得同一时刻只有一个原生线程能够在CPython VM内部真正获得控制权去评估字节码指令序列。所以在混合编程场景里务必记得适时管理gil状态以避免潜在死锁风险[^3]. --- ### 总结 虽然两者之间存在着本质区别(C 属底层硬件抽象层面上的操作 而 Py 则偏向高层级脚本语言范畴),但是它们都试图解决同一个问题即灵活性与兼容性的平衡取舍.
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yank_k

点个关注加分享,一起探讨学习!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值