The Compilation Process Preprocessing: o processes include-files, - - PDF document

the compilation process
SMART_READER_LITE
LIVE PREVIEW

The Compilation Process Preprocessing: o processes include-files, - - PDF document

4/1/14 The Compilation Process Preprocessing: o processes include-files, conditional compilation and macros. Compilation: Writing Large Programs o takes the output of the preprocessor and the source code, and generates assembler


slide-1
SLIDE 1

4/1/14 ¡ 1 ¡

Writing Large Programs

Based on slides from K. N. King and Dianna Xu Bryn Mawr College CS246 Programming Paradigm

The Compilation Process

  • Preprocessing:
  • processes include-files, conditional compilation and macros.
  • Compilation:
  • takes the output of the preprocessor and the source code, and

generates assembler source code, i.e., making .s file.

  • Assembly:
  • takes the assembly source code and produces an assembly

listing with offsets. The assembler output is stored in an

  • bject file, i.e., .o file.
  • Linking:
  • takes one or more object files or libraries as input and

combines them to produce a single (usually executable) file.

Compilation Compiler/Assembler and Linker

  • Compile green.o: cc –c green.c
  • Compile blue.o: cc –c blue.c
  • Link together: cc green.o blue.o

Multiple Source Files

  • The decision to divide your program into multiple

source files is not only a matter of size.

  • One and only one .c file must contain a main.
  • Functions that handle some common aspects of a

program should be grouped into the same file.

  • main, data structure implementation (i.e. linked

list), I/O, utilities, display/GUI, etc

Header Files

  • How can a function in one file call a function

that’s defined in another file?

  • How can a function access an external variable in

another file?

  • How can two files share the same macro definition
  • r type definition?
  • The #include directive tells the preprocessor to

insert the contents of a specified file.

slide-2
SLIDE 2

4/1/14 ¡ 2 ¡

Sharing Function Prototypes

  • To share a function among files, we put its

definition in one source file, then put declarations in other files that need to call the function.

  • If a function is to be called in more than one file,

put its prototype into a .h.

  • Always include the .h with f’s prototype in

the .c that contains f’s definition.

  • For any .c, always include your own .h.
  • A header file should never contain function

definitions.

The #include Directive

  • The #include directive has two primary forms.
  • For C’s own library:
  • #include <filename>: Search the directory (or

directories) in which system header files reside.

  • For all other header files:
  • #include "filename": Search the current

directory, then search the directory (or directories) in which system header files reside.

  • The difference between the two has to do with how

the compiler locates the header file.

The #include Directive

  • Don’t use brackets when including header files

that you have written:

#include <myheader.h> /*** WRONG ***/

  • The preprocessor will probably look for

myheader.h where the system header files are kept.

The #include Directive

  • The file name in an #include directive may

include information that helps locate the file, such as a directory path or drive specifier:

#include "c:\cprogs\utils.h" /* Windows path */ #include "/cprogs/utils.h" /* UNIX path */

  • Although the quotation marks in the #include

directive make file names look like string literals, the preprocessor doesn’t treat them that way.

The #include Directive

  • It’s usually best not to include path or drive

information in #include directives.

  • Bad examples of Windows #include directives:

#include "d:utils.h" #include "\cprogs\include\utils.h" #include "d:\cprogs\include\utils.h"

  • Better versions:

#include "utils.h" #include "..\include\utils.h"

Sharing Variables

  • Variables shared between files are defined in one

file, and declared in all files that need to access it.

  • Definition of a variable causes the compiler to set

memory aside

  • extern
  • extern int i, a[];
  • extern informs the compiler that the variables i and

a are defined elsewhere.

slide-3
SLIDE 3

4/1/14 ¡ 3 ¡

extern variables

  • extern declarations often go in to a header file.
  • The variable must have one (and only one) definition

among all files.

  • int x;
  • Any file that wishes to access a variable that is defined in

another file must declare such a variable as extern

  • extern int x;
  • When we use extern in the declaration of an array, we

can omit the length of the array:

