My C style

I got into C by wa of OpenGL and 3D graphics. My first attempts were just copy-pasting from the web and trial-n-error. I was already a farly proficient Perl developer, so I understood well enough that there was a "right way" and a "wrong way" and that the "wrong way" in C didn't just mean bad code - it also meant Segmentation faults and the specture of exploitable and insecure code. So what was the "right way" to program in C? I searched and searched and even bought a physical book to reassure me. I tried to make my own way and do things that felt right to me. This is the current state of that journey. I think it goes without saying that it is not the only way (in fact it might be the wrong way), but if you are searching for a path it might give some helpful hints.

Use stdint (uint8_t and friends)

I try to use these types wherever possible because they make so much sense to me. I just can't remember the length of an unsigned long long so I like that these types explicitly have their lengths right there in the name. On a standard platform an unsigned char is an 8-bit number from 0 to 255 (256 possible values). A signed char (or usually just char) is also 8-bit but covers a range -127 to +127. In stdint these correspond to uint8_t and int8_t which makes so much more sense. The name "char" implies we are talking about ASCII characters which causes problems when you start thinking about multibyte characters in unicode, or if you are just trying to manipulate some arbitrary chunks of memory.

#include <stdint.h>

Building

I use make for building my programs and I write my Makefile's by hand.

Hand rolled Makefiles

I tried to understand autotools and ./configure but just never got my head around it. I understand the problem that toolchain tries and I feel bad about not using it, but I also write C code as a hobby and just don't expect to ever need to compile my code for anything apart from my platform. My typical Makefile boilerplate looks like this:

CC = gcc
CFLAGS = -Wall -Werror -ggdb

LD =
LD += -lsomelib
OBJECTS =
OBJECTS += mylib1.o
OBJECTS += mylib2.o

all: myprog

myprog: myprog.c $(OBJECTS)
    $(CC) $(CFLAGS) $(LD) -o $@ $(OBJECTS) $<

%.o: %.c %.h
    $(CC) $(CFLAGS) $(LD) -c -o $@ $<

clean:
    rm -f $(OBJECTS) myprog

I then have sources myprog.c, mylib1.c + mylib1.h, mylib2.c + mtlib2.h. I can then add new objects easily as I break my code up more into components or add new components.

I put all my sources in the root directory

When I first started new projects I would create a src/ directory for all my .c files, an include/ directory for all my headers and a lib/ directory for my intermediate .o libs. I found this really clunky after a while and I started to notice many other projects didn't do this. My largest projects (which are not very big) are not a problem with all source files in the root directory of the project.

Components

I try to build components of my system as separate [component].h and [component].c files and compile them to [component].o files. Mostly I treat these components as sort-of object classes, meaning they usually have at least one main structure definition, and that structure (or a pointer to that structure) is the first parameter to most of the functions in that component. When I first started using this pattern I would create a constructor function that malloc'ed a struct pointer into existence, but I have since decided that malloc doesn't belong in components.

Kinda OOP but not really

I first saw these components as object classes and I got into a pattern of always having a constructor and always passing a struct ref as the first argument. I have since decided it doesn't make much sense to be so dogmatic, and my components now just have related functionality in them. So a component file might have several related structs and typedefs - things that you might use as one unit. I try to write them such that the might make sense distributing as a shared object for more general use.

Naming conventions

I tend to prefix my functions and structs with the name of the component file. For example if I have mylib1.c and mylib1.h I might have:

void mylib1_do_a_thing(mylib1_obj_t* object, uint8_t arg) {
}

This helps to avoid clashes with other libs and code.

Abusing typedef struct

I really abuse typedef struct a lot. For me it makes for less writing later on:

typedef struct mylib1_obj {
    uint8_t field1;
    uint32_t field2;
} mylib1_obj_t;

How they look in my Makefile

I append the objects in my Makefile so it's easier to add new components as I go:

OBJECTS =
OBJECTS += mylib1.o
OBJECTS += mylib2.o

I then have a generic template for compiling object files:

%.o: %.c %.h
    $(CC) $(CFLAGS) $(LD) -c -o $@ $<

And I can clean them in one go:

clean:
    rm -f $(OBJECTS)

Header include tree mess

When I started developing I would try to keep my includes organised so I was only #include'ing a single file once. I felt that accidentially including a header more than once indicated the project was somehow structured incorrectly. However I eventually got tired of tracking down redefine errors. So now I use the o'l #ifndef BLAH #define BLAH dance:

#ifndef MYLIB1_H   <-- first line of mylib1.h
#define MYLIB1_H

... all the code

#endif             <-- last line of mylib1.h

This means I can include the header where it needs to be included, and not worry about redefine errors or maintaining some complex tree of dependencies.

Avoiding global variables (mostly)

I prefer to avoid global variables, pretty much at all costs. I consider them in my main.c if they represent some "program state" variable that I know for sure will only exist once.

Memory safety

I used to think having object "constructors" and "destructors" was good enough as long as I remembered to destroy everything at the right moment. Then I realised the stack was a nice place to create variables that would get thrown away automatically.

Using the stack for storage

I read something recently that suggested avoiding this, but I find it too useful to follow that advice:

void my_function() {
    uint8_t list_ints[10];
    ...
}

There list_ints gets created on entry to the function, and automatically destroyed when the function exits. It's like a garbage collector!

Arrays of 256 items

If I make an array, and that array will have in the order of 265 items, I will make it 256 items long and use a uint8_t as an index. That way if I make a mistake and keep incrementing my index variable I can't overflow the array. For example if I am accepting a chunk of text as imput often I don't need to accept more than 256 bytes at a time, so I just make it 256 bytes long and use a uint8_t to index it. And then just set list_ints[255] = '\0' just to make sure.

Malloc does not belong in components

I don't think malloc belongs inside components or libraries in the general case. It assumes that there is only one way to manage heap memory when using the component. If there is no way around it I will consider a separate component that does heap management and interacts with the locic component.

Moved to initializers

I stopped using constructors that used malloc, and started creating initialisers that could set default values on struct pointers passed in. That gives calling code freedom to manage memory how they see fit, but giving them a useful tool for initialising their things:

mylib1_obj_t list_of_objs[2];
...
mylib1_init(&list_of_objs[0]);
mylib1_init(&list_of_objs[1]);

Testing components

Not too happy with test frameworks

I searched and read about

Rolled my own

Dependencies

Haven't figured this out yet

I like single header dependencies

Notes and misc

You don't "run" C

Think like a PDP-11

https://queue.acm.org/detail.cfm?id=3212479)

Time to learn CMake