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

Advanced C 1992

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

Part IV • Documenting the Differences

The pack Pragma

The pack pragma enables you to control the packing of structure members. This pragma usually is used with a parameter; used without a parameter, however, it restores the default packing value. Figure 15.1 shows an example of the effects of packing structures. This packing, called data alignment, affects the way each element in a structure is stored.

Figure 15.1. The effect of packing and the pack pragma.

All computers most efficiently access objects aligned on boundaries that are even multiples of the memory bus width. For an 80286 CPU, which accesses memory in 16-bit widths, accessing words that are aligned to even bytes is optimal. For an 80386/ 486—that is, a 32-bit processor that accesses memory in 32-bit widths—memory is accessed optimally using every other even address. The effects of accessing data objects that are not optimally aligned are difficult to predict. On one hand, aligning on a byte boundary makes the aggregate data objects smaller (and if they are used in arrays, the size difference can be significant), and the speed of even alignment may also be significant for an object that is accessed often.

Many programmers experiment with alignment to find the optimal compromise between size and speed. I usually use byte alignment for infrequently accessed objects and word alignment for objects that are accessed more frequently.

636

Preprocessor Directives

C C C

 

15C

 

C C C

 

C C

The #undef Directive

At times you might need to redefine a macro, often when an identifier has been defined outside your program and you are using it for a different purpose. To remove a definition of a macro, you should use the #undef directive. The following code segment shows how this directive is used:

#if defined(VERSION1) #undef VERSION1 #endif

#define VERSION1 "The is Version 1.0"

Using #undef to remove a definition of a predefined macro is not a good idea.

Predefined Macros

ANSI C defines a number of macros. Although each one is available for your use in programming, the predefined macros should not be directly modified.

The _ _DATE_ _Macro

The _ _DATE _ _macro contains the current date as a character literal in the following format:

"MMM DD YYYY"

where MMM is the three-character month abbreviation, in mixed case; DD is the current day, padded with a blank if the day is less than 10; and YYYY is the year.

The _ _TIME_ _Macro

The _ _TIME _ _macro contains the current time as a character literal in the following format:

"HH:MM:SS"

where HH is hours, MM is minutes, and SS is seconds.

If any of the three is less than 10, the field is padded with a leading 0.

637

Part IV • Documenting the Differences

The_ _FILE_ _Macro

The _ _FILE _ _macro contains the current filename as a string literal. This macro’s contents can be changed using the #line directive.

The_ _LINE_ _Macro

The _ _LINE _ _macro contains the current line number as a decimal constant. This macro’s contents can be changed using the #line directive.

The_ _STDC_ _Macro

The _ _STDC _ _macro is defined as a decimal constant (value of 1) when the compiler complies with the ANSI standard.

NULL

NULL is defined as a pointer guaranteed not to point to anything valid. This pointer often is used as both an initializing value and an error return. You generally should use the NULL macro rather than 0 when you are assigning a null pointer to a pointer variable.

NULL commonly is assigned to variables of other types; you must be sure, however, that the results are what are expected. You should not assume that NULL is the equivalent to zero (which is often defined as a macro called FALSE), because this may not be the case.

The offsetof() Macro

The offsetof() macro returns the offset (in bytes) of a member of a structure from the beginning of the structure.

638

Preprocessor Directives

C C C

 

15C

 

C C C

 

C C

Summary

In this chapter, you learned about the preprocessor’s directives.

The preprocessor directives enable you to control the compilation of the program and to optionally include or exclude parts of a program based on predefined identifiers.

Preprocessor directives can be in header files and in your source files.

All identifiers defined with the #define directive are macros; a macro that has no parameters, however, often is referred to as simply a defined identifier.

ANSI standard C defines a number of macros that can be used in programming to assist in debugging and development of a program.

639

Part IV • Documenting the Differences

640

CDebuggingCand Efficiency CC C C

C16C C

C16C CC C C

C C C

Debugging and

Efficiency

Just because a program has been written does not mean that it works correctly or is efficient. This chapter looks at debugging a program and methods for making the program more efficient.

Debugging

It is a rare program that works the first time. Many C programs are complex, containing thousands of lines of code. Even if the programmer has not made any syntax errors, the assumptions made when writing a program can create serious problems.

641

Part IV • Documenting the Differences

Debugging a program involves several steps:

1.Correct all warnings and errors that the compiler finds. Many programmers assume that the default error level is enough. This is incorrect—set the error level as high as possible. Correct all warnings and errors, or understand why you do not have to correct them.

2.After the program compiles without warnings or errors, the real debugging takes place. Use the program and make sure that it performs as you expect. This process, called alpha testing, may take some time. The more problems you find and correct, however, the fewer the user will find.

