Correct. You can use it in a simple way to free memory, but we've also used it to create scoped locks[1].
This being C, it's not without its problems. You cannot use it for values that you want to return from the function (as you don't want those to be freed), so any such variables cannot be automatically cleaned up on error paths either. Also there's no automated checking (it's not Rust!)
Note it's {...} scoped, not function scoped, which makes it more useful than Golang's defer.
Even with scope-based defer, you can accomplish conditional defers easily enough. In a sane language, where conditions are expressions, you could just do:
defer if complex_nested_condition { cleanup() } else { noop() }
In Go, you could do:
defer func(run bool) {
if !run { return }
}(condition)
Which admittedly wastes stack space with a noop function in the false case, but whatever.
I feel like the number of times I've needed conditional defers is almost zero, while the number of times I've had to make a new function to ensure scoping is correct is huge.
Of especial note, 'mu.Lock(), defer mu.Unlock()' not being scope-based is the largest source of deadlocks in code. People don't use 'defer' because the scoping rules are wrong, code panics before the manual unlock call, and then the program is deadlocked forever.
Interesting. I'm not very proficient in C, this looks like some sort of finalizers for local variables?