尽管cleanup属性是仅由GCC / Clang支持的扩展,但我认为 是纯C中最接近RAII的近似值。例如
#define loc_str __attribute__((cleanup(free_loc_str)))
void free_loc_str(char **str)
{ if(str && *str) free(*str); }
int main(void)
{
loc_str char *s = malloc(10);
return 0; // Great! s is freed when it exit its scope
}
但是,该属性仅适用于自动范围但不适用于函数参数。即。
void func(loc_str char *str)
{
return; // XXX - str will not be freed (compiled without any warning)
}
我已经了解上述情况,但是,为什么?有没有理由制造这种限制?
- 更新 -
触发此问题的完整故事:
我尝试为C创建共享指针(或智能指针)。以下是非线程安全和简化的代码段
struct impl_t;
struct impl_t* ctor();
void dtor(struct impl_t* inst);
struct shared_ptr_s
{
struct impl_t* inst;
int *use_cnt;
};
void free_shared(struct shared_ptr_s* ptr)
{
if(!ptr) return;
if(0 == --(*ptr->use_cnt)) {
dtor(ptr->inst);
free(ptr->use_cnt);
}
ptr->inst = 0;
ptr->use_cnt = 0;
}
#define shared_ptr struct shared_ptr_s __attribute__((cleanup(free_shared)))
void func(shared_ptr sp)
{
// shared_ptr loc_sp = sp; // works but make no sense
return; // sp will not be freed since cleanup function is not triggered
}
int main(void)
{
shared_ptr sp = {
.inst = ctor(),
.use_cnt = malloc(sizeof(int))
};
++*sp.use_cnt; // please bear this simplification.
{
++*sp.use_cnt;
shared_ptr sp2 = sp;
} // sp.inst is still there since use_cnt > 0
++*sp.use_cnt;
func(sp); // leak!
return 0;
}
这就是为什么我希望清理属性可以使用函数参数 - 尽可能地手动释放。
答案 0 :(得分:6)
大概是因为,在函数参数的情况下,思维是调用者的,而不是被调用者,负责管理参数的内存。如果你确实需要被调用者在退出时释放参数,那么解决方法很简单,只需制作一个由cleanup属性装饰的参数的本地副本。
void func(char *str)
{
loc_str char *str1 = str;
return;
} // now str1 will be free when func exits
当然,在这种情况下,不要在调用者传递给func()
的参数上使用cleanup属性,否则你手上就会有双倍空格。
对于您的用例,我建议创建一个增加使用次数的宏,并声明类型为shared_ptr
的局部变量。
#define SHARED_PTR_GET_ADD_REF(sp_in, sp_name) ++(*sp_in.use_cnt); \
shared_ptr sp_name = sp_in;
在任何需要增加使用次数的地方使用该宏。因此,使用调试语句的示例如下所示:
#include <stdlib.h>
#include <stdio.h>
struct shared_ptr_s
{
// struct impl_t* inst;
int *use_cnt;
};
typedef struct shared_ptr_s shared_ptr_t; // unadorned type
#define shared_ptr struct shared_ptr_s __attribute__((cleanup(free_shared)))
#define SHARED_PTR_GET_ADD_REF(sp_in, sp_name) ++(*sp_in.use_cnt); \
printf("add use_cnt = %d\n", *sp_in.use_cnt); \
shared_ptr sp_name = sp_in;
void free_shared(struct shared_ptr_s* ptr)
{
if(!ptr) return;
printf("del use_cnt = %d\n", *ptr->use_cnt - 1);
if(0 == --(*ptr->use_cnt)) {
// dtor(ptr->inst);
printf("freeing %p\n", (void *)ptr->use_cnt);
free(ptr->use_cnt);
}
// ptr->inst = 0;
ptr->use_cnt = 0;
}
void func(shared_ptr_t sp)
{
SHARED_PTR_GET_ADD_REF(sp, sp_loc);
return;
}
int main(void)
{
shared_ptr_t sp = { // original type does not use __attribute__(cleanup)
// .inst = ctor(),
.use_cnt = malloc(sizeof(int))
};
SHARED_PTR_GET_ADD_REF(sp, sp_loc);
func(sp_loc);
return 0;
}