3.After finding and correcting problems during the alpha testing, find a few qualified users to test the program in an environment as close as possible to that which the program will be used in. This testing is called beta testing. Make sure that the testers really use the program. Generally, beta testers get a free copy of the product for their effort.

4.After beta testing, the product should be bug free. Any problems found by you (in alpha testing) and your test users (in beta testing) should be corrected. Beta testers often test two or three versions of the product.

5.Now that the product is finished, you ship it. Remember the first customer’s name, because this is the person who finds the first bug, usually within a few minutes of using the product. Find ways to minimize the bug’s effect. After the product has been used for a few months, create an updated version that corrects all the bugs the users have discovered.

The first part of debugging is finding the errors, the second part is correcting them, and the third and final part is ensuring the correction does not create new errors.

Common Bugs

Following are some of the most common errors made when programming in C:

Using uninitialized variables

Misusing the assignment and equality operators

Unexpected side effects

Misuse of global variables

642

Debugging and Efficiency

C C C

 

16C

 

C C C

 

C C

Misuse of automatic variables

Using variables of different sizes or types together

Incorrect operator precedence

Not using the proper array bounds

Misusing pointers

Assuming the order of evaluation for function parameters

Assuming the order of evaluation for operations where the order of evaluation is undefined

Each of these errors is discussed in this section.

Uninitialized Variables

Using a variable that has not been initialized is a problem when the variable is a floatingpoint variable. Almost anything can happen, such as a trashed operating system, Windows programs that create UAEs, or OS/2 programs that do not work correctly.

Misused Operators

Misusing the assignment operator (=) and the equality (==) operator is another common bug. For example, the following for() loop will never end:

for (i = 0; i = 1; i++)

You will get a warning—assignment within conditional expression—but if you do not understand the warning message, you may think the compiler is referring to the initialization section (where i is initialized to 0) of the for() loop. The variable i is initialized to 0, then it is assigned the value of 1 in the test section of the for() loop, then it is incremented at the end. The next time around, i is again set to 1, and so on. If the loop is tight (no calls to I/O functions or functions that may call DOS), it’s power-switch time.

In the next example, the compiler does not return a warning:

i == 20;

This line of code compares i and 20, then throws away the result. If you don’t look sharply, you’ll spend a lot of time wondering why i is never set to 20.

643

Part IV • Documenting the Differences

Side Effects

Most macros can cause strange side effects. Consider the following, where max() is a macro:

i = max(i++, j);

If i and j are originally 1, what is i on return? Try it, and you will find that i is still 1. The i variable is incremented after the max() macro is evaluated but before the result of the max() macro is assigned to i.

So, what is the result of the following?

i = max(++i, j);

The result is not 2, but 3. The i variable is incremented when the comparison is performed, and because the variable is then 2, it is assigned to i. But there is a prefix increment on the assignment, so the variable is incremented again!

Be careful when you use prefix and postfix increments and decrements with macros, or you may get different results than what you expect.

Global Variables

Never use a global variable as a scratch variable. Even if you know that the global variable is not used elsewhere, you may choose to use it later, and then... When you change the value assigned to a global variable, think about the effects your change will have on the other places where the variable is used.

Automatic Variables

When a variable is defined inside a block and has not been declared with the static identifier, the variable is called an automatic variable and exists only while the block is executing.

Automatic variables do not retain their value between calls to the function. Automatic variables cannot be passed back to the calling function (the compiler creates a static variable to pass back if necessary). They are not initialized and therefore may contain unpredictable values.

644

Debugging and Efficiency

C C C

 

16C

 

C C C

 

C C

Mixed Variable Sizes and Types

Another common bug results when you use variables of different sizes or types together. You cannot successfully assign the unsigned int uSomething, which has a value of 45000, to the signed int iElse. The results are not right, because iElse will have a value of -20536.

The same is true if you try to assign lThing, which has a value of 2345678, to iElse. The result (-13619) is again incorrect. Unlike signed errors, size errors return a warning.

Operator Precedence

I have adopted a simple rule regarding operator precedence: When in doubt, use parentheses, lots of parentheses, because they override any other order of precedence.

In the following example:

int i = 3; int j = 5;

if (i = max(i, j) == 5)

1 is assigned to i. This is the TRUE condition because max(i, j) is equal to 5.

When the example is changed to the following:

int i = 3; int j = 5;

if ((i = max(i, j)) == 5)

the correct value, 5, is assigned to i. The parentheses force the assignment to occur first, even though the conditional test normally has a higher order of precedence.

When in doubt, use parentheses. Table 16.1 lists the operator precedence for C. When more than one operator belongs to the same group, they have the same precedence and are evaluated according to the rules of associativity.

645