Out of Scope
Can C free something when it goes out of scope, the way C++ does with RAII? I have wondered for a while, mostly because the fetcher already has an atexit handler that frees my globals on the way out. That works for whatever lives as long as the process; it does nothing for the two arrays a single function allocates and drops. A bit of reading later, it turns out clang has the cleanup attribute, which runs a function when a variable leaves its scope (gcc has it too, though I build with clang). Wrapped in a macro, the whole thing came to eight lines in models.h:
static inline void auto_free(void *ptr) {
void **p = (void **)ptr;
if (p && *p) {
free(*p);
*p = NULL;
}
}
#define AUTO_FREE __attribute__((cleanup(auto_free)))
And the declaration in fetch_collection_pages became:
AUTO_FREE Website* pages = calloc(total, sizeof(Website));
No cycles saved; same free(), same moment. Is that an optimisation, then? Of a sort, though the cost it removes is mine. Every exit from that function, the early return included, used to be something I had to remember, and the only thing enforcing it was me. Now the compiler is. I do prefer a check in the build to a note in a wiki.
Of course, I broke it first. I had left the old manual frees in the early-return path, so when calloc failed the function freed each pointer by hand and the cleanup freed it once more. A double free, on a path my runs never took. An AI model, reviewing the change, caught it, and two lines were commented out. Worth saying plainly that the review was a model’s, and that I would take that second look again.
The cause was not the macro. It was two ownership schemes on one pointer; a new helper only helps if it takes over the whole job, and the old way goes out in the same change. True for a pointer, and truer for a team convention, I suspect.
The trade-offs, briefly. The attribute is an extension, not standard C; mine builds with clang, so I can live with that. And a pointer I want to return gets freed on the way out unless I hand ownership over first. I have not needed that yet. When I do, a small helper that returns the pointer and nulls the variable should do, and I will have to name it, which is not my strong suit.
The atexit handler still runs at the end, of course; it just has less to remember now.