Tricks and Tips

C Programming

  • equivalency tests

    If you write your equivalency tests in C "backwards", your are much less likely to accidentally use = where you meant ==. For instance:

       if (VALUE == variable)
       {
          /* ... */
       }
    
    rathern than:
       if (variable == VALUE)
       {
          /* ... */
       }
    
    because the 1st method will generate an error on the compile if VALUE is not a modifiable lvalue (a variable into which you can assign a value). Using the 2nd method, you could accidentally write if (variable = VALUE) and the mistake may go unnoticed, for a while anyway.
    BTW, this trick isn't really my own, but I use it.

  • sizeof

    Did you know that sizeof is an ANSI C operator, not a function or macro?
    Therefore, it doesn't require parentheses, as seems to be the common usage. Note that the parentheses are required only if a type is used as the operand to sizeof, as in sizeof (int); it can be thought of as taking the sizeof a cast. However, this construct is almost never required because you nearly always have a properly typed data object or pointer to use instead, as in:

    {
       size_t size;
       my_type something;
    
       size = sizeof something; /* look ma, no parentheses! */
    }
    
    #include <stdlib.h> /* malloc */
    
    {
       my_type *pointer;
    
       pointer = malloc(sizeof *pointer);
       /* ... */
    }
    
  • sizeof revisited

    If you declare your arrays properly, you can determine the number of elements, and perform some operation on each element, without ever referring explicitly to the size used to dimension the array. This method has the advantage that, if the size or type of the arrray element changes, only the defintion of the array needs to change. For example:

    {
       my_type array[100];
    
       for (i = 0; i < (sizeof array / sizeof *array); i++)
       {
          /* ... */
       }
    }
    
  • goto

    gotos in C are OK. There, I said it and I mean it. Don't be afraid to use any C language feature that may be useful to you. There are situations, especially in exception handling, in which goto is the only easy way out. One rule-of-thumb to be sure you're not using goto where another construct would work better is, "Never goto backwards" or "Always goto forward" in the code. This rule will keep you from using goto for loop-control, where for or while are more appropriate.
    For example:

    BOOLEAN do_stuff(void)
    {
       if (!do_something()) goto error_label;
       if (!do_something_else()) goto error_label;
       if (!do_something_also())
       {
    error_label:
          clean_up_everything();
          return FALSE;
       }
    
       return TRUE;
    }
    
  • #ifdef out

    One of the simplest ways to conditionally compile out a portion of code is to use the #if preprocessor directive. This method has the advantage over "commenting out" code that #if directives can be nested. Also, the original behavior can be restored by changing just one character.
    Here's an example:

    #include <unistd.h> /* sleep */
    
    /* ... */
    
    #if 0
       /* don't do this code! */
       while (1);
    #else
       /* do this code instead ... */
       sleep(1);
    #endif
    
    If you decide to reinstate the original portion of code, simply change the directive to #if 1.

    shells (Korn, POSIX, Bourne Again)

  • whence

    The whence shell builtin can do some neat tricks. Since it prints an absolute path to a file, it can be used as short-hand for the absolute path, when combined with sub-shell expansion.
    For instance, say you want to do an ls -l of some executable in your path:

    $ ls -l $(whence some_executuble)
    
    Or, you're working on a script that is in your path, and want to edit it:
    $ vi $(whence some_script)
    

  • variable references

    When you refer to a variable in the shell, it's much safer to use this syntax:

       ${my_variable?}
    
    than simply:
       $my_variable
    
    The curly brace characters guarantee that the variable name is bounded, so that the shell won't misinterpret the name if it occurs amid a string, as in:
       ${TERM?}INFO
    
    Also, the ? indicates that the shell should abort execution of the command or script if the variable is null or not set.

  • numeric base conversions

    Did you know that the Korn shell can do base conversions? Using the shells typeset keyword to create an integer variable, you can specify the base. Constants can be pre-pended with base# to indicate their base.

    Say you wanted to find the decimal and binary representation from 0xFFF:

    $ typeset -i10 num=16#FFF    # assign 0xFFF into a base 10 integer variable
    $ print $num
    4095
    $ typeset -i2 num            # convert the variable to base 2
    $ print $num
    2#111111111111
    $
    
  • "auto-cd"

    Some shells have a cutesy little feature that if you try to execute a directory as if it were a command, the shell will make that your current working directory, ie. chdir(2).

    While ksh doesn't explicitly have this feature, as the result of someone in comp.unix.shell asking if it's possible, I wrote this hack:

    # trap to automatically cd to directory if you try to execute it
    if [[ -t 0 ]]
    then
       trap 'dir=$(fc -nl -0 -0) && dir=${dir#	} && [[ -d ${dir?} ]] && cd ${dir?}' ERR
       #                                      ^ Hey! this is a tab!
    fi
    
    Here's an explanation:
    If you try to execute a directory, ksh will generate the error message ksh: dir: cannot execute and raise its ERR pseudo-signal. We can trap ERR to have some shell code be executed. We use fc (fix command) to retrieve the previous (failed) command from the shell command history. If it is a directory, we cd to it.