there's one ? the "C" ABI is just the ABI of whatever platform it's running on, which may or may not have funky behaviour that vendor-provided compilers kindly hide for you - functions being prepended with '_' on macOS, the two-dozen calling conventions on windows with i386, sysv and itanium ABIs...
Do you think you can tell what's the ABI of
struct foo some_function(struct bar);
?will bar be passed in a register, on the stack ? who knows, that depends on your platform, your compiler, etc etc. Things going cleanly on the stack is just a convenient lie that your first year comp. sci teachers tell you because it's too early to talk about how the real world works yet.
But typically the C ABI is the only stable ABI those platforms provide. That's a huge benefit.
https://developer.r-project.org/Blog/public/2020/11/02/will-...
https://developer.apple.com/documentation/xcode/writing_arm6...
https://docs.microsoft.com/en-us/cpp/cpp/argument-passing-an...
As far as the ABI goes: the important thing is that there is a standard ABI on a specific platform that all compilers on that platform agree on. Sounds kinda obvious, but it's not common in other languages.
it definitely is - see e.g. https://github.com/llvm-mirror/lld/blob/master/lib/ReaderWri...
Sure, there are compiler switches and language extensions that can break the ABI if you use them. But, well, you don't have to use them (at the interop boundary), and neither do your API clients.
What is the nit of it is it almost works. You have a good shot at getting it to compile in a short amount of time. The rest of the work will be lots of time in ye old debugger and going over the docs for your platform. The fun part is you will find bugs that were there already, or are they just part of the platform, or were you using it wrong?
In effect it is yet another CRT and the idea is sound. But many times what I found was you may have one that works on say linux and windows and bsd. All the same 'code' but you dig under the covers a bit and it is a maze of ifdefs so each platform has its own quirks. For example threading between fork and createthread is on the surface not too different and you can wrap createthread with it (several libs did). But you dig into it a bit and you find portions that just do not map at all between the systems (usually with IPC and locks). At best they do not compile, slightly worse they return error codes, at worst they act like they work.
A real good example of what I am talking about is the pthread library. It works up to a point but it is a very linux/bsd orientated library. There are some gaps in there from windows that just do not map and the other way around. What is worse is the docs on some of these do not talk about cross platform issues. Luckily you can see the source code of most of them and can tell what is going on. Annoying but one of the things I learned moving code between platforms is that each one has its own way of doing things. You can try to work against it or sit down and unwind what is going on, which takes time. I have even seen this sort of issue in python and java. Where you get down to some low level thing and it just is different on different platforms.
The moment you start doing things like threads or shared libraries, yeah, it all breaks down very quickly. But that isn't standard C.
But I can't think of any implementation that doesn't conform to C90 in that regard. So long as you don't venture into implementation defined / UB category...
[1] But you need to write a bit of C glue code for OCaml so it's not quite so seamless.
But the C ABI is awful to work with. The C language itself offers no help to guarantee ABI compatibility. What ABI it compiles for depends on headers, which may depend on a jungle of ifdefs and typedefs.
* It's everywhere
* There's a standard ABI