extern int a[];

  • Since the compiler doesn’t allocate space for a at this

time, there’s no need for it to know a’s length.

Example: Arithmetic Expression

Infix notation:

  • Operators are written between the operands they act on;

e.g., “2+2”.

  • Parentheses surrounding groups of operands and
  • perators are used to indicate the intended order in

which operations are to be performed.

  • In the absence of parentheses, precedence rules

determine the order of operations. E.g., Because “-” has lower precedence than “*”, the infix expression “3-4*5” is evaluated as “3-(4*5)”, not as “(3-4)*5”. If you want it to evaluate the second way, you need to parenthesize.

Example: Arithmetic Expression

Postfix notation (a.k.a. Reverse Polish Notation (RPN))

  • Operators follow their operands, e.g., adding three

and four is written as “3 4 +” rather than “3+4”.

  • If there are multiple operations, the operator is given

immediately after its second operand; so the expression written “3 - 4 + 5” in infix notation would be written “3 4 - 5 +” in RPN: first subtract 4 from 3, then add 5 to that.

Example: Arithmetic Expression

Advantage of RPN: The postfix notation obviates the need for parentheses that are required by infix.

  • With infix notation, “3-4*5” can be written as “3-(4*5)”,

that means something quite different from “(3-4)*5”.

  • In postfix, the former is written as “3 4 5 * -”, which

unambiguously means “3 (4 5 *) -”, and the latter is written as “3 4 - 5 *”, which unambiguously means “(3 4 -) 5 *”.

  • Postfix notation is easier to parse by computer than infix

notation, but many programming languages use infix due to its familiarity.

Example: Arithmetic Expression

  • Interpreters of RPN are often stack-based.
  • General idea:
  • Operands are pushed onto a stack, and when an
  • peration is performed, its operands are popped from

a stack and its result pushed back on.

  • At the end, the value of the postfix expression is on

the top of the stack.

  • Since all the needed stack operations take constant

time, and the evaluation algorithm is quite simple, RPN expressions can be evaluated quickly and easily.

Example (RPN)

  • How the expression 30 5 - 7 *: will be evaluated:
  • 1. Push 30 onto the stack.
  • 2. Push 5 onto the stack.
  • 3. Pop the top two numbers from the stack, subtract 5

from 30, giving 25, and then push the result back

  • nto the stack.
  • 4. Push 7 onto the stack.
  • 5. Pop the top two numbers from the stack, multiply

them, and then push the result back onto the stack.

  • The stack will now contain 175, the value of the

expression.

slide-4
SLIDE 4

4/1/14 ¡ 4 ¡

Example

  • The implementation of a stack-based calculator:
  • 1 2 – 4 5 + * ==> (1-2) * (4+5)
  • Two globals:
  • double s[MAX];
  • int sp = 0;
  • Stack related operations
  • I/O operations

Program Structure

#include <stdio.h> enum _bool {FALSE, TRUE} Bool;

main.h init.c #include "init.h" #include "main.h" void init() {} #include <stdlib.h> #incluce "init.h" #include "io.h" #include "stack.h" #include "main.h" int sp = 0; double s[MAX]; int main () {} main.c #include "stack.h" #include "main.h" extern int sp; extern double s[]; void push(double d){} double pop(){} double top(){} int isempty(){} int isfull(){} stack.c #include <ctype.h> #include "io.h" #include "main.h“ Optype getop(char s[]){} io.c void init(); init.h #define MAX 100 void initstack(); void push(double d); double pop(); double top(); int isempty(); int isfull(); stack.h #define MAXOP 100 typedef enum _optype {NUM='n', PLUS='+', MINUS='-', MULT='*', DIV='/', NEWLINE='\n'} Optype; Optype getop(char s[]); io.h

main.c

int main() { Optype t; char str[MAXOP]; double d; init(); while((t = getop(str)) != EOF) { switch(t) { case NUM: push(atof(str)); break; case PLUS: d=pop(); push(pop()-d); break; case MINUS: push(pop()-pop()); break; case NEWLINE: printf("\t%.2f\n", pop()); break; default: fprintf(stderr, "Error, unknown command %s\n", str); break; } } return 0; }

