A.2 Compilation

Team-FLY

The C compiler, cc , translates a collection of C source programs and object files into either an executable file or an object file. On your system, the compiler may have another name , such as gcc . The cc command may be a symbolic link to another executable.

Compilation proceeds in stages. In the first stage, a preprocessor expands macros and includes header files. The compiler then makes several passes to translate the code, first to the assembly language of the target machine and then into machine code. The result is an object module , which has machine code and tables of unresolved references. The final stage of compilation links a collection of object modules together to form the executable module with all references resolved. An executable file is ready to be loaded and run. The executable contains exactly one main function.

Example A.2

The following command compiles mine.c and produces the executable mine .

 cc -o mine mine.c 

If the -o mine option is omitted, the C compiler produces an executable called a.out . Use the -o option to avoid the noninformative default name.

Example A.3

The following mine.c source file contains an undefined reference to the serr function.

 void serr(char *msg); int main(void) {    serr("This program does not do much\n");    return 0; } 

When mine.c of Example A.3 is compiled as in Example A.2, the C compiler displays a message indicating that serr is an unresolved reference and does not produce an executable.

Programs are usually organized into multiple source files that must be linked together. You can compile all the source files with a single cc command. Alternatively, you can compile the source into separate object modules and link these object modules to form an executable module in a separate step.

Example A.4

Suppose that the serr function is contained in the source file minelib.c . The following command compiles the mine.c source file of Example A.3 with minelib.c to produce an executable module called mine .

 cc -o mine mine.c minelib.c 

The -c option of cc causes the C compiler to produce an object module rather than an executable. An object module cannot be loaded into memory or executed until it is linked to libraries and other modules to resolve references. The C compiler does not complain about unresolved references in object modules. A misspelled variable or missing library function might not be detected until that object module is linked into an executable.

Example A.5

The following command produces the object module mine.o .

 cc -c mine.c 

When the -c option is used, the C compiler produces an object module named with the .o extension. The mine.o produced by the cc command of Example A.5 can later be linked with another object file (e.g., minelib.o ) to produce an executable.

Example A.6

The following command links the object modules mine.o and minelib.o to produce the executable mine .

 cc -o mine mine.o minelib.o 

A.2.1 Header files

Before a function such as serr in Example A.3 is referenced, it should either be defined or have a prototype. Often, prototypes are contained in header files.

Before compilation, the C preprocessor copies the header files specified by #include statements into the source. By convention, header files have a .h extension. Put declarations of constants, types and functions in header files. Do not put variable declarations in header files, because this can result in multiply-defined variables . The next exercise illustrates the difficulties caused by placing variable declarations in header files.

Exercise A.7

What happens if you execute the following commands?

 cc -o mystuff my.c mylib.c mystuff 

The file myinc.h contains the following segment.

 #include <stdio.h> static int num; void changenum(void); 

The file my.c contains the following main program.

 #include "myinc.h" int main (void) {    num = 10;    changenum();    printf("num is %d\n", num);    return 0; } 

The file mylib.c contains the following function.

 #include "myinc.h" void changenum(void) {    num = 20; } 

Answer:

Both my.c and mylib.c contain a num variable because its definition appears in myinc.h . The call by the main program to changenum does not affect the value of the variable num defined in my.c . The mystuff program outputs 10 rather than 20 .

