Languages/C/tips

(Difference between revisions)
Jump to: navigation, search
(Trap : C++ : virtual destructor)
m (Tip : if (result = function_call()) warning: typo)
Line 145: Line 145:
  
 
<source lang='C'>
 
<source lang='C'>
 
+
int a;
 
/* This should do a warning */
 
/* This should do a warning */
if (a = strlen("toto)")
+
if ( a = strlen("toto") )
 
{
 
{
   
+
 
 
}
 
}
  
/* This will not warn */
+
/* This one will not warn */
if ((a = strlen("toto"))
+
if ( (a = strlen("toto")) )
 
{
 
{
  
 
}
 
}
 
 
</source>
 
</source>
  

Revision as of 13:55, 25 January 2013

Contents

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 compilers will remove stupid code which (seems to) do nothing.

/**
 * Using a decent compiler, this function will be completely optimized away.
 */
static 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 an empty 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)
{
    toto_t toto;
 
    /* This is the slow and boring way */
    toto->a = 0;
    toto->b = 0;
    toto->c = 0;
 
    /* Here is a smarter way */
    memset(toto, 0x0, sizeof(*toto));
}

Tip : Write readable code

  • First, write and test code that works can be easily understood.
  • If and only if it is too slow or memory inefficient try to optimize 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. For instance gcc will tell something like warning: suggest parentheses around assignment used as truth value

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

int a;
/* This should do a warning */
if ( a = strlen("toto") )
{
 
}
 
/* This one 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);
}

Trap : C++ : virtual destructor

Here is why you shouldn't forget to declare your destructors virtual.

/**
 * \brief Why use virtual destructors in c++, a definitive answer
 *
 * \author Marc Pignat < pim at hevs dot ch >
 */
 
#include <iostream>
 
using namespace std;
 
/**
 * Why not?
 */
#define LESS_COPY_PASTE {cout << __FUNCTION__ << "\t";}
 
/**
 * Let's define 4 classes
 *
 *    SuperA    SuperB
 *      |         |
 *      v         v
 *    SubA      SubB
 *
 * All 4 classes only have a default constructor and a destructor.
 *
 * The SuperA class has it's destructor declared *virtual* and the SuperB don't.
 */
class SuperA
{
	public:
		SuperA() LESS_COPY_PASTE
		virtual ~SuperA() LESS_COPY_PASTE
};
 
class SubA : public SuperA
{
	public:
		SubA() LESS_COPY_PASTE
		~SubA() LESS_COPY_PASTE
};
 
class SuperB
{
	public:
		SuperB() LESS_COPY_PASTE
		~SuperB() LESS_COPY_PASTE
};
 
class SubB : public SuperB
{
	public:
		SubB() LESS_COPY_PASTE
		~SubB() LESS_COPY_PASTE
};
 
int main(int argc, char *argv[])
{
	{
		/**
		 *  output : "SuperA SubA ~SubA ~SuperA"
		 */
		SubA a;
	}
	cout << endl;
 
	{
		/**
		 * output : "SuperB SubB ~SubB ~SuperB"
		 */
		SubB b;
	}
	cout << endl;
 
	/**
	 * output : "SuperA SubA ~SubA ~SuperA"
	 */
	SubA *pSA = new SubA();
	delete pSA;
 
	cout << endl;
 
	/**
	 * output : "SuperB SubB ~SubB ~SuperB"
	 */
	SubB *pSB = new SubB();
	delete pSB;
 
	cout << endl;
 
	/**
	 * output : "SuperA SubA ~SubA ~SuperA"
	 */
	SuperA *pA = new SubA();
	delete pA;
 
	cout << endl;
 
	/**
	 *
	 *                           ,--.!,
	 *                        __/   -*-
	 *                      ,d08b.  '|`
	 *                      0088MM
	 *                      `9MMP'
	 *                   hjm
	 *
	 * output : "SuperB SubB ~SuperB"
	 *
	 * !!! ~SubB is never called !!!
	 *
	 */
	SuperB *pB = new SubB();
	delete pB;
}
 
/**
 * Explaining why this language is so overly complicated is an exercise left
 * to the reader.
 */
Personal tools
Namespaces
Variants
Actions
Navigation
Browse
Toolbox