|
How to Create Multi-Module Programs
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!
Maintainer: Paul Carter ( email: pacman128@gmail.com )
Copyright © 2002
All Rights Resevered
Last Updated: Thu Oct 10 09:17:44 CDT 2002
|