Rcjp's Weblog

C

Asserting

Prior to c99, assert needed an integral constant so had to write::

   assert(NULL!=p);

modern way:

   assert(p && "Valid pointer...");

as the string (which always evaluates to true) now appears in the errorfile.
Remember to compile with NDEBUG defined to remove assertions.

Initialising

To avoid magic numbers, rather than do Paper(10,0) or commenting do:

    const size_t X_CM = 10;
    const size_t Y_CM = 20;
    Paper(X_CM, Y_CM);

or if not a constant::

    size_t x_cm = 0;  // not const
    Paper(x_cm=10, ...)
    Paper(x_cm=20, ...)

Remember sizeof is a compile time operator not a function so you can do::

    Buffer *buf = malloc(sizeof *buf)


and avoid specifying sizes twice, you still often see people write sizeof(Buffer)

Initialising compound types::

    struct WeatherNode {
        double todaysHigh;
        struct WeatherNode *nextWeather;
    };

dont do:

    struct WeatherNode node;
    memset(&node, 0, sizeof node)

because all bits zero does not necessarily represent zero in floating point etc., instead:

    struct WeatherNode node = {0};

or, if you need to zeroise it at some later stage of the code then:

    const struct WeatherNode zeronode = {0};
    ...
    memcopy(&node, &zeronode, sizeof node);

Right-Left-Rule

To read a complex cast expression

  • Start with the identifier,
  • look right, and then look left
  • repeat last step

Interpret * as a pointer with the lowest precidence. e.g. if you’ve got a pointer to a block of memory ptr=(int*)malloc(51200), to call a function requiring a 2D with dimensions 100 by 512 which is declared
as:

    void fff(int array[][512])

you must cast the call as

    fff( (int (*)[512]) ptr);

Right left rules says… No identifier, start with the innermost *

    (*)           its a pointer
    (*) [512]     a pointer to an array of 512,
    int (*) [512] a pointer to an array of 512 integers.

Linked lists

Define using:

    typedef struct node *NODEPTR;

    struct node {
        char *item;
        NODEPTR next;
    };

This declares a new typedef name involving struct node even though struct
node has not been completely defined yet; this is legal. Alternatively::

    struct node {
        char *item;
        struct node *next;
    };

    typedef struct node *NODEPTR;

A standard structure initialisation idiom::

    struct  FontFamilyInfo {
        const char* name;
        int family;
    };
    const FontFamilyInfo FamilyInfo[] = {
        {"DECORATIVE", FF_DECORATIVE},
        {"MODERN",     FF_MODERN},
        {"ROMAN",      FF_ROMAN},
        {"SCRIPT",     FF_SCRIPT},
        {"SWISS",      FF_SWISS}
    };
    const int nFamilyInfo = (sizeof FamilyInfo) / (sizeof FamilyInfo[0]);

Variable args

