I love programming in C (99) but I have to say that one of the things I'd throw out is everything that leads to header file dependency problems.
If I #include a file... I've included it. If it was already included, then I don't need to include it again. But, no, you have to put magic #ifdef's through 50 files to stop that happening.
If I #include a file, and it has a structure or other definition in it... then that structure of definition is defined. I shouldn't have to play games so that it is prototyped in files to let them know it's defined (even though they think it isn't yet) so that when it is actually defined, they know where it is (and god-forbid you define it twice).
Then the real magic comes when you want to include structures inside structures, when you want nothing more than a pointer to a structure inside a structure that you haven't defined yet (you don't need to know what type it is yet... it's just a pointer!), so you end up playing games with void pointers that then get cast to the proper type.
Then you have things like you miss off the include in one file - while processing 50 files simultaneously. There's no "I think you meant this, and I can't see any conflicting definitions" logic. Then you have "Do I define this structure before I include that other file, because it needs it, or do I do it after? Do I mess up my nice list of top-of-file includes just to resolve a header dependency issue?"
I actually kept a screenshot I got while coding a game with a copy of GCC - the whole code compiled except for ONE error. It said precisely this:
expected 'struct Player *' but argument is to type 'struct Player *'
Now, it doesn't matter how many times you read that, it's nonsense. It wasn't a typo, or similarly named but different structures, it wasn't a formatting error - the "struct Player *" in that error line is 100%, absolutely, totally identical (I know, I compared them in a text editor!) and there's only a single definition of that structure in all the code. It came about from some weird header dependency where it thought it had a perfectly defined struct Player, but had somehow managed to include the file twice, without it realising that it was the same file/structure inside. So instead of warning about duplicate definitions, the original "player.c/h" files believed they had the definition, but the function they were used in somehow included them via a roundabout route that thought that *it's* inclusion was the correct definition. And the compiler had obviously given the same structure, only ever defined in only one file, two different "internal" names that it thought were different.
It's 2020. I understand the *legacy* of things like C header file inclusion, the preprocessor, etc. but I do still think it's about time that we got a compiler switch which basically means "just sort it out". If you don't have a definition, hold onto that thought and come back to it later in the compile. If you have double definitions but they're from the same file or the structure is identical, maybe warn but carry on regardless. If you need the size of a structure pointer - it's a pointer. Put it in as a pointer. Worry about the structure inside it later. If I #include but the file is in a slightly different sub folder (e.g. #include "SDL/SDL.h"), work it out... warn me or let me choose as necessary. "Did you mean SDL2/SDL.h? Would you like me to modify the include line to reflect this in the future?".
There's no reason that a C compiler, even in "C99 mode", can't take the whole compile as a process, and colourise parts as it goes, leaving unknowns in a grey area until it can determine what was actually meant. Then filling in the gaps later piecemeal (not on a file-by-file basis as that would just leave you in the same position because it can't resolve the entire file, so nothing else is "ready" yet either). Then warning/asking the user exactly what they want to do.
Especially with things like preprocessor file paths (is it SDL.h or SDL/SDL.h?), it could just search, and tell you, because those things might well change from every different computer that code is compiled on. Work it out, present options, because those options are only ever going to affect me anyway, if I'm the one with the weird-arse include layout.
The problem with C is that it's a fabulous language to program in, but far too much time is spent faffing getting compilers, linkers, Makefiles (YURK! Why are we still using that junk?), etc. to work. I use it on several platforms, with several different architecture targets on each, with all kinds of libraries, and in several ways (command line, Makefile, CMake, Eclipse IDE, etc.) and it's the damn setup that takes most of the time to make sure that the libraries are linked when you use their headers, that they link in the right order to fulfill the stupid compiler's demands, that header inclusion takes place in the right way, etc.
It should just be... compile main.c - it includes a bunch of files, we know they are there in the same folder, pick them up and use them if we've obviously intended to, inform the user of what you did, link it with the libraries that included those headers (it's really not hard to have a #pragma or similar for this... if you include SDL.h, link against libSDL, if you include SDL_Mixer.h, link against libSDLmixer... it would take one line in the header!).
I get the reasoning, I know there's the chance of including the wrong version header, library or whatever, but that's not unresolvable (especially if the compiler/linker tell you what did!), and it's very easily avoided.
But recursive-header-includes are a pain in the butt. In some projects where I know the code isn't for public consumption, I just have a master header file and include it on every C file, and that master file includes everything else "in the right order". Why not? Literally a hundred files with just "include "main.h" " and then let the compiler sort it out. But that's not what the header files are designed to do... I should only be including what I need, I know. And the compiler shouldn't be as dumb as a bag of rocks when I miss something. But if it's going to be, then I'm going to make IT do all the hard, unnecessary work, because I'll be damned if the tool I'm using to make it easy to write tools I'm going to use is just going to spew errors at me and expect me to do the manual legwork. It's a machine... it should be doing that for me.