The ugliest C feature: <tgmath.h>
carolina.mff.cuni.cz
carolina.mff.cuni.cz
...
if one operand is a null pointer constant, the result has the type of the other operand;
This is quite possibly one of the most idiotic semantics I've ever seen specified for a language. Why does the type of an expression depend on the partially evaluated result of one of its subexpressions? This by itself is enough to throw out the window any sane reduction semantics (an important mechanism in language design whereby semantics are defined by specifying how to transform any program into an equivalent but strictly smaller program), though I'm sure there are plenty more such constructs in C.
Ridiculous semantics like this are not only the definition of C99; they're the definition of designed by committee.
In the above case, I wonder if it's related to
#define NULL ...something that is a null pointer constant...
and then wanting expressions like test ? some_double_pointer : NULL
test ? NULL : some_int_pointer
to have the type one would "expect" rather than whatever type NULL has in isolation.I'm not sure why the definition of a null pointer has to be so liberal, but I suspect there's a backwards compatibility constraint in there.
"In C++, the definition of NULL is 0, so there is only an aesthetic difference. I prefer to avoid macros, so I use 0."
Though there are cases in which passing an unadorned "0" as a null pointer can be wrong; there's a null-terminated varargs function example here:
Nowadays we shouldn't be dealing with pitfalls and complexities which resulted from thirty years old design decisions. New versions of a language should reduce complexity of a language instead of increasing it (for the same semantics).
I'm very happy of having left my job as a C++ programmer: I don't have to deal anymore with such madness. And I'd bet that many members of C++ committee are Ph.D. and beyond, showing once more that the more you study, the more you are in danger of losing contact with reality and practicality.
This will always be an issue when dealing with a language as entrenched as C: Most of your user-base will have oodles of code written under the previous standard. If you break backwards-compatibility on a level that's going to take them multiple man-months just to get working again, most aren't going to bother and will stay with the older standard instead. Your new standard stagnates and dies.
There IS a way to do this, but it's not nearly as simple as you seem to believe. Take a look at what's happening with Python 2 & 3. It's a difficult, albeit possible, transition, and I'm not sure how often exactly one can reasonably expect to pull it off.
I agree that old code is sacred and I agree that handling backward compatibility is not an easy task, but carrying decades old baggage isn't a solution either, if you ask me. I remember how one day while I was coding in C++ and I realized how much baggage I was carrying in my mind at each step while writing code.
Of course programmers will not switch to the new standard if it does not provide enough advantages, but then history shows that people are willing to even switch languages if their current one becomes an unmanageable mess.
As PG said in an essay of his, different versions of a language are different languages. Then just treat them as such.
Also, I've long wondered why complex numbers were added to C. They seem like an inappropiately feature for a systems language.
I assume complex numbers were added for the same reason as tgmath.h: a mostly failed attempt to get people to switch from Fortran to C.
Edit: I guess that isn't so ugly as it is completely pointless. There's a difference.
Even if this isn't necessary today, it's a pretty interesting brain exercise.
In fact clang recently added an extension to c for function overloading in a clean way.