Sams Teach Yourself C in 24 Hours (62 page)

BOOK: Sams Teach Yourself C in 24 Hours
12.26Mb size Format: txt, pdf, ePub

Q Can you redirect a standard stream to a disk file?

A
Yes. With the help of the freopen() function, you can redirect a standard stream and associate the stream with a disk file.

Workshop

To help solidify your understanding of this hour’s lesson, you are encouraged to answer the quiz questions and finish the exercises provided in the workshop before you move to the next lesson. The answers and hints to the questions and exercises are given in Appendix B, “Answers to Quiz Questions and Exercises.”

Quiz

1. Are the following two statements equivalent?

rewind(fptr);

fseek(fptr, 0L, SEEK_SET);

2. Are the following two statements equivalent?

rewind(fptr);

fseek(fptr, 0L, SEEK_CUR);

3. After the statement

freopen(“test.txt”, “r”, stdin);

is executed successfully, where does the scanf() function in the following statement read from?

scanf(“%s%d”, str, &num);

4. Given that the size of the double data type is 8 bytes long, and you have four double data items, if you write the four double data items into a binary file, how many bytes do the four data items take in the file?

28 067231861x CH22 4.10.2000 11:04 AM Page 389

Using Special File Functions

389

Exercises

1. Assume that the following paragraph of
Tao Te Ching
is saved in a text file called
22

LaoTzu.txt:

Be bent, and you will remain straight.

Be vacant, and you will remain full.

Be worn, and you will remain new.

Write a program to use ftell() to find the positions of the three strings in the file, and then call fseek() to set the file position indicator in such a way that the three strings are printed out in reverse order.

2. Rewrite the program you made in exercise 1 by calling the rewind() function to reset the file position indicator at the beginning of the LaoTzu.txt file.

3. Given a double value of 123.45, and an int value of 10000, write a program to save them into a binary file, called data.bin, and then read them back from the binary file. Also, print out what you’re writing or reading. What do you think the size of the binary file will be?

4. Read the text file strnum.mix, which is created by the program in Listing 22.3.

Redirect the input stream so that you can use the scanf() function to perform the reading operation.

28 067231861x CH22 4.10.2000 11:04 AM Page 390

29 067231861x CH23 1/25/00 11:03 AM Page 391

HOUR 23

Compiling Programs: The

C Preprocessor

Intelligence is the faculty of making artificial objects, especially tools to
make tools.

—H. Bergson

In Hour 2, “Your First C Program,” you learned how to use the #include

preprocessor directive to include C header files. Since then, the #include directive has been used in every program in this book. In this lesson you’ll learn more about the C preprocessor and making macro definitions with the preprocessor directives. The following topics are discussed in this hour:

• What the C preprocessor can do

• Macro definitions and macro substitutions

• The #define and #undef directives

• How to define function-like macros with #define

• The #ifdef, #ifndef, and #endif directives

• The #if, #elif, and #else directives

• How to nest #if and #elif directives

29 067231861x CH23 1/25/00 11:03 AM Page 392

392

Hour 23

What Is the C Preprocessor?

If there is a constant appearing in several places in your program, it’s a good idea to associate a symbolic name to the constant, and then use the symbolic name to replace the constant throughout the program. There are two advantages to doing so. First, your program will be more readable. Second, it’s easier to maintain your program. For instance, if the value of the constant needs to be changed, you just find the statement that associates the constant with the symbolic name and replace the constant with the new one. Without using the symbolic name, you have to look everywhere in your program to replace the constant. Sounds great, but can you do this in C?

Well, C has a special program called the C preprocessor that allows you to define and associate symbolic names with constants. In fact, the C preprocessor uses the terminology
macro names
and
macro body
to refer to the symbolic names and the constants. The C preprocessor runs before the compiler. During the preprocessing, the operation to replace a macro name with its associated macro body is called
macro substitution
or
macro expansion
.

You can put a macro definition anywhere in your program. However, a macro name has to be defined before it can be used in your program.

In addition, the C preprocessor gives you the ability to include other source files. For instance, you’ve been using the preprocessor directive #include to include C header files, such as stdio.h, stdlib.h, and string.h, in the programs throughout this book.

Also, the C preprocessor enables you to compile different sections of your program under specified conditions.

The C Preprocessor versus the Compiler

One important thing you need to remember is that the C preprocessor is not part of the C

compiler.

