I'm seeing Rust with stricter aliasing rules, targeting microcontrollers, using the same high performing LLVM backend, without undefined behavior lurking behind every corner. That price is not a given.

The one advantage C has on portability is that for any given platform, you can probably be assured there's at least one buggy C compiler for it that doesn't properly comply with the standard out there. There's a niche for that, but it is a niche.

Otherwise? C and C++ aren't great at portability. I've spent a lot of time porting games, and a lot of that time has been spent porting C and C++. I can't even assume two compilers will compile the same defined-behavior code targeting x86-64 the same way - someone somewhere will have typedefed something in terms of long, and put that into a bytewise serialized struct, and one compiler will be LP64 whereas the other will be LLP64.

Add ARM, PPC, or even just 32-bit x86 into the mix, and suddenly previously working programs turn into multi-month fixup projects on medium sized codebases, before even touching all the system APIs I need to change because of the completely anemic standard library.

The further off the beaten path we go, the more likely we are to encounter straight up compiler bugs where not even the authors correctly interpreted the standard, so now we get to debug other people's compilers and write extensive test suites which we pray will catch all the codegen bugs. They won't, but they'll catch some percentage of them, at least.

There's a whole slew of porting issues that just don't show up in C#, Java, JavaScript, ActionScript, Lua, Python, Squirrel, or any number of other programming languages games and game tools use - that are uniquely terrible about C and C++. Maybe I fixup a few paths, maybe there's a few filesystem permission issues, maybe a few OS APIs don't work quite the same.

This is all time not spent optimizing your actual program, nor porting things in your program that are actually supposed to be platform specific.

C and C++ aren't the greatest languages from a performance standpoint either. I still can't convince MSVC to properly align stack variables consistently, so I must resort to my own stupid padding hacks or dynamic allocation. Not that this is sufficient to coax the optimizer into emitting appropriate SIMD instructions, so I resort to doing so by hand. The standard library completely lacks any means of actually doing this, and whatever middleware I choose probably lacks targeting for at least one platform or instruction set, so I get to port that too, taking extreme care to not introduce extra overhead via the supposedly "zero cost" abstractions these languages give me. Maybe it's just easier to write the same math N times instead. They're not JIT compiled, although function multi-versioning is finally spreading (quite late) to enough compilers that you might not need to choose between a baseline architecture or multiple binaries.

Then a constexpr function shows up in my profiling results, I see trivial static init invoked at runtime, I chase down another optimizer induced heisenbug and figure out if it's UB or a compiler codegen bug, and I'm left wondering why people are so busy defending int overflow being UB for some loop microbenchmark instead of fixing something that would actually help me with perf.