220 likes | 247 Views
Learn how to compile a multi-module program in C using Makefile rules, macros, implicit build rules, and dependency trees. Understand the principles of header files and how to link math libraries. Implement a Reverse Polish Notation (RPN) calculator program.
E N D
Homework • K&R chapter 4
Multi-Module Programs(Glass & Ables Linux Book p. 411) • To compile all .c files and create executable gcc main.c getop.c stack.c getch.c –o calcit • To compile only one and create executable with objects of previously compiled files gcc main.c getop.o stack.o getch.o –o calcit • To build an executable from a complex set of separate files skipping any unneeded steps make –f makefile (makefile documents build rules) (-f option allows you to specify makefile name)
Dependency Tree for a Build calcit main.o getop.o stack.o getch.o main.c getop.c stack.c getch.c getop.h stack.h getch.h
Make (Glass & Ables p. 412) • makefile rule format target: dependency list for needed files command list to create target • Example: calcit: main.o getop.o stack.o getch.o gcc main.o getop.o stack.o getch.o -o calcit main.o: main.c getop.h stack.h gcc -c main.c ….. Just compile, no linking
Macros in Make • Macro CC is defined early in the makefile with the line: CC = gcc • Then using $(CC) causes substitution calcit: main.o getop.o stack.o getch.o $(CC) main.o getop.o stack.o getch.o -o calcit • You can override the macro in the make command linemake CC=gccx86
Make • The user can type the command "make", or make with a named target, for example: “make getop.o” to cause the rules leading up to the target getop.o to be executed, if needed. • When user types "make" without a target name, the FIRST rule listed will be the default target constructed. Thus, the "calcit" rule should be the first rule in the makefile
Make • At the end of the makefile, there is a rule with a target name "clean". clean: rm *.o • Since "clean" is not a file: • There is no dependency list (ages of files don't matter) • It is called when you give the command "make clean". • It deletes .o files to reduce the clutter in the directory.
Implicit Build Rules • makefile can use implicit rule to build intermediate files, e.g. calcit: main.ogetop.ostack.ogetch.o gccmain.ogetop.ostack.ogetch.o -o calcit main.o: main.cgetop.hstack.h getop.o: getop.cgetop.hgetch.h …… • Since there is no explicit rule, the files main.o, getop.o … will be built using built-in implicit rules. For C, it is using a compiler named CC with options CFLAGS, e.g CC= gcc CFLAGS = -m32 calcit: main.ogetop.ostack.ogetch.o $(CC) $(CFLAGS) main.ogetop.ostack.ogetch.o -o calcit main.o: …. • You can bypass the implicit rules by defining pattern rules (see https://www.gnu.org/software/make/manual/html_node/Implicit-Rules.html for details) No build rules
Header Files • Principles for contents of .h files • Each .h file includes all of the function prototypes and symbolic constants needed to invoke those functions int function (int ); /* no code for function */ #define VALUE0_FOR_ARGUMENT 0 • No statements that allocate memory in .h files!! • Don’t use a single “calc.h” file (K&R, pg 82) • A reusable solution shown in “Dependency Tree” • An xxx.h file for each xxx.c source file of functions • #include getop.h in getop.c and all source files that call functions provided in the getop.c source file (i.e. main)
Header Files – Math Library • When you want to use math functions Example: SIN( ) or SQRT( ) • Write #include <math.h> • But also invoke compiler with special flag: gcc . . . -lm (lm means library, math) *Note: -lm has to be at the end
Reverse Polish Notation(RPN) • For hw4, you need to know how to convert an algebraic (infix) expression to RPN • Some calculators work this way, e.g H-P • Algebraic (123 + 21) * (567 – 432) • RPN 123 21 + 567 432 - * • Operand1 Operand2 Operator
Reverse Polish Notation(RPN) • Read from left and push the numbers in to the stack. • When you read an operator, then pop 1 (or 2) number(s) from stack and do the operation and push the result in to the stack … • 15 7 1 1 + − ÷ 3 × 2 1 1 + + − = 5
Reverse Polish Notation Enter: 123 21 + 567 432 - * Stack States: 123 21 144 567 432 135 19440 Empty 123 144 567 144 144
RPN Calculator Program • Pseudo-code for RPN calculator (See K&R, pg 75) while (next the character is not an EOF) if (number) push it on the stack else if (operator) pop operand(s) /* may be one or two */ do operation push result back on the stack else if (newline) pop and print value from top of stack else error
RPN Calculator Program • See K&R, page 76 and then page 78 • Operand order is not important for some ops + , *, &, | • Operand order is important for some ops! -, /, % • There is only one operand for some ops ~, !
RPN Calculator Program • Break down the pseudo-code into functions • push / pop • get next number or operator, i.e. get op ( ) • getch / ungetch • Both push and pop access a set of variables for the stack static external • Both getch and ungetch access a buffer for characters from stdin static external
Communication Between Functions • Sometimes closely related functions need to share access to variables in situations where they never call each other and therefore can’t pass arguments to the shared variables • Ex: push and pop share the stack variables • Declare “static” variables inside source file for the functions (before / outside the braces for any of the functions) “Static External” per K&R • Makes them “globally” accessible to all functions in the source file
Back to RPN Calculator Program • Look at getop, getch, ungetch functions • In getop( ), we try to input a char string for a number. It uses getch() to read a char. If it is a number, how do we know where the number ends? • But if the next char is an operator (e.g. '+'), then we know the string of numbers has ended. But, we have to put the operator back in stdin. We do this with ungetch( ) • Next invocation of getch() will read the operator from stdin.
getop.c /* * getop.c * * gets next token: operator or numeric operand */ #include <stdio.h> #include <ctype.h> #include "calc.h" int getop(char s[]) { int i, c; while ((s[0] = c = getch()) = = ' ' || c = = '\t') ; s[1] = '\0'; if (!isdigit(c) && c != '.') return c; /* not a number */
getop.c (cont’d) /* collect integer part in string s */ i = 0; if (isdigit(c)) while (isdigit(s[++i] = c = getch())) ; /* collect fractional part in string s */ if (c = = '.') while (isdigit(s[++i] = c = getch())) ; s[i] = '\0'; if (c != EOF) ungetch(c); return NUMBER; }
getch.c #define BUFSIZE 100 static char buf[BUFSIZE]; /* buffer for ungetch */ static int bufp = 0; /* next free position in buf */ /* get a possibly pushed back char from stdin */ int getch(void) { return (bufp > 0) ? buf[--bufp] : getchar( ); }
ungetch.c /* push char back for getch later */ void ungetch(int c) { if (bufp >= BUFSIZE) printf("ungetch: too many characters in buffer.\n"); else buf[bufp++] = c; }