SEPARATE COMPILATION Which is better? a. 1 file with 500 lines of code b. 7 files with 500 lines of code Why? more logical organization isolate parts reuse some parts DECLARATIONS AND DEFINITIONS def: a *declaration* describes a name's type or usage with enough info. to check that def: a *definition* describes the details of its implementation allocates storage or provides an implementation of a function Declaration examples: int somefun(); extern int from_roman(char *num); static int three(); extern int counter; struct complex { double x, y; }; enum sign {positive, zero, negative}; typedef struct { double r,theta;} cmplx; Definition examples: int somefun() { return three(); } int from_roman(char* num) { int val = 0; /* ... */ return val; } static int three() { return 3; } int counter; struct complex coordinates; enum sign the_sign = positive; cmplx i; RULES FOR DECLARATIONS AND DEFINITIONS There must be only 1 definition of a name There can be multiple declarations of a name as long as they are all the same. The *scope* of a declaration extends from the point of declaration to the end of the file or block where it's declared int x; // the different x { int x; // a block /* ... */ x = 3; } x // a different x FORWARD DECLARATIONS NEEDED IN C What does a compiler say about: // fixed by extern void parseTerm(); // forward decl. void parseExpr() { /* ... */ parseTerm(); /* ... */ } void parseTerm() { /* ... */ parseExpr(); /* ... */ } How to fix it? add a "forward declaration" in C those are "extern" declarations SIMULTANEOUS DECLARATION AND DEFINITION For local variables and functions not used elsewhere: Example: static int two() { return 2; } int four() { return 4; } int five = 5; Every definition is also a declaration but not vice versa DEFAULT COMPILATION: ENTIRE PROGRAM gcc myprog.c produces an executable in a.out What if there are multiple .c files? EXAMPLE ENTIRE PROGRAM // file sayHelloProgram.c #include #include extern void sayHello(); extern const char *who(); int main(int argc, char *argv[]) { sayHello(); return EXIT_SUCCESS; } void sayHello() { printf("Hello, %s!\n", who()); } const char *who() { return "class"; } // FILE sayHelloMain.c #include #include "sayHello.h" int main(int argc, char *argv[]) { sayHello(); return EXIT_SUCCESS; } // FILE sayHello.h #ifndef _SAYHELLO_H #define _SAYHELLO_H extern void sayHello(); #endif // FILE sayHello.c #include #include "sayHello.h" #include "who.h" void sayHello() { printf("Hello, %s!\n", who()); } // FILE who.h /* $Id$ */ #ifndef _WHO_H #define _WHO_H extern const char *who(); #endif // FILE who.c #include "who.h" const char *who() { return "class of modular programmers"; } SEPARATE COMPILATION Compile each .c file to an object file: gcc -c sayHelloMain.c sayHello.c who.c UNIX MAKE TOOL Rules in a file named "makefile" (or "Makefile") Makefile rules: # names CC = gcc CFLAGS = -g -std=c17 -Wall OBJECTS = sayHelloMain.o sayHello.o who.o sayHelloMain.o: sayHelloMain.c $(CC) $(CFLAGS) -c sayHelloMain.c %.o: %.c %.h $(CC) $(CFLAGS) -c $< sayHello: $(OBJECTS) $(CC) $(CFLAGS) -o sayHello \ $(OBJECTS) LINKING def: *linking* brings together several separately compiled files into one executable Commands that do linking: ld, gcc What it does: EXAMPLES OF MODULARITY IN REAL LIFE Electric plugs, can plug in anything: - toaster, - TV - computer - phone charger - coffee maker Cooking ingredients: - meat (chicken, etc.) - butter - fruits - spices What makes a system modular? a standardized interface between users (clients) and the thing (implementation) EXAMPLES OF MODULARITY IN SOFTWARE SQL queries work on: - many different databases - on many different computers - give same results TCP/IP network protocol: - connects many different systems (different hardware, different OS) - clients include: browsers, email, ... Mathematical libraries: - correct computation of trig. functions - on many different computers - approximately same results MODULARITY IN SOFTWARE 2 roles: - client: uses the software (calls functions, may read/write variables) - implementation: provides the service/computation Agreed upon interface: - names and how they can be used (syntax and semantics) MODULES IN PROGRAMMING A module: - provides a service/computation - hides some design/implementation decisions In programming want to hide decisions about: - algorithms - data structures - how algorithms and data structures work together INFORMATION HIDING Some information is kept from clients and is only known to the implementation Ideally, information about decisions that are likely to change e.g.: - info that needs to be stored/remembered - data structures - algorithms PROG. LANG. SUPPORT FOR INFORMATION HIDING Limit clients: - discovery of secrets - manipulation of secrets - creation of secrets Mechanisms: - function mechanisms - hides algorithms - scope limitations - keeps names hidden in some area of the program - export/import of names - keeps some names private, - other public names can be used - type limitations - implement and use new types only in certain places INFO. HIDING IN C Modules are files (usually .c files) Names private to module: static Names available to clients: not static EXAMPLE IN C: SET OF STRINGS // FILE string_set.h #ifndef _STRING_SET_H #define _STRING_SET #include // Initialize the string set extern void string_set_init(); // Put s into the set extern void string_set_add(const char *s); // Is s in the set? extern bool string_set_has(const char *s); // ... #endif ONE IMPLEMENTATION // FILE string_set.c #include #include #include #include #include "string_set.h" typedef struct element_s { const char *val; struct element_s *next; } element; static element *elements; // invariant: elements is // a singly-linked list with no cycles // invariant: no duplicates // in the list of elements // Initialize the string set void string_set_init() { elements = NULL; } // Put s into the set void string_set_add(const char *s) { if (!string_set_has(s)) { // s is not already in the list element *newelem = malloc(sizeof(element)); if (newelem == NULL) { fprintf(stderr, "No space!\n"); exit(EXIT_FAILURE); } newelem->val = s; newelem->next = elements; elements = newelem; } assert(string_set_has(s)); } // Is s in the set? extern bool string_set_has(const char *s) { element *p = elements; while (p != NULL) { if (strcmp(p->val, s) == 0) { return true; } p = p->next; } return false; } // ... ABSTRACT DATA TYPES def: an *abstract data type* (ADT) is a set of values (objects) and operations on them (functions/methods) with some specified behavior Benefits: - clients can ignore the implementation details so their programming is more efficient - easier to change/maintain programs Drawbacks: - programs may be slightly slower because data is manipulated by functions However: it's easier to optimize data structures