jotux / c_tricks

Little C tricks that are sometimes helpful

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Use double bang(!!) to change a variable to 0 or 1

Useful for returning true or false based on a value > 0

main()
{
    unsigned long int var = 5;
    printf("%d %d %d\n",var, !var, !!var);
}

Use printf("%.*s",length,buf) to print a specific length from a buffer

Not very safe but useful for debugging circular buffers and the like.

char buf[10] = "abcdefghij";
main()
{
    printf("%.*s\n",2,buf);
    printf("%.*s\n",4,buf);
    printf("%.*s\n",6,buf);
    printf("%.*s\n",8,buf);
    printf("%.*s\n",10,buf);
}

Associate strings with values

If you have some defined or enummed values sometimes you want to associate strings with them to help with debug

enum MyErrors
{
    EINVAL,
    ENOMEM,
    EFAULT
};

You can init your string array using these names to make it either to correlate them

char *err_strings[] =
{
    [0]      = "Success",
    [EINVAL] = "Invalid argument",
    [ENOMEM] = "Not enough memory",
    [EFAULT] = "Bad address"
};

Now you can printf("%s",err_string[err]) and easily see what's going on

Sometimes all you want to do is spit the string itself out for debug

enum Events
{
    IDLE,
    ENTER,
    EXIT,
    TIMER_TICK
};

With a little preprocessor help we can make the initialization of these strings a lot easier

const char* event_string[] =
{
#define EVENT_NAME(s) [s] = #s
    EVENT_NAME(IDLE),
    EVENT_NAME(ENTER),
    EVENT_NAME(EXIT),
    EVENT_NAME(TIMER_TICK)
};

Call a list of functions

#include <stdarg.h>

void CallThese(void (*first)(void), ...)
{
    va_list arg_list;
    void (*fn)() = NULL;
    va_start(arg_list, first);
    (*first)();
    while ((fn = va_arg(arg_list, void*)) != NULL)
    {
        (*fn)();
    }
    va_end(arg_list);
}

void f1(){printf("one\n");}
void f2(){printf("two\n");}
void f3(){printf("three\n");}

void main()
{
    CallThese(f1,f2,f3,f1,f2,f3,f1);
}

Define case statements in a switch to reduce code redundancy

This is really useful on embedded systems where you may have multiple copies of a specific hardware block with the same initialization but different register names.

void SpecificHardwareInit(uint8_t channel)
{
#define REG_INIT_CASE(x)    \
    case x:                 \
        REG_##x##_A = 0;    \
        REG_##x##_B = 0x55; \
        REG_##x##_C = 0x02; \
        break;

    switch(channel)
    {
        REG_INIT_CASE(0);
        REG_INIT_CASE(1);
        REG_INIT_CASE(2);
        REG_INIT_CASE(3);
    }
#undef REG_INIT_CASE
}

Glue register names together to create optimized hardware init code

If you're working on a very space-limited device but still want clear hardware init code you can abuse the preprocessor and use it to glue together statements that compile to single statements.

#define _BV(x)       (1<<(x))

#define CLEAR_OP    &=~
#define SET_OP      |=
#define TOGGLE_OP   ^=

#define _INPUT  CLEAR_OP
#define _OUTPUT SET_OP

#define HW_DIRECTION(n,op)    HW_DIRECTION_(n,op)
#define HW_DIRECTION_(p,i,op) P##p##DIR op _BV(i)

#define LED 1,3

HW_DIRECTION(LED,GPIO_INPUT); // This line will be converted to P1DIR &=~(1<<(3));

About

Little C tricks that are sometimes helpful