[an error occurred while processing this directive]
But for larger programs, higher-level components are needed. Consider a calculator program that calculates the values of C-like real expressions involving variables and can assign values to variables. A program designer can come up with a reasonable decomposition of this program into high-level components without having to consider details about how those components are implemented.
First of all, since the expressions involve variables, there is a need to be able to save and retrieve values of variables. The need for this kind of functionality arises often in programs, and there are well-known data structures called tables that provide that kind of functionality.
Second, file input is in terms of characters, but understanding expressions and how they are evaluated is easiest with units called tokens. A token is a unit of text, such as a number or identifier or an operator that may contain more than one character. For a calculator program, it is useful to have a high-level component that breaks up a stream of characters into these tokens and classifies them.
With these two components, the work of the main program is much easier to accomplish, with the responsibility of token classification and variable value storage and retrieval delegated to other components.
Both components involve several functions that work together in a
For example, the table component will require a function for assigning a
value to a variable and another for retrieving the value of a variable, and
it will also need some sort of body of data (perhaps an array) for saving
Since these components have distinct responsibilities, it is desirable to
be able to put them into separate code files
tokens.C, and compile them and test them separately.
The main program is then written in a third file
calculator.C, and it is tested and compiled after the
others have been thoroughly tested.
An object file contains coding of the source code file into language that
the machine understands.
It is incomplete in that it may contain calls to functions that are in
other files, and it need not contain a main function.
When Unix compilers produce an object file, it has the same name as the
source code file except that the suffix is changed to
Suppose you want to compile
prog.C to produce the
Normally, you would give the command
g++ -Wall -o prog prog.C
prog.Cand creates the object file
prog.o. Then it links
prog.owith the standard C libraries and creates the executable file
prog. When called with a single
g++program removes the
The compilation and linking steps can be executed separately as follows:
g++ -Wall -c prog.C g++ -o prog prog.o
g++command just compiles
prog.o, and the second links it to create
g++program recognizes the
.osuffix so that it doesn't try to compile
Now suppose you have C code files described above for the calculator program. Then the following commands will create the corresponding object files:
g++ -Wall -c calculator.C g++ -Wall -c table.C g++ -Wall -c tokens.C
g++program is called with a list of
.ofiles it will link them all together to form an executable program. So the following command creates the calculator program:
g++ -o calculator calculator.o table.o tokens.o
In the example calculator program, the files
tokens.C should have header files
These header files define types and functions that are shared with other
A pair of files consisting of a code file and its associated header file
are referred to as a module.
When a shared type or function is declared in a header file, the
declaration should be the same as a declaration in a C file that is not
calculator.C should include both of the header files
since it will use types and functions from both of the other modules.
tokens.C does not need any types or functions from
the tables module, so it should only include
Finally, the file
tables.C does not need any types or
functions from the tokens module, so it should only include
g++to compile all of your pieces. This means you have a lot of opportunities for mistyping commands. Secondly, you must remember which files are up-to-date, and which need to be recompiled. Forgetting to recompile something which has been corrected means the mistake doesn't go away, even though you think you've fixed it. This can lead to terrible confusion. The solution to these difficulties is to use the Unix tool
maketo manage your compilations for you.
When you type the Unix command
make, the make program looks in
the current working directory for a file named either
The make program then uses the makefile to determine how to update your
program files to produce a completely compiled package.
targets : prerequisites commands
The commands part of each entry consists of zero or more lines, each starting with a tab character, followed by a command to be executed. These commands should tell the make program how to remake target files. The tab is crucial; if it's not there then the make program does not know that it is reading a command line.
When the make program starts up, it determines the age of each file in the current working directory by looking at time stamp information, which is a part of all Unix files. If a target file for an entry does not exist, or if it is older than any of the prerequisite files, then the make program issues all of the commands.
In simple makefiles there will be two kinds of entries. One kind creates an object file by compiling a C source code file. The second kind links object files into an executable program.
table.o : table.C table.h g++ -Wall -c table.C
table.ois dependent on the files
table.h. When the make program starts up, it determines the age of the files
table.odoes not exist, or if it is older than either of the files
table.h, then the make program issues the command
g++ -Wall -c table.Cto make or remake
When writing a program in pieces, each
.o file should be made
dependent on all of the header files that are included in the corresponding
The reason for this is that if the header file is changed, the
.o file needs to be remade.
.o file is not remade, it is likely that different
versions of the header file will be used in different parts of the program.
The symptoms of this problem are unpredictable, so take care in setting up
The make program should not be responsible for making
These are made by the programmer using an editor.
.C files and
.h files should not be target files.
The make program also should not be responsible for making
files if their
.C file is not available.
For example, if you are working on a team and you are using a
.o file provided by someone else, then your makefile should
not contain an entry for that
calculator : calculator.o tokens.o table.o g++ -o calculator calculator.o tokens.o table.o
calculatorshould be remade whenever it is older than any of the object files
table.o. The compiler, when given a list of object files, will just link them to form the executable program. If the makefile also contains entries in which
table.oappear as targets then the
makeprogram will try to make them first.
calculator.C) and the table module (
table.h), but someone else has provided you with the object file
tokens.oand the header file
tokens.h. Then the following makefile could be used.
calculator : calculator.o tokens.o table.o g++ -o calculator calculator.o tokens.o table.o calculator.o : calculator.C tokens.h table.h g++ -Wall -c calculator.C table.o : table.C table.h g++ -Wall -c table.C
tokens.oas a target since it is provided by another person; there is no way that the make program can recreate it. It is best to leave
tokens.oas a prerequisite for
calculatorin case you get an updated version.
Usually the command
make is given by itself in response to the
When the make program is run this way, it tries to make the file or files
appearing before the colon in the first entry of the makefile.
It will check the files after the colon to see if they are up to date (as
determined by other entries in the makefile) and, if necessary, update
Thus with the given makefile, the make program will first check the object
files to see if they are up-to-date.
If not, or if they don't exist, they will be created using the commands
given in the later entries.
Once these files are made, the executable program
can be made using the command in the first entry of the makefile.
The make program can also be run with the command
table.owithout going on to make the rest of the package.
[an error occurred while processing this directive]