Running Linux, 5th Edition - Matthias Kalle Dalheimer [383]
You may notice in your dealings with Linux that a number of programs are compiled using the switch -O6 (the Linux kernel being a good example). The current version of gcc does not support optimization up to -O6, so this defaults to (presently) the equivalent of -O2. However, -O6 is sometimes used for compatibility with future versions of gcc to ensure that the greatest level of optimization is used.
Enabling Debugging Code
The -g switch to gcc turns on debugging code in your compiled object files. That is, extra information is added to the object file, as well as the resulting executable, allowing the program to be traced with a debugger such as gdb. The downside to using debugging code is that it greatly increases the size of the resulting object files. It's usually best to use -g only while developing and testing your programs and to leave it out for the "final" compilation.
Happily, debug-enabled code is not incompatible with code optimization. This means that you can safely use the command:
papaya$ gcc -O -g -o mumble mumble.c
However, certain optimizations enabled by -O or -O2 may cause the program to appear to behave erratically while under a debugger. It is usually best to use either -O or -g, not both.
More Fun with Libraries
Before we leave the realm of gcc, a few words on linking and libraries are in order. For one thing, it's easy for you to create your own libraries. If you have a set of routines you use often, you may wish to group them into a set of source files, compile each source file into an object file, and then create a library from the object files. This saves you from having to compile these routines individually for each program in which you use them.
Let's say you have a set of source files containing oft-used routines, such as:
float square(float x) {
/* Code for square()... */
}
int factorial(int x, int n) {
/* Code for factorial()... */
}
and so on (of course, the gcc standard libraries provide analogs to these common routines, so don't be misled by our choice of example). Furthermore, let's say that the code for square(), which both takes and returns a float, is in the file square.c and that the code for factorial() is in factorial.c. Simple enough, right?
To produce a library containing these routines, all you do is compile each source file, as so:
papaya$ gcc -c square.c factorial.c
which leaves you with square.o and factorial.o. Next, create a library from the object files. As it turns out, a library is just an archive file created using ar (a close counterpart to tar). Let's call our library libstuff.a and create it this way:
papaya$ ar r libstuff.a square.o factorial.o
When updating a library such as this, you may need to delete the old libstuff.a, if it exists. The last step is to generate an index for the library, which enables the linker to find routines within the library. To do this, use the ranlib command, as so:
papaya$ ranlib libstuff.a
This command adds information to the library itself; no separate index file is created. You could also combine the two steps of running ar and ranlib by using the s command to ar:
papaya$ ar rs libstuff.a square.o factorial.o
Now you have libstuff.a, a static library containing your routines. Before you can link programs against it, you'll need to create a header file describing the contents of the library. For example, we could create libstuff.h with the contents:
/* libstuff.h: routines in libstuff.a */
extern float square(float);
extern int factorial(int, int);
Every source file that uses routines from libstuff.a should contain an #include "libstuff.h" line, as you would do with standard header files.
Now that we have our library and header file, how do we compile programs to use them? First, we need to put the library and header file someplace where the compiler can find them. Many users place personal libraries in the directory lib in their home directory, and personal include files under include. Assuming