e.g. pass list of strings

    #include <stdarg.h>

    char ttt(char first, ...)
    {
      size_t len;
      char *p,
      va_list argp;

      if(first == NULL) return NULL;
      len = strlen(first);
      va_start(argp, first);
      while((p = va_arg(argp, char *)) != NULL)
        len += strlen(p);
      va_end(argp);
      ...

then call like

  char *str = tt("Hello, ", "world!", (char *)NULL);

Macros

In #define statements ‘#’ gets expanded into the argument surrounded by quotes
so::

    #define TEST(a,b) printf(#a "<" #b "=%d\n",  (a)<(b) )

gives e.g. TEST(0, 0xFFF) -> printf("0" "<" "0xFFF" "=%d\n", (0)<(0xFFF));

## is like a concatenation operator (must be in the middle of something) and is
useful for forming variable names e.g.::

    #define TEMP(i) temp ## i

    TEMP(i) = TEMP(2 + k) + x; -> temp1 = temp2 + k + x;

Variable Length Arrays

In C99, can do::

    void f(int r, int c, int a[c][r])

which would have a prototype::

    void f(int, int, int[*][*]);

to indicate variable lengths. But note r & c must come before a!!

Pointer to Functions

    #include <stdio.h>
    #include <math.h>

    main()
    {
       double (*ptr)(double) = sin;//pointer to a function
       float (*fptr)(float) = (float (*)(float))sin;  //casting
       double a = (*ptr)(1.0);
       a = ptr(1.0);   // alternative shorthand to above
       float  b = (*fptr)(1.0);
       printf("a is %lf, b is %f\n", a, b);
    }

Calling a function via its name – keep a table::

    int func(), anotherfunc();

    struct {
      char *name;
      int (*funcptr)();
    } symtab[] = {
            "func",         func,
            "anotherfunc",  anotherfunc,
    };

Then, search the table for the name, and call via the associated function pointer.

Array References

*(a+k) is the same as a[k], or *(k+a) or k[a]::

    int calendar[12][31];
    int *p;
    p = calendar; // illegal since calendar converts
                  // to a pointer to an array
    int (*monthp)[31];
    monthp = calendar; // ok

When allocating memory for strings, remember to allocate LENGTH+1 to allow for . There is no way to pass an array to a function directly: it is immediately converted to a pointer, so a definition::

    int strlen(char s[])          eqv. strlen(char *s)  
    main(int argc, char *argv[])  eqv. main(int argc, char **argv)

–n >=0 subtracts one from and compares the result to zero, whereas n– >0 saves n, subtracts one from n, and then compares the saved value with 0, so the former is potentially faster.::

    char *p = "some string";
    while(*p++);

p is now pointing to 1 _past_ the NULL; (so probably want to back up with p–)

Strings

        char *p = "...";   // maybe writable (string literal might be in read-only memory)
       char p[] = "...";   // always writable - string is used as an array initialiser
 const char p[] = "...";   // never writable

No case insensitive string comparison, have to use strcasecmp (unix)
or strcmpi (win32).

    char *p = NULL; // is a null character pointer, which is not the same as...
    char *p = "";   // which is a pointer to an empty string ie. a pointer to a NULL

when passing null as an argument use ttt((char *)0);

Security problems with strings – strcpy(aaa, bbb) will keep going until it finds
a NULL pointer. (Note use memove instead of strcpy if the strings might overlap
in memory)

    char aaa[10]={"1234567890"} // copies 10 characters without the trailing NULL.
                                // so use strncpy instead, but...  
    strncpy(bbb, aaa, 5)        // does not NULL terminate bbb

if using strncpy do::

    char buf[6];
    buf[5]=0;
    strncpy(buf,"welcome",5);

You get [w][e][l][c][o][\0] 

or maybe::

    char dest[N+1];
    strncpy(dest, src, N)[N]=0 

since dest is returned from strncpy.

Don’t do::

    char buf[100];
    strncpy(buf,src,sizeof(buf));
    buf[ sizeof(buf)-1 ] = 0;

because that may get changed to::

    char *buf = malloc(512);

in which case sizeof(buf) will be 4!!!

Also note: the sizeof operator does not work with arrays passed as function arguments: since only a pointer is passed.

BSD’s use strlcpy, strlcat etc.

For sprintf::

    char buf[BUFFER_SIZE];
    sprintf(buf, "%*s",  sizeof(buf)-1, "long-string");  /* WRONG */
    sprintf(buf, "%.*s", sizeof(buf)-1, "long-string");  /* RIGHT */

but again if buf is malloc’ed sizeof wont work. Incidentally sizeof(char) is, by definition, exactly 1.

External Functions

In C, a global const has external linkage even without extern (in C++ it doesnt). In C, ‘static’ is used to make a global local to that file (in C++ use namespaces).

Arrays

The last specified array argument changes fastest, opposite to FORTRAN. An array of 2 rows and 3 columns – aaa[2][3]then element [i][j]can be calculated by i*3+j so the first dimension is not used.

To create a multidimensional array (from cfaq…)

            #include <stdlib.h>

            int **array1 = malloc(nrows * sizeof(int *));
            for(i = 0; i < nrows; i++)
                array1[i] = malloc(ncolumns * sizeof(int));

        /* (In real code, of course, all of malloc's return values would
        be checked.)

        You can keep the array's contents contiguous, at the cost of
        making later reallocation of individual rows more difficult,
        with a bit of explicit pointer arithmetic: */

            int **array2 = malloc(nrows * sizeof(int *));
            array2[0] = malloc(nrows * ncolumns * sizeof(int));
            for(i = 1; i < nrows; i++)
                array2[i] = array2[0] + i * ncolumns;


The rule by which arrays decay into pointers is not applied recursively. An array of arrays (i.e. a two- dimensional array in C) decays into a pointer to an array, not a pointer to a pointer.

If you are passing a two-dimensional array to a function:

    int array[NROWS][NCOLUMNS];
    f(array);

the function’s declaration must match:

    void f(int a[][NCOLUMNS])
    { ... }

or

    void f(int (*ap)[NCOLUMNS])  /* ap is a pointer to an array */

getline

Only use fgets if you are certain the data read cannot contain a null; otherwise, use getline (but that is gnu only!).

also be careful of scanf especially on stdin since it leaves characters behind which mess up later input.. better to use fgets + sscanf

        char answer[100], *p;
        printf("Type something:\n");
        fgets(answer, sizeof answer, stdin);
        if((p = strchr(answer, '\n')) != NULL)
           *p = '';
        printf("You typed \"%s\"\n", answer);

Advertisements

Leave a Comment »

No comments yet.

RSS feed for comments on this post.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: