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

Advanced C 1992

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

Part I • Honing Your C Skills

Using #include

The preprocessor’s #include statement is a powerful part of creating a C program. You cannot create a program that compiles without warnings if you do not use at least one #include statement. A set of include files is supplied with every C compiler. These files always have the .h extension, which is shorthand for headers. The ANSI C definitions, prototypes, and other necessary information the compiler needs to function properly are contained in these files (see Chapter 13, “All About Header Files”).

The #include statement can be coded in two formats. Each is slightly different; the differences, however, are easy to understand. The first format is

#include “stdio.h”

In this format, the file to be included is delimited by the character. The delimiter in this #include statement means: “When the compiler searches for the file to be included, the search starts with the current directory of the file that has the #include; if the file is not found and the file containing the #include is also an included file, then it’s a parent.” This process continues recursively until all directories in the chain of included files have been searched. If the file still has not been found, the search continues as though the second format has been specified. The second format is

#include <stdio.h>

In this format, the file to be included is delimited by the < and > characters. When the compiler searches for the file to be included, the search starts with the directories specified on the compile command line (using the /I option) and then the directories specified using the environment variable include. The current directory is not searched unless it has been specified in one of these two places. If the file cannot be found, the compiler issues an error message and the compile ends.

You know that you must include C’s header files because they have the function prototypes and other defined identifiers needed to write your program. What else can be included? An include file can contain anything that could have been put in a source file. You can have preprocessor statements, C source code, and so on, and the compiler treats the information as though it all were in the same file. The only difference is that when an error is in an include file, the compiler’s error message provides the necessary name and line number of the include file.

166

Separate Compilation and Linking

C C C

 

C6C

 

C C C

 

C

For large projects, I generally recommend that you have the following custom include files. Although some projects do not need all these files, you can create and include them at any time.

The first file is named with the same name as the program. This file has only include statements and looks like Listing 6.1. It contains only #include statements to include the other include files.

The second file, defines.h, contains all common #define statements. Using a single, included file for a define helps prevent the use of the same identifier being defined for two purposes.

The next file, typedef.h, contains the program’s typedef statements. By placing all typedef statements in a single include file, all parts of the program can access them. There is no possibility of the same name being used for two different types if a single include file is used.

The vars.h file contains all the variable definitions (and declarations). To see how a single file can contain both, see Listing 6.4, later in this chapter.

The final file, prototyp.h, contains the function prototypes for each of the program’s functions. Always keep prototypes in a single file, using the format shown in Listing 6.5, later in this chapter.

Listing 6.1 shows the main include file for a multisource file program.

Listing 6.1. An example of a main include file for a large project.

#include “defines.h” #include “typedef.h” #include “vars.h” #include “prototyp.h”

Listing 6.2 shows the defines.h file. You should document each #define’s use as shown in this example.

Listing 6.2. An example of the defines.h include file.

#ifndef DEFINES_H #define DEFINES_H

continues

167

Part I • Honing Your C Skills

Listing 6.2. continued

#define

MAX_SIZE

123 /* Maximum size of array */

#define

USER

“I AM USER” /* The user’s name */

#define

MAXFONT

50

/* Maximum number of fonts available */

#ifndef MIN

 

 

#define

MIN(a, b)

(((a) < (b)) ? (a) : (b))

#endif /* MIN */

 

 

#ifndef MAX

 

 

#define

MAX(a, b)

(((a) > (b)) ? (a) : (b))

#endif /* MAX */

 

 

#ifndef TRUE

 

 

#define

TRUE

1 /* LOGICAL TRUE */

#endif /* TRUE */

 

 

#ifndef FALSE

 

 

#define

FALSE

0

/* if not TRUE, must be FALSE */

#endif /*

FALSE */

 

 

#endif /*

DEFINES_H */

 

 

 

 

 

 

Listing 6.3 shows the typedef.h file. As in other include files, you should document each typedef’s use as the example shows.

Listing 6.3. An example of the typedef.h include file.

#ifndef TYPEDEF_H #define TYPEDEF_H

typedef struct

 

 

