Advanced C 1992
.pdf
Part IV • Documenting the Differences
could be at a data object. Memory breakpoints are useful if you want to determine what part of the code is modifying a variable. You could specify a value at the given location, and when the value is stored there, the breakpoint is entered.
•Program modification. Some debuggers enable you to correct minor errors, then continue program execution. This feature is useful for programs with complex setup or initialization processes.
•Execution stepping. Almost all debuggers can execute statements one line at a time. This technique, called stepping, enables you to trace the flow of the program (for example, the number of times the loop executed, and the functions and subroutines that were called). Some debuggers enable you to trace in both the high-level language (such as C) and assembly (or machine) language. Tracing the program’s flow in C is often the fastest and most valuable option because you can see the effects of each program statement.
When using a debugger, you must balance the learning curve (that is, how long it takes you to learn to use the debugger), the amount of time needed to set up the program and the debugger, and how quickly you can find the problem.
In C programming on the PC, each compiler has its own debugger. There are also a number of stand-alone debuggers, most of which require additional hardware (plugin cards or special switches). Soft-Ice/W, Multiscope, and Periscope are examples of debuggers that are not compiler-specific. Each offers features unavailable in compilersupplied debuggers.
Codeview
Microsoft’s Codeview debugger has been around for a long time. Its predecessors include the DOS DEBUG command (a crude and simple debugger) and the SYMDEB debugger.
You can debug most complex programs with Codeview. Codeview enables you to debug to a serial monitor or to configure two monitors for debugging. Under Windows, you can debug to a separate window. Unless you have a large screen monitor and a high-resolution video system (1024 x 768 at a minimum), however, this mode of debugging can be difficult.
656
Debugging and Efficiency |
C C C |
|
16C |
|
C C C |
|
C C |
QuickC for Windows
QuickC for Windows from Microsoft offers an integrated debugger. (It doesn’t have its own name because it is part of QuickC’s Integrated Development Environment.) This debugger can debug only at the C source level; it cannot debug in assembly. Therefore, you must have the source code for the sections of the program that you want to debug.
When using QuickC’s debugger, you lose the advanced features of the Windows debugging kernel, which helps find programming errors caused by incorrect calls to Windows functions.
Turbo Debugger
Borland’s Turbo Debugger enables you to debug programs compiled with Borland’s compilers. This debugger offers many of the features found in Microsoft’s Codeview, including dual-monitor support and the ability to use a debugging terminal.
Watcom’s VIDEO Debugger
When using Watcom’s 32-bit compiler (Watcom C/386), you have access to their debugger, called VIDEO. This debugger offers many interesting features. The compiler is compatible with DOS, Windows, and OS/2, so the debugger supports each of these environments, as well as QNX.
VIDEO, like many of the other debuggers, offers dual-monitor support. It also debugs using two PCs linked by serial ports, by parallel ports, or over networks.
Efficiency
Program efficiency is an important aspect of programming. It does not matter how well your program performs if it takes forever and a day to accomplish the job. Efficiency consists of many parts—you cannot use some aspects of creating an efficient program while ignoring others.
Statements that slow a program usually are in loops. For example, a typical for() loop that calls some functions can use a lot of time. When you write a loop, consider how many times the loop will be executed, which functions are called, and what the functions are doing. If the loop will be executed a large number of times (thousands
657
Part IV • Documenting the Differences
of times or more), you should consider optimizing the loop. When efficiency is the most important objective, you can sometimes make the program run faster by coding a frequently called function inline, instead of calling the code as a function.
32-Bit Programs
Most PC programming uses the 16-bit programming model. Every time a long int is added, two 16-bit adds are performed. For programs that will be running only on 386 (or higher) CPUs, it might be more efficient to use the 32-bit instructions that these CPUs offer. Remember, however, that a program written for the 32-bit instruction set of the 386 cannot run on a 286 or an original PC.
Programs written using the 32-bit mode of the CPU run only in protected mode. Therefore, an interface is required between DOS (which runs only in real mode) and your application running in 32-bit mode. This interface is called DPMI (DOS Protected Mode Interface). Following is a list of common DPMI products:
•Microsoft Windows 3.1 in 386 enhanced mode provides DOS sessions with DPMI service by Windows. This interface is included with Windows 3.1 at no charge.
•DOS/4G from Rational Systems, Inc. (and DOS/4GW, which is supplied with Watcom’s C/386 compiler) provides the necessary DPMI services for your programs. DOS loads DOS/4G each time a program that must use its services is loaded by the user or executed by DOS.
•Phar Lap 386/DOS-Extender from Phar Lap Software, Inc. is another DPMI interface. It is similar to DOS/4G in that it is loaded each time a program requiring its services is executed.
•OS/386 from ERGO Computing, Inc. provides DPMI services. Because OS/386 is a TSR, it must be loaded only once. The drawback is that the DPMI interface continues to occupy memory after the program that required it completes its execution.
•Intel Code Builder is another DPMI system.
•OS/2 V2 from IBM. This is not a true DPMI because it is not MS-DOS. It is, however, a competent 32-bit platform, offering one of the best 32-bit environments with some MS-DOS compatibility. Watcom’s C-386 is an excellent 32-bit compiler for OS/2.
658
Debugging and Efficiency |
C C C |
|
16C |
|
C C C |
|
C C |
•Windows NT from Microsoft is a 32-bit operating system that is not CPUdependent. The user interface of this OS, which was initially produced for 386/486 CPUs and the MIPS ARC CPU, resembles Windows 3.1. To write Windows NT software, you must have a special version of Microsoft C7 that produces 32-bit code.
A number of 32-bit C compilers support C++ as well as standard C programming. The more popular 32-bit C compilers include the following:
•Watcom C/386 is an excellent compiler that you can use to develop applications that execute under MS-DOS, OS/2 V2, and Windows 3.x. This compiler supports only C programming, but the C++ version should be available soon. A royalty-free copy of DOS/4GW is included with Watcom C/386, and the compiler supports all other DPMI interfaces (see the preceding list). Watcom’s compiler provides the only 32-bit support for Windows 3.1. The ability to write 32-bit Windows programs is a powerful feature that should not be ignored by Windows programmers.
•Microway NDP C/C++ is an expensive compiler that offers much to programmers writing math-intensive code. This compiler has excellent support for math coprocessors but lacks a DPMI interface program, so one must be purchased separately.
•Zortech C/C++ is a compiler that can produce either 16-bit or 32-bit programs. It comes with a DPMI program (called DOSX) that you can use royalty-free, or you can use Phar Lap’s DOS Extender.
•Intel’s Code Builder provides a 32-bit compiler based on Microsoft’s C compilers. This product is most compatible with the Microsoft compiler. Version 1.1 supports 386MAX and QEMM-386 as well.
Creating 32-bit code can improve your program’s performance from several standpoints. One, you can perform math on 32-bit data objects. Two, your program can access protected-mode memory. Many DPMI programs provide access to virtual memory, allowing truly large programs. Finally, 32-bit programs are more compact (after they are loaded into memory). In summary, if you can live with applications that run only on 386 (and greater) systems, converting to a 32-bit compiler can substantially improve the performance of your programs.
659
Part IV • Documenting the Differences
Compiler Optimization
One of the easiest ways to gain more performance from your applications is to have the compiler optimize the program. Most compilers allow optimization for size (important if you are developing for a plain DOS environment with its 640K memory limit) or speed. Some compilers optimize both for size and for speed, although you cannot ensure that compromises between the two will produce code that is both small and efficient.
Typical optimizations are shown in Table 16.2. If an optimization is not offered by your compiler, another optimization might perform a similar task.
Table 16.2. Typical optimizations for compilers.
Microsoft |
Borland |
Watcom |
Function |
/G0 |
-1- |
/G1 |
-1 |
/G2 |
-2 |
/G3 |
-3 |
/G4 |
N/A |
/Gc |
-p |
Generate code for 8086
Generate code for 80186
Generate code for 80286
Generate code for 80386
Generate code for 80486
FORTRAN/Pascal calling conventions
/Gr |
-pr |
Register calling conventions
/Gs |
-N- |
Remove stack overflow |
|
|
checks |
/Gy |
|
Function-level linking |
|
|
(links only called |
|
|
functions) |
/O, |
/Ot |
-Ox, -G, |
/ot |
|
|
-Ot |
|
/Oa |
|
-Oa, -G- |
/oa |
Minimize execution speed (default)
Assume no aliasing
660
Debugging and Efficiency
Microsoft |
Borland |
Watcom |
Function |
/Obn |
-Oi |
|
Control inline |
|
|
|
expansion (n |
|
|
|
is a digit from 0 |
|
|
|
through 2) |
C C C
C16C C
C C C
/Oc |
-Oc |
Enable block-level common subexpression optimization (default)
/Od |
-Od |
/od |
Turn off all |
|
|
|
optimization |
/Oe |
-Oe |
|
Ignore register keyword |
|
|
|
and allow compiler to |
|
|
|
perform global register |
|
|
|
allocation |
/Of |
|
/Of- |
|
/Og |
-Og |
Turn on P-code quoting (default)
Turn off P-code quoting
Enable global-level common subexpression optimization
/Oi |
-Oi |
/oi |
/Ol |
-Ol |
/ol |
/On |
-Ol-Om |
|
Generate intrinsic functions
Enable loop optimization
Turn off potentially unsafe loop optimizations
/Oo |
Turn on post code- |
|
generation optimizing |
|
(default) |
/Oo- |
Turn off post code- |
|
generation optimizing |
continues
661
Part IV • Documenting the Differences
Table 16.2. continued
Microsoft |
Borland |
Watcom |
Function |
/Op |
|
|
Improve float |
|
|
|
consistency |
/Oq |
N/A |
/Or |
N/A |
Turn on P-code optimization
Enable single exit point from functions (useful when debugging with CodeView)
/Os |
-O1, -Os |
/os |
Minimize executable |
|
|
|
file size |
/Ov |
N/A |
|
Sort local variables by |
|
|
|
frequency of use; P- |
|
|
|
code only (default) |
/Ov- |
N/A |
|
Sort local variables in |
|
|
|
the order that they |
|
|
|
occur; P-code only |
/Ow |
|
|
Assume aliasing across |
|
|
|
function calls |
/Ox |
-Ox |
/oxat |
Maximize optimization |
/Oz |
|
|
Turn on potentially |
|
|
|
unsafe loop |
|
|
|
optimizations |
|
|
|
|
As you can see from Table 16.2, each compiler offers some similar optimization options. The Microsoft compiler offers more different optimizations, but some of these are specific to how the compiler works (such as the Microsoft compiler’s ability to generate P-code).
Some of the more common optimizations include loop optimization, intrinsic function generation, and common subexpression optimization. These are the subjects of the following sections.
662
Debugging and Efficiency |
C C C |
|
16C |
|
C C C |
|
C C |
Loop Optimization
In loop optimization, expressions that always result in the same value are moved outside the loop. An example of this follows:
//Local (auto) variables: int i = 0;
int x = 1; int y = 2;
while(i < 250)
{
i += x + y; OurFunction(i);
}
The value of the i variable is incremented by the sum of x and y. The values of x and y do not change in the loop, so their sum does not change. However, x and y are summed each time the loop is iterated. Perhaps the programmer could be more careful, but I have found it difficult to catch simple things such as invariant expressions.
When the compiler performs a loop optimization, it modifies the code to resemble the following:
//Local (auto) variables: int i = 0;
int x = 1; int y = 2;
//Compiler added temporary storage: int _ _temp = x + y;
while(i < 250)
{
i += _ _temp; OurFunction(i);
}
Notice that the compiler has created a new temporary variable (which actually doesn’t have a name). This temporary variable is assigned the sum of x and y. In the loop, i is incremented by the value of the temporary variable, so the sum does not have to be recomputed for each loop iteration.
663
Part IV • Documenting the Differences
Sometimes loop optimization can create problems during debugging. For example, suppose there is a problem in the temporary variable’s computation (such as a divide-by-zero error). The line number for the error will not match a line in the program, making it difficult to determine the location of the error. As well, when tracing the program’s execution, the machine instructions will not have a linear correspondence to the source code lines, making it more difficult to debug the program. For these reasons, you might want to turn off loop optimization when you initially debug your program.
Generating Intrinsic Functions
The library functions are defined as separate, callable functions. Some compilers, however, can generate inline code for many library functions. Inline code generation for these functions saves the overhead of a function call, which can be important when the function is called from a loop that has many iterations.
The functions in Table 16.3 have intrinsic forms. Only the ANSI functions are listed. Each compiler, however, offers a number of other intrinsic functions that are compiler-specific.
Table 16.3. ANSI-supported intrinsic functions.
Function |
Microsoft |
Borland |
Watcom C/386 |
abs()
acos()
asin()
atan()
atan2()
ceil()
cos()
cosh()
div()
exp()
fabs()
Ö |
|
Ö |
Ö |
|
|
Ö |
|
|
Ö |
|
|
Ö |
|
|
Ö |
|
|
Ö |
|
|
Ö |
|
|
|
|
Ö |
Ö |
|
|
Ö |
Ö |
Ö |
664
|
|
|
Debugging and Efficiency |
C C C |
|
|
|
|
|
16C |
|
|
|
|
|
C C C |
|
|
|
|
|
C C |
|
|
|
|
|
|
|
|
Function |
Microsoft Borland |
Watcom C/386 |
|
|
floor()
fmod()
inp()
inpw()
labs()
labs()
ldiv()
log()
log10()
memchr()
memcmp()
memcpy()
memset()
movedata()
outp()
outpw()
pow()
rotl()
rotr()
sin()
sinh()
sqrt()
stpcpy()
strcat()
strchr()
Ö |
|
|
Ö |
|
|
|
|
Ö |
|
|
Ö |
|
|
Ö |
Ö |
|
|
|
|
Ö |
Ö |
|
|
Ö |
|
|
|
Ö |
Ö |
Ö |
Ö |
Ö |
Ö |
Ö |
Ö |
Ö |
Ö |
Ö |
|
|
Ö |
|
|
Ö |
|
|
Ö |
Ö |
|
|
|
Ö |
|
|
Ö |
|
Ö |
|
|
Ö |
|
|
Ö |
|
|
ÖÖ
Ö |
Ö |
Ö |
|
Ö |
Ö |
continues
665
