Our Coding Standards
How To ANSI C
All of our production code is written in ANSI C. The motivation behind this is pretty simple. We want maximum compatibility and maximum simplicity. ANSI C might feel rather rudimentary by today’s standards (not even having a formalized way to guarantee a particular size for numerical data types) but there’s something to be gleamed from the simplicity, and most importantly raw power.
The myths of scary memory scribbles are well and truly myths today. With all the modern compiler features and static analysis alongside some sensible interfaces memory management is not the frightening beast we’ve lent it the moniker of. Most applications have precise requirements, and that goes for memory management as well. I’m not a fan of any blanket language feature solutions.
Once you set aside that particular prejudice, I think you’ll find the raw unlimited power ANSI-C provides is quite liberating. I never have to look anything up, the standard library fits on the back of your hand as do the primitive data types. Obviously we’re still pretty early in but I’ve been thoroughly enjoying my time with C.
Boring Stuff, brackets and other animals
I’ve been deferring to the original K&R text for this mostly. There’s really no reason to go to war over the style of brackets. Spaces I mean I can’t even start to really care. 2,4,8 are all sensible numbers. We’re just going to do 3 so nobody is satisfied. I prefer brackets on the line except for functions since it’s nice to easily see begin/end for those. functions snake_case, variables camelCase. structs/enums/unions double CamelCase or whatever that ice cream flavour is called. Like I can’t even really pretend to be passionate about this. Macros should be SCREAMING_SNAKE_CASE and that’s about it I think? Use ANSI-C comments obviously or it won’t compile. Just relax and have fun with it, as long as it’s consistent it really doesn’t matter.
Design Interfaces Well
Make interfaces consistent, usually add a prefix to help with namespace resolution. b_my_function or something to that effect. Always forward declare structs/unions, we don’t want to expose implementation details in headers. Stop people being able to access the internal data so they can’t do something silly. If implementation data must be shared between multiple c files put it in an b_stuff_internal.h. The goal is to completely replace the C standard library since it’s a bit dated and rubbish.
Great Code Is Debuggable
Make sure all your code is easily debugged from the start. The elegant interfaces will certainly help with that. Instead of using defines in the implementation it’s much better to swap out functions instead. Either hide behind a macro or something else. Make sure debug code can be called in shipping if you really need it. In the code mines you want many canaries at hand, I would suggest initializing to a garbage value that will crash quickly vs. initializing to a zeroed value. (easy to implement in your memory manager)
Have a great debugger. Just because you’re on unix doesn’t mean you have to suffer with prints. I recommend GDB with a frontend that matches your tastes. There’s lots of viable tui based ones.
Solve the Problems You Need to Solve
Don’t overdesign things. Only write the functions you need. Only pass the variables you need. Try not to repeat yourself. Prefer large, dumb and complete functions vs lots of cute small ones stitched together.
Respect the Compiler
The compiler is your best friend. Just because you’re in ANSI-C doesn’t mean you have to use a CRT and have 80 characters a line with no modern static analysis. Turn on all the warnings, -Wall, -pedantic, everything. Maybe not -WCRT and -WLongLines. Make use of platform specific safety features like address sanitization and shadow call stacks. Refer to either the GCC or Clang docs to find what you need, depending on your flavour (this is a clang household due to licensing but all compilers are welcome here).
Cheers, or how we say goodbye in C, segmentation fault