{

 

 

 

char

FontList[MAXFONT][LF_FACESIZE]; // MAXFONT is

50. LF_FACESIZE

 

 

// is in windows.h file.

BYTE

CharSet[MAXFONT];

// The font’s character set

BYTE

PitchAndFamily[MAXFONT];

// The font’s pitch and

 

 

// family

 

BYTE

VectorOrRaster[MAXFONT];

// The font’s type

168

 

 

 

Separate Compilation and Linking

C C C

 

 

 

 

C6C

 

 

 

 

C C C

BYTE

FontType[MAXFONT];

// RASTER_FONTTYPE,

C

 

 

 

 

// DEVICE_FONTTYPE, or

 

 

 

 

// TRUETYPE_FONTTYPE

 

 

 

 

// (windows.h)

 

int

nSizeIndex;

 

// Index to the font size.

int

nFontIndex;

 

// Index to the font.

 

int

nSizeList[MAX_SIZE];

// List of font’s sizes.

 

} FONTSPECS;

 

 

 

typedef

FONTSPECS

*PFONTSPECS;

 

 

typedef

FONTSPECS NEAR *NPFONTSPECS;

 

 

typedef

FONTSPECS FAR

*LPFONTSPECS;

 

 

#endif /* TYPEDEF_H */

The typedef.h file includes not only a typedef for the structure, but also typedefs for various pointers that may point to this structure. This inclusion makes it easy to create prototypes and to define the necessary structures and pointers later in the program.

Listing 6.4, the vars.h file, includes all the global variables. It does not contain any of the static variables because they are known in only the current source file. Notice the use of the defined identifier EXTERN. This identifier is defined to the C keyword extern if the file that is including the vars.h file is not the main file. The variables then can be either declarations (done only once) or definitions (done in each file). For any initialized variable, you must check the EXTERN identifier and process each one as necessary. As in the other include files, you should document each variable’s use as the example shows.

Listing 6.4. An example of the vars.h include file.

#ifndef VARS_H #define VARS_H

#ifndef EXTERN

#define EXTERN /*NULL, do variable declarations */

continues

169

Part I • Honing Your C Skills

Listing 6.4. continued

#define INITIALIZE_EXTERN #endif /* EXTERN */

EXTERN

char

szBuffer[257];

/* Scratch buffer, contents undefined */

EXTERN

char

szFileName[129]; /* Input filename */

EXTERN

int

nErrorCount;

/* How many errors so far? */

EXTERN

int

nErrorValue

 

#if defined(INITIALIZE_EXTERN) /* Do the initialization */

= {NO_ERROR} /* Initialized */

#endif

 

 

 

;

 

 

 

#if defined (INITIALIZE_EXTERN)

#undef

INITIALIZE_EXTERN

 

#endif

 

 

 

#endif /* VARS_H */

Notice that vars.h uses the identifier EXTERN and defines a new identifier called INITIALIZE_EXTERN. Whenever you are declaring a variable that you want to initialize, you can use this example to make sure that the variable is not declared twice.

Listing 6.5, the prototyp.h file, includes all the function prototypes for the various functions in the program. This file should be the last of the group of included files because it uses the typedefs created in typedef.h. As with the other include files, you should document each function’s use and the file in which it is found, as the example shows.

Listing 6.5. An example of the prototyp.h include file.

#ifndef PROTOTYP_H #define PROTOTYP_H

/* source file

return

name(parameters);

*/

/* ADDARRAY.C */

int

ArrayAdd(LPARRAY,

LPARRAY);

/* SUBARRAY.C */

int

ArraySubtract(LPARRAY, LPARRAY);

170

 

 

Separate Compilation and Linking

C C C

 

 

 

C6C

 

 

 

C C C

 

/* UTILITY.C */ void

ErrorMessage(LPSTR szSubString, WORD wError,

C

 

 

 

 

 

long lSomething);

 

 

 

#endif /* PROTOYTP_H */

 

 

 

 

 

 

 

 

The prototyp.h file has enough information for you to know each function’s parameters, what the function returns, and where it is located (so that you can fix it when it breaks).

By using these include files for your project, you can be confident that you have much of the project under control. You will not have duplicate external variables with the same name and different usage, and you won’t have functions defined with one set of parameters and declared with another. You must work at keeping the include files in order; however, in the long run, the result is worth the effort.

External Variables

This chapter has discussed using a set of standard include files. These files enable you to control the way objects are defined in your programs, preventing duplicate identifiers with different meanings. Chapter 2, “Data Types, Constants, Variables, and Arrays,” discussed variables, including external variables, and this chapter has discussed using a single include file to create both a definition and a declaration for an external variable. Now let’s look at a “real” program that shows how external variables work for you.

