Languages/C/tips
m |
|||
Line 2: | Line 2: | ||
= C tips, tricks and... '''traps''' = | = C tips, tricks and... '''traps''' = | ||
After ten years of C programming, let me gather some piece of code I saw... | 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. | ||
+ | <source lang='C'> | ||
+ | |||
+ | /** | ||
+ | * 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); | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | == Trap : compilation and memory mapped registers == | ||
+ | Don't forget to use the <code>volatile</code> keyword when accessing a register | ||
+ | |||
+ | == Tip : Use static functions == | ||
+ | Trust the compiler, use <code>static</code> (<code>inline</code>) function instead of <code>#define</code>, | ||
+ | The function is far more readable, and a decent compiler will generate exactly the same code. | ||
+ | |||
+ | <source lang='C'> | ||
+ | <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) | ||
+ | |||
+ | </source> | ||
+ | |||
+ | == Trick : Initializing a struct == | ||
+ | <source lang='C'> | ||
+ | <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)); | ||
+ | } | ||
+ | </source> | ||
== Tip : Write readable code == | == Tip : Write readable code == |
Revision as of 09:00, 13 July 2012
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); }