| Debugging C and C++ code in a Unix environment | ||
|---|---|---|
| Prev | Chapter 3. Aspects of debugging C and C++ code | Next |
There are some features of the C and C++ languages and the associated build process that often lead to problems.
C and C++ use a preprocessor to expand macro's, declare dependencies and import declarations and to do conditional compilation. In itself, this is quite reasonable. You should realise however that all of these are done on a textual level. The C/C++ preprocessor does not
This can make it difficult to track down missing declarations, it can lead to semantic problems because of macro expansion and it can cause subtle problems.
If you suspect a problem due to preprocessing, check out the preprocessor's manual (e.g. [CPP]) and let it expand your file for examination.
C was developed for use as a systems programming language. C and also C++ can give you access to a lot of operating system functionality. Unfortunately, there are a lot of small but significant differences among various Unix systems:
Some system calls are not available on all systems.
Some system calls and library functions are defined in different header files on different systems.
There may be differing semantics for particular routines. For example, on Sys V-like systems, a signal handler reinstalled. On BSD-like systems, a signal handler stays in place until explicitly removed.
C and C++ have a type system, but it is very weak. You can do all kinds of conversions, many of which can be system dependent or meaningless. Also, the compiler can do some implicit conversions that may cause havoc.
Most errors due to the weak type system can be caught in the bud by doing static analysis early; see the section called Using the compiler's features.
In C and C++, you have to explicitly allocate and deallocate dynamic storage through malloc and free (for C) and through new and delete (for C++). If memory (de)allocation is done incorrectly, it can cause problems at run time such as memory corruption and memory leaks (the memory use of a program keeps on increasing during execution).
Common errors are:
Trying to use memory that has not been allocated yet.
Trying to access memory that has been deallocated already.
Deallocating memory twice.
These errors are difficult to correct without using proper tools; see the section called Memory allocation debugging tools.
In C and C++ programmers commonly do not to try to prevent name space pollution (name conflicts).
Use the static keyword to indicate functions and variables whose scope is restricted to the current file.
Use as few global variables and functions as necessary. If you have to use a large number of them, prefix their names consistently (e.g. MYPROJECT_someglobal).
C and C++ code can be built incrementally; usually make is used to specify dependencies among files for a build. If a Makefile does not specify dependencies properly, you can end up with executables linked to old versions of modules which can be buggy or incompatible with recently introduced changes in other modules.