Notes from How I program in C by Eskil Steenberg
In the beginning you always want Results. In the end all you want is control.
This post is a collection of rules of programming that I made for myself by watching Eskil Steenberg talk "How I program in C"
I highly recommend you watch the talk before reading the my rules, and making your own rules instead of blindly copying other rules.
The talk consist of rules that Eskil applys to help him write better programs. Although Eskil made these rules for his C development, I'm going to use following rules for every language that I make. But some rules will be modify to better suit my language need. Because every language has it's own structure of doing things.
As Eskil said the goals of these rules is make our program code more maintainable and consistent. There are some other goals but these two I think are most important for good programmer.
Note some rules are my own and have no relation to anything that Eskil said.
Rules
Make technology footprint small
- Use the technology that has proven itself.
- The less things technology does is better.
- Less dependencies is good. You should not trust external world to your program to function.
- Good programmer have 100% control to his program.
- Make program that is going to last forever and ever.
Simple is good
- Having more code for simplicity is better.
- Having 10 lines of code don't have bug in is better then having 2 lines of that does have bug in it.
- So typing is not the problem.
- A programmer spend more time reading then writing.
- Explicit code is better then Implicit code.
- What you believe your code and what compiler believe your code should be the same.
Clever is evil
- Clever code is makes programmer think that they are doing something very smart, when they are doing something very dumb.
- Undefined behavior in program should be avoided.
Crashes are good.
- Crashes while is devlopment is good.
- Crashes make you fix thing.
- Debuggers are super cool if haven't used one.
- Language shouldn't be complix, it sould be simple as possible.
- If want to do something complix, you build tool that does that.
Wide code is good code
- Big variable & function names are good because they say explicitly what the program does.
Heres how I define things:
.c// Mutable Global Varibale are all capital & with underscore. GLOBAL_VARIBALE // Vriable & Function first letter are capital without undersore or spacing between words. MyVariable MyFunction() // Types are all small with underscore between words. my_type // Spaces are good, it makes thing easer to search. a = b a == b
Reuse names
Define word:
.carray // array of data type // enum node // links to other nodes entity // Generic networked things handle // Pointer opaqe data func // Function pointer or fuction used as fucntion pointer internal // Function internal to a module i, j, k // integers used for for loops f // floats Get Set // geting and setting data throug a fucntion count length found next previous list
Sequential Code is good
- The code that you seen sequential from top to bottom without good jumping one file to other or one function to other is very good.
- Hence, long function are good.
- Don't write code that handles other pices of code.
- One leval function are good.
Function Naming
Bad nameing:
.c// Bad beacuse what is object comes secondd what it does come first. // Hard to see function are for same object CreateObject(); DestroyObject(); MoveObject(); // Worse becase grammer doesn't match and same object fucntion but different // naming. CreateThing(); RemoveObject()
Good nameing
.c// Better becasue can know function are for same object. ObjectCreate(); ObjectDestroy(); ObjectMove(); // Best becase, it is more specific what function does. ModuleObjectCreate(); ModuleObjectDestroy(); ModuleObjectMove();
Function are better then objects
Thing = ObjectCreate();
ObjectDoSomething(Thing);
// is better then
Thing = Object();
Thing.DoSomething();
Pointer
Pointer should have same datatype that it's points to.
.cvoid *p; short *ShortPointer; int *IntPointer ShortPointer = p; IntPointer = p; short a = *ShortPointer; // Reads 2 bytes as a short int b = *IntPointer; // Reads 4 bytes as a int ShortPointer++; // Adds 2 bytes IntPointer++; // Adds 4 bytes if(ShortPointer != IntPointer) printf("Not thee same");
void
pointer are good when you don't want to tell where the pointer points to.Better way to write sizeof
.cdouble *a; a = malloc(sizeof (double)); // Breaks when you change type a = malloc((sizeof *a)); // Doen't break when you change type
Using pointer as counter
.c// This produce more byte code uint i; for(i = 0; i < 10; i++) // Under hood this code is doint `p + (sizeof *p) * i` p[i] = 0; // This produce less byte code void *end; for(end = &p[10]; p != end; p++) // Only raw pointer are used to add the value. *p = 0;
Structs
How to get
sizeof
structs offset types:.ctypedef struct { uint type; char name[32]; float size; } my_struct_type; uint offset = (uint)(&((my_struct_type *)NULL)->size);
Memory
Calcuation is faster then Access
uint a = a * a * a * a * a * a * a * a * a * a; // is fater then uint b = *a;
Array are better then linked list.
.c// Linked list typedef struct { void *Data; void *Next; } linked_list; // Not memory coherent for(l = List; l != NULL; l = List->next) do_something(l); // Memory coherent for(i = 0; i < ArrayLength; i++) DoSomething(&Array[i]); // Realloc rerely when adding if(ArreyLength == ArrayAllocated) { ArreyAllocated += 16; Array = realloc(Array, (site * Array) * ArrayAllocated); } Array[ArrayLength] = Value; ArrayLength++; // To remove last item Array[i] = Array[--ArrayLength]; // Backwards remove is also faster for(i = ArrayLength; i != 0; i--) { if(Array[i] == TheOneWeWantToRemove) break; Last = Save; Save = Array[i]; Array[i] = Last; }
Don't recomute the data
// Bad programming
typedef struct {
float Width;
float Length;
float Area; // Bad
// If you have width length then you can recomute the area.
// If somebudy updateds the Width & Length but forgot to update Area then
// error will happen.
// Also calcuation on data is easeir then storing the data or access the
// data
} my_plane
// If you have to save area, because recomputing the are more expensive.
typedef void my_plain;
void MyPlainWidthSet(my_plain *plain, float width);
float MyPlainWidthGet(my_plain *plain);
void MyPlainWidthSet(my_plain *plain, float length);
float MyPlainLengthGet(my_plain *plain);
float MyPlainAreaGet(MyPlain *plain);
Fix code now
- If you know your code should be different fix it now.
- Don't wait to fix it.
- It will harder to fix in future and you will not get more time to do it.
- It more rewarding then one might think.
Squre root aproxmiation
float DSqrt(float Number)
{
int i;
float x, y;
x = number * 0.5;
y = number;
i = * (int *) &y;
i = 0x5f3759df - (i >> 1);
y = *(float *) &i;
y = y * (1.5 - (x * y * y));
y = y * (1.5 - (x * y * y));
return number * y;
}
Random number genrator
uint FRandom(uint32 Index)
{
Index = (Index << 13) ^ Index;
return ((Index * (Index * Index * 15731 + 789221) + 1376312589) & 0x7fffffff);
}