There's strictly no warranty for the correctness of this text. You use any of the information provided here at your own risk.
Preface (2021): This page was written many years ago.
"gcc" is the GNU project's open-source C-compiler.
It's freely available on Linux and also on Windows as part of the "MinGW"-project.
If you have some valid C-source-file called "hello.c" like:
#include <stdio.h> int main(void) { puts("Hello World !"); return 0; }
you can run
gcc hello.c
and gcc creates an executable called "a.out" in the current directory. On Linux, this can then be run doing
./a.out
So far, so good.
You can give the executable another filename than "a.out" with gcc's "-o"-option:
gcc hello.c -o hello
If your code has severe mistakes, gcc aborts printing an error-message.
There may be other constructions in your code, gcc doesn't like very much, but don't make it abort. In this situation, gcc can print warnings, but this behaviour isn't enabled by default. You can use the "-Wall"-option to make gcc print warnings:
gcc -Wall hello.c -o hello
You should write C-code in a way, gcc doesn't show warnings, so you should use "-Wall". Remember, what Brian W. Kernighan, one of the two authors of "The C Programming Language" said:
Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.
So you should leave as few mistakes for debugging as possible. "-Wall" helps finding some of them at compilation-time.
Additionally, you can use the "-Wextra"-option (previously just called "-W") for even more warnings:
gcc -Wall -Wextra hello.c -o hello
Programming languages develop throughout the times. For C, the ANSI-committee has defined standards, what valid C-syntax is.
An often-used standard is ISO C90.
There is also ISO C99, but some of its new features haven't been received well by a lot of programmers, so several major compilers like "Microsoft Visual C++" or "Borland C++BuilderX" still don't support it.
In fact, gcc does support C99 to a certain extend (you can use the "-std=c99"-option for this), but if you want your code to be able to be compiled by the other compilers mentioned above as well, you should still write in ISO C90.
To make gcc check, if your code is ISO C90-compatible, you can use the option "-ansi -pedantic":
gcc -Wall -Wextra -ansi -pedantic hello.c -o hello
If you want to debug your program lateron, for example using "gdb", the GNU debugger, you need to write certain information into the executable at compilation-time, so gdb knows the source-code of your program for example.
gcc writes this information, if the "-g"-option is given:
gcc -g -Wall -Wextra -ansi -pedantic hello.c -o hello
You can then run
gdb hello
and type "list" there to see the result: gdb can print the source-code written into the executable.
If your program runs fine and doesn't have any known bugs, you may want gcc to compile it to run even faster or to be smaller in size:
The gcc-options
When you use the "#include"-directive, the program is linked against some library.
You can check the libraries used by an executable with the "ldd"-command. For example shows
ldd ./hello
the libraries, an executable "hello" is linked against.
Some standard libraries like "libc.so.6" are recognized by "gcc" automatically. But the names of other libraries have to be passed to "gcc" manually.
If, for example, you want to use the math-library "libm.so.6" in a program "printsin.c", that prints the sinus of 45 degrees:
#include <stdio.h> #include <math.h> int main(void) { printf("%f\n", sin(45)); return 0; }
compilation with
gcc -Wall -W -ansi -pedantic -o prsin printsin.c
doesn't work, because the math-library isn't found. You have to tell "gcc", that the program needs this library.
You can do that, using the "-l"-option:
Directly after the "-l", you have to pass the library-name: It's got to be just the part of the name between "lib" and ".so.6". So to link against "libm.so.6", it's just "-lm". Not more:
gcc -Wall -W -ansi -pedantic -o prsin printsin.c -lm
compiles the program above.
When "-l" is used, "gcc" searches its default-path for the library.
Several directories like "/lib" and "/usr/lib" are in this path. That's why the compilation above already worked.
But you could ask "gcc" to search for a library in a certain other directory using the "-L"-option. Here's an example, but with "/usr/lib" again:
gcc -Wall -W -ansi -pedantic -o prsin printsin.c -lm -L/usr/lib
To make "-L" work with any directory, you would have to do
/sbin/ldconfig /path/to/my/directory
as "root" first to add the directory to the "ld-library-cache". Usually, this isn't necessary, because most libraries install themselves into default-directories, because they want to be found by programs.
Notice, that the options "-l" and "-L" have to be given after the file-names to compile (because they refer to the linking-process).
If you want to find out more about libraries, you can just read on or go straight here.
The code of larger C-programs is often separated into several source-files:
First, there is a ".c"-file containing the "main()"-function.
Then, there are one or more ".h"-files (header-file): In these file, there are the declarations of the program's functions and structs and also the "#define"-directives.
The ".c"-file with the "main()"-function contains "#include"-directives pointing to the ".h"-files, for example:
#include "mine.h"
Notice, that quotation-marks are used in the "#include"-directive, if the header-file is in the same directory as the main ".c"-file.
If gcc shall search for the header-file in the default-path, the header-file's name is given in angle brackets ("<>") like in
#include <stdio.h>
The functions declared in the ".h"-files are then written into ".c"-files with a name corresponding to that of the ".h"-files.
gcc has to be called with the names of the ".c"-files then.
Please take a look at this example with three files now (we are going to use this example for several purposes):
"hello.c" (1):
#include <stdio.h> #include "mine.h" int main(void) { puts(printhello()); return 0; }
"mine.h" (2):
const char *printhello(void);
and "mine.c" (3):
const char *printhello(void) { return "Hello World !"; }
These three files can be compiled together in a directory with
gcc -Wall -Wextra -ansi -pedantic hello.c mine.c -o hello
Compiling larger applications is rather time-consuming. So it would be rather painful, if the whole program had to be recompiled each time changes are made to only one or a few of many source-files.
Fortunately there's a solution for that: With the "-c"-option, gcc can produce ".o"-files (object-files) of ".c"-files.
".o"-files already contain machine-code, but they are not yet linked together to an executable.
So if you take the three files "hello.c", "mine.c" and "mine.h" from above and run
gcc -c hello.c mine.c
on them, the files "hello.o" and "mine.o" are created.
If for example "hello.c" is changed during further development, "mine.o" can be used for the next compilation-process instead of "mine.c".
If there are a lot of ".c"-files, using ".o"-files instead makes compilation significantly faster.
04-19-2020
"make" is a GNU-tool for compiling larger applications. It reads in a file called "Makefile", that contains instructions in a certain format, and executes these instructions.
It would take too much time to recompile a whole project, when just small changes have been made to one or more source-files. Using "make" and a Makefile makes it possible, to just recompile the required files of a project. The rest of the project is relinked from already existing object-files (".o"), which contain previously compiled code.
gcc's option "-c" makes it create just the ".o"-object-files. Another "gcc source.o" then creates the executable.
After reading the Makefile, "make" recognizes automatically, which source files have been changed and need recompiling, simply by checking the time signature of the source files (command "touch").
In a Makefile, you first declare what file should be compiled, followed by a colon. After the colon you declare the object-files (".o") that are needed to compile the executable.
In the next line, you press one "tab" (it has to be a "tab", four space characters won't work), then specify the "gcc"-command that is needed for compiling (including "-o [filename]").
You don't have to mention the header-files (".h") in the Makefile, because they are included in the source-files themselves.
Header-files contain the declarations of the used structures and functions. The declarations should be inside an "#ifndef-statement ("ifndef" for "if not defined"), to make sure, that the declarations are only imported once into the source-files:
#ifndef NAME #define NAME ... ... ... #endif
Here's an example of a Makefile, two according ".c"-source-files and a ".h"-header-file.
example: example1.o example2.o gcc example1.o example2.o -o example clean: rm ./*.o ./example
And the two according ".c"-files and the ".h"-file:
#include <stdio.h> #include "example.h" /* example1.c */ int main() { int size = 10; int numbers[size]; int i; for (i = 0; i < size; i++) { numbers[i] = i; } printIntArray(numbers, size); int sum = sumNumbers(numbers, size); printf("%d\n", sum); return 0; }
---------------------------------------------------------
#include <stdio.h> #include "example.h" /* example2.c */ void printIntArray(int arr[], int size) { int i; printf("["); for (i=0; i < size; i++) { printf("%d", arr[i]); if (i < size - 1) { printf("%d", ", "); } } printf("]\n"); } int sumNumbers(int arr[], int size) { int sum = 0; int i; for (i=0; i < size; i++) { sum += arr[i]; } return sum; }
---------------------------------------------------------
/* example.h */ #ifndef EXAMPLE #define EXAMPLE void printIntArray(int arr[], int size); int sumNumbers(int arr[], int size); #endif
---------------------------------------------------------
This also works for C++. Here's the same example, translated to C++11:
example: example1.o example2.o g++ -std=c++11 example1.o example2.o -o example example1.o: example1.cpp g++ -std=c++11 -c example1.cpp example2.o: example2.cpp g++ -std=c++11 -c example2.cpp clean: rm ./*.o ./example
---------------------------------------------------------
#include <iostream> #include <vector> #include "example.h" // example1.cpp using namespace std; int main() { vector<int> numbers; for (int i = 0; i < 10; i++) { numbers.push_back(i); } printIntVector(numbers); int sum = sumNumbers(numbers); cout << sum << endl; return 0; }
---------------------------------------------------------
#include <iostream> #include <vector> #include "example.h" // example2.cpp void printIntVector(vector<int> vec) { int i; int vecsize = vec.size(); cout << "["; for (i=0; i < vecsize; i++) { cout << vec[i]; if (i < vecsize - 1) { cout << ", "; } } cout << "]" << endl; } int sumNumbers(vector<int> vec) { int sum = 0; for (auto i: vec) { sum += i; } return sum; }
---------------------------------------------------------
// example.h #ifndef EXAMPLE #define EXAMPLE using namespace std; void printIntVector(vector<int> vec); int sumNumbers(vector<int> vec); #endif
With the just described usage of object-files, collections of functions can be created, that can be linked with several programs to executables.
One disadvantage of this way of reusing code is, that all functions of the object-file are linked into the executable at compilation-time.
If a program needs just one or a few of these functions, the size of the executable gets bigger than necessary.
To avoid this, programs can be linked against socalled libraries, that can be compiled from object-files (or directly from source-files).
There are:
Especially shared libraries can also be used as socalled "dynamically loaded libraries": That means, it is possible to implement into the main-program, when exactly the library shall be loaded into memory (see "man 3 dlopen" for details).
Note: Libraries contain already compiled code, so the library-developer can choose, if he wants to make the library's source-code available to the library-user (another programmer who wants to link his program against the library) or not.
So let's create a shared library for Linux from "mine.c", one of the three example-files "hello.c", "mine.c" and "mine.h" above, and then link "hello.c" against this library. What we're after is a library-file called "libmine.so.0".
It is possible to use "gcc" directly to create libraries, but it is quite difficult. Fortunately, there is a GNU shell-script (with over 7.000 lines of code) called
libtool
that takes care of the complicated "gcc"-calls needed (see "info libtool" for its documentation).
So, having "hello.c", "mine.c" and "mine.h" in a directory, we do
libtool --mode=compile gcc -c -Wall -W -ansi -pedantic mine.c
In the directory, the files "mine.o" and "mine.o" are created. Notice, doing
ls -A
that a hidden directory called
.libs
containing "mine.o" has also been created by "libtool".
Then we do
libtool --mode=link gcc -Wall -W -ansi -pedantic -o libmine.la mine.lo -rpath /usr/lib
Now move to the ".libs"-directory ("cd .libs"): There you find several files with "libmine.so..." !
There are some more files in this directory, but we want to get rid of them, so do in the ".lib"-directory:
rm libmine.a libmine.la libmine.lai mine.o
Ok, now only
libmine.so libmine.so.0 libmine.so.0.0.0
should be left there. Become "root" and copy those to "/usr/lib" ("cp * /usr/lib").
Become a normal user again and move back to the parent directory with "hello.c" in it. We only need "hello.c" and "mine.h" here now. So delete the rest doing
rm -r .libs libmine.la mine.c mine.lo mine.o
Don't worry, that "mine.c" is deleted too, as you can always find its source-text above in this document.
Then we link "hello.c" agains our shared library "libmine.so.0" doing
gcc -Wall -W -ansi -pedantic -o hello hello.c -lmine
The last option "-lmine" is also quite important. It has already been explained above.
Then our working executable "./hello" is created, that is linked against our library "libmine.so.0", as you can test with the "ldd"-command:
ldd ./hello
After going through this example and testing the executable doing "./hello", you may want to delete the "mine"-library from your system again. To do that, do as "root":
rm /usr/lib/libmine.so /usr/lib/libmine.so.0 /usr/lib/libmine.so.0.0.0
You could also place the library somewhere else than in "/usr/lib". To do that you would have to
After that you could compile your executable with gcc's "-l" and "-L"-options.
But notice, that it wouldn't make much sense to install a shared library into a non-default-directory, because other programs on other systems may not find it then.
Here's a short example of compiling "hello.c", "mine.c" and "mine.h" with "autoconf" and "automake". If you have the three files in a directory, create the following two more files:
"Makefile.am" (1):
bin_PROGRAMS = hello hello_SOURCES = hello.c mine.c
and "configure.in" (2):
AC_INIT(hello.c) AM_INIT_AUTOMAKE(hello,0.1) AC_PROG_CC AC_PROG_INSTALL AC_OUTPUT(Makefile)
Then, do:
aclocal autoconf touch NEWS README AUTHORS ChangeLog automake -a
After that you can build the "hello"-executable with the commonly used commands:
./configure make
With the information provided here you should be able to understand GNU's manual on development tools.