The simplest case for each compiler involves compiling a
single-file program (or a program with one .cpp
or .c
and a number of .h
files that are combined by #include
statements). For
example, use an editor to prepare the following files:
hello.cpp
#include <iostream> using namespace std; int main () { cout << "Hello from C++ !" << endl; return 0; }
hello.c
#include <stdio.h> int main () { printf ("Hello from C!\n"); return 0; }
To compile and run these, the commands are:
g++ -g hello.cpp
./a.out
gcc -g hello.c
./a.out
The compiler generates an executable program called a.out. If you don't like that name, you can use the mv command to rename it. Alternatively, use a -o option to specify the name you would like for the compiled program:
g++ -g -o hello1 hello.cpp
./hello1
gcc -g -o hello2 hello.c
./hello2
As you progress in your study of programming, you will discover
that programs that can (or should) be written in a single source code
file become increasingly rare. Most C and C++ programs are constructed
from a collection of header (.h
) and
implementation (.c
, .cpp
)
files.
A .h
file is intended to be
#include
d from many different .cpp
files that make up a single program. In fact, the earliest stage of
compilation, the preprocessor, actually
replaces each #include
by the full contents of the
included file.
A .cpp
file is intended to be compiled once
for any given build of the program.
A typical program will consist of many .cpp
files. (See Figure 1, “Building 1 program from many files”) Usually, each class or
group of utility functions will have their definitions in a separate
.cpp
file that defines everything declared in the
corresponding .h
file. The
.h
file can then be #include
d by
many different parts of the program that use those classes or
functions, and the .cpp
file can be separately
compiled once, then the resulting object code file is
linked together with the object code from other
.cpp
files to form the complete program.
Splitting the program into pieces like this helps, among other things, divide the responsibility for who can change what and reduces the amount of compilation that must take place after a change to a function body.
When you have a program consisting of multiple files to be
compiled separately, add a -c option to each
compilation. This will cause the compiler to generate a
.o
object code file instead of an executable.
Then invoke the compiler on all the .o
files
together without the -c to link them together and
produce an executable:
g++ -g -c file1.cpp
g++ -g -c file2.cpp
g++ -g -c file3.cpp
g++ -g -o programName file1.o file2.o file3.o
(If there are no other .o files in that directory, the last command can often be abbreviated to “g++ -o programName -g *.o”.) The same procedure works for the gcc compiler as well.
Actually, you don't have to type separate compilation commands for each file. You can do the whole thing in one step:
g++ -g -o programName file1.cpp file2.cpp file3.cpp
But the step-by-step procedure is a good habit
to get into. As you begin debugging your code, you are likely to make
changes to only one file at a time. If, for example, you find and fix
a bug in file2.cpp
, you need to only recompile
that file and relink:
g++ -g -c file2.cpp
g++ -g -o programName file1.o file2.o file3.o
An even better way to manage multiple source files is to use the make command.
Another useful option in these compilers is
-D. If you add an option
-Dname
=value
,
then all occurrences of the identifier name
in the program will be replaced by value
.
This can be useful as a way of customizing programs without editing
them. If you use this option without a value,
-Dname
, then the
compiler still notes that name
has been
“defined”. This is useful in conjunction with compiler
directive #ifdef
, which causes certain code to be
compiled only if a particular name is defined. For example, many
programmers will insert debugging output into their code this way:
⋮ x = f(x, y, z); #ifdef DEBUG cerr << "the value of X is: " << x << endl; #endif y = g(z,x); ⋮
The output statement in this code will be ignored by the compiler unless the option -DDEBUG is included in the command line when the compiler is run.[3]
Sometimes your program may need functions from a
previously-compiled library. For example, the sqrt
and
other mathmatical functions are kept in the
“m
” library (the filename is
actually libm.a
). To add functions from this
library to your program, you would use the
“-lm” option. (The “m” in
“-lm” is the library name.) This is a
linkage option, so it goes at the end of the command:
g++ -g -c file1.cpp
g++ -g -c file2.cpp
g++ -g -c file3.cpp
g++ -g -o programName file1.o file2.o file3.o -lm
The general form of gcc/g++ commands is
g++compilation-options
files
linkage-options
Here is a summary of the most commonly used options[4] for gcc/g++:
Compilation Flags | |
---|---|
-c | compile only, do not link |
-o
filename |
Use filename as the name of the
compiled program
|
-Dsymbol =value |
Define symbol during compilation.
|
-g | Include debugging information in compiled code (required if you want to be able to run the gdb debugger. |
-O | Optimize the compiled code (produces smaller, faster programs but takes longer to compile) |
-I
directory |
Add directory to the list of places
searched when a ``system'' include (#include … ) is
encountered.
|
Linkage Flags | |
-L
directory |
Add directory to the list of places
searched for pre-compiled libraries.
|
-llibname |
Link with the precompiled library
lib |
[3] Zeil's 1st Rule of Debugging: Never remove debugging output. Just make it conditional. If you remove it, you're bound to want it again later.
Zeil's 2nd Rule of Debugging: Never leave your debugging code active when you submit your programs for grading. If the grader is using an automatic program to check the correctness of the output, unexpected output will make your program fail the tests. On the other hand, if the grader is reading the output to check its correctness, wading through extra output really ticks the grader off!
[4] A note for CygWin users: By default, CygWin gcc and g++ produce Windows “console” applications --- applications designed to run from within a shell (bash or the Windows command line tool). They can, however, produce GUI applications with windows, menus, etc., by using -l to link in the windowing libraries. The list of libraries involved is rather long, so a shortcut option is provided: -mwindows.
Programming windowing code is a fairly involved process. I suggest getting a library that simplifies this process for beginners. The V library is a good choice, and has the additional advantage that code written for use with V can be compiled to produce either Microsoft Windows or Unix X windows programs.
Discuss This Page: