I found some ambiguous wording in the c89(/c90) standard, where GCC and Clang disagree on the interpretation. It concerns the behavior of implicit function declarations, which were removed in c99, so this was never disambiguated.
↫ Sebastian at sebsite
I am not going pretend to understand any of this.

Undeclared functions was always a terrible feature from day 1. Even if you don’t intentionally use bad features like this, I’ve seen many code bases inadvertently trigger them through through language defaults and end up tripping on them anyways. No code should rely on this; it would be best to delete the feature altogether so it never comes up again, but…backwards compatibility.
This is the problem with C roping in historic quirks for backwards compatibility versus a modern language that doesn’t carry legacy baggage. Even if we want to overlook C’s propensity for memory faults, I still hate some aspects of the language like CPP/include files/forward declarations. Practically all modern languages have solved this. Alas, despite C’s shortcomings everything relies on it and it’s too big to fail so we keep propping it up. Despite all the baggage it’s still useful obviously, It is what it is.
> it would be best to delete the feature altogether so it never comes up again, but…backwards compatibility.
Implicit function declarations were removed in c99…
If you compile using -std=c99 (and newer), compilers won’t allow you to use this feature. How is it wrong to keep it supported in the language version it is valid in?
> Practically all modern languages have solved [include files]
how have modern languages solved include files? The only ones I know are language package mangers, which create a clusterfuck on a system using a normal package manager. (my hatred for pip has no bounds…)
C++ modules are a similar mess (minus the package manager), which is why they’re not being widely used. C++ modules brought to us courtesy of corporations only using monorepos.
The trouble here is that once you try making something more complex than include files, you need to specify some kind of an ABI, which C/C++ famously don’t.
You also could argue that the problem of C/C++ is there being multiple compilers for a single platform…
Serafean,
The most visible example are the templates.
There have been many different attempts to “precompile” templates. I think Visual C++ had one at one point too, but need to double check. And all of them failed.
Basically C++’s most powerful feature, compile time programming, i.e.: meta programming, is also its worst double edged swords.
Templates are turing complete
The new constexpr path, which is supposed to make it cleaner, is also in the same category
And worse? The macros are also turing complete in some implementations
(I’m not even touching “concepts” that were added to templates, external template processors like Jinja, just the basic language)
If one language has at least 3 different turing complete meta programming methods, of course it would be extremely difficult to manage.
But it also is one of the most expressive languages out there, outside of Lisp or Python (both of which are interpreted)
Boost, for example has several DSLs entirely within C++
For example,
https://en.wikipedia.org/wiki/Spirit_Parser_Framework
Entire parse framework!
My favourite is CTRE : https://github.com/hanickadot/compile-time-regular-expressions
This is way cool.
And now with C++26 and reflections, I guess the fun continues.
Serafean,
Sure, it’s just one of those features that should never have existed. Many projects using the defaults are still affected. Sure we can blame projects for using bad defaults, but I still feel justified in saying the defaults are bad and should have never existed.
I’m not referring to package management, include files themselves are poorly integrated because CPP is a hack on top of the C language, it’s not an elegant solution for any of the problems it gets used for.
This can have very severe consequences because unlike most other languages in existence it creates scoping and side effect issues.
99% of the time when I use include files it is to use it’s function prototypes. But languages that support interfaces this can cause all manor of chaos including changing the syntax tree of the file doing the including. I’m ok with languages having include constructs that affect the current scope, but the side effects present in C are rarely desirable the majority of the time and it should never be the default. Yet with C there’s no choice. And it has negative implications for the compiler’s ability to precompile code too.
Include files often need recursive inclusions. This would be ok if it had it’s own namespace, but since C has no concept of namespaces, the result unintentionally clobbers one’s own namespace with completely undesirable transient inclusions. The preprocessor makes these side effects unavoidable. Other languages have far better scoping rules, not to mention getting rid of the need for annoying forward declarations. Just think of how much worse languages like javascript would be if it had to use forward declarations…yuck.
Most of C’s shortcomings could have been avoided with the benefit of hindsight, but I don’t really blame the authors, how could they have known what today’s language authors do? Heck, software was being written in assembly and C is clearly a huge step up.
Clearly interoperability standards are needed for compilers to be able to call each other’s code. Most languages are compatible with C object files and C’s calling conventions get supported as a defacto standard. Even C++ doesn’t get this treatment. This is unfortunate because C++ does address some of C’s shortcomings, albeit adding some of it’s own.
Calling conventions are one thing, compilers accepting each other’s AST (which is what modules would currently require) is a whole other story.
> I’m not referring to package management,
I understand that, however wherever I look, files, imports and packages are tightly coupled concepts. That coupling is what C++ aims to avoid. I look mainly at python and rust though. If you have an example where it’s done differently, I’d be happy to read about it.
Serafean,
The world standardized on C compatibility, and unfortunately that holds everything else back to very limited features. Even relatively simple features like namespaces are impacted. I wouldn’t really care so much that C doesn’t have modern features if it hadn’t become the defacto standard, but it did and that’s what the complaint is about.
The thing is almost every other language does it better without having C’s preprocessor, whether it be pascal for decades back or C#, Java, and new languages like rust too. In short, In short, it was always just a hack for an immature language and not something that was well planned.