C Coding Convention
Improvements? Suggestions? email dna@hola.org
Consistent and Minimal
Most important rules: Be consistent. Be minimal. Read these sections carefully.
Text file layout
Tab size is 8 spaces.
Shift width (indentation) is 4 spaces.
Column width 79 char, so it fits well with 80 col terminal.
Indentation is always one shift-width(4):
printf("closing file %s on server\n", file_name);
printf("closing file %s on server\n", file_name);
Braces
open and close braces of a section should be on the same level.
if (close_files) { fclose(fp1); fclose(fp2); }
if (close_files) { fclose(fp1); fclose(fp2); }
Breaking a long line
when breaking up a long line, it should continue with one shift-width for indentation
if (line_length>1 && (screen->sz->vertical<buffer->sz->vertical || explicit_lines)) { printf("this is a test section that will show how to handle " "long lines, such as %s which is %d lines long", "this", 2); SetWindowText( hWnd, "Button Name" ); }
if (line_length>1 && (screen->sz->vertical<buffer->sz->vertical || explicit_lines)) { printf("this is a test section that will show how to handle " "long lines, such as %s which is %d lines long", "this", 2); SetWindowText(hWnd, "Button Name"); }
if for while statement
if/for/while statement that takes more than one line should always have braces; same thing goes when the then/loop part is more than one line
for (ret=0; ret<pciScan.dwCards;ret++) if (slot.dwBus==pciScan.cardSlot[ret].dwBus && slot.dwSlot==pciScan.cardSlot[ret].dwSlot && slot.dwFunction==pciScan.cardSlot[ret].dwFunction) break;
if (slot.dwBus==pciScan.cardSlot[ret].dwBus && slot.dwSlot==pciScan.cardSlot[ret].dwSlot && slot.dwFunction==pciScan.cardSlot[ret].dwFunction) { break; }
if (x==y) { my_func(param1, param2, param3, param4, param5, param6, param7); }
Functions prototypes space
function prototypes should be spaced like function calls
void do_keyboard_input( int key, char *name, int list_box, BOOL is_read, BOOL is_password ) { // functions code }
void do_keyboard_input(int key, char *name, int list_box, BOOL is_read, BOOL is_password);
// in header file void do_keyboard_input(int key, char *name, int list_box, BOOL is_read, BOOL is_password); // in code file void do_keyboard_input(int key, char *name, int list_box, BOOL is_read, BOOL is_password) { // functions code } // if you need to comment parameters void do_keyboard_input( int key, // Encryption key char *name, // encrypted user name int list_box, // list of destinations to choose from BOOL is_read, BOOL is_password) // destination specific psw { // functions code }
Unneeded casting
do not cast void *, this is unneeded in C.
estream = (estream_t *)malloc(sizeof(*estream));
estream = malloc(sizeof(*estream));
Do not cast function prototypes
do not cast function prototypes - if the implementation changes in the future, it will be hard to discover. the only exception is typecasting xxx_free() functions (functions from type void xxx_close(xxx_t *xxx)).
typedef void (*etimer_func_t)(void *); void event_timer_set(double ms, etimer_func_t func, void *data); void estream_read(estream_t *estream) { read(estream->fd); } void do_something(void) { estream_t *estream; event_timer_set(0, (etimer_func_t) estream_read, estream); }
void estream_read(estream_t *estream) { read(estream->fd); } void estream_read_et(void *o) { estream_read((estream_t *)o); } void do_something(void) { estream_t *estream; event_timer_set(0, estream_read_et, estream); }
void estream_read(void *o) { estream_t *estream = o; read(estream->fd); } void do_something(void) { estream_t *estream; event_timer_set(0, estream_read, estream); }
switch statement
switch statements should have the case on the same level, no
space before ':'.
if the case is one line, then one space after ':'.
never 'break' in 'default'.
switch (selection) { case KEY_UP : x--; break; case KEY_DOWN : x++; break; }
switch (selection) { case KEY_UP: x--; break; case KEY_DOWN: x++; break; }
switch (selection) { case KEY_UP: x--; break; case KEY_DOWN: x++; break; }
switch (selection) { case KEY_UP: x--; break; case KEY_DOWN: x++; break; default: invalid = 1; break; }
switch (selection) { case KEY_UP: x--; break; case KEY_DOWN: x++; break; default: invalid = 1; }
switch (selection) { case KEY_UP: key = UP; break; case KEY_DOWN: key = DN; break; case KEY_LEFT: key = LEFT; break; case KEY_RIGHT: key = RIGHT; break; default: key = NONE; }
Spaces
no space between function name and opening parenthesis
no space between opening parenthesis and first parameter
one space after comma:
printf ("hello %s\n", "world"); printf( "hello world\n" ); printf("hello world\n","world");
printf("hello %s\n", "world");
one space after reserved words before the opening parenthesis:
if(close_file)
if (close_file)
'then' part of if statement
'then' part of if statement should be in a separete line:
reason: allows to debug the if and see when it is run.
if (close_file) fclose(fp);
if (close_file) fclose(fp);
else if
else if
statements should be on the same level at the starting if
reason: this is similar to switch statement
if (!strcmp(argv[1],"--help")) print_usage(); else if (!strcmp(argv[1], "--run")) { run_application(); print_results(); } else print_error();
if (!strcmp(argv[1], "--help")) print_usage(); else if (!strcmp(argv[1], "--run")) { run_application(); print_results(); } else print_error();
return
return statements should not have parentheses and should not have a space after them:
return (0);
return 0;
return ;
return;
do not use unneeded parentheses in expressions
if ((a!=b) || (c!=d)) ...
if (a!=b || c!=d) ...
do not call return at the end of a function returning void
void f(void) { printf("hello"); return; }
void f(void) { printf("hello"); }
should not separate with more than one blank line between sections, functions etc.
structure and enum definitions
structure and enum definitions, itializations should open { at the same line. it is recommended that the name will end with a _t. typedef the struct, to make it easy to use later on.
typedef struct _WD_INTERNAL { int setup; int offset; } WD_INTERNAL; typedef struct { int setup; int offset; } wd_internal_t; struct wd_internal_t { int setup; int offset; };
// when forward structure definition needed: typedef struct wd_internal_t { struct wd_internal_t *next; int setup; int offset; } wd_internal_t;
typedef struct { int setup; int offset; } wd_internal_t;
wd_internal_t wd_initial = { 8, 16, };
Comments
prefer C++ comments to C comments
/* close all files */ fclose(fp);
// close all files fclose(fp);
Comments aligned
comments should be aligned as the code they comment, or one space after the end of the line.
/* * close all files */ fclose(fp);
// // close all files // fclose(fp);
/* close all files */ fclose(fp); GOOD better: // close all files fclose(fp);
fclose(fp); // close all files
Multiline comments
comments which occupy more than one line should adhere to the following guidline
// // close all the files that were opened before the function was called // and send them to the output file // fclose(fp);
// close all the files that were opened before the function was called // and send them to the output file fclose(fp);
Comments ident
Comment should never be ident more the 4 spaces even in
structures.
If longer, put the comment at the line above.
typedef struct { isa_pnp_id search_id; /* if search_id.vendor[0]==0 - scan all vendor * IDs * if searchId.dwSerial==0 - scan all serial * numbers */ int cards; /* number of cards found */ isa_pnp_card card[ISAPNP_CARDS]; /* cards found */ } wd_isapnp_scan_cards_t;
typedef struct { isa_pnp_id search_id; /* if search_id.vendor[0]==0 - scan * all vendor IDs * if searchId.dwSerial==0 - scan all * serial numbers */ int cards; /* number of cards found */ isa_pnp_card card[ISAPNP_CARDS]; /* cards found */ } wd_isapnp_scan_cards_t;
typedef struct { /* if search_id.vendor[0]==0 - scan all vendor IDs * if searchId.dwSerial==0 - scan all serial numbers */ isa_pnp_id search_id; int cards; /* number of cards found */ isa_pnp_card card[ISAPNP_CARDS]; /* cards found */ } wd_isapnp_scan_cards_t;
typedef struct { // if search_id.vendor[0]==0 - scan all vendor IDs // if searchId.dwSerial==0 - scan all serial numbers isa_pnp_id search_id; int cards; // number of cards found isa_pnp_card card[ISAPNP_CARDS]; // cards found } wd_isapnp_scan_cards_t;
Comments of XXX
Comments of XXX should be <keywords>: <comment>. multiple names should be separated with a '/':
XXX: derry: move to compat XXX derry: ANDROID move to compat XXX derry: arik: move to compat
XXX: move to compat XXX derry: move to compat XXX derry ANDROID: move to compat XXX derry/arik: move to compat
for or while loop
common sense should come into play when deciding whether to use
for
or while
loops
i = 0; while (i<10) { ... i++; }
for (i = 0; i<10; i++) ...;
for (p = p->next; p; p = p->next) ...;
while (p = p->next) ...;
while for if without a statement
while(pop_first(list)) ;
while(pop_first(list));
for(i=0; i<*p; i++) ;
for (i=0; i<*p; i++);
if (a>b+10) ; else if (a>b+5) do_x(); else if (a>b+2) ; else do_y();
if (a>b+10); else if (a>b+5) do_x(); else if (a>b+2); else do_y();
Spacing
One space after assignment
put one space before and after an assignment.
int a=0; x= x+1; bits|=BIT5;
int a = 0; x = x+1; bits |= BIT5;
Assignment in for loop
if it is in a for
loop, you can skip the spaces. if you skip one of the spaces,
skip both
for (i= 0; i; i--) printf("%d", i);
for (i=0; i; i--) printf("%d", i);
for (i = 0; i; i--) printf("%d", i);
No space before statement seperator
don't put a space before a statement seperator, put one after it
for (i=0 ;i ;i--, j*=2) ;
for (i=0; i; i--, j*=2);
for (i = 0; i; i--, j *= 2);
No space after increment/decrement
don't put a space after increment and decrement. increment after the value, not before.
i --; ++j;
i--; j++;
Don't increment, set or change value of variable inside a function call
calc_x_y(i++, 2);
// if i is not a global: calc_x_y(i, 2); i++;
// if i is a global that calc_x_y() uses: i++; calc_x_y(i-1, 2);
if (i++) a[i++] = 4;
set_x(i=3);
// if i is not a global: i = 3; set_x(i);
for loops assign inside a statement
in for loops, when there is a function that gets the next
element, it should be done once (inside the step
condition):
assign inside a statement:
for (i = 0, result = get_char(); result=='\r'; result = get_char(), i++) handle_result(result);
for (i = 0; (result = get_char())=='\r'; i++) handle_result(result);
truth value in if/for/while
when assigning in a truth value (if for while), use parentheses. This helps eliminating compilation warnings.
for (i = 0; result = get_result(); i++) handle_result(result); if (x = compute_num()) return x;
for (i = 0; (result = get_result()); i++) handle_result(result); if ((x = compute_num())) return x;
string initialization and zero termination
in string initialization and zero termination, you must use 0
string[0] = '\0'; if (string[3]=='\0') ...
*string = 0; if (!string[3]) ....
function return values
- in functions returning a pointer, return NULL on fail.
- in functions that only have success/fail, use 0 for success and -1 for failure (instead of TRUE and FALSE).
- in functions with many error reasons, use negative values for the reason.
- functions returning and integer that is "small" and positive, return -1 if fails. if 0 is an invalid success value, then you may choose to return 0 for failure.
- in functions that can return any integer, you have two
options:
- if its error is not important, then check if you can logically return 0 or anything else that will bring desired behaviour.
- if the error indication is important, then add another parameter to pass the error.
- functions that return boolean values (is_big, is_directory, is_download) should return 0 for false and non 0 for true, according to standard K&R C. (not to use FALSE and TRUE).
int do_load(void) { if (ioctl(image_fd, SIOC_LOAD, 1)<0) return FALSE; return TRUE; // action executed }
int do_load(void) { if (ioctl(image_fd, SIOC_LOAD, 1)<0) return -1; return 0; // action executed }
int is_loaded(void) { if (in_memory_count>=1) return TRUE; return FALSE; }
int is_loaded(void) { if (in_memory_count>=1) return 1; return 0; }
int is_loaded(void) { return in_memory_count>=1; }
return ok ? (device_t *) ptr : NULL;
#define EFLASH_READ 1 #define EFLASH_WRITE 2 return -EFLASH_WRITE;
implement error handling only once
implement error handling only once, by setting all variables to zero at the begining, and performing a goto to exit the function. If no error handling is required (i.e. no memory to release, no files to close, etc) - plain return should be used
device_t *open_device(char *dev_name) { device_t *dev; file *fp; dev = malloc(sizeof(*dev)); if (!dev) return null; bzero(*dev); dev->name = strdup(dev_name); if (!dev->name) { free(dev); return null; } dev->fp = fopen(dev->name); if (!dev->fp) { free(dev->name); free(dev); return null; } fp = fopen(std_name) if (!fp) { fclose(dev->fp); free(dev->name); free(dev); return null; } fclose(fp); return dev; }
device_t *open_device(char *dev_name) { device_t *dev; file *fp = null; dev = malloc(sizeof(*dev)); if (!dev) return NULL; // no error handling required BZERO(*dev); dev->name = strdup(dev_name); if (!dev->name) goto Error; dev->fp = fopen(dev->name); if (!dev->fp) goto Error; fp = fopen(std_name) if (!fp) goto Error; goto Exit; Error: if (dev->name) free(dev->name); if (dev->fp) fclose(fp); free(dev); dev = NULL; Exit: if (fp) fclose(fp); return dev; }
Do not use goto for anything other then error handling.
for (cnt = 0; cnt < sizeof(index_names)/sizeof(char *); cnt++) { struct stat sb; strcpy(index_name + filename_len, index_names[cnt]); // Check if the index file exists if (stat(index_name, &sb)>=0) { ... hc->sb = sb; goto Next; } } return http_dir_handler(hc); } Next: ....
for (cnt = 0; cnt < sizeof(index_names)/sizeof(char *); cnt++) { struct stat sb; strcpy(index_name + filename_len, index_names[cnt]); // Check if the index file exists if (stat(index_name, &sb)>=0) { ... hc->sb = sb; break; } } if (S_ISDIR(hc->sb.st_mode)) return http_dir_handler(hc); } ....
naming conventions
code will be in UNIX notation. UNIX names should not include the data type, rather the meaning of the information in the data.
int iCount; char *szName;
int is_completed; int8 data; int16 vendor_id; uint count; void *buffer; char *new_name; char *name; typedef struct {...} device_t; int read_data_block(void); void pci_detection(void);
checking function return values
functions that return int or a pointer will be checked without comparing, if they return 0 or non 0.
if (strcmp(str, "exit")==0) run_app();
if (!strcmp(str, "exit")) run_app();
if (quit_app()==TRUE) SendMessage(WM_QUIT);
if (quit_app()) SendMessage(WM_QUIT);
fp = fopen("test"); if (fp==NULL) return NULL;
fp = fopen("test"); if (!fp) return NULL;
char *buffer = locate_buffer(); if (buffer!=NULL) fill_buffer(buffer);
char *buffer = locate_buffer(); if (buffer) fill_buffer(buffer);
static functions when possible
functions
when possible use static when declaring functions which are only in scope for the file they're in. functions which are not declared static are considered a part of the API. if init() is the only function calling calc_init_time() then:
int init(void) { return calc_init_time(); } int calc_init_time() { return 5; }
int init(void) { return calc_init_time(); } static int calc_init_time() { return 5; }
variables
// inside c file struct cmd { char *name; int param_num; }; struct cmd commands[] = { {"init", 1}, {"shutdown", 0}, };
// inside c file struct cmd { char *name; int param_num; }; static struct cmd commands[] = { {"init", 1}, {"shutdown", 0}, };
allocating
when allocating or using a structre, use ZALLOC/calloc or
BZERO its elements.
try using BZERO instead of memset().
dev_t *dev = malloc(sizeof(dev_t)); locate_t locate; memset(dev, 0, sizeof *dev); memset(&locate, 0, sizeof locate);
dev_t *ZALLOC(dev); locate_t locate; if (!dev) goto Error; BZERO(locate);
Check constructor initing
perfer to check constructor initing in same line of calling the constructor. this allows variable re-use, and makes it more obvious the constructor may fail.
// GOOD but not prefered: FILE *fp; if (!(fp = fopen("data")) goto Exit;
// better: FILE *fp = fopen("data"); if (!fp) goto Exit;
// GOOD but not prefered: char *s = strstr(list, "hello"); if (!s) ...;
// better: char *s; if (!(s = strstr(list, "hello"))) ...;
macros
functions instead of macros
try using functions instead of macros. in most cases there is no performance problem.
#define CONVERT_TIME_TO_MILLI(t) \ (((t)->days*24*3600 + (t)->sec) * 1000)
int convert_time_to_milli(time_struct *t) { return (t->days*24*3600 + t->sec) * 1000; }
macro names
macro names should normally be all upper case, with normal spacing as rest of code
#define CONVERT_TIME_TO_MILLI( t ) (((t)->days*24*3600 + (t)->sec) * 1000) #define CONVERT_TIME_TO_MILLI(t) \ (((t)->days*24*3600 + (t)->sec) * 1000)
#define CONVERT_TIME_TO_MILLI(t) (((t)->days*24*3600 + (t)->sec) * 1000)
#define CONVERT_TIME_TO_MILLI(t) \ (((t)->days*24*3600 + (t)->sec) * 1000)
macro multi arguments
using ## for macro multi arguments (x...) will result in a compilation error when using gcc 3.3.x and up
#define IOW(func,a...) wfs_##func(##a)
#define IOW(func,a...) wfs_##func(a)
macros with more than one statement
macros that are more than one statement long should be encapsulated by do-while(0). this enables using them inside if-else statements:
// example usage: if (cond) DO_A_AND_B; else do_something_else();
#define DO_A_AND_B() \ { \ do_a(); \ do_b(); \ }
#define DO_A_AND_B() \ do { \ do_a(); \ do_b(); \ } while (0)
static functions in header instead of macros
if you need a function, but you don't want a library to link with, then you can use static functions in the header file instead of macros
#define CREATE_HANDLE() \ (handle_t h, h = wd_open(), h==-1 ? NULL : h)
static handle_t create_handle(void) { handle_t h = wd_open(); if (h==-1) return NULL; return h; }
static handle_t create_handle(void) { handle_t h; if ((h = wd_open())==-1) return NULL; return h; }
Don't use forward declarations
don't use forward declarations unless they are necessary. Instead change the order so the caller is below the callee.
void callee(...); void caller(...) { callee(); } void callee(...) { }
void callee(...) { } void caller(...) { callee(); }
Prefer native c data types instead of OS specific data types.
BYTE b; DWORD w;
unsigned char b; unsigned long b;
Double inclusion protection
at the begining of a file, add double inclusion protection, where the file path and name is surrounted by two underscores before and after; a slash and a dot are translated to an underscore.
#ifndef __UTIL_ZERR_H__ #define __UTIL_ZERR_H__ // the pkg/util/zerr.h content comes here #endif
Files terminate
files should terminate with a carriage return
reason: compilers can ignore lines that are not terminated by
a CR.
#endif<EOF>
#endif <EOF>
Nested #if
nested #if directives should not be indented (neither the # character nor the directive itself, regardless of how complex they are).
#include <stdio.h> #ifdef UNIX #include <unistd.h> #else #include <windows.h> #ifdef WINCE #include <wince.h> #endif #endif
#include <stdio.h> #ifdef UNIX #include <unistd.h> #else #include <windows.h> #ifdef WINCE #include <wince.h> #endif #endif
void sleep_ms(int ms) { #ifdef WINDOWS Sleep(ms); #else sleep(ms); #endif }
void sleep_ms(int ms) { #ifdef WINDOWS Sleep(ms); #else sleep(ms); #endif }
#endif comments
You can put an #endif a comment telling to what if it belongs if there is a large gap between the #if and its corresponding #endif.
#ifdef WINDOWS int htonl(int i) { return (i << 16) & 0xffff0000 | (i >> 16) & 0xffff; } #endif // WINDOWS
#ifdef WINDOWS int htonl(int i) { return (i << 16) & 0xffff0000 | (i >> 16) & 0xffff; } #endif
#ifdef CONFIG_RG_FIREWALL // Lots of firewall code, that you really have to scroll down // to see all of it. #endif // CONFIG_RG_FIREWALL
enum
enum should be in the same line if they are very short
enum {STATUS_OK=DEVIOCTL_NOERROR, STATUS_FAIL=-1};
enum { CMD_NONE=0, CMD_END=1, WP_WORD=14, };
enum { CMD_NONE = 0, CMD_END = 1, WP_WORD = 14, };
enums should have values attached
enum { DO_RESET = 1, DO_LOAD, DO_SAVE, };
enum { DO_RESET = 1, DO_LOAD = 2, DO_SAVE = 3, };
enum { DO_RESET = 10, DO_LOAD = 20, DO_SAVE = 30, };
all enum lines be comma terminated
all enum lines be comma terminated - this makes the enum
extensible.
only separate the last line, if it is meant to never be
surpassed.
enum { DO_RESET = 1, DO_LOAD = 2, DO_SAVE = 3 };
enum { DO_RESET = 1, DO_LOAD = 2, DO_SAVE = 3, };
enum { BANDIT1 = 1, BANDIT2 = 2, ALI_BABA = 41 };
Spaces only after commas
enum in same line should have spaces only after commas
enum { DO_RESET = 1, DO_LOAD = 2, DO_SAVE = 3 };
enum {DO_RESET=1, DO_LOAD=2, DO_SAVE=3};
Put the pointer next to the variable
char* strrev(char* s);
char *strrev(char *s);
Dynamic allocation only when needed
use stack allocation for local variables of const size. use dynamic allocation only when needed.
type_t *o = malloc(sizeof(*o)); ... free(o);
type_t o;
type_t *ZALLOC(o); ... free(o);
// if possible: type_t o = {0};
type_t o; MZERO(o);
defined()
only use the defined() macro when you have a complex
expression or in #elif.
If you find that you do need to use the defined() macro for
more than one flag, see if the 2 flags can be grouped under
another one new flag.
#ifdef CONFIG_CRAMFS_FS #ifndef CONFIG_ARCH_IXP22X cramfs_init(); #endif #endif
#if defined(CONFIG_CRAMFS_FS) cramfs_init(); #endif
#ifdef CONFIG_CRAMFS_FS cramfs_init(); #endif
// since you really need both flags: #if defined(CONFIG_CRAMFS_FS) || defined(CONFIG_NFS_FS) cramfs_to_nfs_copy(); #endif
#ifdef CONFIG_CRAMFS_FS cramfs_init(); #elif defined(CONFIG_NFS_FS) nfs_init(); #endif
#ifdef SPECIFIC_OS order
how to do and #ifdef SPECIFIC_OS
what comes first, the right order is
#ifdef LINUX ..... #elif defined(SOLARIS) ..... #elif defined(VXWORKS) ..... #elif defined(OS2) ..... #elif defined(WIN95) ..... #elif defined(WINNT) ..... #elif defined(WINCE) ..... #endif
C++ data members names
in C++ classes give data members names starting with m_
class find_t { int m_index; char *m_text; };
functions
functions used in one C file: should be implemented static,
and a declaration in the C file should only be added if used
before implemented. (example: queue_locate_any()).
functions used out of the C file ('global'): should be
declared in the H file. (example: queue_locate_item() and
queue_activate_operation()). structures used in one C file:
should be declared inside the C file. (example:
sched_operation_t).
a pointer to the structure is needed out of the C file: a
forward declaration should be in the H file, and the full
decleration in the C file. (example: sched_queue_t).
the structure's members are needed out of the C file: declare
it in the H file. (no example below).
// in H file typedef struct sched_queue_t sched_queue_t; sched_queue_t *queue_locate_item(char *name); void queue_activate_operation(sched_queue_t *sched); // in C file typedef struct { char *operation; char *user; } sched_operation_t; sched_operation_t valid_operations[] = { {"ls", "root"}, {"rm", "anonymous"}, {NULL, NULL} }; struct sched_queue_t { struct sched_queue_t *next; char *name; char *operation; int expire; }; sched_queue_t *the_queue; static sched_queue_t *queue_locate_any(char *name) { sched_queue_t *search; for (search = the_queue; search && strcmp(search->name, name); search = search->next); return search; } sched_queue_t *queue_locate_item(char *name) { sched_queue_t *search = queue_locate_any(name); // check if already expired if (expire>get_current_time()) { queue_remove(search); return NULL; } return search; }
object oriented naming
the data structure should be the name of the object with _t at
the end.
functions should all start with the object name, then
operation starting with a verb.
typedef struct { the_objects_data_members; } xxx_t; // use _alloc and _free if it only allocates memory, and does not do // "active" operations xxx_t *xxx_alloc(initialization_param); void xxx_free(xxx_t *xxx); // use _open and _close if it does active stuff like call many functions, // register events, IOCTLs etc... xxx_t *xxx_open(initialization_param); void xxx_close(xxx_t *xxx); // use _init and _uninit if it does not allocate the xxx_t structure, only // its sons - i.e. xxx_alloc can be a simple zalloc() while xxx_init // can be a simple ZEROM(). xxx_uninit frees data structures that the xxx_t // points to, without freeing xxx_t itself void xxx_init(xxx_t *xxx, initialization_param); void xxx_uninit(xxx_t *xxx); // methods on the xxx object char *xxx_get_by_index(xxx_t *xxx, int index); int xxx_get_by_name(xxx_t *xxx, char *name); char **xxx_get_name_list(xxx_t *xxx); void xxx_set(xxx_t *xxx, char *name); void xxx_set_nofail(xxx_t *xxx, char *name);
in_addr dev_if_ip_get(dev_if_t *dev);
// operation ('get_ip') must start with a verb ('get') in_addr dev_if_get_ip(dev_if_t *dev);
linked lists
simple linked lists always have a 'next' variable as the first structure member.
functions for list are xxx_free() and xxx_list_free()
the xxx_yyy (xxx is object name and yyy is function name), for
functions on the object, NOT on list of objects.
the xxx_list_yyy is for functions on the list of objects (such
as xxx_list_add, xxx_list_remove).
// sample functions: xxx_t *xxx_dup(xxx_t *p); // retunes a duplicated object void xxx_copy(xxx_t *dst, xxx_t *src); // copy object's data void xxx_free(xxx_t *p); // free the object xxx_t *xxx_alloc(init_data....); // creates a new initialized object void xxx_init(xxx_t *p, init_data....); // initializes an object void xxx_uninit(xxx_t *p, init_data....); // un-initializes an object // frees the list. list now is destroyed void xxx_list_free(xxx_t *xxx_list); // adds an object to the list void xxx_list_add(xxx_t **xxx_list, xxx_t *new_elem);
Don't implement add and remove functions
normally, we should not implement add and remove functions
(xxx_list_add and xxx_list_remove). these should only be
implemented if there is specific added value (specific code)
that needs to be done in addition to adding to the list.
when scanning lists in order to add/remove elements, a pointer
to pointer should be used.
typedef struct file_t { struct file_t *next; char *name; } file_t; // frees a single file object file_free(file_t *p) { if (p->name) free(p->name); free(p); } // frees the whole list of object file_list_free(file_t *p) { while (p) { file_t *tmp = p->next; file_free(p); p = tmp; } }
free() handles NULL
free() also handles NULL, so no need for 'if ()' before it
if (str) free(str);
free(str);
exit a function with a negative value
to exit a function with a negative value, to indicate an error, and send a log message, you may use the fact that zerr() returns negative value
// GOOD but a little long: if (ioctl()<0) { zerr_f(LERR, "failed ioctl()"); return -1; }
// GOOD short and neat: if (ioctl()<0) return zerr_f(LERR, "failed ioctl()");
constant strings longer than one line
printf("Hello world, this is a long string that we want to print and \ is more than 80 chars long so we need to split it");
printf("Hello world, this is a long string that we want to print" " and is more than 80 chars long so we need to split it");
printf("Hello world, this is a long string that we want to print " "and is more than 80 chars long so we need to split it");
Dont use const char *
Don't use 'const char *' (or 'char const *') in code. It is impossible to integrate code written with consts into a large system.
const char *gen_token(const char *seed);
// Return a static buffer - don't change. char *gen_token(char *seed);
Zero parameters functions
Functions that accept zero parameters should be defined as accepting void
double get_time_ms();
double get_time_ms(void);
Compile time arrays
Compile time arrays should be terminated with a NULL (or -1 when 0 is valid) element, and not by calculating the size of the the array using sizeof.
name_val_t arr[] = { {"PPPoA", 1}, {"ETHoA", 2}, }; for (i = 0; i < sizeof(arr) / sizeof(arr[0]; i++) elem_print(arr + i);
name_val_t arr[] = { {"PPPoA", 1}, {"ETHoA", 2}, {NULL} }; for (i = 0; arr[i].name; i++) elem_print(arr + i);
Trivial
Trivial > >= < <= == !=
expressions should not have spaces around them.
Trivial expressions examples: a_var, array[3].var[1], sizeof(xxx), (x + 1)
Pointers ->
are not counted as trivial expressions for > >= < <=
, since its hard to read x->y>z
, while its not hard to read x->y==z
.
if (x > 5)
if (x>5)
if (x+1>5)
if (x+1 > 5)
if (f(x, y) > g(y, z))
if (f(x, y)>g(y, z))
if (x->y == 5)
if (x->y==5)
if (x->y>=5)
if (x->y >= 5)
Empty lines
Don't put empty lines between code inside functions. If you want to separate code fragments, put a comment line.
for (i=0; i<5; i++) printf(do_stuff); if (x>5) zerr(LERR, ...);
for (i=0; i<5; i++) printf(do_stuff); // check for invalid results if (x>5) zerr(LERR, ...);
Templates
Module foo in pkg/bar/foo.c & foo.h. All files should follow
this exact example.
#include "config.h" must be first in the *.c file to make sure
the features configuration affect all the rest of the
files.
Header files must be self contained, i.e. - they must be able
to compile without relying on another include line to come
before them.
Therefore #include "foo.h" must be immediately after to make
sure foo.h compiles stand alone without any dependency on
includes before it.
_BEGIN_EXTERN_C and _END_EXTERN_C allow the code to link to
C++.
pkg/bar/foo.c template
// LICENSE_CODE ZON #include "config.h" #include "foo.h" ... put here minimal includes required by foo.c ... ... put here your code ...
pkg/bar/foo.h template
// LICENSE_CODE ZON #ifndef __BAR_FOO_H__ #define __BAR_FOO_H__ #include "config.h" ... put here minimal includes required by foo.h ... _BEGIN_EXTERN_C ... put here functions, structures and other definitions ... _END_EXTERN_C #endif
#include "foo.h"
Use #include "foo.h" for headers in the same directory as the
source including them.
Use #include "path_to_foo/foo.h" for headers in other
directories, where path_to_foo is relative to root pkg
directory.
Use #include <foo.h>
for external system files.
#include "stdio.h" #include <dhcp_lease.h> #include "hash.h"
#include <stdio.h> #include "dhcp_lease.h" // we are in pkg/dhcp directory #include "util/hash.h" // from pkg/util/hash.h
Temporary disabling test
When temporary disabling test code that fail:
Do not indent the code of the disabled tests.
if (!running_on_valgrind) // XXX: yoni: fails on BAT jtest_eq(...);
if (!running_on_valgrind) // XXX: yoni: fails on BAT jtest_eq(...);
If it is only one test
If it is only one test (one statement), then don't use { }
even if the statement is written in 2 lines
if (!running_on_valgrind) // XXX: yoni: fails on BAT { jtest_run(xxx, yyy, zzz); }
if (!running_on_valgrind) // XXX: yoni: fails on BAT jtest_run(xxx, yyy, zzz);
Open { on the same if() line
// XXX: yoni: fails on BAT if (!running_on_valgrind) { jtest_run(xxx, yyy, zzz); jtest_run(xxx, yyy, zzz); }
if (!running_on_valgrind) { // XXX: yoni: fails on BAT jtest_run(xxx, yyy, zzz); jtest_run(xxx, yyy, zzz); }