Chapter 19. Compiling with GCC

This chapter explains how to use GCC to compile executable programs from C source code. First, we present the basic program control options in the order of the corresponding steps in the compiling process. Then we look at how you can use GCC’s warning options to troubleshoot your programs. Finally, we summarize the options for optimized compiling.

This chapter should provide you with a basic working knowledge of GCC. If you later need information on special details, such as architecture-specific or system-specific options, this basic orientation will enable you to find what you need in the GCC manual. The manual is included in Texinfo format in the GCC distribution, and is also available in PostScript and HTML formats.

The GNU Compiler Collection

GCC originally stood for the “GNU C Compiler.” Since its beginnings, the program has grown to support a number of other programming languages besides C, including C++, Ada, Objective-C, Fortran, and Java. The acronym GCC has therefore been redefined to mean “GNU Compiler Collection.” The compiler incorporates a number of frontends to translate different languages. In this book, of course, we are concerned only with the C frontend.

GCC is also a multitarget compiler; in other words, it has interchangeable backends to produce executable output for a number of different computer architectures. As the modular concept would suggest, GCC can also be used as a cross-compiler; that is, you can produce executable programs for machines and operating systems other than the one GCC is running on. However, doing so requires special configuration and installation, and most GCC installations are adapted to compile programs only for the system on which they are hosted.

GCC not only supports many “dialects” of C, but also distinguishes between them; that is, you can use command-line options to control which C standard the compiler adheres to in translating your source code. For example, when you start GCC with the command-line argument -std=c99, the compiler supports the C99 standard. Support for the C11 standard in GCC is incomplete, especially in regard to the multithreading functions declared in the header thread.h. This is because GCC’s C library has long supported very similar multithreading capabilities under the POSIX standard. For more details, see the page on C11 support in the GCC developers’ wiki.

Obtaining and Installing GCC

If you have a Unix-like system, there’s a fair chance that GCC is already installed. To find out, type cc --version at the command prompt. If GCC is installed and linked to the default C compiler name cc, you will see the compiler’s version number and copyright information:

$ cc --version
cc (GCC) 4.9.2
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.
Tip

In the examples in this chapter, the dollar sign ($) at the beginning of a line represents the command prompt. The text that follows it is a command line that you would enter at a console to invoke GCC (or whatever program is named in the command).

It’s possible that GCC is installed but not linked to the program name cc. Just in case, try calling the compiler by its proper name:

$ gcc --version

If GCC is not installed, consult your system vendor to see whether GCC is available in a binary package for your system’s software installation mechanism. GCC binary packages are also included in free software systems such as MacPorts and Homebrew for OS X, and Cygwin and MinGW for Windows.

Cygwin is an extensive collection of GNU and other open source tools that provide a Unix-like environment, oriented after the POSIX standard, on Windows. The foundation of Cygwin is the dynamically shared library cygwin1.dll, which provides Unix-like system functions to the Cygwin programs while interacting with the underlying Windows system. Programs compiled with GCC for Cygwin also require the runtime library cygwin1.dll. The Cygwin setup program initially installs the base packages, and launches a package manager in which you can select other Cygwin software to install, including gcc, make, and gdb. You can run the setup program at any time to add, remove, and update programs.

MinGW also provides the GCC compiler for Windows, but unlike Cygwin, the MinGW version of GCC generates native 32-bit Windows programs that require no special runtime library. The variant MinGW-w64, also called MinGW64, can also produce 64-bit programs. You can install the latest version of MinGW using the setup program available at http://sourceforge.net/projects/mingw or http://sourceforge.net/projects/mingw-w64.

Note that the Cygwin package manager also allows you to install MinGW-GCC packages. GCC then functions as a cross-compiler, running on Cygwin but producing Windows programs that do not use cygwin1.dll.

The GCC website maintains a list of GCC binary packages compiled by third parties for a variety of systems at http://gcc.gnu.org/install/binaries.html. Otherwise, if your system has another C compiler, you can obtain the source code of GCC from the Free Software Foundation and compile it on your system according to the step-by-step instructions at http://gcc.gnu.org/install/.

Compiling C Programs with GCC

When you run GCC, its default behavior is to produce an executable program file from one or more specified source code files. To start with a simple example, we’ll run GCC to make a finished executable program from the C source code in Example 1-1:

$ gcc -Wall circle.c

This command line contains only the compiler’s name, the source filename, and one option: -Wall instructs GCC to print warnings if it finds certain problems in the program (see “Compiler Warnings” for more information). If there are no errors in the source code, GCC runs and exits without writing to the screen. Its output is a program file in the current working directory with the default name a.out (in Windows, the default name is a.exe). We can run this new program file:

$ ./a.out

The program then produces the screen output shown in Example 1-1.

If you do not want the executable program file to be named a.out, you can specify an output filename on the command line using the -o option:

$ gcc -Wall -o circle circle.c

This command produces the same executable, but it is now named circle.

Step by Step

The following sections present GCC options to let you control each stage of the compiling process: preprocessing, compiling, assembling, and linking. You can also perform the individual steps by invoking separate tools, such as the C preprocessor cpp, the assembler as, and the linker ld. GCC can also be configured to use such external programs on a given host system. For the sake of a uniform overview, however, this chapter shows you how to perform all four steps by invoking GCC and letting it control the process.

