Table of Contents
1. Introduction
2. Header Files
3. Functions and Global Variables
4. Do's and Don'ts
5. Example
1. Introduction
This document describes how to construct multi-file programs in C and C++.
The first task is to decide how your program will be divided into multiple files or modules. If your program design contains ADT's then usually each ADT's code should be in it's own file. Other functions should be grouped by their purpose. For example, all procedures that produce graphics might be stored in one file.
The next task is to determine what parts of a module is private (i.e. , not needed by other modules) and what parts are public. These parts include functions, types and variables. In general, there should be a good reason for all public objects. (This is known as encapsulation.)
2. Header Files
The public parts of a module must be stored in a header (.h
) file. This header is included by files that use its corresponding module and the module itself. This insures that the module and the files that use the module agree on types and functions. Header files typically contain macros, typedef's and function prototypes. In C++, they also contain class definitions, inline code, template definitions and constants. Remember that even when compiling several files, the compiler looks at each file separately. Each file must include headers for all the info that the compiler needs to compile it.
It is also very common for headers to include other headers. This can lead to problems when header x.h
and header y.h
are included in a file, but both headers also include z.h
. So header z.h
is included twice. Most of time this will cause a compiler error, because of multiple declarations of a type or macro. There is a solution to this problem using the conditional compilation features of the preprocessor. The following illustrates the solution:
#ifndef UNIQUE_MACRO_NAME
#define UNIQUE_MACRO_NAME
/* body of header */
#endif
The body of the header is only seen by the compiler if UNQIUE_MACRO_NAME
is not defined, then it is immediately defined. Thus, the body of the header is only included once, the first time the header is encountered by the compiler. Afterward, the macro is defined which keeps the body from being included again. Of course, each header file must use a different macro!
3. Functions and Global Variables
Functions and global variables by default have external scope . This means that if function f
is defined in file x.c
, then it is available to use in file y.c
or any other file. The linker combines the object files (.o
or .obj
) into a single executable file. Part of the linker's job is to match up global names that files reference to their definition in another file. However, if f
is part of the implementation of module x
, then there is no reason that any other file should use f
. In this case, f
should instead be defined with internal scope . Internal scope means that only the code in x.c
can directly access function f
. To give a function internal scope, prefix its definition by the reserved word static
. This is somewhat confusing, because static
has an entirely different meaning when used with local variables of a function. One technique to clarify this process is to use macros with the more meaningful names, PUBLIC
and PRIVATE
.
#define PRIVATE static
#define PUBLIC
PUBLIC
is defined to be nothing because functions have external scope by default. Now to define a function with internal scope, use:
PRIVATE void f()
Of course, private functions are not prototyped in header files.
Global variables work much like functions. To give a global variable internal scope precede its definition by static
(or better PRIVATE
). For a file to use a PUBLIC
global variable, it must declare it as an extern
variable. This is commonly done in the header file. (Global variables should be used sparingly and for good reasons only.)
Only one file has a PUBLIC
function named main
. This function is where execution begins just as in a single-file program.
The make utility is very useful for maintaining multi-module programs.
4. Do's and Don'ts
- Never include code in header files! (Actually in C++, the rule is never include non-inline or non-template code in header files!)
- If using Borland's Project Make to build multi-module programs, never include header files in the Project Make window!
- If using a command-line compiler (like
cc
), do not compile the header files! - Never include source files!
5. Example
This section shows a multi-module program example:
File: header.h
#ifndef HEADER_EXAMPLE
#define HEADER_EXAMPLE
typedef long int INT32; /* Used in prototype below so must be in header */
void f( INT32 ); /* external scope function prototypes go in header */
#endif
File: sub.c
#include <stdio.h>
#include "header.h" /* " " in include causes compiler to look
for header in the current working dir */
typedef double Real; /* This type only used internally to this file, so
it's not in header file */
static void g( INT32, Real ); /* internal scope function prototypes do not
go in header */
void f( INT32 x )
{
g(x, 2.5);
}
static void g( INT32 x, Real d )
{
printf("%f/n", x/d);
}
File: main.c
#include <stdio.h>
#include "header.h"
int main( void )
{
INT32 x;
printf("Enter a integer: ");
scanf("%ld", &x);
f(x);
return 0;
}
One could compile this on UNIX with the command:
cc main.c sub.c
Note that the header file is not listed!