__attribute__ ((constructor))和__attribute__ ((destructor))用法

本文介绍了__attribute__在C/C++中的使用,特别是constructor和destructor属性,它们影响函数的执行顺序,以及如何利用attribute和deprecated标记废弃接口。注意这些特性仅限于GNU的gcc编译器。

目录

1. 前言       

2. __attribute__介绍

3. 测试代码

4. 总结


1. 前言       

       最近看代码,有个函数根本就没被任何函数调用,但从程序运行结果来看,该函数是被调用了的,找很久都没找到哪里调用了,最后发现该函数前面用__attribute__((constructor))修饰,如下:

__attribute__((constructor)) void load_file()
{
    printf("Constructor is called.\n");
    g_count = (int *)malloc(sizeof(int));
}

2. __attribute__介绍

       __attribute__可以设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)。__attribute__前后都有两个下划线,并且后面会紧跟一对原括弧,括弧里面是相应的__attribute__参数。

__attribute__语法格式为:__attribute__ ( ( attribute-list ) )  函数定义

如果函数被设定为constructor属性,则该函数会在main()函数执行之前被自动的执行;若函数被设定为destructor属性,则该函数会在main()函数执行之后或者exit()被调用后被自动的执行。

3. 测试代码

#include <stdio.h>  
__attribute__((constructor)) void load_file()
{
    printf("Constructor is called.\n");
}
 
__attribute__((constructor(100))) void load_file1()
{
    printf("Constructor 100 is called.\n");
}
 
__attribute__((constructor(102))) void load_file2()
{
    printf("Constructor 102 is called.\n");
}
 
__attribute__((constructor(99))) void load_file3()
{
    printf("Constructor 99 is called.\n");
}
 
 
__attribute__((destructor)) void unload_file()
{
    printf("destructor is called.\n");
}
  
int main(int argc, char **argv)  
{  
    printf("this is function %s\n", __func__);  
    return 0;  
}  

执行结果如下:

 

只有constructor有优先级,指定优先级时,先执行优先级小的,再执行优先级大的,最后执行没有指定优先级。0-100是保留优先级。 

       另外在升级接口时,希望用户使用新接口,而不希望用户再使用原来的旧接口,可以利用类似如下的机制提示用户,当用户在自己的项目使用旧接口时,编译器就会发出警告,提示旧接口已经废弃:

__attribute__((deprecated("该方法已废弃,不建议使用"))) void load_file4()
{

}

int main(int argc, char **argv)
{
    load_file4();
    return 0;
}

4. 总结

  • attribute((constructor)) 先于main()函数调用,attribute((destructor)) 在main()函数后调用
  • 只有constructor有优先级,指定优先级时,先执行优先级小的,再执行优先级大的,最后执行没有指定优先级。0-100是保留优先级。
  • 可以利用attribute和deprecated对不建议使用或废弃的接口进行说明。

注意:attribute((constructor)) 、attribute((destructor))功能仅仅适用于GNU的gcc编译器,在  MSVC编译器中使用会编译报错,提示不支持。

参考链接:

【1】:https://blog.youkuaiyun.com/m0_51139226/article/details/126457719

【2】:https://cloud.tencent.com/developer/article/2008172

在C语言中,`__init` 宏定义为 `__attribute__((constructor))` 的用途是将某个函数标记为构造函数,使得该函数在程序启动时自动执行,而无需显式调用。这种机制常用于模块的自动初始化,确保在程序运行前完成必要的准备工作[^1]。 ### 用途 1. **自动初始化**:适用于需要在程序启动时自动执行的初始化逻辑,例如注册回调函数、初始化全局数据结构、加载配置等。 2. **模块解耦**:通过构造函数机制,各个模块可以在不依赖外部显式调用的情况下完成自身的初始化,降低模块间的耦合度。 3. **资源预加载**:可用于在程序开始执行前加载所需的资源,例如网络库的初始化、数据库连接池的准备等。 ### 用法 使用 `__init` 宏修饰的函数会在 `main` 函数执行之前被自动调用。其基本用法如下: ```c #include <stdio.h> #define __init __attribute__((constructor)) #define __exit __attribute__((destructor)) __init void initialize_module() { printf("Module initialized before main\n"); } __exit void finalize_module() { printf("Module finalized after main\n"); } int main() { printf("Main function executed\n"); return 0; } ``` 在这个示例中,`initialize_module` 函数会在 `main` 函数执行之前自动运行,而 `finalize_module` 函数则会在程序退出时(即 `exit` 被调用之后)执行[^3]。 ### 注意事项 - **调用顺序**:多个构造函数或析构函数之间没有确定的执行顺序,因此应避免它们之间的依赖关系。 - **线程安全**:构造函数在单线程环境下执行,因此不需要考虑多线程同步问题。 - **错误处理**:构造函数中发生的错误通常难以处理,因为此时程序尚未进入 `main` 函数,错误信息可能无法正确传递或记录。 ### 应用场景 - **库的初始化**:开发库时,可以使用构造函数自动注册内部组件、初始化全局状态。 - **插件系统**:插件可以在加载时通过构造函数自动注册到主系统中。 - **测试框架**:在测试框架中,构造函数可用于注册测试用例。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值