Preprocessing

Before submitting the source code to the compiler, the preprocessor executes directives and expands macros in the source files (see steps 1 through 4 in “The C Compiler’s Translation Phases”). GCC ordinarily leaves no intermediate output file containing the results of this preprocessing stage. However, you can save the preprocessor output for diagnostic purposes by using the -E option, which directs GCC to stop after preprocessing. The preprocessor output is directed to the standard output stream, unless you indicate an output filename using the -o option:

$ gcc -E -o circle.i circle.c

Because header files can be large, the preprocessor output from source files that include several headers is often unwieldy.

You may find it helpful to use the -C option as well, which prevents the preprocessor from removing comments from source and header files:

$ gcc -E -C -o circle.i circle.c

The following commonly used options affect GCC’s behavior in the preprocessor phase:

-Dname[=definition]

Defines the symbol name before preprocessing the source files. The macro name must not be defined in the source and header files themselves. Use this option together with #ifdef name directives in the source code for conditional compiling. If you do not specify a replacement value, the macro is defined with the value 1.

-Uname

“Undefines” the symbol name, if defined on the command line or in GCC’s default settings. The -D and -U options are processed in the order in which they occur on the command line.

-Idirectory[:directory[…]]

When header files are required by #include directives in the source code, search for them in the specified directory (or directories), in addition to the system’s standard include directories.

-iquote directory[:directory[…]]

This option is new in recent versions of GCC, and specifies a directory to be searched for header files named in quotation marks, not angle brackets, in an #include directive.

-isystem directory[:directory[…]]

This option specifies a directory to be searched for system header files in addition to, and before, the standard system include directories. An equals sign at the beginning of the directory specification is treated as a placeholder for the system root directory, which you can modify for this purpose using the --sysroot or -isysroot option.

-isysroot directory

This option specifies the system root directory for the purpose of searching for header files. For example, if the compiler would ordinarily search for system header files in /usr/include and its subdirectories, this option causes it to search for them in directory/usr/include and its subdirectories instead. (The --sysroot option, with a second hyphen instead of the i, does the same thing for library searches—or for both library and header file searches if no isysroot option is present.)

-I-

This deprecated option has been made unnecessary in newer GCC versions by the -iquote option. It was formerly used to divide all the -Idirectory options on the command line into two groups. All directories appended to an -I option to the left of -I- are treated as if named in -iquote options; that is, they are searched only for header files named in quotation marks in the #include directive.

All directories appended to an -I option to the right of -I- are searched for header files named in any #include directive, whether the filename is enclosed in quotation marks or in angle brackets.

Furthermore, if -I- appears on the command line, then the directory containing the source file itself is no longer automatically searched first for header files.