The C preprocessor uses a different syntax. All directives in the C preprocessor begin with a pound sign (#). In other words, the pound sign denotes the beginning of a preprocessor directive, and it must be the first nonspace character on the line.

The C preprocessor is line-oriented. Each macro statement ends with a newline character, not a semicolon. (Only C statements end with semicolons.) One of the most common mistakes made by the programmer is to place a semicolon at the end of a macro statement. Fortunately, many C compilers can catch such errors.

The following sections describe some of the most frequently used directives, such as

#define, #undef, #if, #elif, #else, #ifdef, #ifndef, and #endif.

29 067231861x CH23 1/25/00 11:03 AM Page 393

Compiling Programs: The C Preprocessor

393

Macro names, especially those that will be substituted by constants, are normally represented with uppercase letters so that they can be distinguished from other variable names in the program.

The
#define
and
#undef
Directives

23

The #define directive is the most common preprocessor directive, which tells the preprocessor to replace every occurrence of a particular character string (that is, macro name) with a specified value (that is, macro body).

The syntax for the #define directive is

AX

#define macro_name macro_body

YNT

Here macro_name is an identifier that can contain letters, numerals, or underscores.

S
macro_body

,

may be a string or a data item, which is used to substitute each macro_name found in the program.

As mentioned earlier, the operation to replace occurrences of macro_name with the value specified by macro_body is known as
macro substitution
or
macro expansion
.

The value of the macro body specified by a #define directive can be any character string or number. For example, the following definition associates STATE_NAME with the string

“Texas” (including the quotation marks):

#define STATE_NAME “Texas”

Then, during the preprocessing, all occurrences of STATE_NAME will be replaced by

“Texas”.

Likewise, the following statement tells the C preprocessor to replace SUM with the string (12 + 8):

#define SUM (12 + 8)

On the other hand, you can use the #undef directive to remove the definition of a macro name that has been previously defined.

The syntax for the #undef directive is

AX

#undef macro_name

YNT

Here macro_name is an identifier that has been previously defined by a #define
S
directive.

29 067231861x CH23 1/25/00 11:03 AM Page 394

394

Hour 23

You can think of the #undef directive as “undefining” a macro name. For instance, the following segment of code:

#define STATE_NAME “Texas”

printf(“I am moving out of %s.\n”, STATE_NAME);

#undef STATE_NAME

defines the macro name STATE_NAME first, and uses the macro name in the printf() function; then it removes the macro name. From that point in the program, STATE_NAME

cannot be used again (unless, of course, it is redefined first).

Defining Function-Like Macros with
#define

You can specify one or more arguments to a macro name defined by the #define directive, so that the macro name can be treated like a simple function that accepts arguments.

For instance, the following macro name, MULTIPLY, takes two arguments:

#define MULTIPLY(val1, val2) ((val1) * (val2))

When the following statement:

result = MULTIPLY(2, 3) + 10;

is preprocessed, the preprocessor substitutes the expression 2 for val1 and 3 for val2, and then produces the following equivalent:

result = ((2) * (3)) + 10;

The program in Listing 23.1 is an example of using the #define directive to perform macro substitution.

TYPE

LISTING 23.1

Using the #define Directive

1: /* 23L01.c: Using #define */

2: #include

3:

4: #define METHOD “ABS”

5: #define ABS(val) ((val) < 0 ? -(val) : (val))

6: #define MAX_LEN 8

7: #define NEGATIVE_NUM -10

8:

9: main(void)

10: {

11: char *str = METHOD;

12: int array[MAX_LEN];

13: int i;

14:

15: printf(“The orignal values in array:\n”);

16: for (i=0; i

29 067231861x CH23 1/25/00 11:03 AM Page 395

Compiling Programs: The C Preprocessor

395

17: array[i] = (i + 1) * NEGATIVE_NUM;

18: printf(“array[%d]: %d\n”, i, array[i]);

19: }

20:

21: printf(“\nApplying the %s macro:\n”, str);

22: for (i=0; i

23: printf(“ABS(%d): %3d\n”, array[i], ABS(array[i]));

24: }

25:

26: return 0;

23

27: }

The following output appears on the screen of my computer after I run the executable 23L01.exe of the program in Listing 23.1:

The orignal values in array:

OUTPUT
array[0]: -10

array[1]: -20

array[2]: -30

array[3]: -40

array[4]: -50

array[5]: -60

array[6]: -70

array[7]: -80

Applying the ABS macro:

ABS(-10): 10

ABS(-20): 20

ABS(-30): 30

ABS(-40): 40

ABS(-50): 50

ABS(-60): 60

ABS(-70): 70

ABS(-80): 80

The purpose of the program in Listing 23.1 is to define different macro names,
ANALYSIS
including a function-like macro, and use them in the program.

In lines 4–7, four macro names, METHOD, ABS, MAX_LEN, and NEGATIVE_NUM are defined with the #define directive. Among them, ABS can accept one argument. The definition of ABS in line 5 checks the value of the argument and returns the absolute value of the argument. Note that the conditional operator ?: is used to find the absolute value for the incoming argument. (The ?: operator was introduced in Hour 8, “Using Conditional Operators.”)

Then, inside the main() function, the char pointer str is defined and assigned with METHOD in line 11. As you can see, METHOD is associated with the string “ABS”. In line 12, an int array called array is defined with the element number specified by MAX_LEN.

29 067231861x CH23 1/25/00 11:03 AM Page 396

396

Hour 23

In lines 16–19, each element of array is initialized with the value represented by the (i + 1) * NEGATIVE_NUM expression that produces a series of negative integer numbers.

The for loop in lines 22–24 applies the function-like macro ABS to each element of array and obtains the absolute value for each element. Then, all of the absolute values are displayed on the screen. The output from the program in Listing 23.1 proves that each macro defined in the program works very well.

Nested Macro Definitions

A previously defined macro can be used as the value in another #define statement. The following is an example:

#define ONE 1

#define TWO (ONE + ONE)

#define THREE (ONE + TWO)

result = TWO * THREE;

Here the macro ONE is defined to be equivalent to the value 1, and TWO is defined to be equivalent to (ONE + ONE), where ONE has been defined in the previous macro definition.

Likewise, THREE is defined to be equivalent to (ONE + TWO), where both ONE and TWO are previously defined.

Other books

The Killer Angels by Michael Shaara
Troublemaker by Joseph Hansen
Heaven by Ian Stewart
Tell Me It's Real by TJ Klune
Nothing but Blue Skies by Thomas McGuane
The Hero Sandwich by Gerrard, Karyn, Taylor, Gayl