c语言自动内存回收(RAII实现)

简述

什么是RAII

RAII(Resource Acquisition Is Initialization)是c++之父Bjarne Stroustrup提出的概念。资源一般分三个步骤:获取、使用和销毁,而在自由使用内存的c语言中,资源的销毁常常是程序员容易遗漏的事情,让程序自动销毁资源也成为了业界的常规方案。

c实现

/* 入参是析构函数,在malloc资源时也指定free要使用的函数,尽量不在宏中写函数,减少后续定位的复杂度,并且也满足malloc和free的配对出现,可读性更好 */
#define RAII_FREE(FreeFunc) __attribute__((cleanup(FreeFunc)))

以上代码使用了编译属性__attribute__,在函数退出后自动回收资源,使用方式如下:

  • 示例一:释放内存
void MemFreeL2PointerAndSetNull(void *ptr)
{
    void **pptr = (void **) ptr;
    if (*pptr != NULL)
    {
        free(*pptr);
        *pptr = NULL;
    }
}

void TestFunc()
{
    RAII_FREE(MemFreeL2PointerAndSetNull) TestObj *obj = (TestObj *)malloc(sizeof(TestObj));
    obj->grade = 1;
}
  • 示例二:关闭文件
void CloseFile(void *ptr)
{
    void **pptr = (void **) ptr;
    if (*pptr != NULL)
    {
        (void) fclose(*pptr);
    }
}

void test_whenOpenFile_thenCloseFile(const char *flePath)
{
    RAII_FREE(CloseFile) FILE *handle = fopen(flePath, "r");
    if (!handle)
    {
        printf("open file filed");
    }
}
  • 示例三:释放锁
void ReleaseMutexLock(void *ptr)
{
    void **pptr = (void **) ptr;
    if (*pptr != NULL)
    {
        pthread_mutex_unlock(*pptr);
    }
}

void test_whenLock_thenFree(pthread_mutex_t *lock)
{
    RAII_FREE(ReleaseMutexLock) pthread_mutex_t *mutex = lock;
    pthread_mutex_lock(mutex);
}

理论依据

1、现有的语言c++、rust都有RAII的实现
2、 Linux平台下最常用的C语言函数库 (glib) 中的公共 API (g_autoptr)中使用了该能力
3、A simple defer feature for C(2021)这篇论文也讨论了c的defer关键字,即在函数返回之前执行,原理跟cleanup是一样的

考虑到__attribute__是编译属性,笔者调研了几款常见编译器,支持程度如下:

编译器 是否支持 参考
GCC 支持 https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html
Clang 支持 https://clang.llvm.org/docs/AttributeReference.html
MSVC 不支持,可以通过 _try 和 _finally 关键字实现类似的功能 https://learn.microsoft.com/en-us/cpp/c-language/try-finally-statement-c?view=msvc-170

优缺点

  • 优点:
    • 宏简单易用,并且没有在宏里定义复杂逻辑,debug和运维都很方便
    • 使代码结构简单画(减少变量/资源清理的代码或者goto跳转)
    • 降低内存泄露、打开文件、锁定互斥锁的风险
  • 缺点:
    • 不完全惯用的c风格
    • 需要依赖编译器来清理

扩展

  • 智能指针:FreeFunc中加入引用技术,则可实现c++中的智能指针功能

参考

RAII in C: cleanup gcc compiler extension

A simple defer feature for C

Add support for GCC-like cleanup attribute for plain C

你可能感兴趣的:(c语言,c语言,开发语言)