By now, most programmers are familiar with the defer mechanism, at least in the context of Go code. Many also recognize that it’s a “nice-to-have feature” that hasn’t been implemented by mainstream C compilers.
ISO C23 has no defer
. But of course, GCC and Clang have all the necessary C23 features and compiler extensions to implement defer as a simple macro. One defer C23 macro is described in Jens Gustedt’s article as a “simple defer, ready to use” solution. This macro works with both GCC and Clang right now in March 2025.
Here is GO lang defer, simply explained in the GO documentation
A defer statement defers the execution of a function until the surrounding function returns.
The deferred call’s arguments are evaluated immediately, but the function call is not executed until the surrounding function returns.
That is simple and exactly what Jens’ “simple defer, ready to use” accomplishes. A convincing way to demonstrate this would be to create a Godbolt example using the code from that article. Here is the Godbolt sample, with just the main function pasted below.
1 2 3 4 5 6 7 8 9 10 11 12 |
// GCC/CLANG defer in action int main (void) { // anchor block char* p = strdup("XYZ"); defer { FX(free(p)); }; // 1st defer char* p2 = strdup("ABC"); defer { FX(free(p2)); }; // 2nd defer // 2nd executed here // 1st executed here } |
The output is:
1 2 3 4 |
Program returned: 0 #0027 free(p2) #0024 free(p) |
Simple to implement for GCC/Clang enthusiasts. Importantly, defer {} blocks are executed in reverse order, with the last one running first. If you consistently use the defer mechanism in your GCC/Clang C23 code, the benefits are clear: your code will be safer and more resilient.
But, what about the rest of us?
Who are “us”? We are those using Visual Studio and Microsoft Visual C++ (MSVC). We need a simple, MSVC-friendly solution, even if it’s not perfect. And here it is without further explanation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#ifndef DBJ_DEFER_BEGIN_END_H #define DBJ_DEFER_BEGIN_END_H #undef macro_concat_ #undef macro_concat #undef macro_var #define macro_concat_(a,b)\ a ## b #define macro_concat(a,b)\ macro_concat_(a,b) #define macro_var(name)\ macro_concat( name, __LINE__) #undef beginend #define beginend( start,end) \ for ( int macro_var(_i_) = ( start, 0 ) ; ! macro_var(_i_) ; ( macro_var(_i_) += 1, end) ) // #define defer( at_scope_end ) beginend( __LINE__, at_scope_end ) #undef defer #define defer( at_scope_end) \ for ( int macro_var(_i_) = 0; ! macro_var(_i_) ; ( macro_var(_i_) += 1, at_scope_end) ) #endif // DBJ_DEFER_BEGIN_END_H |
It is a known “trick” of using the for () loop in a macro and its inherent behaviour. Here is a short explanation of how that works.
The first thing to notice is beginend
and defer
arguments are function calls. But: not executed because they are macro arguments. They are executed when it is their turn in the for loop “header”. Thus the following code is possible:
1 2 3 4 5 6 7 8 |
// using the beginend macro // make() and free_() are *not* executed on this line beginend( make_( &bufy_, 0xFF ), free_( bufy_ ) ) { // make_ is executed here memcpy( bufy_, "DATA", 5 ); printf("\nusing (%p) : \"%s\"", (void*)bufy_, bufy_); // free_ is executed here } |
Macro is just a text replacement mechanism. Ditto, the above is translated into this:
1 2 3 4 5 6 |
// __LINE__ was 123 for ( int _i_123 = ( make_( &bufy_, 0xFF ) , 0 ) ; ! _i_123 ; ( _i_123 += 1, free_( bufy_ )) ) { memcpy( bufy_, "DATA", 5 ); printf("\nusing (%p) : \"%s\"", (void*)bufy_, bufy_); } |
Notice the comma operators in the for() above:
1 2 3 |
// notice the 0 after the comma operator // after make_ is called , _123 value is set to 0 int _i_123 = ( make_( &bufy_, 0xFF ) , 0 ) |
The make_() function gets called, and then the comma operator is used with 0 appearing after the comma. This means the entire expression evaluates to 0, which becomes the value of the int _i_123 variable.
The for() {} body runs while _i_123 is 0. When it becomes 1, this part is executed immediately after the block ends:
1 |
( _i_123 += 1, free_( bufy_ )) |
_i_123
becomes 1, and then after the comma operator, that free_
gets called. And defer
macro uses the same “trick”, but with no beginning argument.
Here is a simple usage example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// a poor man's defer in action int main(void) { char* p = strdup("XYZ"); defer(FX(free(p))) // 1st defer { XF("%s", p); // 1st free is executed here } char* p2 = strdup("ABC"); defer(FX(free(p2))) // 2nd defer { XF("%s", p2); // 2nd free is executed here } printf("\n\n"); return 0; // using GCC/CLANG solution defer executes here } |
Yes, that also is not a defer {}
syntax that will eventually come with (apparently) the next ISO C after 23. Our required syntax is:
beginend ( block_begin_expression , block_end_expression ) { /* adjacent code block */ }
defer ( block_end_expression ) { /* adjacent code block */ }
That “adjacent block” is just a for block coming right after.
Conclusion
One could argue that this is merely an “illusion of deferring.” I might agree, but the important point is that it works “everywhere” and is currently workable (as of Q2 2025) also with Microsoft Visual C++. It’s worth noting that defer is not part of the ISO C23 standard. However, it can be easily implemented as a macro without using GCC/CLANG. And yes, this defer
implementation works with C++ as well. (My Microsoft C/C++ compiler version, right now, is 19.43.34808 for x86)
I last saw it here https://youtu.be/QpAhX-gsHMs