Yep, for plugin systems, etc, then C might be a good choice.
I'm assuming that if you have any sort of modular interface between libraries, using a C interface is the way to go.
It can be dynamic, as in, a .dll or .so file... but yes, it's not replaceable by the user, unless they create a build from the same compiler.
Well, for it to be dynamic, it'd have to have a C interface, making the point moot
From a professional QA perspective, that kind of environment isn't feasible. When shipping a game, QA needs to be able to reliably reproduce the same binary distribution on every machine (e.g. if a bug comes in from a user, you need to be able to replicate the user's installation on one of your PCs).
I am used to the ideology that I distribute a library that an application depends on, and I can swap out that library with a newer version if I choose, while having multiple applications still use it
In a world where one of your dependencies is installed into some shared system directory, and is outside of your control, you basically just can't do QA on the game.
So in order to be able to be in control of the quality of the product you're selling, you need to bundle all of your dependencies into the game's installation, whether that be putting the .dll/.so files in the same place as the executable, or statically linking those libraries into the executable file itself. In this case, there is going to be a shared compiler between all components, so you (the game author) can update DLLs with new versions.
I understand the system-wide library is a popular methodology, especially on Linux systems... but a big studio would never be able to release a game using that model.
In that case... If you're writing "good" C++ code, then you should be using std::vector, and shared_ptr and unique_ptr to do your memory management... but in the embedded case, then the oft-criticized "C with classes" style of C++ can actually be useful.
Well... Yes, actually. My phone has 23mb of usable RAM
The old PS2-era engines that I worked with were all written in C++, but in a very C-style of it (no usage of std::*, no constructors/destructors, etc).
Personally, I still think that RAII and proper use of the rule-of-three, and constructors/destructors is key to good C++ code in any style (whereas the typical "C with classes" style usually shuns these C++ concepts, I'd still recommend using them), but for embedded systems, I do shun pretty much any part of std::* that deals with memory.
However, the point I was making before is, I also shun C's malloc/free in these situations.
In my engine I use a custom new keyword, which uses a "scope" allocator, which internally uses a stack allocator. The vast majority of the engine is scope/stack allocated in this way (I can count the malloc calls on one hand), and although this type of allocator doesn't support random free/realloc semantics, it instead makes allocations almost free, almost eliminates fragmentation, removes leaks like RAII smart pointers (but without the burden of ref-counting or GC), still respects C++ destructors, and makes your memory allocation patterns extremely predictable, which is great for RAM-constrained situations.