Common Programming Mistakes - Programming in C (Fourth Edition) (2015)

Programming in C (Fourth Edition) (2015)

D. Common Programming Mistakes

The following list summarizes some of the more common programming mistakes made in C. They are not arranged in any particular order. Knowledge of these mistakes will hopefully help you avoid them in your own programs.

1. Misplacing a semicolon.

Example

if ( j == 100 );
j = 0;

In the previous statements, the value of j will always be set to 0 due to the misplaced semicolon after the closing parenthesis. Remember, this semicolon is syntactically valid (it represents the null statement), and, therefore, no error is produced by the compiler. This same type of mistake is frequently made in while and for loops.

2. Confusing the operator = with the operator ==.

This mistake is usually made inside an if, while, or do statement.

Example

if ( a = 2 )
printf ("Your turn.\n");

The preceding statement is perfectly valid and has the effect of assigning 2 to a and then executing the printf() call. The printf() function will always be called because the value of the expression contained in the if statement will always be nonzero. (Its value will be 2.)

3. Omitting prototype declarations.

Example

result = squareRoot (2);

If squareRoot is defined later in the program, or in another file, and is not explicitly declared otherwise, the compiler assumes that the function returns an int. Furthermore, the compiler converts float arguments to double, and _Bool, char, and short arguments to int. No other conversion of arguments is done. Remember, it’s always safest to include a prototype declaration for all functions that you call (either explicitly yourself or implicitly by including the correct header file in your program), even if they’re defined earlier.

4. Confusing the precedences of the various operators.

Examples

while ( c = getchar () != EOF )
...
if ( x & 0xF == y )
...

In the first example, the value returned by getchar is compared against the value EOF first. This is because the inequality test has higher precedence than the assignment operator. The value that is therefore assigned to c is the TRUE/FALSE result of the test: 1 if the value returned by getchar is not equal to EOF, and 0 otherwise. In the second example, the integer constant 0xF is compared against y first because the equality test has higher precedence than any of the bitwise operators. The result of this test (0 or 1) is then ANDed with the value of x.

5. Confusing a character constant and a character string.

In the statement

text = 'a';

a single character is assigned to text. In the statement

text = "a";

a pointer to the character string "a" is assigned to text. Whereas, in the first case, text is normally declared to be a char variable, in the second case, it should be declared to be of type "pointer to char".

6. Using the wrong bounds for an array.

Example

int a[100], i, sum = 0;
...
for ( i = 1; i <= 100; ++i )
sum += a[i];

Valid subscripts of an array range from 0 through the number of elements minus one. Therefore, the preceding loop is incorrect because the last valid subscript of a is 99 and not 100. The writer of this statement also probably intended to start with the first element of the array; therefore, i should have been initially set to 0.

7. Forgetting to reserve an extra location in an array for the terminating null character of a string.

Remember to declare character arrays so that they are large enough to contain the terminating null character. For example, the character string "hello" would require six locations in a character array if you wanted to store a null at the end.

8. Confusing the operator -> with the operator . when referencing structure members.

Remember, the operator . is used for structure variables, whereas the operator -> is used for structure pointer variables. So, if x is a structure variable, the notation x.m is used to reference the member m of x. On the other hand, if x is a pointer to a structure, the notation x->m is used to reference the member m of the structure pointed to by x.

9. Omitting the ampersand before nonpointer variables in a scanf() call.

Example

int number;
...
scanf ("%i", number);

Remember that all arguments appearing after the format string in a scanf() call must be pointers.

10. Using a pointer variable before it’s initialized.

Example

char *char_pointer;
*char_pointer = 'X';

You can only apply the indirection operator to a pointer variable after you have set the variable pointing somewhere. In this example, char_pointer is never set pointing to anything, so the assignment is not meaningful.

11. Omitting the break statement at the end of a case in a switch statement.

Remember that if a break is not included at the end of a case, then execution continues into the next case.

12. Inserting a semicolon at the end of a preprocessor definition.

This usually happens because it becomes a matter of habit to end all statements with semicolons. Remember that everything appearing to the right of the defined name in the #define statement gets directly substituted into the program. So the definition

#define END_OF_DATA 999;

leads to a syntax error if used in an expression such as

if ( value == END_OF_DATA )
...

because the compiler will see this statement after preprocessing:

if ( value == 999; )
...

13. Omitting parentheses around arguments in macro definitions.

Example

#define reciprocal(x) 1 / x
...
w = reciprocal (a + b);

The preceding assignment statement would be incorrectly evaluated as

w = 1 / a + b;

14. Omitting a closing parenthesis or closing quotation marks on any statement.

Example

total_earning = (cash + (investments * inv_interest) + (savings * sav_interest);
printf("Your total money to date is %.2f, total_earning);

On the first line, the use of embedded parentheses to set apart each portion of the equation makes for a more readable line of code, but there is always the possibility of missing a closing parenthesis (or in some occasions, adding one too many). The second line also is missing a closing quotation mark for the string being sent to the printf() function. Both of these will generate a compiler error, but sometimes the error will be identified as coming on a different line, depending on whether the compiler uses a parenthesis or quotation mark on a subsequent line to complete the expression, and therefore move the missing character to a place later in the program.

15. Failing to include the header file that includes the definition for a C-programming library function being used in the program.

Example

double answer = sqrt(value1);

If this program does not #include the <math.h> file, this will generate an error that sqrt() is undefined.

16. Leaving a blank space between the name of a macro and its argument list in the #define statement.

Example

#define MIN (a,b) ( ( (a) < (b) ) ? (a) : (b) )

This definition is incorrect, as the preprocessor considers the first blank space after the defined name as the start of the definition for that name. In this case, the statement

minVal = MIN (val1, val2);

gets expanded by the preprocessor into

minVal = (a,b) ( ( (a) < (b) ) ? (a) : (b) )(3,2);

which is obviously not what is intended.

17. Using an expression that has side effects in a macro call.

Example

#define SQUARE(x) (x) * (x)
...
w = SQUARE (++v);

The invocation of the SQUARE macro causes v to be incremented twice because this statement is expanded by the preprocessor to

w = (++v) * (++v);