Enclose system-defined header files in angle brackets (as in #include <stdio.h> ) since the compiler then looks in the standard place for the file. The standard place depends on the system, but the man page for cc usually describes how the standard search occurs. The /usr/include directory holds many of the standard header files. The files in this directory often include other .h files from subdirectories beneath /usr/include . The /usr/include/sys directory is a standard location for many of the .h files needed for this book. Be sure to include the header files specified by the man page synopsis when using a library function. Enclose personal header filenames in double quotes as follows .

 #include "myinc.h" 

The quotes tells the compiler to look for the header file in the directory containing the source file before looking in the standard place.

Exercise A.8

A program uses the error symbol EAGAIN in conjunction with a call to write . The compiler complains that EAGAIN is not defined. Now what?

Answer:

Try the following steps to solve the problem.

  • Make sure to include all the header files mentioned in the synopsis for write . The man page specifies the header file <unistd.h> .

  • Buried somewhere in the man pages is a statement mentioning that errno.h must be included in programs that refer to error symbols. If the program includes errno.h , the problem is solved .

  • If the errno.h statement in the man page escapes your notice, look for the symbol EAGAIN directly in the system header files by using

     cd /usr/include grep EAGAIN * 

    The grep command searches for the string EAGAIN in all of the files in the directory /usr/include . Unfortunately, the EAGAIN symbol is not in any of the files in /usr/include .

  • Change to the /usr/include/sys directory and try grep again. The following is a typical response to grep .

     errno.h:#define EAGAIN 11 errno.h:#define EWOULDBLOCK        EAGAIN 

    It might be tempting to eliminate the problem by including the file sys/errno.h in the source, but what the compiler really wants is errno.h . Using errno.h directly is better because it includes sys/errno.h and also contains additional definitions.

A.2.2 Linking and libraries

Just because a program has the right header files does not mean that your troubles are over. A header file gives symbol declarations and function prototypes, but it does not supply the actual code for the function call.

Exercise A.9

The mylog.c source file calculates the logarithm of a value. After including math.h in that source file, the user compiles the program and receives an error message that the log function could not be found. Why not?

Answer:

The math.h header file just tells the C compiler what the form (prototype) of the log function is. It does not actually supply the function.

Compilation takes place in two distinct phases. In the first phase, the compiler translates each C source file into object code. The cc -c option stops at this point. Object code is not ready to execute because the program may reference outside items that have not been located. To produce an executable module, the compile must find all the undefined symbols ( unresolved external references ). The cc compiler calls the link editor, ld , to accomplish this task.

Example A.10

The following command compiles the mylog.c source file with the system math library to produce an executable called mylog .

 cc -o mylog mylog.c -lm 

To use C mathematics library functions, put #include <math.h> in the source file and also specify that the program should be linked with the math library ( -lm ) when it is compiled.

The names of libraries are specified by the -l option. The object files are processed in the order in which they appear on the cc command line, so the location of -l on the cc line is significant. It should come after the object files because only those entries that match unresolved references are loaded. By default, the link editor automatically searches the standard C library.

Exercise A.11

What happens if the math library in Example A.10 is linked, but the header file math.h is not included in the source?

Answer:

The compiler assumes that log has a return value of type int rather than double . If the program calls the log function, the calculation produces an incorrect numerical result. The compiler may not produce an error or warning message. However, lint (Section A.4) reports that log has been implicitly declared to return int .

Example 1.12

The following linking command processes the object files in the order my.o , the math library, and then mylib.o .

 cc -o my my.o -lm mylib.o 

The link editor includes only those objects in the library that correspond to unresolved references. Thus, if mylib.o contains a reference to the math library, that reference is not resolved by this command.

The -lx option is short for either libx.a (a library archive ) or libx.so (a shared library ). Which is the default depends on how the system is set up. Many compilers allow you to specify -Bstatic -lx in the cc command for a library archive and -Bdynamic -lx for a shared library. The compiler scans the shared libraries for references, but it does not actually put the functions in the executable output file. Instead, the runtime system loads them by dynamic loading and binding.

Several versions of a particular library may coexist on a system ”at least one for each version of the C compiler. A typical search order for libraries is the following.

  • -L directories specified on the cc line

  • Directories in the LD_LIBRARY_PATH environment variable

  • Standard library directories (e.g., /usr/lib )

The -L option of cc explicitly specifies pathnames for directories to be searched for libraries. The LD_LIBRARY_PATH environment variable specifies default pathnames for searching for load libraries. Generally, LD_LIBRARY_PATH includes pathnames for the directories in which the compilers are installed, as well as directories such as /usr/local/lib . Your system administrator has probably set up the LD_LIBRARY_PATH variable for using the standard compilers.

A.2.3 Macros and conditional compilation

Before the Single UNIX Specification, there were several incompatible UNIX standards, and vendors would use conditional compilation to adjust for these differences. The preprocessor can produce different code for the compiler from a single source file through the use of the #if, #ifdef and # ifndef preprocessor statements. Such conditional compilation can be used to allow a program to be compiled under different implementations or in different environments.

Example A.13

The UICI restart library sets errno to ETIME when the function waitfdtimed times out. Some systems do not define ETIME but instead use the error ETIMEDOUT . The file restart.h solves this problem with the following.

 #ifndef ETIME #define ETIME ETIMEDOUT #endif 

If ETIME is not already defined, it is defined as ETIMEDOUT .

ETIME and ETIMEDOUT are examples of simple macros specified by a #define statement. The preprocessor replaces these defined constants with their values before passing the code to the C compiler.

Most C compilers have a -D option that allows the setting of macros at compile time.

Example A.14

The Linux header files provide a number of options to support different standards and implementations. Linux uses the constant _GNU_SOURCE for many of the features that are now part of the Single UNIX Specification. If this constant is defined, then these features are turned on. Some of the programs in this book require this constant to be defined when the programs are compiled under Linux. To compile the program myprog.c with this constant defined, use the following command.

 cc -D_GNU_SOURCE -o myprog myprog.c 

This causes the constant _GNU_SOURCE to be defined with the default value of 1, as if the following statement appeared as the first line of the source file.

 #define _GNU_SOURCE 1 
Example A.15

The UICI name library in Section C.2 gives four implementations of the function addr2name and name2addr , using conditional compilation to choose one of the implementations. The general format of the code is as follows.

 #ifndef REENTRANCY #define REENTRANCY_NONE #endif #if REENTRANCY==REENTRANT_NONE    /* default code using gethostbyname and gethostbyaddr */ #elif REENTRANCY==REENTRANT_R    /* code using gethostbyname_r and gethostbyaddr_r */ #elif REENTRANCY==REENTRANT_MUTEX    /* code using mutex locks */ #elfi REENTRANCY==REENTRANT_POSIX    /* code using getnameinfo and getaddrinfo */ #endif 

The first three lines guarantee that REENTRANCY has its default value if it is not otherwise defined.

Example A.16

Execute the following command to compile the program client.c with the restart library, the UICI library, and the UICI name library. Use the getnameinfo and getaddrinfo functions.

 cc -DREENTRANCY=REENTRANT_POSIX -o client client.c restart.c uiciname.c uici.c 

Additional libraries may be needed on your system.

Team-FLY


Unix Systems Programming
UNIX Systems Programming: Communication, Concurrency and Threads
ISBN: 0130424110
EAN: 2147483647
Year: 2003
Pages: 274

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net