Pointers
Pointer declarations should have the * right after the base type because it is part of the type.
Proof: You can do this in C, so the * is clearly part of the type:
typedef int* IntPtr;
IntPtr a, b;
Another proof:
int* LV = (int*)function();
This is casting the return value of a function to a type int*, which is also the type of LV.
Good form:
int* LVar0 = NULL;int* LVar1 = NULL;
typedef int* IntPtr;
IntPtr LVar0 = NULL, LVar1 = NULL;
Bad / confusing:
int *LVar0 = NULL, LVar1 = 7;
We consider C allowing such mixing of int* and int, as a bug in the standard.
Another reason not to put the * to the left of a variable name in declarations is that it usually means dereferencing a pointer:
*LVar = 42;
The location of the * should consistently indicate its function:
float* Func(const int* PSrc, int* PDst) // Declaring pointer parameters and a pointer return value
int* LP; // Declaring a pointer variable
*LP = 42; // Dereferencing a pointer.
LZ = LX * LY; // Multiplication (space on both sides of the *)
Prefixes
Prefixes are used for name spaces to make global searches easy.
For example:
E3d library types look like this:
E3dMesh, E3dPolyGroup
Image ("picture") library types use the 'Ep' prefix:
EpImage, EpPixelBuffer, EpToneMapper
Types are used as function name prefixes, before an "_'.
Examples:
E3dMesh_PolyGroupAdd(E3dMesh* PMesh)
EpImage_Resize(EpImage* PImage, ...)
This makes things familiar to C++ folks:
E3dMesh::PolyGroupAdd()
EpImage::Resize()
Function parameters are prefixed with a 'P' and local variables in functions are prefixed with an 'L'.
This makes it easy to tell them apart with a quick glance:
void E3dEdge* E3dMesh_EdgesAdd(E3dMesh* PMesh, const EIndex PCount, const EBool PInitialize)
{
E3dEdge* LNewEdges = NULL;
EIndex LI = 0;for(;LI < LN;LI++)
{
if(PMesh->Vertices.Count > 0) // I can immediately tell that PMesh is a function parameter and not a local or a global variable
{
}
...
}
return(LNewEdges);
}
Macro function parameters and local variables are prefixed with a lower-case "p" a lower-case 'l', respectively. This helps avoid clashes with parameters and local varialbles of "real" functions.
The use of "do" and "while(0)" are encouraged for macros, as the "while(0)" "swallows" the semicolon when calling the macro,
so macro function calls are consistent with regular function calls:
#define E3dMesh_SomeMacroFunc(pOldMesh) do\
{\
int lSomeCounter = 0;\
E3dMesh* lNewMesh = NULL;\
} while(0)
...
E3dMesh* LMesh = LSomeMesh;
E3dMesh_SomeMacroFunc(LMesh); // <-- ';' is swallowed by the 'while(0)' in the definition of E3dMesh_SomeMacroFunc
Global variables should be avoided, if possible, but if used, they should have some namespace prefix, to minimize clashes. Example:
EIndex E3d_MaxNumOfSomething = 42;
Static global variables and function names should start with an '_'
static int _SomeVariable = 42;
static void _SomeLocalFunction(void);
Comments
Switch / case
Format switch / case statements like this for good readability:
switch{LVariable)
{
case 0:
{ // Need a '{' because we are declaring variables
int LBlah = 72;
...
}
break;
case 1:
...
return(42);
default: // Always have a 'default'. It's also good practice to catch unhandled defaults with a warning
if(DEBUG) { printf("%s() %s:%d %sIMPLEMENT ME%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, ECodeNormal);fflush(stdout); }
break;
}