UNIX C Programming

Chapter 16: The C Preprocessor and the C Library



This chapter introduces several new types of #define directives, provides additional information about .h files, and introduces the functions sqrt(), atan(), atan2(), atexit(), malloc(), calloc(), and free(). The objectives important to this chapter include:

  • Clarify the reasons for using macro functions
  • Review the different types of #define directives
  • Provide examples of conditional compilation using directives
  • Describe the enum type
  • Clarify the reasons for the dynamic allocation of memory
  • Discuss the use of malloc(), calloc(), and free()

Preprocessor directives run until the first newline following the #. Each #define has three parts: the #define directive, the macro name, and the body. The process of going from a macro to a final replacement is called macro expansion.

The preprocessor does no calculations. It just makes the suggested substitutions. When the preprocessor finds one of your macros in your program, it literally replaces it with the equivalent replacement text.

The body of a macro consists of a string of tokens. Each token is separated by whitespace.

Once macros are defined, they cannot be redefined unless the new definition duplicates the old one.

Macros have the advantage over functions in that they don't worry about variable types. They deal with character strings and not actual values.

Rules and guidelines to remember in writing macros are:
  1. Leave no spaces in the macro name.
  2. Use parentheses around each argument and around the definition as a whole.
  3. Use capital letters for macro names.
  4. To improve the speed of processing , a macro will save time only if it used several times in a program.

Header files are used for storing a variety of information. This includes: manifest constants, macro function definitions, function declaration prototypes, structure template definitions, and type definitions.

The common directives used in conditional complication include: #ifdef, #else, and #endif.

Other directives include #undef (to undefine a #define), #ifndef (to ask if an identifier has been defined), #if (to test a condition), and #elif (to extend an if-else sequence).

Enumerated types are used to declare symbolic names to represent integer constants. They are used to help with the readability of a program.

Enum is type int. In structures such as

	enum spectrum {red, orange, yellow };

The default values for red, orange, and yellow are 0, 1, and 2, respectively. These values can be changed as follows:

	enum spectrum {red, orange = 10, yellow };

With this change, the values for red, orange, and yellow are 0, 10, and 11, respectively.

The ANSI C committee developed an official standard C library. One of these libraries is math.h. This library stores a variety of mathematical functions such as square root (sqrt()) and arctangent (atan()). Review Appendix F for the functions associated with the stdio.h file.

General purpose functions are found in the stdlib.h library.

The at exit function (atexit()) differs from the exit() function. The atexit() function is used to register which functions to execute after an exit() is reached in a program.

Dynamic allocation of memory is possible using the malloc() and calloc() functions. With malloc(), the function takes one argument, the number of bytes of memory required. The malloc() function returns the address of the first byte of that block.

With calloc(), the function takes two arguments, the number of memory cells required and the size of each cell in bytes.

Memory is released from malloc() or calloc() using the function free().

The key point with malloc() and calloc() is that the programmer determines how much memory is needed. The programmer also controls memory persistence.