COP 3402 Lecture -*- Outline -*- * Separate Compilation Advert: We'll look at the basics of dividing up your program into separate files, and see how to get the parts to communicate. ------------------------------------------ SEPARATE COMPILATION Which is better? a. 1 file with 500 lines of code b. 7 files with 500 lines of code Why? ------------------------------------------ b seems better, as we get more: - logical structure - isolation of parts - reuse of (some) parts - easier for multiple people to work on it Now let's look at how to structure programs in C ** declarations vs. definitions ------------------------------------------ DECLARATIONS AND DEFINITIONS def: a *declaration* describes a name's def: a *definition* describes 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; ------------------------------------------ ... type (or usage) with enough information to check how it is to be used (it's a specification) Note: a declaration describes attributes of a name (e.g. type) ... the details of an implementation Note: a definition allocates storage, or provides an implementation of a function Note: in C you can't use "extern" on a definition! See the files decldef.h (for the declarations) and decldef.c (for the definitions) *** Rules for declarations and definitions ------------------------------------------ RULES FOR DECLARATIONS AND DEFINITIONS There must be only There can be The *scope* of a declaration extends ------------------------------------------ ... 1 *definition* of a name in a scope (i.e., in an area of a program's text) ... multiple declarations of a name, as long as they are all the same ... from the point of declaration to the end of the file or block where the declaration occurs (i.e., from { to } that defines a block) Q: Why is only one definition allowed for a name? So the compiler or linker doesn't have to pick the "right" one **** forward declarations ------------------------------------------ FORWARD DECLARATIONS NEEDED IN C What does a compiler say about: void parseExpr() { /* ... */ parseTerm(); /* ... */ } void parseTerm() { /* ... */ parseExpr(); /* ... */ } How to fix it? ------------------------------------------ ... an error for the first call to parseTerm(), as it's not declared where it is first used! ... declare parseTerm() before it is used with extern void parseTerm(); or just: void parseTerm(); *** simultaneous declaration and definition ------------------------------------------ SIMULTANEOUS DECLARATION AND DEFINITION For local variables and functions not used elsewhere: Example: Every definition is also a declaration ------------------------------------------ ... can declare and define them all at once static int two() { return 2; } int four() { return 4; } ... but not vice versa (declarations are not always definitions) ** Compiler options *** default: compile and link a complete program ------------------------------------------ DEFAULT COMPILATION: ENTIRE PROGRAM gcc myprog.c produces an executable in a.out What if there are multiple .c files? ------------------------------------------ ... pass them all to the compiler gcc helper.c myprog.c makes them compiled together to make a.out Q: How does this work? Compilation turns C into object code (by way of assembler) Linking makes all the functions and data in your program work together, tells each function where the other functions are ------------------------------------------ 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"; } ------------------------------------------ See file sayHelloProgram.c Q: How do you compile and link this into an executable program, when all the code is contained in "sayHelloProgram.c"? gcc sayHelloProgram.c produces file a.out gcc -o sayHelloProgram sayHelloProgram.c produces file sayHelloProgram (or sayHelloProgram.exe on Windows) *** saving results of file compilation Q: Why would you want to save the results of compiling a single file? If compilation takes a long time so when make a mistake don't have to recompile everything: faster debugging If we want to sell a module (or its functionality) to a client, but not give them the source code **** example See the files with the names given below ***** main module ------------------------------------------ // FILE sayHelloMain.c #include #include "sayHello.h" int main(int argc, char *argv[]) { sayHello(); return EXIT_SUCCESS; } ------------------------------------------ Q: Why doesn't this main program need to include stdio.h? Because it isn't doing any printing... (but it needs stdlib.h for EXIT_SUCCESS) ***** sayHello module ------------------------------------------ // 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()); } ------------------------------------------ Q: Why does sayHello.h not need to include stdio.h? Because it doesn't need to use that, it is only declaring sayHello Q: Why does sayHello.c include sayHello.h? So that the compiler can check the consistency of sayHello's declaration and definition types Q: Why does sayHello.c need to include "who.h"? So it has a declaration of who() to use in checking the call ***** who module ------------------------------------------ // 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 of the example ------------------------------------------ SEPARATE COMPILATION Compile each .c file to an object file: gcc -c sayHelloMain.c sayHello.c who.c ------------------------------------------ ... produces sayHelloMain.o sayHello.o and who.o **** using the Unix "make" tool ------------------------------------------ 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) ------------------------------------------ Explain the Makefile rules The one for sayHelloMain.o is a simple one, and is actually needed (because there is no .h file for it) The implicit rule (%.o: ... and the next line) compiles f.c ($<, the first dependency) by running the compile command given to produce f.o, whenever f.c or f.h is newer than f.o, The rule for making sayHello as a program (sayHello: $(OBJECTS) and the next line) says that it depends on the .o files (the names in $(OBJECTS)) and so those have to be made up to date first, then the .o files are all linked together by the compiler ($(CC)) Q: What would happen if we give the compiler the same .o file twice? There would be multiple definitions of the same name. *** linking ------------------------------------------ LINKING def: *linking* brings together several separately compiled files into one executable Commands that do linking: ld, gcc What it does: ------------------------------------------ ... matches declarations and definitions for each name There must be at one definition for each declared name What if there is no definition? then get a complaint that the definition is missing What if there is no declaration? no complaint unless the name is used without being declared