io.c

int getop(char s[]) { int i=0; char c; while ((s[0] = c = getchar()) == ' ' || c == '\t') ; s[1] = '\0'; if (!isdigit(c) && c != '.') /* not a number */ return c; if (isdigit(c)) /* collect int part */ while(isdigit(s[++i] = c = getchar())) ; if (c == '.') /* collect fractional part */ while(isdigit(s[++i] = c = getchar())) ; s[i] = '\0'; if (c != EOF) ungetc(c, stdin); return NUM; }

  • If a source file includes the same header file twice,

compilation errors may result.

  • When prog.c is compiled, file3.h will be

compiled twice.

Protecting Header Files

file3.h #include “file3.h” file1.h file2.h prog.c #include “file3.h” #include “file1.h” #include “file2.h”

Protecting Header Files

  • Including the same header file twice doesn’t

always cause a compilation error.

  • If the file contains only macro definitions, function

prototypes, and/or variable declarations, there won’t be any difficulty.

  • If the file contains a type definition, however,

we’ll get a compilation error.

slide-5
SLIDE 5

4/1/14 ¡ 5 ¡

Protecting Header Files

  • To protect a header file, we’ll enclose the contents
  • f the file in an #ifndef-#endif pair.
  • How to protect the boolean.h file:

#ifndef BOOLEAN_H #define BOOLEAN_H #define TRUE 1 #define FALSE 0 typedef int Bool; #endif

#error Directives in Header Files

  • #error directives are often put in header files to

check for conditions under which the header file shouldn’t be included.

  • Suppose that a header file uses a feature that didn’t

exist prior to the original C89 standard.

  • An #ifndef directive that tests for the existence
  • f the __STDC__ macro:

#ifndef __STDC__ #error This header requires a Standard C compiler #endif

Building a Multiple-File Program

  • Makefile
  • List all source files to be compiled and linked
  • Lists dependencies among all files

calc: main.o init.o io.o stack.o cc –o calc main.o init.o io.o stack.o main.o: main.h init.h io.h stack.h cc –c main.c

  • target: list of files
  • build/rebuild command

Dependency Graph

  • The principle by which Make operates
  • In writing a Makefile, you are specifying the

dependencies needed to build your executable

Updates According to Dependencies

  • Suppose you edited io.c
  • Make realizes the update based on timestamp of

io.c

  • Make will recompile io.o and relink

project1 automatically

Dependencies in Make syntax

  • target:

source file(s) command (tabs in front!!)

slide-6
SLIDE 6

4/1/14 ¡ 6 ¡

Makefile Flags/Macros

  • CC = gcc
  • CFLAGS = -g -Wall
  • -D – allows the value of a named macro to be

specified

  • -DDEBUG=1 == -DDEBUG
  • -UD – undefines a named macro
  • $(CC) $(CFLAGS) –DDEBUG –c main.c

Programming-in-the-large

  • Most full-featured programs today are at least

100,000 lines long.

  • Careful design
  • Do not limit any possible future extensions
  • Extreme attention to style
  • Lots of people are going to be working on it
  • Planning for maintenance
  • It IS going to be modified many times

Module-based Programming

  • A module is a collection of services (functions),

some of which are available to other parts of the program (clients) through an interface (header file).

  • Abstraction – carefully designed modules should

hide implementations

  • Reusability – in order to maximize this feature,

modules should be as basic and as minimal as possible

  • Maintainability – easier to locate and fix bugs

Cohesion and Coupling

  • High cohesion
  • Elements in each module should be closely related –

(i.e. all the stack functions)

  • Low coupling
  • Modules should be as independent of each other as

possible

Summary

  • Learn to master the structure of a multiple-file

program.

  • Properly setting up the “bones” of a program will

extend the initial set-up time, but one ends up saving time and frustration in the end.

  • Think 3 times before you start, and if you have

partners, meet 5 times before you code.