Coding Standards for pure C (not C++)

I have no professionnal experience on C (only on C++), so don’t take my advices, tricks and tips too seriously, as they are “object-like-oriented”.

Almost Object C?

Simulating basic object-like features can be done easily:

In the header, forward declare your type, typedef it, and declare the “methods”. For example:

/* MyString.h */

#include <string.h>

/* Forward declaration */
struct StructMyString ;

/* Typedef of forward-declaration (note: Not possible in C++) */
typedef struct StructMyString MyString ;

MyString *       MyString_new() ;
MyString *       MyString_create(const char * p_pString) ;
void             MyString_delete(MyString * p_pThis) ;
size_t           MyString_length(const MyString * p_pThis) ;

MyString *       MyString_copy(MyString * p_pThis, const MyString * p_pSource) ;
MyString *       MyString_concat(MyString * p_pThis, const MyString * p_pSource) ;

const char *     MyString_get_c_string(const MyString * p_pThis) ;
MyString *       MyString_copy_c_string(MyString * p_pThis, const char * p_pSource) ;
MyString *       MyString_concat_c_string(MyString * p_pThis, const char * p_pSource) ;

You’ll see each functions is prefixed. I choose the name of the “struct” to make sure there won’t be collision with another code.

You’ll see, too, that I used “p_pThis” to keep with the OO-like idea.

In the source file, define your type, and define the functions:

/* MyString.c */

#include "MyString.h"

#include <string.h>
#include <stdlib.h>

struct StructMyString
{
   char *      m_pString ;
   size_t      m_iSize ;
} ;

MyString * MyString_new()
{
   MyString * pMyString = malloc(sizeof(MyString)) ;

   pMyString->m_iSize = 0 ;
   pMyString->m_pString = malloc((pMyString->m_iSize + 1) * sizeof(char)) ;
   pMyString->m_pString[0] = 0 ;

   return pMyString ;
}

/* etc. */

If you want “private” functions (or private global variables), declare them static in the C source. This way, they won’t be visible outside:

static void doSomethingPrivate()
{
   /* etc. */
}

static int g_iMyPrivateCounter = 0 ;

If you want inheritance, then you’re almost screwed. If you believed everything in C was global, including variable, then you should get more experience in C before even trying to think how inheritance could be simulated.

Misc. Tips

Avoid multiple code-paths.

For example, multiple returns is risky. For example:

void doSomething(int i)
{
   void * p = malloc(25) ;

   if(i > 0)
   {
      /* this will leak memory ! */
      return ;
   }

   free(p) ;
}

Avoid non-const globals

This include “static” variables (which are not static functions).

Global non-const variables are almost always a bad idea (i.e. see C API strtok for an example of crappy function), and if producing multithread safe code, they are a pain to handle.

Avoid name collision

Choose a “namespace” for your functions, and for your defines. This could be:

#define GROOVY_LIB_x_MY_CONST_INT 42

void GroovyLib_dosomething() ;

Beware defines

Defines can’t be avoided in C, but they can have side effects!

#define MAX(a, b) (a > b) ? (a) : (b)

void doSomething()
{
   int i = 0, j = 1, k ;
   k = MAX(i, j) ;   /* now, k == 1, i == 0 and j == 1 */
   k = MAX(i, j++) ; /* now, k == 2, i == 0 and j == 3, NOT 2, and NOT 1 !!! */
}

Initialize your variables

Avoid declaring variables without initializing them:

int i = 42 ; /* now i = 42 */
int j ;      /* now j can have any value */
double k ;   /* now f can have any value, including invalid ones ! */

Uninitialized variables are causes of painful bugs.

Know all the C API

The C API function list as described in the K&R is quite small. You’ll read the whole list in 20 minutes. You must know those functions.

Wanna some experience?

Rewrite the C API. For example, try to write your own version of the string.h functions, to see how it is done.

Leave a Comment