0% found this document useful (0 votes)
10 views38 pages

Modular Programming

The document discusses the importance of modular programming in managing large software projects, emphasizing the need to break down functionality into smaller, manageable chunks. It covers key concepts such as module interfaces, implementation, compilation, and the use of Makefiles for efficient code management. The document also highlights the significance of protecting header files from multiple inclusions and the benefits of selective compilation to optimize the development process.

Uploaded by

woxine32767
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views38 pages

Modular Programming

The document discusses the importance of modular programming in managing large software projects, emphasizing the need to break down functionality into smaller, manageable chunks. It covers key concepts such as module interfaces, implementation, compilation, and the use of Makefiles for efficient code management. The document also highlights the significance of protecting header files from multiple inclusions and the benefits of selective compilation to optimize the development process.

Uploaded by

woxine32767
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

XJCO1921 PROGRAMMING PROJECT

Modular Programming
Zheng Wang

1
Software is big
KLOC = 1000 lines of code; MLOC = 1,000,000 lines of code
Bar code scanners 10-50KLOC
4-speed transmissions (motor 20KLOC
control)
Air traffic ground system 130KLOC
ATM 600KLOC
Call router 2.1MLOC
B-2 Stealth Bomber 3.5MLOC
Windows OS 60M+LOC
Break it up
• Big programs get BIG.
• Most software is many millions of lines of code.
• Organisation is important

3
Let’s think a bit smaller
• Separate functionality into smaller chunks
• Use functions whenever possible
• Avoids repeated code
• Sometimes requires more “communication”
• Organise connections between chunks
• How does this statement get this value?
• Should this loop contain a function call, or should the
• function contain the loop?
• Should I group these together, or do them separately?

4
Scale
• We are thinking today about the code as a whole
• The purpose of a module should be clear and separated
from other modules
• Modularity also applies to functions
• If you can’t describe what your function does in one
sentence you should consider splitting it

5
Module Terminology

• Module Interface (.h)


• The formal description of what is in it
• Module implementation (.c)
• Human-readable description of the details
• Object file (.o)
• Machine-readable description of the details
Interface & Implementation

• If I edit an implementation (.c) file


• It shouldn't affect other modules
• Since they don't have access to the .c file
• But if I edit a header (.h) file
• It should affect any module that uses it
• So we set up the headers first
• Then try not to modify them at all
Compilation pipeline
Helloworld.c

Pre-processing

Compilation

Linking
Pre-processing
The pre-processor expands all the directives that start with
“#”, such as:
#include //importing libraries

#define //creating macros

//conditional comilation:

#ifdef
#if
#ifndef
#elif
#endif
#else
Pre-processing - Example
#define N 10
int main()
int main() {
{ int x = 10;
int x = N; int y = 10;
int y = N; return 0;
return 0; }
}
What does the compiler see? Use: gcc -E file.c
E-commercial shopping website
• A toy online order system (similar to Taobao and
ebay)
• Need to manage customer orders
• We define our key data structures and functions (or
APIs)in a header file
customer.h

11
customer.h
typedef struct customer {
int id;
char *name; Customer data
int n_orders;
order_t *orders; structure
} customer_t;
Functions

int customer_new (customer_t **);


int customer_read (id_t const *, customer_t **);
int customer_update (customer_t const *);
int customer_delete (customer_t *);
int customer_retrive_orders (customer_t *, int *);
12
Header files
• Typically contain
• Data structures
• Function prototypes
• Compilers require function definitions for checking at
compile time
• Functions implemented in another source file, e.g.,
customer.c
• Data struct definitions can be separated from the
body of the code
13
Protecting headers from multiple inclusions
Header files are prone to multiple inclusions:
// customer.h // customer_utils.h

typedef struct customer{ void customer_enqueue_order


... (customer_t);
} customer_t;

What problem we may have?

// main.c //customer_util.c
Protecting headers from multiple inclusions

// customer.h
error: redefinition of ‘struct customer’
typedef struct customer{
...
} customer_t;
Headers’ Guards
//in customer.h
The second inclusion
#ifndef _CUSTOMER_H_
#define _CUSTOMER_H_
will be false, hence
the data structure
typedef struct customer {
int id; will not be defined
char *name;
int n_orders; again
order_t *orders;
} customer_t;

#endif
16
Manual Compilation

• Every time we compile, we type gcc ...


• gcc –g –O3 –o helloworld helloworld.c

• This gets old pretty fast


• So we use the terminal history
• Or we put it in a script
• But it's wasteful of compile time
• Since we can reuse unchanged modules
Quick Recap on Modularity

• If you have 600 .c files


