Languages/C/tips
C tips, tricks and... traps
After ten years of C programming, let me gather some piece of code I saw...
Trap : delay loop optimized away
Modern compiler will remove stupid code which seems to do nothing.
/** * This loop will probably take 0 nanosecond, it does really nothing * and the compiler can throw it away. */ void zero_delay(void) { int i; for (i = 0 ; i < 10*1000 ; i++) { } } /** * This loop will take some time, but using the cpu for wasting time is not a * so good idea. */ void dumb_delay(void) { int i; for (i = 0 ; i < 10*1000 ; i++) { asm("nop"); /* replace with the nop instruction from your cpu*/ } } /** * The good way(tm) */ void smart_delay(void) { /* Initialize a timer for the delay you want */ /* put the cpu to sleep until the timer has fired */ /* In any case if your OS already provide this kind of function, use it !*/ #include <unistd.h> usleep(33); }
Trap : compilation and memory mapped registers
Don't forget to use the volatile
keyword when accessing a register
Tip : Use static functions
Trust the compiler, use static
(inline
) function instead of #define
,
The function is far more readable, and a decent compiler will generate exactly the same code.
<stdint.h> /* This function */ static uint8_t increment_func(uint8_t a) { return ++a; } /* Will work as fast as this define */ #define increment(a) ((a)+1)
Trick : Initializing a struct
<stdint.h> <string.h> struct toto_t { uint8_t a; uint8_t b; uint8_t c; } void init_this_struct(struct toto_t *toto) { /* This is the dumb way */ toto->a = 0; toto->b = 0; toto->c = 0; /* This is the smart way */ memset(toto, 0x0, sizeof(*toto)); }
Tip : Write readable code
- First, write and test code that works can be easily understood.
- If it has no efficiency (CPU or memory) problems, let it.
Tip : dummy read of a register
Some register peripherals, especially on embedded systems must be read to be cleared. Here is a nice way of doing it.
/* Suppose this is the address of a memory mapped status register */ volatile uint32_t *some_status_register; /** * Now suppose that we want to clear it, so here is a way */ void read_and_discard_status_reg(void) { uint32_t dummy; dummy = *some_status_register; } /** * And here is another way, cleaner */ void read_and_discard_status_reg(void) { /* the void cast will do the read, sure ;) */ (void)*some_status_register; }
Trap : if (a = b)
Remember, '=' is different from '=='. Any decent compiler used with decent options will warn you.
int a = 12; int b = 13; if (a = b) { printf("12 = 13 ?!?"); }
Tip : if (result = function_call()) warning
Your compiler warn you if you test the return value in the if ? add one stage of braces around and it's gone
/* This should do a warning */ if (a = strlen("toto)") { } /* This will not warn */ if ((a = strlen("toto")) { }
Trap : sizeof('a')
Here is a bad joke, what is sizeof('a') ?
#include <stdio.h> int main(int argc, char *argv[]) { return printf("sizeof('a') = %ld\n", sizeof('a')); }
And the result is... 4, or at least sizeof(int) which depends on your compiler!
Tip : signed
or unsigned
char ?!?
gcc char
type is neither signed
nor unsigned
.
In the following code, gcc will only compile the test_char
function without warning.
Solution : cast your unsigned char*
to a char*
((char*)msg
)
#include <stdint.h> #include <string.h> int test_unsigned_char(const unsigned char *msg) { return strlen(msg); } int test_signed_char(const signed char *msg) { return strlen(msg); } int test_char(const char *msg) { return strlen(msg); }