背景
假设你在开发一个基础组件x,然后你设计了一个x_init接口用来初始化这个组件,相应地你设计了一个x_deinit来去初始化。这样其它模块要用到这个组件时,先调一下x_init, 用完了再调一下x_deinit。init和deinit这是一对很常见的接口,这种用法也很常见。
那么问题来了,假设同一进程中使用你这个的组件不止一个,比如A,B两个独立模块都要使用,甚至A和B都不知道对方的存在,它们最开始都要调用x_init, 这个只要在内部实现做好保护,x_init多次调用,并发调用也能hold得住 。但是对于x_deinit该怎么调用,该谁去调用呢?
假设A先退出,然后调用x_deinit, 这时,如果B还在使用就有可能出现问题。如果A不调deinit, 到B退出时,B也不知道还有没有人在使用x, 所以B也没有调deinit, 最后导致的结果可能就是资源泄漏。
如果是C++, 这个问题比较好解决,只需要定义一个全局变量,然后在析构函数里边调用x_deinit即可。
如果是C语言怎么办?我们需要的是在程序退出时,能够自动调用x_deinit.
__attribute__中的constructor和destructor
__attribute__可以设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)。__attribute__前后都有两个下划线,并且后面会紧跟一对原括弧,括弧里面是相应的__attribute__参数
__attribute__语法格式为:attribute ( ( attribute-list ) )
若函数被设定为constructor属性,则该函数会在main()函数执行之前被自动的执行。类似的,若函数被设定为destructor属性,则该函数会在main()函数执行之后或者exit()被调用后被自动的执行。例如下面的程序:
#include <stdio.h>
#include <stdlib.h>
static int * g_count = NULL;
__attribute__((constructor)) void x_init()
{
printf("Constructor is called.\n");
g_count = (int *)malloc(sizeof(int));
if (g_count == NULL)
{
fprintf(stderr, "Failed to malloc memory.\n");
}
}
__attribute__((destructor)) void x_deinit()
{
printf("destructor is called.\n");
if (g_count)
free(g_count);
}
int main()
{
return 0;
}
程序执行结果如下:
利用destructor这个属性,我们就能做到让程序退出时自动调用deinit函数,完美解决可能的资源泄漏问题