| |
Common Programming Errors
| 3.1 |
Forgetting to include the math header file when using math library functions is a syntax error. |
| 3.2 |
Forgetting to return a value from a function that is supposed to return a value is a syntax error. |
| 3.3 |
Returning a value from a function whose return type has been declared void is a syntax error. |
| 3.4 |
Declaring function parameters of the same type, such as float x, y instead of float x, float y, is a syntax error. |
| 3.5 |
Placing a semicolon after the right parenthesis enclosing the parameter list of a function definition is a syntax error. |
| 3.6 |
Defining a function parameter again as a local variable in the function is a syntax error. |
| 3.7 |
Defining a function inside another function is a syntax error. |
| 3.8 |
It is a syntax error if the function prototype, function header and function calls do not all agree in the number, type and order of arguments and parameters and in the return-value type. |
| 3.9 |
Forgetting the semicolon at the end of a function prototype is a syntax error. |
| 3.10 |
A function call that does not match the function prototype is a syntax error. |
| 3.11 |
Forgetting a function prototype when a function is not defined before it is first invoked is a syntax error. |
| 3.12 |
When compiling a function definition, it is an error if the return type and signature in the function prototype and the function definition disagree. |
| 3.13 |
Converting from a higher data type in the promotion hierarchy to a lower type can change the data value. |
| 3.14 |
Calling function srand more than once in a program restarts the pseudo-random-number sequence and can affect the randomness of the numbers produced by rand. |
| 3.15 |
Using srand in place of rand to attempt to generate random numbers is a syntax error--function srand does not return a value. |
| 3.16 |
Assigning the integer equivalent of an enumeration constant to a variable of the enumeration type is a syntax error. |
| 3.17 |
After an enumeration constant has been defined, attempting to assign another value to the enumeration constant is a syntax error. |
| 3.18 |
Using multiple storage-class specifiers for an identifier is a syntax error. Only one storage-class specifier can be applied to an identifier. For example, if you include register, do not also include auto. |
| 3.19 |
Accidentally using the same name for an identifier in an inner block that is used for an identifier in an outer block, when in fact the programmer wants the identifier in the outer block to be active for the duration of the inner block, is normally a logic error. |
| 3.20 |
Either omitting the base case, or writing the recursion step incorrectly so that it does not converge on the base case, causes "infinite" recursion, eventually exhausting memory. This is analogous to the problem of an infinite loop in an iterative (nonrecursive) solution. |
| 3.21 |
Writing programs that depend on the order of evaluation of the operands of operators other than &&, ||, ?: and the comma (,) operator can lead to logic errors. |
| 3.22 |
Accidentally having a nonrecursive function call itself, either directly or indirectly (through another function), is a logic error. |
| 3.23 |
C++ programs do not compile unless function prototypes are provided for every function or each function is defined before it is called. |
| 3.24 |
Because reference parameters are mentioned only by name in the body of the called function, the programmer might inadvertently treat reference parameters as pass-by-value parameters. This can cause unexpected side effects if the original copies of the variables are changed by the calling function. |
| 3.25 |
Not initializing a reference variable when it is declared is a syntax error (unless the declaration is part of a function's parameter list). |
| 3.26 |
Attempting to reassign a previously declared reference to be an alias to another variable is a logic error. The value of the other variable is simply assigned to the variable for which the reference is already an alias. |
| 3.27 |
Returning a reference to an automatic variable in a called function is a logic error. Some compilers issue a warning when this occurs in a program. |
| 3.28 |
Specifying and attempting to use a default argument that is not a rightmost (trailing) argument (while not simultaneously defaulting all the rightmost arguments) is a syntax error. |
| 3.29 |
It is a syntax error to use the unary scope resolution operator (::) to access a global variable if a global variable of that name does not exist. |
| 3.30 |
It is an error to attempt to use the unary scope resolution operator (::) to access a non-global variable in an outer block--it is a syntax error if no global variable with that name exists; it is a logic error if a global variable with that name exists (because the unary scope resolution operator will cause the program to refer to the global variable when, in fact, you are trying to improperly access the non-global variable in the outer block). |
| 3.31 |
Creating overloaded functions with identical parameter lists and different return types is a syntax error. |
| 3.32 |
A function with default arguments omitted might be called identically to another overloaded function; this is a syntax error. For example, having in a program both a function that explicitly takes no arguments and a function of the same name that contains all default arguments results in a syntax error when an attempt is made to use that function name in a call passing no arguments. The compiler does not know which version of the function to choose. |
| 3.33 |
Not placing either keyword class or keyword typename before every formal type parameter of a function template (e.g., < class S, class T >) is a syntax error. |
Back to Tips
Good Programming Practices
| 3.1 |
To avoid ambiguity, do not use the same names for the arguments passed to a function and the corresponding parameters in the function definition. |
| 3.2 |
Place a blank line between function definitions to separate the functions and enhance program readability. |
| 3.3 |
Choosing meaningful function names and meaningful parameter names makes programs more readable and helps avoid excessive use of comments. |
| 3.4 |
Although parameter names in function prototypes are optional, many programmers use these names for documentation purposes. |
| 3.5 |
Capitalize the first letter of an identifier used as a user-defined type name. |
| 3.6 |
Use only uppercase letters in the names of enumeration constants. This makes these constants stand out in a program and reminds the programmer that enumeration constants are not variables. |
| 3.7 |
Using enumerations rather than integer constants can make programs clearer and more maintainable. If you need to change the value of an enumeration constant, it can be changed once in the enumeration declaration. |
| 3.8 |
Avoid variable names that hide names in outer scopes. This can be accomplished by avoiding the use of duplicate identifiers in a program. |
| 3.9 |
The inline qualifier should be used only with small, frequently used functions. |
| 3.10 |
Using default arguments can simplify writing function calls. However, some programmers feel that explicitly specifying all arguments is clearer. If the default values for a function change, the program may not yield the desired results. |
| 3.11 |
Always using the unary scope resolution operator (::) to refer to global variables makes programs easier to read and understand, because it makes it clear that you are intending to access a global variable rather than a non-global variable. |
| 3.12 |
Overloading functions that perform closely related tasks can make programs more readable and understandable. |
Back to Tips
Performance Tips
| 3.1 |
Do not try to rewrite existing library routines to make them more efficient. You usually will not be able to increase the performance of these routines and you may introduce errors. |
| 3.2 |
Automatic storage is a means of conserving memory because automatic storage class variables exist in memory only when the block in which they are defined is executing. |
| 3.3 |
The storage-class specifier register can be placed before an automatic variable declaration to suggest that the compiler maintain the variable in one of the computer's high-speed hardware registers rather than in memory. If intensely used variables such as counters or totals can be maintained in hardware registers, the overhead of repeatedly loading the variables from memory into the registers and storing the results back into memory can be eliminated. |
| 3.4 |
Often, register is unnecessary. Today's optimizing compilers are capable of recognizing frequently used variables and can decide to place them in registers without the need for a register declaration from the programmer. |
| 3.5 |
Avoid Fibonacci-style recursive programs that result in an exponential "explosion" of calls. |
| 3.6 |
Avoid using recursion in performance situations. Recursive calls take time and consume additional memory. |
| 3.7 |
A heavily functionalized program--as compared to a monolithic (i.e., one-piece) program without functions--makes potentially large numbers of function calls that can slow down a program's execution speed. However, monolithic programs are difficult to program, test, debug, maintain and evolve. |
| 3.8 |
Using inline functions can reduce execution time, but often increases program size. |
| 3.9 |
One disadvantage of pass-by-value is that, if a large data item is being passed, copying that data can take a considerable amount of execution time and memory space. |
| 3.10 |
Pass-by-reference is good for performance reasons, because it can eliminate the overhead of copying large amounts of data. |
| 3.11 |
For passing large objects, use a constant reference parameter to simulate the appearance and security of pass-by-value and avoid the overhead of passing a copy of the large object. |
Back to Tips
Portability Tips
| 3.1 |
Using the functions in the C++ standard library helps make programs more portable. |
| 3.2 |
Programs that depend on the order of evaluation of the operands of operators other than &&, ||, ?: and the comma (,) operator can function differently on systems with different compilers. |
| 3.3 |
The meaning of an empty function parameter list in C++ is dramatically different than in C. In C, it means all argument checking is disabled (i.e., the function call can pass any arguments it wants). In C++, it means that the function takes no arguments. Thus, C programs using this feature might report syntax errors when compiled in C++. |
Back to Tips
Software Engineering Observations
| 3.1 |
Use the online documentation for your compiler to familiarize yourself with the rich collection of functions and classes in the C++ standard library. |
| 3.2 |
Avoid reinventing the wheel. When possible, use C++ standard library functions instead of writing new functions. This reduces program development time. |
| 3.3 |
In programs containing many functions, main should be implemented as a group of calls to functions that perform the bulk of the program's work. |
| 3.4 |
Each function should be limited to performing a single, well-defined task, and the function name should effectively express that task. This promotes software reusability. |
| 3.5 |
If you cannot choose a concise name that expresses what the function does, it is possible that your function is attempting to perform too many diverse tasks. It is usually best to break such a function into several smaller functions. Then the original function can call the smaller functions to perform the complete task. |
| 3.6 |
Try to keep functions small. Regardless of how long a function is, it should perform one task well. Small functions promote software reusability. |
| 3.7 |
Programs should be written as collections of small functions. This makes programs easier to write, debug, maintain and modify. |
| 3.8 |
A function requiring a large number of parameters might be performing too many tasks. Consider dividing the function into smaller functions that perform the separate tasks. |
| 3.9 |
Function prototypes are required in C++. Use #include preprocessor directives to obtain function prototypes for the standard library functions from the header files for the appropriate libraries (e.g., the prototype for math function sqrt is in header file <cmath>; a list of standard library header files appears in Section 3.7). Also use #include to obtain header files containing function prototypes used by you or your group members. |
| 3.10 |
Automatic storage is an example of the principle of least privilege. Why have variables stored in memory and accessible when they are not needed? |
| 3.11 |
Declaring a variable as global rather than local allows unintended side effects to occur when a function that does not need access to the variable accidentally or maliciously modifies it. In general, use of global variables should be avoided except in certain situations with unique performance requirements. |
| 3.12 |
Variables used only in a particular function should be declared as local variables in that function rather than as global variables. |
| 3.13 |
Any problem that can be solved recursively can also be solved iteratively (nonrecursively). A recursive approach is normally chosen in preference to an iterative approach when the recursive approach more naturally mirrors the problem and results in a program that is easier to understand and debug. Another reason to choose a recursive solution is that an iterative solution is not apparent. |
| 3.14 |
Functionalizing programs in a neat, hierarchical manner promotes good software engineering, but it has a price. |
| 3.15 |
Always provide function prototypes, even though it is possible to omit them when functions are defined before they are used (in which case the first line of the function definition acts as the function prototype as well). Providing the prototypes avoids tying the code to the order in which functions are defined (which can easily change as a program evolves). |
| 3.16 |
Any change to an inline function could require all clients of the function to be recompiled. This can be significant in some program-development and maintenance situations. |
| 3.17 |
Many programmers do not bother to declare value parameters as const, even though the called function should not be modifying the passed argument. Keyword const in this context would only protect a copy of the original argument, not the original argument itself. |
| 3.18 |
Pass-by-reference can weaken security, because the called function can corrupt the caller's data. |
| 3.19 |
For the combined reasons of clarity and performance, many C++ programmers prefer that modifiable arguments be passed to functions by using pointers, small nonmodifiable arguments be passed by value and large nonmodifiable arguments be passed to functions by using references to constants. |
| 3.20 |
Always using the unary scope resolution operator (::) to refer to global variables makes programs easier to modify by reducing the risk of name collisions with non-global variables. |
Back to Tips
Testing and Debugging Tips
| 3.1 |
Provide a default case in a switch to catch errors even if you are absolutely, positively certain that you have no bugs! |
| 3.2 |
Always using the unary scope resolution operator (::) to refer to a global variable eliminates possible logic errors that might occur if a non-global variable hides the global variable. |
| 3.3 |
Avoid using variables of the same name for different purposes in a program. Although this is allowed in various circumstances, it can lead to errors. |
Back to Tips
|