• You don't want to recompile all of them
• It can take all day (literally!)
• And you want to break the program down
• To make it easier to think about
• So we keep already compiled object files (.o)
• And relink them to get the new executable
Linking

Compilation
Linking

You can give the compiler either C files, in which case it compiles them, or .o files, in which case it only links them:

gcc mod1.o mod2.o main.o -o exec


Compilation

• We can stop compilation before linking


• Using the –c flag for gcc
• And saving to a '.o' file
• gcc –c –g –O3 –o helloworld.o helloworld.c

• Then use objdump to examine it


• objdump –h helloworld.o (headers)
• objdump –Sl helloworld.o (disassembly & source)
• objdump –syms helloworld.o (symbols)
Selective Compilation

• If the error module hasn't changed


• Why should we recompile it?
• Doesn't seem like a big idea for small code
• But for big code it becomes significant
• This is one of the ideas behind modules
• We only want to recompile changed modules
• Which is an "if" statement at heart
Dependencies

• Executable changes when the modules do


• helloworld depends on helloworld.o
• Object changes when files it uses do
• helloworld.o depends on helloworld.c (potneital some other head files xx.h)
Make

•Make is a utility to automate this


•A specialised scripting language
•Based on:
•Targets: files to be remade
•Dependencies: files they depend on
•Commands: details of how to make them
•Plus lots of standard conveniences
Make
• Good for modular programming
• The Makefile is a plain text build script
• Efficiently deals with modular code projects by
managing the (slower) compilation process to only
recompile modified code

24
A Trivial Makefile
# Trivial Makefile for helloworld.c
# Z Wang, XJCO 1921
# 1/3/21
#
helloworld: helloworld.c
gcc –o helloworld helloworld.c
A Trivial Makefile
# Trivial Makefile for helloworld.c
# Z Wang, XJCO 1921
# 1/3/21
# COMMENTS
helloworld: helloworld.c
gcc –o helloworld helloworld.c
A Trivial Makefile
# Trivial Makefile for helloworld.c
# Z Wang, XJCO 1921
# 1/3/21 Target Prerequisites
Target
#
helloworld: helloworld.c Dependence line
gcc –o helloworld helloworld.c
What’s happening here?

Source File
helloworld.c
Compiler

gcc
Target
Depends on
helloworld
A Trivial Makefile
# Trivial Makefile for helloworld.c
# Z Wang, XJCO 1921
# 1/3/21 Time stamp on this file
is compared to the time stamp on this file.
#
helloworld: hellowrold.c Dependency Line
gcc –o helloworld helloworld.c

If the target’s time stamp is earlier than the prerequisite’s time stamp, the rule below
this dependency line is executed.
So what is a time stamp?
• The time that the file was last modified.
• If the target’s time is earlier than the prerequisite’s
time, this means that the prerequisite file has been
changed, but the run image has not been updated.
A Trivial Makefile
# Trivial Makefile for helloworld.c
# Z Wang, XJCO 1921
# 1/3/21 Any command (e.g., rm *.o)
#
helloworld: helloworld.c
gcc –o helloworld helloworld.c Rule Line

Must be a tab!!
Example time stamps
• Assume helloworld has TS Jan 22 14:00
• Assume helloworld.c has TS Jan 21 13:55
• Target is LATER than prerequisite, so rule is NOT
executed!
Example time stamps
• Assume helloworld has TS Jan 21 13:00
• Assume helloworld.c has TS Jan 21 13:55
• Target is EARLIER than prerequisite, so rule is
executed!
Example
• Build a source file with code for helloworld.c
• Build a Makefile with an appropriate dependency line.
• Run make
$ make
gcc -o helloworld helloworld.c
• The rule executes since the file helloworld does not even exist yet!
What if you tried make again?
$ make
make: ‘helloworld’ is up to date
$
Makefile Structure EXE DIR = .
EXE = $(EXE DIR)/solver
SRC= main.c checks.c solve.c
CC= cc
COPT= -O
CFLAGS=
OBJ= $(SRC:.c=.o)
.c.o:
$(CC) $(COPT) -c -o $@ $<
$(EXE): $(OBJ)
$(CC) $(OBJ) $(CFLAGS) -o $(EXE)
clean:
rm -f $(OBJ) $(EXE)

main.o: main.c main.h solve.h checks.h


Online tutorials: solve.o: solve.c solve.h checks.h
checks.o: checks.c checks.h
[Link]
36
Makefile Summary
• Build a dependency line for every target.
• Add a rule line to rebuild the target.
• Start the rule line with a tab.
• Add comments to the top of the file.
• Save these lines in an ASCII file called Makefile
Summary
• Modular programming is key to writing larger C
programs
• It is part of the design process
• Makefiles enable efficient compilation

38

You might also like