CPS 250, 356, & 444/544 Lecture notes: Compiling C in UNIX

Coverage: [UPE] §6.6 (pp. 187-190) and [USP] Appendices A.2 (pp. 800-807) and A.4 (pp. 809-812)


  • static vs. dynamic linking
  • macros
  • conditional compilation
  • error handling
  • debugging

Header files vs. libraries

  • header files contain prototypes for functions defined in libraries
  • while function definitions are pre-compiled in libraries, libraries must be linked
  • to include
    • system header files, use <filename.h> (e.g., #include <stdio.h>; same as #include "/usr/include/stdio.h")
    • user-defined header files, use (double quotes) "filename.h" (e.g., #include "myheader.h")

Standard C library

  • consists of the functions prototyped in <ctype.h>, <stdio.h>, <string.h>, <stdlib.h> and others
  • functions in header files given above are automatically linked
  • other libraries must be explicitly linked using -l option to gcc (e.g., gcc -lm ... (to link the math library))

Compiling a C program in UNIX

  • use gcc (gNU c compiler; GNU = Gnu is Not Unix)
  • gcc ptrEx1.c (compiles and links; produces executable a.out)
  • gcc -o ptrEx1 ptrEx1.c (produces executable ptrEx1)


GNU C and C++ Compiler

  • to compile a C program use gcc
  • to compile a C++ program use g++
  • examples:
      $ gcc -c parser.c
      $ gcc -g -c parser.c
        compiles, but does not link, parser.c;
        produces object file parser.o
      $ gcc parser.c
        compiles and links parser.c;
        produces executable a.out
      $ gcc parser.c main.o
        compiles and links parser.c with main.o;
        produces executable a.out
      $ gcc -o parse parser.c main.o
        compiles and links parser.c with main.o;
        produces executable parse

C compilation steps using gcc

  • gcc -E pgm.c
    • see stdio.h?
    • writes (expanded C source code) to stdout
  • gcc -S pgm.c: writes (assembly language code) to pgm.s
  • gcc -c pgm.c: writes (object code) to pgm.o
  • gcc pgm.o: links and writes (executable) to a.out
  • ar t /usr/lib64/libc.a | grep '^printf.o' (culls out the object code for printf)

gcc options graphically

C compilation steps graphically

Another view of the C compilation steps

(ref. O'Reilly)

file command

  • determines file type
  • syntax: file <filename(s)>
  • pretty sophisticated, does not just determine file type based on the file extension
  • for instance,
    $ file textfile.txt
    textfile.txt:   ascii text
    $ file cat.c
    cat.c:          c program text
    $ mv textfile.txt textfile.c
    $ file textfile.c
    textfile.c:     ascii text         $ file cat.i
    cat.i:          ascii text
    $ mv cat.i mycat.c
    $ file mycat.c
    mycat.c:          ascii text
    $ file cat.s
    cat.s:          assembler program text
    $ file cat.o
    cat.o:          ELF 32-bit MSB relocatable SPARC Version 1
    $ file a.out
    a.out:          ELF 32-bit MSB executable SPARC Version 1,
    dynamically linked, not stripped, no debugging information available

More on compiling with gcc

  • gcc -I <directory to add to search path for include files>; searched before the standard system include directories
  • gcc -l<abbrev>
    • explicitly links library corresponding to the library with the abbreviation following the -l
    • for instance, gcc -lm log10.c (link against the math library)
    • typically only C standard library linked by default
  • gcc -L <directory to add to search path for libraries>; those searched for -l
  • g++ (GNU C++ compiler) works the same way

Static vs. dynamic linking

  • static linking
    • compiles slower
    • larger executable
    • faster executable
    • less flexible
  • dynamic linking
    • compiles quicker
    • smaller executable (how much smaller?)
    • slower executable
    • more flexible (can upgrade library on-the-fly)
    • ls is dynamically linked against libc
      $ file -L /lib64/libc.so.6
      libc.so.6: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.4, not stripped
      $ file -L libc.so.6
      libc.so.6: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.4, not stripped
      be careful, can hose your system

Macros: the #define preprocessor directive

May be defined with or without arguments
  • a macro without arguments is processed like a symbolic constant (e.g., #define TRUE 1)
  • a macro with arguments is processed like a function call without the stack overhead
    • #define identifier(arg[, arg] ...) token-string
    • short routine which accepts arguments
    • upper-case convention
    • parenthesize if used with operators
    • #define SQUARE(X) ((X)*(X))
      #define PRINT(A, B) printf(#A ": %d, " #B ": %d\n", A, B)
      main() {
         int x = SQUARE(3);
         int y = SQUARE(x+1);
         PRINT(x, y);
    • output: x: 9, y: 100
    • #A in a replacement string of a macro
      1. replace by an actual parameter
      2. enclose it in quotes
    • expanded source code
      main() {
         int x = ((3)*(3));
         int y = ((x+1)*(x+1));
         printf("x" ": %d, " "y" ": %d\n", x, y);
  • macros are not quite as expressive as C++ templates; consider how things can go terribly awry
      (ref. [CPLS] p. 387)
      #define MAX(A, B) ((A) > (B)) ? (A) : (B)
      this generic works fine as long as the parameters are free of side-effects; MAX(x++, y) expands to ((x++) > (y) ? (x++) : (y)) and therefore `whenever the value of x is greater than that of y, x will be incremented twice' [CPLS] (p. 387)
  • 3 versions of swap function (call-by-value, call-by-reference, macro)
  • new macros

Macros vs. functions

  • speed
    • macros faster; in-line replacement
    • functions slower; have stack overhead
  • size of executable program; smaller if functions used; code appears once
  • software engineering principles (decomposition, functional overhead, and readability) vs. efficiency
  • other
    • functions can return value with return statement; macros cannot
    • recursion impossible with macros
    • macros are often more challenging to debug

Simple macro vs. constant

  • #define PI 3.14
  • #define TRUE 1
  • #define FALSE 0
    • just a macro, no memory allocated
    • C preprocessor replaces
  • float pi = 3.14;
  • typedef int bool;
  • which is more efficient in time or space?

Conditional compilation

#if, #ifdef, and #ifndef
  • conditionally adds C and/or preprocessor directives to a program
  • allows us to simulate multiple versions of a program from a single source file
  • helpful for controlling the inclusion/exclusion of echo prints for debugging
  • each of the conditional preprocessor directives evaluates a constant integer expression
  • #include "local.h"
    /* code ref. [C] (4-27) with minor modifications */
    /* we would normally indent the body of conditional,
       but not permitted here */
    #if vax || u3b || u3b5 || u3b2
    #define MAGIC 330
    #define MAGIC 500
    #ifdef LIMIT
    #undef LIMIT
    #define LIMIT 1000
    /* when return type omitted, int assumed */
    f() {
       /* allowed to indent here */
    /* to use debugging statements, #define DEBUG
       anywhere before #ifdef finds it;
       or use gcc -DDEBUG pgm.c */
    #ifdef DEBUG
       printf ("x is %d\n", x);
       printf ("y is %d\n", y);
       /* allowed to indent here */
  • C preprocessor is not as intelligent as the C compiler when evaluating

Error handling


  • lint
  • gdb
  • ddd
  • truss
  • leaks
  • heap
  • use conditional compilation: #ifdef and #endif, #define DEBUG or gcc -DDEBUG


    [C] C Language for Experienced Programmers, Version 2.0.0, AT&T, 1988.
    [CPL] B.W. Kernighan and D.M. Ritchie. The C Programming Language. Prentice Hall, Upper Saddle River, NJ, Second edition, 1988.
    [UPE] B.W. Kernighan and R. Pike. The UNIX Programming Environment. Prentice Hall, Upper Saddle River, NJ, Second edition, 1984.
    [USP] K.A. Robbins and S. Robbins. UNIX Systems Programming: Concurrency, Communication, and Threads. Prentice Hall, Upper Saddle River, NJ, Second edition, 2003

Return Home