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)


Overview

  • 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)


Compiling

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
    #else
    #define MAGIC 500
    #endif
    
    #ifdef LIMIT
    #undef LIMIT
    #endif
    #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);
    #endif
       /* allowed to indent here */
       ...
    }
    
  • C preprocessor is not as intelligent as the C compiler when evaluating


Error handling


Debugging

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


References

    [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