The usual search order for include directories is:

  1. The directory containing the given source file (for filenames given in quotation marks in an #include directive).

  2. Directories specified by -iquote options, in command-line order. These directories are searched only for header files named in quotation marks in the #include directive.

  3. Directories specified by -I options, in command-line order.

  4. Directories specified in the environment variable CPATH.

  5. Directories specified by -isystem options, in command-line order.

  6. Directories specified in the environment variable C_INCLUDE_PATH.

  7. The system’s default include directories.

See also the section on #include directives, “Inserting the Contents of Header Files”.

Compiling

At the heart of the compiler’s job is the translation of C programs into the machine’s assembly language.1 Assembly language is a human-readable programming language that correlates closely to the actual machine code. Consequently, there is a different assembly language for each CPU architecture.

Tip

Assembly language is often referred to more simply as “assembler.” Strictly speaking, however, the term “assembler” refers to the program that translates assembly language into machine code. In this chapter, we use “assembly language” to refer to the human-readable code and “assembler” to refer to the program that translates assembly language into a binary object file.

Ordinarily, GCC stores its assembly-language output in temporary files, and deletes them immediately after the assembler has run. But you can use the -S option to stop the compiling process after the assembly-language output has been generated. If you do not specify an output filename, GCC with the-S option creates an assembly-language file with a name ending in .s for each input file compiled. Here is an example:

$ gcc -S circle.c

The compiler preprocesses circle.c and translates it into assembly language, and saves the results in the file circle.s. To include the names of C variables as comments on the assembly language statements that access those variables, use the additional option -fverbose-asm:

$ gcc -S -fverbose-asm circle.c

Assembling

Because each machine architecture has its own assembly language, GCC invokes an assembler on the host system to translate the assembly-language program into executable binary code. The result is an object file, which contains the machine code to perform the functions defined in the corresponding source file, and also contains a symbol table describing all objects in the file that have external linkage.

If you invoke GCC to compile and link a program in one command, then its object files are only temporary, and are deleted after the linker has run. Most often, however, compiling and linking are done separately. The -c option instructs GCC not to link the program but to produce an object file with the filename ending .o for each input file:

$ gcc -c circle.c

This command produces the object file circle.o.

You can use GCC’s option -Wa to pass command-line options to the assembler itself. For example, suppose we want the assembler to run with the following options:

-as=circle.sym

Print the module’s symbol table in a separate listing, and save the specified listing output in a filenamed circle.sym.

-L

Include local symbols—that is, symbols representing C identifiers with internal linkage—in the symbol table. (Don’t confuse this assembler option with the GCC option -L!)

We can have GCC add these options to its invocation of the assembler by appending them as a comma-separated list to GCC’s own-Wa option:

$ gcc -v -o circle -Wa,-as=circle.sym,-L circle.c

The list must begin with a comma after -Wa and contain no spaces. You can also use additional -Wa options in the same command. The -v option, which makes GCC print the options applied at each step of compiling, allows you to see the resulting assembler command line (along with a great deal of other information).

You can append several switches to the assembler’s -a option to control the listing output. For a full reference, see the assembler’s manual. The default listing output, produced when you simply specify -a with no additional switches, contains the assembly language code followed by the symbol table.

GCC’s -g option makes the compiler include debugging information in its output. If you specify the -g option in addition to the assembler’s -a option, then the resulting assembly language listing is interspersed with the corresponding lines of C source code:

$ gcc -g -o circle -Wa,-a=circle.list,-L circle.c

The resulting listing file, circle.list, allows you to examine, line by line, how the compiler has translated the C statements in the program circle.

Linking

The linker joins a number of binary object files into a single executable file. In the process, it has to complete the external references among your program’s various modules by substituting the final locations of the objects for the symbolic references. The linker does this using the same information that the assembler provides in the symbol table.

Furthermore, the linker must also add the code for any C standard library functions you have used in your program. In the context of linking, a library is simply a set of object files collected in a single archive file for easier handling.

Tip

When you link your program to a library, only its member object files containing the functions you use are actually linked into your program. To make libraries of your own out of object files that you have compiled, use the utility ar. See its manual page for information.

The bulk of the standard library functions are ordinarily in the file libc.a (the ending .a stands for “archive”) or in a shareable version for dynamic linking in libc.so (the ending .so stands for “shared object”). These libraries are generally in /lib/ or /usr/lib/, or in another library directory that GCC searches by default.

Non-standard libraries

Certain functions are contained in separate library files, and for many applications you will want to use library functions that are not part of the C standard library. To see how to link such libraries, let’s write another version of circle.c from Example 1-1 that uses the ncurses console output library available on many systems.

Example 19-1. A version of circle.c with ncurses console output
// circle.c: Calculate the areas of circles and
// print them in ncurses mode

#include <curses.h>             // Console control functions
double circularArea( double r );  // Function for the math
void circle();                    // Function for output

int main()        // Starts and stops curses display mode
{

/* Set up the console behavior: */
    (void) initscr();      // Initialize the curses system
    keypad(stdscr, TRUE);  // Enable keyboard mapping
    (void) nonl();         // Disable line-end translation
    (void) cbreak();       // Take single input characters

/* Run the circle routine: */
    circle();
    printw( "Press any key to exit." );
    refresh();             // Put the output on the screen

/* Finish: */
    getch();               // Wait for user to press a key
    endwin();              // Shut down the ncurses console
    return 0;
}

// The circle.c program from Example 1.1 but replacing the
// standard library function printf() with printw() from the
// ncurses library.
void circle()
{
  double radius = 1.0, area = 0.0;
  printw("    Areas of Circles\n\n" );
  printw("     Radius          Area\n"
          "-------------------------\n" );
  area = circularArea( radius );
  printw( "%10.1f     %10.2f\n", radius, area );
  radius = 5.0;
  area = circularArea( radius );
  printw( "%10.1f     %10.2f\n", radius, area );
}

// Return the area of a circle with radius r
double circularArea( double r )
{
  const double pi = 3.1415926536; // Pi is a constant
  return  pi * r * r;
}

Example 19-1 adds the directive #include <curses.h> at the beginning of the source file to declare the new external functions, because the ncurses functions initscr(), printw(), refresh(), etc. are not defined in the source code, nor in the C standard library. To compile this circle.c, we have to use the -l option to link the ncurses library as well:

$ gcc -o circle circle.c -lncurses

The filename of the ncurses library is libncurses.a. (On systems that support dynamic linking, GCC automatically uses the shared library libncurses.so, or libncurses.dylib on Darwin if it is available. See “Dynamic Linking and Shared Object Files” for more details.) The prefix lib and the suffix .a are standard, and GCC adds them automatically to whatever base name follows the -l on the command line—in this case, ncurses.

Normally, GCC automatically searches for a file with the library’s name in standard library directories, such as /usr/lib. There are three ways to link a library that is not in a path where GCC searches for it. One is to present GCC with the full path and filename of the library as if it were an object file. For example, if the library were named libncurses.a and located in /usr/local/lib, the following command would make GCC compile circle.c, and then link the resulting circle.o with libncurses.a:

$ gcc -o circle circle.c /usr/local/lib/libncurses.a

In this case the library filename must be placed after the name of the source or object files that use it. This is because the linker works through the files on its command line sequentially, and does not go back to an earlier library file to resolve a reference in a later object.

The second way to link a library that is not in GCC’s search path is to use the -L option to add another directory for GCC to search for libraries:

$ gcc -o circle -L/usr/local/lib -lncurses circle.c

You can add more than one library directory either by using multiple -L options, or by using one -L followed by a colon-separated path list. The third way to make sure GCC finds the necessary libraries is to make sure that the directories containing your libraries are listed in the environment variable LIBRARYPATH.

Passing options to the linker

You can pass options directly to the linker stage using-Wl followed by a comma-separated list, as in this command:

$ gcc -lncurses -Wl,-Map,circle.map circle.c circulararea.c

The option -Wl,-Map,circle.map on the GCC command line passes the option -Map,circle.map to the linker command line, instructing the linker to print a link script and a memory map of the linked executable to the specified file, circle.map.

The list must begin with a comma after -Wl, and must contain no spaces. In case of doubt, you can use several -Wl options in the same GCC command line. Use the -v option to see the resulting linker command.

All of the above

There is another GCC option that offers a convenient way to obtain all the intermediate output files at once, and that is -save-temps. When you use that option, GCC will compile and link normally, but will save all preprocessor output, assembly language, and object files in the current directory. The intermediate files produced with the -save-temps option have the same base filename as the corresponding source files, with the endings .i, .s, and .o for preprocessor output, assembly language, and object files, respectively.

None of the above

If you invoke GCC with the option -fsyntax-only, it does not preprocess, compile, assemble, or link. It merely tests the input files for correct syntax. See also “Compiler Warnings”.

Multiple Input Files

In Chapter 1, we went on to divide circle.c into two separate source files (see Examples 1-2 and 1-3). Compiling multiple source files results in multiple object files, each containing the machine code and symbols corresponding to the objects in one source file. GCC uses temporary files for the object output, unless you use the option -c to instruct it to compile only, and not link:

$ gcc -c circle.c
$ gcc -c circulararea.c

These commands produce two object files in the current working directory named circle.o and circulararea.o. You can achieve the same result by putting both source filenames on one GCC command line:

$ gcc -c circle.c circulararea.c

In practice, however, the compiler is usually invoked for one small task at a time. Large programs consist of many source files, which have to be compiled, tested, edited, and compiled again many times during development, and very few of the changes made between builds affect all source files. To save time, a tool such as make (see Chapter 20) controls the build process, invoking the compiler to recompile only those object files that are older than the latest version of the corresponding source file.

Once all the object files have been compiled from current source files, you can use GCC to link them:

$ gcc -o circle circle.o circulararea.o -lncurses

GCC assumes that files with the filename extension .o are object files to be linked.

File types

The compiler recognizes a number of file extensions that pertain to C programs, interpreting them as follows:

.c

C source code, to be preprocessed before compiling.

.i

C preprocessor output, ready for compiling.

.h

C header file. (To save time compiling many source files that include the same headers, GCC allows you to create “precompiled header” files, which it then uses automatically as appropriate.)

.s

Assembly language.

.S

Assembly language with C preprocessor directives, to be preprocessed before assembling.

GCC also recognizes the file extensions .ii, .cc, .cp, .cxx, .cpp, .CPP, .c++, .C, .hh, .H, .m, .mi, .f, .for, .FOR, .F, .fpp, .FPP, .r, .ads, and .adb; these file types are involved in compiling C++, Objective-C, Fortran, or Ada programs. A file with any other filename extension is interpreted as an object file ready for linking.

If you use other naming conventions for your input files, you can use the option -xfile_type to specify how GCC should treat them. file_type must be one of the following: c, c-header, cpp-output, assembler (meaning that the file contains assembly language), assembler-with-cpp, or none. All files that you list on the command line following an -x option will be treated as the type that you specify. To change types, use -x again. For example:

$ gcc -o bigprg mainpart.c -x assembler trickypart.asm -x c otherpart.c

You can use the -x option several times on the same command line to indicate files of different types. The option -x none turns off the file type indication, so that subsequent filenames are interpreted according to their endings again.

Mixed input types

You can mix any combination of input file types on the GCC command line. The compiler ignores any files that cannot be processed as you request. Here is an example:

$ gcc -c circle.c circulararea.s /usr/lib/libm.a

With this command line, assuming all the specified files are present, GCC compiles and assembles circle.c, assembles circulararea.s, and ignores the library file, because the -c option says not to do any linking. The results are two object files: circle.o and circulararea.o.

Dynamic Linking and Shared Object Files

Shared libraries are special object files that can be linked to a program at runtime. The use of shared libraries has a number of advantages: a program’s executable file is smaller, and shared modules permit modular updating, as well as more efficient use of the available memory.

To create a shared object file, use GCC’s -shared option. The input file must be an existing object file. Here is a simple example using our circle program:

$ gcc -c circulararea.c
$ gcc -shared -o libcirculararea.so circulararea.o

The second of these two commands creates the shared object file libcirculararea.so. To link an executable to a shared object file, name the object file on the command line like any other object or library file:

$ gcc -c circle.c
$ gcc -o circle circle.o libcirculararea.so -lncurses

This command creates an executable that dynamically links to libcirculararea.so at runtime. Of course, you will also have to make sure that your program can find the shared library at runtime—either by installing your libraries in a standard directory, such as /usr/lib, or by setting an appropriate environment variable such as LIBRARY_PATH. The mechanisms for configuring dynamic loading vary from one system to another.

If shared libraries are available on your system but you want to avoid using them—to exclude a potential opening for rogue code, for example—there are two ways to build statically linked executables with GCC. One is to use the -static option, as in the following command:

$ gcc -static -o circle circle.o circulararea.o -lncurses

The other is to specify a statically linking version of the external library instead of using the -l option on the command line:

$ gcc -o circle circle.o circulararea.o /usr/lib/libncurses.a

The resulting program file may be much larger than the dynamically linked one, however.

Freestanding Programs

In addition to the object and library files you specify on the GCC command line, the linker must also link in the system-specific startup code that the program needs in order to load and interact smoothly with the operating system. This code is already on hand in a standard object file named crt0.o, which contains the actual entry point of the executable program. (The crt stands for “C runtime.”) On most systems, GCC also links programs by default with initialization and clean-up routines in object files named crtbegin.o and crtend.o.

However, if you are writing a freestanding program, such as an operating system or an application for an embedded microcontroller, you can instruct GCC not to link this code by using the -ffreestanding and -nostartfiles options. The option -nostdlib allows you to disable automatic linking to the C standard library. If you use this option, you must provide your own versions of any standard functions used in your program. Finally, in a freestanding environment, a C program need not begin with main(). You can use the linking option -ename on the GCC command line to specify an alternative entry point for your program.

C Dialects

When writing a C program, one of your first tasks is to decide which of the various definitions of the C language applies to your program. GCC’s default dialect is “GNU C11,” which is largely the ISO/IEC 9899:2011 standard, with a certain number of extensions, and without the C11 standard’s optional multithreading features. These extensions include many features that have since been standardized in C99—such as complex floating-point types and long long integers—as well as other features that have not been adopted, such as complex integer types and zero-length arrays. The full list of extensions is provided in the GCC documentation. To turn off all the GNU C extensions, use the command-line option -ansi.

GCC’s language standardization options are:

-std=iso9899:1990, -std=c90, -std=c89, -ansi

These options all mean the same thing: conform to ISO/IEC 9899:1990, including Technical Corrigenda of 1994 and 1996. They do not mean that no extensions are accepted: only those GNU extensions that conflict with the ISO standard are disabled, such as the typeof operator.

-std=iso9899:199409

Conform to “AMD1,” the 1995 internationalization amendment to ISO/IEC 9899:1990.

-std=iso9899:1999, -std=c99

Conform to ISO/IEC 9899:1999, with the Technical Corrigendum of 2001. GCC supports nearly all provisions of C99. See http://gcc.gnu.org/c99status.html for details.

-std=iso9899:2011, -std=c11

Conform to ISO/IEC 9899:2011. Support for the mandatory features of the C11 is largely complete (although the GNU standard C library, GLIBC, does not support the multithreading features defined in threads.h). See https://gcc.gnu.org/wiki/C11Status for details.

-std=gnu89, -std=gnu90

These options are equivalent and mean: Support ISO/IEC 9899:1990 and the GNU extensions. This dialect was GCC’s default before version 5.

-std=gnu99

Conform to ISO/IEC 9899:1999 with the GNU extensions.

-std=gnu11

Conform to ISO/IEC 9899:2011 with the GNU extensions. The GNU dialect of C11 is the default beginning in GCC version 5.

With any of these options, you must also add the option -pedantic if you want GCC to issue all the warnings that are required by the given standard version, and to reject all extensions that are prohibited by the standard. The option -pedantic-errors causes compiling to fail when such warnings occur.

Earlier versions of GCC also offered a -traditional option, which was intended to provide support for pre-ANSI or “K&R-style” C. Currently, GCC supports this option only in the preprocessing stage, and accepts it only in conjunction with the -E option, which directs GCC to perform preprocessing and then exit.

Furthermore, a number of GCC options allow you to enable or disable individual aspects of different standards and extensions. For example, the -trigraphs option enables trigraphs (see “Digraphs and Trigraphs”) even if you have not used the -ansi option. For the full list of available dialect options, see the GCC manual.

Compiler Warnings

You’ll get two types of complaints from GCC when compiling a C program. Error messages refer to problems that make your program impossible to compile. Warnings refer to conditions in your program that you might want to know about and change—for stricter conformance to a given standard, for example—but that do not prevent the compiler from finishing its job. You may be able to compile and run a program in spite of some compiler warnings—although that doesn’t mean it’s a good idea to do so.

GCC gives you very fine control over the warning messages that it provides. For example, if you don’t like the distinction between errors and warnings, you can use the -Werror option to make GCC stop compiling on any warning as if it were an error. Other options let you request warnings about archaic or nonstandard usage, and about many kinds of C constructs in your programs that are considered hazardous, ambiguous, or sloppy.

You can enable most of GCC’s warnings individually by using options that begin with -W. For example, the option -Wswitch-default causes GCC to produce a warning message whenever you use a switch statement without a default label, and -Wsequence-point provides a warning when the value of an expression between two sequence points depends on a subexpression that is modified in the same interval (see “Side Effects and Sequence Points”).

The easiest way to request these and many other warnings from GCC is to use the command-line option -Wall. However, the name of this option is somewhat misleading: -Wall does not enable all of the individual -W options. Quite a few more must be asked for specifically by name, such as -Wshadow; this option gives you a warning whenever you define a variable with block scope that has the same name as, and thus “shadows,” another variable with a larger scope. Such warnings are not among those produced by -Wall.

If you use the -Wall option but want to disable a subset of the warnings it causes, you can insert no- after the -W in the names of individual warning options. For example, -Wno-switch-default turns off warnings about switch statements without default. Furthermore, the -w option (that’s a lowercase w) anywhere on the command line turns off all warnings.

The option -Wextra (formerly named simply -W, with no suffix) adds warnings about a number of legal but questionable expressions, such as testing whether an unsigned value is negative or non-negative:

unsigned int u;
/* ... */
if ( u < 0 )
  { /* ... this block is never executed ... */ }

The -Wextra option also warns about expressions that have no side effects and whose value is discarded. The full set of conditions it checks for is described in the GCC reference manual.

Furthermore, if you are updating older programs, you may want to use -Wtraditional to request warnings about constructs that have different meanings in old-style C and ISO standard C, such as a string literal in a macro body that contains the macro’s argument:

#define printerror(x)    fputs("x\n", stderr)

In older, “traditional” C, this macro would work as intended, but in ISO standard C, it would print the letter “x” and a newline character each time you use it. Hence for this line -Wtraditional would generate a warning such as the following:

file:line:column: warning: macro argument "x" would be stringified in
traditional C

Optimization

GCC can apply many techniques to make the executable program that it generates faster and/or smaller. These techniques all tend to reduce still further the “word-for-word” correspondence between the C program you write and the machine code that the computer reads. As a result, they can make debugging more difficult, and are usually applied only after a program has been tested and debugged without optimization.

There are two kinds of optimization options. You can apply individual optimization techniques by means of options beginning with -f (for flag), such as -fmerge-constants, which causes the compiler to place identical constants in a common location, even across different source files. You can also use the -O options (-O0, -O1, -O2, and -O3) to set an optimization level that cumulatively enables a number of techniques at once.

The -O Levels

Each of the -O options represents a number of individual optimization techniques. The-O optimization levels are cumulative: -O2 includes all the optimizations in-O1, and -O3 includes -O2. For complete and detailed descriptions of the different levels, and the many -f optimization options that they represent, see the GCC reference manual. The following list offers a brief description of each level:

-O0

Turn off all optimization options.

-O, -O1

Try to make the executable program smaller and faster, but without increasing compiling time excessively. The techniques applied include merging identical constants, basic loop optimization, and grouping stack operations after successive function calls. A -O with no number is interpreted as -O1.

-O2

Apply almost all of the supported optimization techniques that do not involve a tradeoff between program size and execution speed. This option generally increases the time needed to compile. In addition to the optimizations enabled by -O1, the compiler performs common subexpression elimination, or CSE, which involves detecting mathematically equivalent expressions in the program and rewriting the code to evaluate them only once, saving the value in an unnamed variable for reuse. Furthermore, instructions are reordered to reduce the time spent waiting for data moving between memory and CPU registers. Incidentally, the data flow analysis performed at this level of optimization also allows the compiler to provide additional warnings about the use of uninitialized variables.

-O3

Generate inline functions and enable more flexible allocation of variables to processor registers. Includes the -O2 optimizations.

-Os

Optimize for size. This option is like -O2, but without those performance optimizations that are likely to increase the code size. Furthermore, block reordering and the alignment of functions and other jump destinations on power-of-two byte boundaries are disabled. If you want small executables, you should be compiling with the GCC option -s, which instructs the linker to strip all the symbol tables out of the executable output file after all the necessary functions and objects have been linked. This makes the finished program file significantly smaller, and is often used in building a production version.

The following example illustrates how -O options are used:

$ gcc -Wall -O3 -o circle circle.c circulararea.c -lncurses

This command uses -O3 to enable the majority of the supported optimization techniques.

The -f Flags

GCC’s many -f options give you even finer control over optimization. For example, you can set a general optimization level using an -O option, and then turn off a certain technique. Here is an example:

$ gcc -Wall -O3 -fno-inline-functions -o circle circle.c \
circulararea.c -lncurses

The options -O3 -fno-inline-functions in this command enable all the optimizations grouped in -O3 except inline compiling of functions.

There are also flags to enable many optimizations that are not included in any -O level, such as -funroll-loops; this option replaces loop statements that have a known, small number of iterations with repetitive, linear code sequences, thus saving jumps and loop-counter operations. A full list of the hundred or so -f options that control GCC’s individual optimization flags would be too long for this chapter, but the examples in this section offer a hint of the capabilities available. If you need a certain compiler feature, there’s a good chance you’ll find it in the GCC manual.

Floating-Point Optimization

Some of the optimization options that are not included in the -O groups pertain to floating-point operations. The C99 floating-point environment supports scientific and mathematical applications with a high degree of numeric accuracy, but for a given application, you might be more interested in speed than in the best floating-point math available. For such cases, the -ffast-math option defines the preprocessor macro __FAST_MATH__, indicating that the compiler makes no claim to conform to IEEE and ISO floating-point math standards. The -ffast-math flag is a group option, which enables the following six individual options:

-fno-math-errno

Disables the use of the global variable errno for math functions that represent a single floating-point instruction.

-funsafe-math-optimizations

The “unsafe math optimizations” are those that might violate floating-point math standards, or that do away with verification of arguments and results. Using such optimizations may involve linking code that modifies the floating-point processor’s control flags.

-fno-trapping-math

Generates “nonstop” code on the assumption that no math exceptions will be raised that can be handled by the user program.

-ffinite-math-only

Generates executable code that disregards infinities and NaN (“not a number”) values in arguments and results.

-fno-rounding-math

This option indicates that your program does not depend on a certain rounding behavior, and does not attempt to change the floating-point environment’s default rounding mode. This setting is currently the default, and its opposite, -frounding-math, is still experimental.

-fno-signaling-nans

This option permits optimizations that limit the number of floating-point exceptions that may be raised by signaling NaNs. This setting is currently the default, and its opposite, -fsignaling-nans, is still experimental.

Architecture-Specific Optimization

For certain system architectures, GCC provides options to produce optimized code for specific members of the processor family, taking into account features such as memory alignment, model-specific CPU instructions, stack structures, increased floating-point precision, prefetching and pipelining, and others. These machine-specific options begin with the prefix -m. If you want to compile your code to make the most of a specific target system, read about the available options in the GCC reference manual.

For several processor types, such as the Sparc, ARM, and RS/6000-PowerPC series, the option -mcpu=cpu generates machine code for the specific CPU type’s register set, instruction set, and scheduling behavior. Programs compiled with this option may not run at all on a different model in the same CPU family. The GCC reference manual lists the available cpu abbreviations for each series.

The option -mtune=cpu is more tolerant. Code generated with -mtune=cpu uses optimized scheduling parameters for the given CPU model but adheres to the family’s common instructions and registers, so that it should still run on a related model.

For the Intel x86 series, the -mcpu=cpu option is the same as -mtune=cpu. The option to enable a model-specific instruction set is -march=cpu. Here’s an example:

$ gcc -Wall -O -march=athlon-4 -o circle circle.c circulararea.c \
-lncurses

This command line compiles a program for the AMD Athlon XP CPU.

Why Not Optimize?

Sometimes there are good reasons not to optimize. In general, compiling with optimization takes longer and requires more memory than without optimization. How much more depends on the techniques that are applied. Furthermore, the performance gains obtained by a given optimization technique depend on both the given program and the target architecture. If you really need optimum performance, you need to choose the techniques that will work in your specific circumstances.

You can combine both -O and-f optimization options with GCC’s -g option to include debugging information in the compiled program, but if you do, the results may be hard to follow in a debugging program; optimization can change the order of operations, and variables defined in the program may not remain associated with one register, or may even be optimized out of existence. For these reasons, many developers find it easier to optimize only after a program has been debugged.

Some optimization options may also conflict with strict conformance to the ISO C standard, such as merging variables declared with const as if they were constants. If standards-conformance is critical, and sometimes it is, there are certain optimizations you may not wish to pursue.

Another issue you may encounter is that some optimization techniques result in nondeterministic code generation. For example, the compiler’s guess as to which branch of a conditional jump will be taken most often may involve randomness. If you are programming real-time applications, you’ll probably want to be careful to ensure deterministic behavior.

Finally, when developing multithreaded programs, you need to be aware of how shared memory access works and the features that the threads library offers to control it (see Chapter 14), because optimization can involve rearranging the order of memory access operations, and even eliminate operations that appear superfluous to the compiler in the scope of a single thread.

In any case, if you want to be sure of getting the greatest possible runtime performance, or if you need to know in detail how GCC is arriving at the exact machine code for your C program, there is no substitute for testing and comparing your specific program’s performance with the various optimization options.

Debugging

Use the -g option to have GCC include symbol and source-line information in its object and executable output files. This information is used by debugging programs to display the contents of variables in registers and memory while stepping through the program. (For more on debugging, see Chapter 21.) There are a number of formats for this symbol information, and GCC uses your system’s native format by default.

You can also use a suffix to the -g option to store the symbol information in a different format from your system’s native format. You might want to do this in order to conform to the specific debugging program that you are using. For example, the option -ggdb chooses the best format available on your system for debugging with the GNU debugger, GDB.

Because the symbol information can increase and even multiply the size of your executable file, you will probably want to recompile without the -g option and link using the -s option when you have completed debugging and testing. However, some software packages are distributed with debugging information in the binaries for use in diagnosing subsequent users’ problems.

Profiling

The -p option adds special functions to your program to output profiling information when you run it. Profiling is useful in resolving performance problems, because it lets you see which functions your program is spending its execution time on. The profiling output is saved in a file called mon.out. You can then use the prof utility to analyze the profiling information in a number of ways; see the prof manual for details.

For the GNU profiler, gprof, compile your program with the-pg option. The default output filename for the profiling information is then gmon.out. gprof in conjunction with the GCC option -pg can generate a call graph showing which functions in your program call which others. If you combine the-pg option with -g, the GCC option that provides source-line information for a debugger, then gprof can also provide line-by-line profiling.

Option and Environment Variable Summary

This section summarizes frequently used GCC options for quick reference, and lists the environment variables used by GCC.

Command-Line Options

-c

Preprocess, compile, and assemble only (i.e., don’t link).

-C

Leave comments in when preprocessing.

-Dname[=definition]

Defines the symbol name.

-ename

Start program execution at name.

-E

Preprocess only; output to stdout, unless used with -o.

-ffast-math

Permit faster floating-point arithmetic methods at the cost of accuracy or precision.

-ffinite-math-only

Disregard infinities and NaN (“not a number”) values.

-ffreestanding

Compile as a freestanding (not hosted) program.

-finline-functions, -fno-inline-functions

Enable/disable inline functions.

-fno-math-errno

Disable the errno variable for simple math functions.

-fmerge-constants

Put identical constants in a single location.

-fno-trapping-math

Generate “nonstop” floating-point code.

-frounding-math

Don’t disregard the rounding-mode features of the floating-point environment (experimental).

-fsignaling-nans

Allow all exceptions raised by signaling NaNs (experimental).

-fsyntax-only

Don’t compile or link; just test input for syntax.

-funroll-loops, -fno-unroll-loops

Enable/disable loop optimization.

-funsafe-math-optimizations

Permit optimizations that don’t conform to standards and/or don’t verify values.

-fverbose-asm

Include C variable names as comments in assembly language.

-g[format]

Compile for debugging.

-Idirectory[:directory[…]]

Search for header files in the specified path.

-I-

Distinguish between -Ipath for #include <filename> and -Ipath for #include "filename" (deprecated).

-iquotedirectory[:directory[…]]

Search the specified path for header files specified in quotation marks (#include "filename").

-isysrootdirectory

Prepend the specified directory to the system root directory to find header files.

-lbasename

Link with library libbasename.so or libbasename.a.

-Ldirectory[:directory[…]]

Search for library files in the specified path.

-march=cpu

Intel x86: Generate model-specific code.

-mcpu=cpu

Sparc, ARM, and RS/6000-PowerPC: Generate model-specific code.

Intel x86: Optimize scheduling for the specified CPU model.

-mtune=cpu

Optimize scheduling for the specified CPU model.

-nostartfiles

Don’t link startup code.

-nostdlib

Don’t link with the standard library.

-o filename

Direct output to the specified file.

-O0

Turn off all optimization options.

-O, -O1

Perform some optimization without taking much time.

-O2

Perform more optimization, including data flow analysis.

-O3

Perform still more optimization, including inline function compilation.

-Os

Optimize for size.

-p

Link in code to output profiling information.

-pedantic-errors

Fail on nonstandard usage.

-pg

Link in code to output profiling information for gprof.

-s

Strip symbol tables from executable file.

-S

Preprocess and translate into assembly language only.

-save-temps

Save intermediate output files.

-shared

Create a shared object file for dynamic linking.

-static

Don’t link to shared object files.

-std=iso9899:1990, -std=c89, -ansi

Support ISO/IEC 9899:1990.

-std=iso9899:199409

Support ISO/IEC 9899:1989 and AMD1.

-std=c99

Support ISO/IEC 9899:1999.

-std=c11

Support ISO/IEC 9899:2011.

-std=gnu89

Like -ansi, plus GNU extensions (GCC’s default before version 5).

-std=gnu99

Like -std=c99, plus GNU extensions.

-std=gnu11

Like -std=c11, plus GNU extensions (GCC’s default beginning with version 5).

--sysrootdirectory

Prepend the specified directory to the system root directory to find header and library files. If the -isysroot option is not used, the --sysroot option affects both library and header file searches.

-traditional

Support old-style C. Deprecated; supported only with -E.

-trigraphs

Support ISO C trigraphs.

-Uname

“Undefine” the symbol name.

-v

Be verbose: print the options applied at each step of compiling.

--version

Output GCC version and license information.

-w

Disable all warnings.

-Wa,option[,option[…]]

Pass options to assembler command line.

-Wall

Output warnings about a broad range of problems in source code.

-Wl,option[,option[…]]

Pass options to linker command line.

-Werror

Fail on all warnings.

-Wextra

Output warnings about legal but questionable usage.

-Wtraditional

Warn about differences to old-style C.

-x filetype

Treat subsequent files as being of the specified type.

Environment Variables

CPATH, C_INCLUDE_PATH

Colon-separated list of directories to search for header files, after those indicated by -Idirectory on the command line.

COMPILER_PATH

Colon-separated list of directories to search for GCC’s own subprogram files.

GCC_EXEC_PREFIX

A prefix for GCC to add to the names of its subprograms when invoking them. May end with a slash.

LIBRARY_PATH

Colon-separated list of directories to search for linker and library files, after directories specified by -Ldirectory on the command line.

LD_LIBRARY_PATH

Colon-separated list of directories to search for shared library files. Read not by GCC but by executables dynamically linked against shared libraries.

TMPDIR

Directory to use for temporary files.

1 Actually, as a retargetable compiler, GCC doesn’t translate C statements directly into the target machine’s assembly language, but uses an intermediate language called Register Transfer Language or RTL, between the input language and the assembly-language output. This abstraction layer allows the compiler to choose the most economical way of coding a given operation in any context. Furthermore, an abstract description of the target machine in an interchangeable file provides a structured way to retarget the compiler to new architectures. From the point of view of GCC users, though, we can ignore this intermediate step.