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.
-
Be sure to
fclose()
after a successfulfopen()
. Similarly for*alloc()
andfree()
. -
An easy way to
malloc()
:ptr = malloc(NUM * sizeof *ptr);
This is good as we don’t need to care about the type of
ptr
. Note thatptr
won’t be dereferenced since it’s used as an operand tosizeof
, hence you don’t need to check whether its value isNULL
. -
When you use command line arguments, be sure to check that they’re really there, for example:
if (argc < 2)
-
This way of using
fread()
is more generic and safer:fread(a, sizeof *a, sizeof a / sizeof *a, ...);
compared to:
fread(a, sizeof(char), MAX...);
Why? Suppose you change the data type of
a
to, say, an array ofdouble
s. If you use the latter form, you must still modifysizeof(char)
tosizeof(double)
. As for the third parameter, you need to be careful, though. Ifa
is an array, usingsizeof a / sizeof *a
always works. Ifa
is a pointer (watch for this if you pass an array to a function, in which the array will decay into a pointer), you need to specify a number for the third parameter. -
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] = {0}; FILE *dfp = NULL; 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, sizeof rec - 1, dfp); printf("%s\n", rec); } /* compare with this: while (fread(rec, sizeof *rec, sizeof rec - 1, dfp) > 0) { printf("%s\n", rec); } */ fclose(dfp); } else { result = EXIT_FAILURE; } return result; }
Further explanation is available.
-
Don’t use
//
in C89.//
is valid as of 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. -
Use correct
printf()
format specifier. Otherwise, your program will invoke undefined behaviour. For example, if the argument is anint
, use"%d"
. -
Use meaningful identifier/macro names.
#define SEVENTY_THOUSANDS 70000
is not useful. Use, for example:
#define NUM_OF_RECORDS 70000
-
Don’t use macros beginning with a “_” (underscore). This is reserved for C implementations.
-
There’s no ‘method’ in C.
-
Don’t put unused variables/functions/macros.
-
Portable return values for
main()
:0
,EXIT_SUCCESS
, andEXIT_FAILURE
(the latter two are available by including<stdlib.h>
). Other than those are implementation-defined/non-portable. -
Avoid unnecessary casting. There are times when casts are appropriate, which may include when using
to*()
andis*()
. -
How to use
fread()
:#define REC_LENGTH 100 char buf[REC_LENGTH]; fread(buf, sizeof *buf, sizeof buf, fp);
-
sizeof(char)
is always 1. So, there’s no point writingany_value * sizeof(char)
. It always evaluates toany_value
. If you have the habit of writingsizeof
multiplied by some multiplier (and insist doing so), you’d better usesizeof object
instead ofsizeof(data_type)
. -
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 and1024
with another valid positive integral value. -
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 replaceif
. Use it to prevent undefined behaviours, or enforce a condition that should or should not be satisfied. -
sizeof
returnssize_t
, notint
, notlong
, notfloat
, etc. That means, strictly speaking, the following code invokes undefined behaviour:int x; printf("%d\n", sizeof x);
To fix this, use the following in C89:
int x; printf("%lu\n", (unsigned long)sizeof x);
or in C99/later standards:
int x; printf("%zu\n", sizeof x);
Similarly for many other functions that return
size_t
, such asstrlen()
andfread()
.