← dev

C Language

Dependable C

source

notes:

introduction:

Code should try to avoid relying on the user having the latest version of the compiler. There's also a recommendation to adhering to a subset of C89, which should be able to compile with any version of a compiler.

general advice:

concepts of c:

C standard targets an "abstract machine" where it defines what should be the expected "observable" behavior of any code. This allows compilers to optimize code and make changes as it sees fit for every target hardware.

there's a lot of content on UB and why it matters, and that's all good, but I would prefer to have some more specifics as to what are UB, like "reading from uninitialized memory is UB" so I'm taking notes on UB on a separate doc (C).

keywords:

types:

Floats and doubles are dependable but there are platforms that may for have a FPU and might emulate in software its behavior or completely miss it. Pretty much all implementations follow the IEEE 754 standard, however different implementations might handle rounding differently.

Use your own implementation of bool since anything not 0 is true. Preferably int

volatile This helps tell the compiler that a variable might change in ways not apparent to the compiler. ie. another thread modifies the variable. And so, it tells the compiler to avoid optimizing out any potential code paths related to an apparently unmodified variable.

volatile int  sth;          // value can change 
volatile int *sth;          // value the pointer points at can change 
int *volatile sth;          // the pointer itself can change 
volatile int *volatile sth; // well... anything can change 

operators:

Be careful when == a float. All floats not-a-number are unequal to themselves, so a isNaN check can look like if (f != f) printf("is Nan")

Null might not reside in the 0x0 address

A shift operator is UB if the left operand is negative or greater-equal than the number of bits in the type

c versions:

c99

c11

memory model:

dependable UB:


The power of 10

Source

notes:

  1. Simple control flow constructs
  2. All loops must have a fixed upper-bound
  3. No dynamic memory allocation after initialization.
  4. No functions bigger than a single print paper. ~60 lines
  5. Assertion density as min 2 per function. Side-effect free. Boolean tests
  6. Data objects should be defined at the smallest level of scope
  7. The return value of every non-void function must be verified by the caller.
  8. The use of the preprocessor should be limited to header files and simple macro statements
  9. The use of pointers should be restricted, to no more than one level of dereferencing.
  10. Compile with all warnings turned on and most pedantic setting. Rely on static code analyzers.

There seems to be value in some of this rules, but there are others that seem a bit arbitrary. I think, I in particular to my coding style don't really care about 4, 9 and 10. The rest are fine, with the caveat that 6 lack of definition for smallest level of scope allows for globals when needed.


Releasing

Include static files in binaries: https://pragmaticaddict.com/c_static_binary_data.html


Undefined behavior

#todo Sources:

The C FAQ defines “undefined behavior” like this:

Anything at all can happen; the Standard imposes no requirements. The program may fail to compile, or it may execute incorrectly (either crashing or silently generating incorrect results), or it may fortuitously do exactly what the programmer intended.

Some of these behaviors allow compilers to optimize certain code-paths as they can "assume" stuff.

Some examples:

Buggy code, may execute correctly, but an update on the compiler might bring issues. Compiler ends up been blamed for already incorrect code.


Hot Loading libraries

Linux: Remember to compile library with position independent flag. (in gcc its -fPIC)

Windows: Remember to compile as a dynamic-library. (in cl its /LD)

// Function definition 
typedef void (*my_function_def)(u8 var_1, char* var_2);

my_function_def my_function = NULL;

int main() { 
    my_library = dlopen("libfilename", FLAGS); 
    my_function = dlsym(my_library, "my_function_in_lib"); 
}

Sources: