Despite the cleanup attribute is an extension that supported by GCC/Clang only, I think it s the nearest approximation to RAII in pure C. e.g.
#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
}
Though, the attribute works only with auto scope but not function parameter. i.e.
void func(loc_str char *str)
{
return; // XXX - str will not be freed (compiled without any warning)
}
I already know above situation, but, why? Is there any reason to create such restriction?
-- Update --
A full story that trigger this question:
I tried to create a shared pointer(or smart pointer) for C. Following is a non-thread safe and simplified snippet
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;
}
That's why I wish the cleanup attribute can work with function parameter - eliminate manually free as much as possible.
main
should beint
(I think you already know that because you have areturn 0;
at the end) – Abutmentloc_str char *s = malloc(10); func(s); return 0;
Double-free bug. – Levitate