The goal of this lab is to learn basic C structs.
This is an exercise we didn't get to in lab 1. Make a folder for this pre-exercise:
mkdir lab1b cd lab1b
Grab the appropriate starter code from the lab 1 folder.
cp ~tvandrun/Public/cs245/lab1/substring.c .
You should remember the Java method substring()
by which you can make a new string from a sequence of characters
from another string.
You will write a similar function in C.
The protocol for the function you will write (you'll find this protocol
at the top of substring.c
and a stub at the
end of the file.
For reference, the stub is
int substring(char source[], char destination[], int start, int stop);
Note that this function takes (references to) two arrays---the first
is the array we are copying from, the second is the array we are copying
the characters to.
You don't need to know their sizes; all you care about is where the string
ends (you have to assume that the arrays are big enough to hold the
respective strings).
The function also takes a starting index (inclusive) and
stopping index (exclusive).
It copies the subsequence source[start, stop]
to destination[0, stop-start]
and adds an end-of-string marker to position
stop-start
.
However, to make this function extra robust, we want to make it
work even when "bad" values for start
and stop
are given.
Here are the rules:
start
is negative, we will assume they meant 0.
stop
is less than or equal to start
or if start
comes after the end-of-string marker,
this function will copy a "trivial" string of length 0.
stop
comes after the end-of-string marker,
this function will copy only to the end-of-string marker.
Moreover, your substring()
function should
return 1 if [start, stop)
is a valid range
in the source string and return 0 if it is not a valid range,
that is, if the function needed to apply the rules above.
Don't forget that the "destination" string must end in an end-of-string marker, even if no characters are copied.
Compile with
gcc substring.c -o substring
Test with
./substring
You are writing software for a Twitter knock-off called Chatter. In the Chatter system, users send messages whose text is at most 140 characters long. Additionally, each message has the user id of the user who sent the message (at most 16 characters long), a time stamp indicating the time the message was sent (a long int indicating the number of milliseconds since midnight, Jan 1, 1970), and up to 5 hash codes indicating categories or topics the message falls under (each hash code is at most 10 characters long).
I have provided some code for producing "random" text components and "random" user names. Your tasks will include designing and implementing a struct to represent messages, writing functions for displaying and sorting messages, and completing drivers to test these messages.
Set up a new directory for this lab and move into it.
mkdir lab3 cd lab3
Copy starter code from the course directory.
cp /homes/tvandrun/Public/cs245/lab3/* .
This will give you the following files:
chatter_util.h
, the header file for a library
of content-creating functions for you to use.
chatter_util.c
, the implementation file of that library.
message.h
, the header file for the message struct
(which you will have to complete) and the prototypes for
the functions that operate on messages.
(You will need to write a message.c
file.)
display_driver.c
, the driver program for testing
some of the functions on messages.
This is completed for you, though it's really simple.
sort_driver.c
, the driver program for testing
some more advanced usage of messages; you will need to finish this one.
makefile
and makefile, which you will need to add to.
The chatter_util
library provides the following
functions:
randomInt()
: produce a uniformly-distributed random int
between 0 (inclusive) and a given int (exclusive).
So randomInt(4)
will return 0, 1, 2, or 3,
with equal probability.
getTimeMillis()
: return the current time
fillRandomText()
: fill the given char
array with a string containing a randomly chosen text
for a chatter message
fillRandomUserName()
: fill the given
char array with a string containing a randomly chosen
text for a user name
fillRandomHashCode()
fill the given
char array with a string containing a randomly chosen
hash code
You need to complete the following tasks:
The first thing is to design and implement a struct to
to represent chatter messages.
This is kind of like writing a class in Java, but simpler.
I have already set up a dummy
struct for a struct message
type
(it's in message.h
since the struct type needs to be seen by
the library and the drivers.).
There is no real testing that can be done yet, but you can use
the makefile to compile the message with the
phony target check_struct
.
make check_struct
If the compiler doesn't complain, then at least you don't have a syntax problem... whether you designed the struct right or not will come out when you've written the other parts of the lab.
[If you look at the way the rule for
phony target check_struct
is defined in the
makefile, you'll notice that it includes -o /dev/null
.
That's because compiling a header file results in a "precompiled header",
something you usually don't want.
Precompiled headers end in h.gch
.
Our trick of compiling the header to check for syntax errors is
kind of a hack; we need to make sure that the resulting precompiled
header is deleted or else strange things will happen later.
/dev/null
is a "black hole" device that
discards everything written to it.
By saying -o /dev/null
this means the output
of that compilation is discarded.]
Now we need to produce a new message, that is, a value
of our new struct message
type.
The header file message.h
gives the prototype for the function newRandomMessage()
which is used by the two driver programs.
Write a definition for this function in an
implementation file (message.c
, which you'll need to
start from scratch) for the message library.
Use the functions from chatter_util
to
produce the text, user name, and hash codes.
Use randomInt()
to determine the number of
hash codes.
Note that functions like fillRandomText()
do not return an array---in fact, we don't know enough C to
use functions that would return an array.
Instead, to use these functions you must pass (references to)
arrays that you want populated.
You'll still need the next piece (displaying a message) to run
the driver, as before you can test check that the syntax is right
by compiling what you have.
Now you can use the makefile to compile message.c
:
make message.o
The headerfile message.h
gives the prototype for the function display()
which displays a message to the screen, nicely formatted,
showing all the information stored about that
message.
The driver display_driver
will test this
(as well as the other stuff you've written so far).
The display of a message should look something like this:
----------------------- Sent by Verna at 1315499047608 Q: How can you tell if an elephant is in the refrigerator? A: The door won't shut. #mischief #concert #admiration #concert
If you want the date to appear as something nice looking instead of a number of milliseconds, instructions for that are found in the optional section at the end of this lab description..
Write the implementation of display()
in
your message.c
.
Then compile the whole thing with make
(display_driver
is the default target)
and test.
Remember, this is testing everything you've done so far, so if it's
not right, you may need to go back to an earlier step.
The headerfile message.h
gives the prototype for the function sortByUserName
,
and the driver sort_driver.c
uses it.
This function must be completed so that it
sorts an array of messages by user name, alphabetically.
You may use any sorting algorithm for this.
What will be different is that you are sorting strings,
not numbers.
To do this alphabetically, first look at the first character of each
string---remember that characters are represented by numbers,
and the numeric code for characters is consistent with
their alphabetical ordering.
If the first character is a tie, then look to the second
character, etc.
Note that sortByUserName()
also needs
the size of the array as a parameter.
The, to test this, finish the sort_driver
program in sort_driver.c
.
Find the place in that file that indicates you should
add some code for this part to dipslay the users names
for all the messages in msgs
.
(Don't call display()
; we don't want to display
the whole message this time, just the user names.)
Also, edit your makefile to handle sort_driver.o
and sort_driver
.
This won't be very hard because you can largely imitate the
rules for display_driver
.
Note that you'll need either to put the rule for sort_driver
first (so it will be the makefile) or to specify it explicitly when you
run make (ie, make sort_driver
).
Compile and test.
(When you run sort_driver
you'll notice it also displays
something for part E, Looking for (some hashcode) ...
.
You can ignore that for now.)
Finally, complete the last part of sort_driver.c
.
In the for loop over the messages, you want to check for
each message whether it contains the hashcode found in
randomHashCode
.
If it does, display that message (using your display()
function).
Remember that a message may have several hashcodes---makes sure
you check each one.
Compile and test.
If you want to make a readable formatting of the time stamp, do the following things to your code:
sys/time.h
in message.h
time.h
in message.c
time_t
instead of long int
newRandomMessage()
,
don't use the getTimeMillis()
function that I provided but instead
use the function time()
.
Pass it the timestamp portion of the struct preceded by an ampersand.
For example, time(& (toReturn.timestamp));
display()
, use the function ctime()
to convert time to a string.
For example, printf("Sent by %s at %ld\n", msg.uid, ctime(& (msg.timestamp)));
Then the display of a message would look something like
----------------------- Sent by Willis at Thu Sep 8 12:32:37 2011 Q: Why do elephants stomp on people? A: They like the squishy feeling between their toes. #surplus #moisture