Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Advanced C 1992

.pdf
Скачиваний:
93
Добавлен:
17.08.2013
Размер:
4.28 Mб
Скачать

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