The article is about a subtle distinction that gets lost in casual talk about tail-call optimization. GCC had some tail-call support a long time ago, but C as a language was built around calling conventions that made proper tail calls awkward, especially for variadic functions, indirect calls, and old-style undeclared functions. Several commenters pushed on the article's historical framing. They argued that for non-variadic functions in post-C89 C, mismatched argument counts are already undefined behavior, so one big obstacle mostly belongs to K&R C and variadic cases, not modern C in general.
Where people landed was more practical than historical. The important fact is that ISO C still does not promise proper tail calls the way Scheme does, so portability stops at the language boundary. What changed is compiler support. GCC, Clang, and
MSVC-family toolchains now expose
`musttail`-style attributes that turn tail calls from a vague optimization into something you can ask for and get a compile error when it cannot be done. That is the line between "nice if it happens" and "safe to build around."
A lot of the energy went into whether
TCO is really just an optimization. Formally, many said yes. Practically, almost nobody treats it that way. If a tail-recursive interpreter, state machine, or continuation-passing program blows the stack when TCO disappears, then the space behavior changed in exactly the way developers care about. That is why several comments treated guaranteed tail calls as a semantic property in practice, even if language lawyers reserve "semantics" for something narrower.
The most useful pattern discussion centered on where this matters in C. Ordinary self-recursion can often be rewritten as a loop, but that misses the cases people actually want: mutually recursive state machines, interpreter dispatch, and continuation-passing style. Those patterns become much cleaner when a tail call can stand in for a jump across function boundaries. The catch is that C lacks first-class closures and still inherits
ABI constraints, so even with modern compiler help, this remains a powerful but sharp tool rather than a seamless language feature.