The TWOFILE program, shown in Listings 6.6 through 6.14, is a simple program with two source C files and a full set of include files that uses shared external (global) variables to share data. TWOFILE doesn’t do much; however, it has the framework to enable you to build a more meaningful application.

Listing 6.6. TWOFILE1.C.

/* Program TWOFILE, written 22 May 1992 by Peter D. Hipson * A multisource file program.

* This is the first source file for TWOFILE.

*/

continues

171

Part I • Honing Your C Skills

Listing 6.6. continued

/* This program assumes and uses Microsoft’s extensions to C.

*Readers with other compilers may need to change the program

*to use the calls their compiler supplies to perform the

*same functions.

*/

#define EXTERN extern

#include “twofile.h” /* TWOFILE’s include has all other #includes. */

int main(

 

int

argc,

 

char

*argv[],

 

char

*envp[]

 

)

 

 

{

 

 

char

*pszTemp;

 

char

szBuffer[129];

/* Temporary work buffer. */

char

szProgram[30];

 

char

szInputFile[132];

/* Make large enough for your OS. */

char

szOutputFile[132]; /* Make large enough for your OS. */

/* strings for _splitpath() (which parses a file name) */

char

szDrive[_MAX_DRIVE];

char

szDir[_MAX_DIR];

 

char

szFname[_MAX_FNAME];

char

szExt[_MAX_EXT];

 

int

i;

 

int

j;

 

int

nCurrentParameter = INNAME;

int

nTempWidth = 0;

 

int

nLineWidth = 80;

 

int

nJustification = LEFT;

if (argc <= 2)

{

GiveHelp(argc, NULL); return(16);

}

172

Separate Compilation and Linking

C C C

 

C6C

 

C C C

 

C

_splitpath(argv[0], szDrive,

szDir,

szFname,

szExt);

strncpy(szProgram, szFname, sizeof(szProgram) - 1);

for (i = 1; argv[i]; i++)

{

if (argv[i][0] == ‘/’ || argv[i][0] == ‘-’)

{/* You have an argument, convert to lowercase, and test. */ pszTemp = strlwr(argv[i]);

for (j = 1; j < (int)strlen(pszTemp); j++)

{

switch(pszTemp[j])

{

case ARG_LEFT: nJustification = LEFT; break;

case ARG_RIGHT: nJustification = RIGHT; break;

case ARG_JUSTIFY: nJustification = JUSTIFY; break;

case ARG_SLASH: case ARG_DASH: break;

default:

GiveHelp(BAD_OPTION, &pszTemp[j]); break;

}

}

}

else

continues

173

Part I • Honing Your C Skills

Listing 6.6. continued

{/* Either a filename or width. */ switch(nCurrentParameter)

{

case INNAME:

strcpy(szInputFile, argv[i]); nCurrentParameter = OUTNAME; break;

case OUTNAME:

strcpy(szOutputFile, argv[i]); nCurrentParameter = WIDTH; break;

case WIDTH:

sscanf(argv[i], “%d”, &nTempWidth);

if (nTempWidth < 20 || nTempWidth > 128)

{

GiveHelp(BAD_WIDTH, NULL);

}

else

{

nLineWidth = nTempWidth;

}

nCurrentParameter = LAST_THING;

break;

default:

GiveHelp(BAD_PARM, NULL); break;

}

}

}

if (nCurrentParameter < WIDTH)

{/* Didn’t get two file names! */ GiveHelp(NAME_MISSING, NULL); return(16);

174

Separate Compilation and Linking

C C C

 

C6C

 

C C C

 

C

}

printf(“\n”);

printf(

“%s would read the file ‘%s’ and write the file ‘%s’\n\n”, szProgram,

szInputFile,

szOutputFile);

switch(nJustification)

{

case LEFT:

printf(“The lines would be %d characters long, left \ aligned\n”,

nLineWidth);

break;

case RIGHT:

printf(“The lines would be %d characters long, right \ aligned\n”,

nLineWidth);

break;

case JUSTIFY:

printf(“The lines would be %d characters long, justified\n”, nLineWidth);

break;

}

/* In the final version of this program, the files next

*are opened, and the input file is read into a buffer,

*formatted according to what the user wants, and written

*out to the output file. At the end, the files are closed,

*and perhaps some statistical information can be presented

*to the user.

*/

return (0);

}

175