E.5. Notes on Compiling Multiple-Source-File Programs
As stated earlier in the text, it is normal to build programs that consist of multiple source files (see Chapter 9, Classes: A Deeper Look, Part 1). There are several considerations when creating programs in multiple files. For example, the definition of a function must be entirely contained in one fileit cannot span two or more files.
In Chapter 6, we introduced the concepts of storage class and scope. We learned that variables declared outside any function definition are of storage class static by default and are referred to as global variables. Global variables are accessible to any function defined in the same file after the variable is declared. Global variables also are accessible to functions in other files; however, the global variables must be declared in each file in which they are used. For example, if we define global integer variable flag in one file, and refer to it in a second file, the second file must contain the declaration
extern int flag;
prior to the variable's use in that file. In the preceding declaration, the storage class-specifier extern indicates to the compiler that variable flag is defined either later in the same file or in a different file. The compiler informs the linker that unresolved references to variable flag appear in the file. (The compiler does not know where flag is defined, so it lets the linker attempt to find flag.) If the linker cannot locate a definition of flag, a linker error is reported. If a proper global definition is located, the linker resolves the references by indicating where flag is located.
Performance Tip E.1
Software Engineering Observation E.2
Just as extern declarations can be used to declare global variables to other program files, function prototypes can be used to declare functions in other program files. (The extern specifier is not required in a function prototype.) This is accomplished by including the function prototype in each file in which the function is invoked, then compiling each source file and linking the resulting object code files together. Function prototypes indicate to the compiler that the specified function is defined either later in the same file or in a different file. The compiler does not attempt to resolve references to such a functionthat task is left to the linker. If the linker cannot locate a function definition, an error is generated.
As an example of using function prototypes to extend the scope of a function, consider any program containing the preprocessor directive #include <cstring>. This directive includes in a file the function prototypes for functions such as strcmp and strcat. Other functions in the file can use strcmp and strcat to accomplish their tasks. The strcmp and strcat functions are defined for us separately. We do not need to know where they are defined. We are simply reusing the code in our programs. The linker resolves our references to these functions. This process enables us to use the functions in the standard library.
Software Engineering Observation E.3
Portability Tip E.1
It is possible to restrict the scope of a global variable or function to the file in which it is defined. The storage-class specifier static, when applied to a file scope variable or a function, prevents it from being used by any function that is not defined in the same file. This is referred to as internal linkage. Global variables (except those that are const) and functions that are not preceded by static in their definitions have external linkagethey can be accessed in other files if those files contain proper declarations and/or function prototypes.
The global variable declaration
static double pi = 3.14159;
creates variable pi of type double, initializes it to 3.14159 and indicates that pi is known only to functions in the file in which it is defined.
The static specifier is commonly used with utility functions that are called only by functions in a particular file. If a function is not required outside a particular file, the principle of least privilege should be enforced by using static. If a function is defined before it is used in a file, static should be applied to the function definition. Otherwise, static should be applied to the function prototype.
When building large programs from multiple source files, compiling the program becomes tedious if making small changes to one file means that the entire program must be recompiled. Many systems provide special utilities that recompile only source files dependant on the modified program file. On UNIX systems, the utility is called make. Utility make reads a file called Makefile that contains instructions for compiling and linking the program. Systems such as Borland C++ and Microsoft Visual C++ for PCs provide make utilities and "projects." For more information on make utilities, see the manual for your particular system.