CS 216 Lab 12
For 8 April 2019
The goal of this final lab is to create a program that actually
runs, by using a Makefile, and several individual files.
(A secondary goal would be that it actually sorts an array.)
Using make
to manage a project - A few files for implementing a sort. Four
options, in quests/ :
- quicksort/ Quicksort - an
array of int - elegant but partition is tricky. Array will be
filled with random numbers.
- sort/ -- Easier
-- an array of int - by any means you choose (you might find
your merge useful)
- stringsort/
-- Will read words, and make an array of pointers to them,
sort in dictionary order
- listsort/
-- Will read words, and set up a "linked list" of them. --
For those familiar with Data Structures
In fact, you may code any sorting method you prefer, and you are
free to change the name of the sort function, so long as you do so
consistently. The real goal is to learn to use make. See below
make is a
unix utility for "automating" the process of compiling, assembling
and linking a program consisting of many files.
Compiling steps
gcc manages the steps of
producing an executable program, which are:
- preprocessing each .c files (carrying out #define and #include
directives)
- Compiling to assembly language (giving a .s file) - not usually saved
- Assembling to machine code .o files
- Linking .o files together, also with library routines
(containing functions such as printf ), giving an "executable
file." By convention, these usually don't hae a .extension. They
have x (execute) permission.
Suppose there are several source files. Once you have compiled
all of them to object files, then you discover one needs a change,
you only need to recompile that one, then link them all together.
(When you are all done, you can get rid of the intermediate object
files.
For example, suppose you have the files: myfunctions.c and
myprog.c, which uses myfunctions. The first time you compile, you
can do this:
gcc -c myprog.c
gcc -c myfunctions.c
gcc -o myprog myprog.o myfunctions.o
(Well, you might have to correct syntax errors and repeat in
between.)
So now you might need to change one of the files, perhaps myprog.c.
Now all you need to do is:
gcc -c myprog.c
gcc -o myprog myprog.o myfunctions.o
because myfunctions.o is still current, since myfunctions.c hasn't
changed.
Header files (.h)
There is a problem here, in that to compile myprog, the compiler
needs to know about the functions that are called, so it can check
that they are being used properly. You have seen this if you forgot
#include <stdio.h>.
You need function prototypes. You could include these in each
program that uses myfuncts, but it is better to write a file with
the information needed to us the functions, and include it when
needed. This "header file" is usually given the name of the file it
"heads", and a .h extension. So ours would be myfunctions.h, and
myprog.c would include the line: #include "myfunctions.h" (Note the
"quotes" imply this file is in the same directory.)
myprog.c would probably start like this:
/* A main program that does I/O and calls functions prototyped in myfunctions.h */
#include <stdio.h>
#include "myfunctions.h"
If myprog.c or myfunctions.h changes, we would want to recompile it
to myprog.o, and also relink myprog, because a .o file has now
changed.
make
the make utility is designed to describe tasks that need to be
done on the basis of file times. It reads the file Makefile
containing rules of the form:
target : prerequisites
"make updates a target if it depends
on prerequisite
files that have been modified since the
target was last modified, or if the target does not exist." (from man make) Thereafter follow line(s)
started with a tab character, with the command(s) required
to update the target. This would a suitable Makefile for the
program just described. We start from a goal, the executable file.
myprog : myprog.o myfunctions.o # goal is this executable file
gcc -o myprog myprog.o myfunctions.o
myprog.o : myprog.c myfunctions.h # (re)compile sources if necessary
gcc -c myprog.c
myfunctions.o : myfunctions.c myfunctions.h
gcc -c myfunctions.c
Once Makefile is created (in the same directory,) all you need to
type for a new myprog
executable is, Ta-Da:
make
Quicksort
Quicksort is an efficient (O(n lg n)), recursive algorithm
published by C. A. R. Hoare in 1961.
We have discussed it in
class. The basic idea is to 'divide and conquer' by partitioning
an array into smaller and larger values, and then repeating the
process on shorter arrays.
I have created a partially completed quicksort program as several
files. You may make changes to any of them, and will have to
complete what's wrong or unfinished. I don't think I made many syntax
errors, but... You may change variable names to ones more
meaningful to you, clean up my comments (which are rough outlines
of the algorithm) and make your own.
Your task today is to create a Makefile for compiling and linking this
program to produce the executable file dosort. and then to make, correct, test,
re-make, until the program can be run. And just maybe, actually
sorts an array. (But the goal is the process, really.)
The files are:
- quicksort.h
- contains prototypes of all the functions. included in all the
.c files
- qsmain.c
- Main program, creates array of random numbers, prints it,
calls quicksort and prints again.
- quicksort.c
- the quicksort function, a skeleton, you fill in the
recursion.
- partition.c
- the tricky one. returns two pointers by storing them in addresses passed as
arguments.
- qsutils.c
- utility functions: fillarray, which probably works, and
printarray -- you must decide the details, however you want the
printed array to look. Probably you should finish with a
newline.
- swap.s
- The familiar swap function. Don't change it, it's been tested
(details later)
- Makefile
- I just got it started, so you need to fill out all the
details, for each file that needs to be compiled.
An easier way .... a simple sort
Perhaps you are overwhelmed by the complexity of partition. And
it is a bit finicky. So if you prefer, code any simple sort
you like, in the file sort.c.
The files are: and they are
in directory sort/ (instead of quicksort)
- sort.h
- contains prototypes of all the functions. included in all the
.c files
- sortmain.c
- Main program, creates array of random numbers, prints it,
calls sort and prints again.
- sort.c
- function that sorts, a skeleton, you fill in the
method.
- arrayutils.c
- utility functions: fillarray, which probably works, and
printarray -- you must decide the details, however you want the
printed array to look. Probably you should finish with a
newline.
- swap.s
- The familiar swap function. Don't change it, it's been tested
(details later) You might find it convenient
- Makefile
- I just got it started, so you need to fill out all the
details, for each file that needs to be compiled.
Notes: A random number generator is used to create test data.
You can choose the size of your array in sortmain.c.
Each run will get different numbers, but if you comment out the line indicated, you will always get the same numbers, this is uesful while debugging.
In arrayutils.c you can change the range of numbers that are generated. Choose a pleasing way to display your array. I don't mind whether you sort ascending or descending, or what method you choose to do it.
Sorting Strings
This might be fun, the main difference is that you rearrange
based on strcmp rather than direct comparison.
Same files as from sort, except
- stringutils.c
- Note different file name. Utilities to read words from stdin
, and print the list
- README
-
- Gives some general instructions.
Two versions of this: 1. Array of pointers, 2. Linked List
- Files in stringsort/ -- the array is of pointers, you
rearrange it.
- Files in listsort/ -- If you
are familiar with the data structure of Linked List, the words
are in nodes that are linked together with "next" pointers,
which you must rearrange. Merging two sorted linked lists, for
example, is quite straightforward and requires no additional
storage.
Quicksort, on the other hand, is totally inappropriate.
Reading is from stdin. That means you can either type (lines of) words, or take input from a file, such as the two .txt files provided.
The syntax for this is
./dosort <quick.txt
Instructions
1. Get the files
In /home/COURSES/CSC116/quests is the directory sort Or
alternatively, quicksort, stringsort, or listsort. Copy one, with its contents to wherever
you like to work. From the command line you can simply type: (note the dot (.), it means "to
current directory)
cp -r /home/COURSES/CSC116/quests/sort .
cd sort
6 or more files should be there.
2. Fill in the blanks, complete algorithms, Makefile, get it
working
The point of this exercise is to learn to combine several files
into a project. You may prefer the convenience of
MegaBlockBuilder++, but hey, it just constructs a Makefile, and
calls on make to do the work! Understand this!
This is your program, you are free to change anything you don't like,
or add additional files. You do not have to use my swap, for instance.
I find it very convenient to use Kwrite or Kate, good highlighting, and
Kate has a built-in terminal (if you don't see it, go to
Settings|Configure|Plugins and check Terminal) Do the cycle of
make
./dosort
there, and edit whatever file is giving a problem.
During debugging, I recommend adding additional print statements.
In particular, you can call printarray whereever you want to see
what's happening.
When it's all working, you no longer need the .o files, or any backups, so clean up before you submit, tehn get out of the directory. Type:
make clean
cd .. #up one directory
3. Submit your directory (not individual files!)
submit cs216 sort #or# other directory
No mipsmark this week. Instead, I will type first make, and then ./qsort, or
./dosort , and see what happens.
Please actually do this yourself. Otherwise, you haven't
tested your makefile!
Note: If you choose to include other files, be sure they are also in the same directory.
So what's this swap.s file all about?
Well, we know that swap can be done with 4 assembly instructions (2
load, 2 store.) One can tell gcc to stop at any point, so I asked it
to stop before the assembly step, giving an assembly language file
(unfortunately not mips). The compiler produced about 20
instructions. From this, I determined which registers contained the
arguments, I knew of 3 temporary registers, so I replaced the mess
with the 4 instructions. It is unnecessary for you to even look at
this file, just use gcc to produce the swap.o