ANSI/ISO C89/C90 Tips

I have used C for my own software development tasks. I have also taught a university-level C course (well, this isn’t really relevant, but some think otherwise. Oh, well). Here are my tips based on my observation of common mistakes (including those I’ve done myself). I hope the following tips can reduce the occurrences of those mistakes.

  1. Be sure to fclose() after a successful fopen(). Similarly for *alloc() and free().
  2. An easy way to malloc():
    ptr = malloc(NUM * sizeof *ptr);
  3. When you use command line arguments, be sure to check that they’re really there, for examples:
    if(argv[1])

    or

    if(argc < 2)
  4. This way of using fread() is more generic and safer:
    fread(a, sizeof a[0], sizeof a/sizeof a[0],
    ...);

    compared to:

    fread(a, sizeof(char), MAX...);

    Why? Suppose you change the data type of a to, say, an array of double‘s. If you use the later form, you must still modify sizeof(char) to sizeof(double).

  5. Pedantically speaking, using such a construct is incorrect:
    while(!feof(fp))
    { fread(...);
      }

    The following program will output the last line twice. Caution: try with a small file (unless you’re really keen waiting).

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    
    #define RECORD_LENGTH 50
    
    int main(int argc, char ** argv)
    { char rec[RECORD_LENGTH + 1];
      FILE * dfp;
      int result = EXIT_SUCCESS;
    
      if(argc < 2)
      { fprintf(stderr, "%s datafile\n", argv[0]);
        result = EXIT_FAILURE;
        }
      else
      { if(dfp = fopen(argv[1], "rb"))
        { while(!feof(dfp))
          { fread(rec, sizeof rec[0], RECORD_LENGTH, dfp);
            rec[RECORD_LENGTH] = '\0';
            printf("%s\n", rec);
            }
    
    /* compare with this:
    
          while(fread(rec, sizeof rec[0], RECORD_LENGTH, dfp)
                > 0)
          { rec[RECORD_LENGTH] = '\0';
            printf("%s\n", rec);
            }
    
    */
    
          fclose(dfp);
          }
        else
        { result = EXIT_FAILURE;
          }
        }
      return result;
      }

    Further explanation is available.

  6. Don’t use // in C89 (the implementation available for students). // is valid in C99, though. It’s available in some C89 implementations as an extension, meaning that your code will lose some portability. If you want your program to be guaranteed to work with C89 implementations, /* */ is the safest.
  7. Use correct printf() format specifier. Otherwise, your program will invoke undefined behaviour. For example, if the argument is an int, use "%d".
  8. Use meaningful identifier/macro names.
    #define SEVENTY_THOUSANDS 70000

    is not useful. Use, for example:

    #define NUM_OF_RECORDS 70000
  9. Don’t use macros beginning with a “_” (underscore). This is reserved for C implementations.
  10. There’s no “method” in C.
  11. Don’t put unused variables/functions/macros.
  12. Portable return values for main(): 0, EXIT_SUCCESS, and EXIT_FAILURE. Other than those are implementation-defined/non-portable.
  13. Avoid unnecessary casting. There are times when casts are appropriate, which may include when using to*() and is*().
  14. How to use fread():
    #define REC_LENGTH 100
    
    char buf[REC_LENGTH];
    
    fread(buf, sizeof buf[0], REC_LENGTH, fp);

    or

    fread(buf, sizeof buf[0], sizeof buf/sizeof buf[0], fp);
  15. sizeof(char) is always 1. So, there’s no point writing any_value * sizeof(char). It always evaluates to any_value. If you have the habit of writingsizeof multiplied by some multiplier (and insist doing so), you’d better use sizeof object instead of sizeof(data_type).
  16. How to use fgets():
    #define MAX_LEN 1024
    
    char buf[MAX_LEN + 1];
    
    if(fgets(buf, sizeof buf, stdin))
    { ...
      }

    Of course, you can replace stdin with other input streams and 1024 with another valid positive integral value.

  17. Don’t use assert() on a condition you can’t control internally from your program. For example, don’t use it to enforce the number of command lines supplied to your program. Don’t use it to replace if. Use it to prevent undefined behaviours, or enforce a condition that should or should not be satisfied.
  18. sizeof returns size_t, not int, not long, not float, etc. That means, strictly speaking, the following code invokes undefined behaviour:
    int x;
    
    printf("%d\n", sizeof x);

    To fix this, use the following:

    int x;
    
    printf("%lu\n", (unsigned long)sizeof x);

    Similarly for many other functions that return size_t, such as strlen() andfread().