Labsheet 1 - some sample solutions
This page provides some sample solutions and discussion on
some of the tasks of
Labsheet 1.
The tasks required you to modify and extend the file
rot.c.
The file appears below, with line numbers added to aid the discussions.
In the sample solutions
some text appears in
red
to highlight the small changes necessary to complete the tasks.
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <ctype.h>
5
6 /* Compile this program as:
7 gcc -std=c99 -Wall -Werror -pedantic -o rot rot.c
8 */
9
10 #define ROT 13
11
12 /* Post: rotate c returns the character ROT positions further along the
13 alphabetic character sequence from c, or c if c is not lower-case
14 */
15 static char rotate(char c)
16 {
17 // Check if c is lower-case or not
18 if(islower(c))
19 {
20 /* The ciphered character is ROT positions beyond c,
21 allowing for wrap-around
22 */
23 return ('a' + (c - 'a' + ROT) % 26);
24 }
25 else
26 {
27 return c;
28 }
29 }
30
31 int main(int argc, char *argv[])
32 {
33 /* Exit with an error if the the number of arguments (including
34 the name of the executable) is not precisely 2
35 */
36 if(argc != 2)
37 {
38 fprintf(stderr, "%s: program expected 1 argument, received %d\n", argv[0], argc-1);
39 exit(EXIT_FAILURE);
40 }
41 else
42 {
43 // Calculate the length of the first argument
44 int length = strlen(argv[1]);
45 // Loop for every character in the text
46 for (int i = 0; i < length; i++)
47 {
48 // Determine and print the ciphered character
49 printf("%c", rotate(argv[1][i]));
50 }
51 // Print one final new-line character
52 printf("\n");
53 // Exit indicating success
54 exit(EXIT_SUCCESS);
55 }
56 return 0;
57 }
|
|
- Modify the program to perform rot-5, rotation by 5 characters
(or Caesar-encoding).
The original program performed a rotation by 13 characters
(a->m, b->n, ...),
and so to make the required change we only need to find all instances of
13, and change them to 5.
It is considered good practice in C programs
to identify all "magic numbers", or constants,
such as our value 13 using C pre-processor tokens.
That way, when we do need to change such a constant,
we can expect to find it as a C pre-processor token,
usually near the beginning of the file,
and we can expect to not have to look through every line to find
where a value such as 13 may need changing.
We only need to change line 10 to now read:
#define ROT 5
Now the rotation will be by 5 characters (a->f, b->g, ...).
You may have been confused by the use of 26 (as it's twice 13).
The 26 appears because there as 26 letters, a..z, in our alphabet.
- Extend the program to print out each character of the ciphered text
one per line instead of all the characters on one line.
To do this task, we first need to locate where the ciphered text is
being printed. Other than the printing of an error message,
there's only 2 lines that are clearly printing something -
lines 49 and 52.
To have only one ciphered character per line,
we need to print a new-line character after each ciphered character.
We can thus change line 49 to become:
printf("%c\n", rotate(argv[1][i]))
where the notation "%c\n" requests that a single character be
printed, immediately followed by a new-line character.
- Extend the program to print out on each line not only the
ciphered character, but also the original character too.
This task is now not much more difficult;
instead of printing just one character we need to print two -
the ciphered character (as convered by our rotate() function),
one the original character (the value before before it is passed
to rotate).
We can change line 49 to become:
char ch = argv[1][i];
printf("%c %c\n", rotate(ch), ch);
where the notation "%c %c\n" requests that two characters
be printed, immediately followed by a new-line character.
To make our code much less verbose,
we've introduced a new character variable, named 'ch', to hold a copy of
the character in which we're interested.
- Extend the program to print out on each line not only the
original and ciphered characters, but also the "position" of the
character in the text.
This task is again a bit more difficult,
but requires us to change the same "area" of our code - just to modify
what is being printed.
The position of the character in the text parallels how many charcaters
we've printed out - initially we've printed out no characters,
after one iteration of the loop, we've printed out 1 character, and so on
(we could quibble about whether the first iteration should be labelled '0'
or '1').
The for loop at line 46 is responsible for counting how many
iterations are made.
Its control variable, i, conveniently starts at zero and is
incremented each time, so we'll just print out i's value each time
through the loop.
i is an integer variable, so we'll need a slightly different
specifier to print it out.
You may have been able to see from line 38 that integers are printed
using '%d', so we'll extend our output line from task 3, above, to:
printf("%c %c %d\n", rotate(ch), ch, i);
- Extend the program to handle both upper-case and lower-case letters.
Our initial program only converted lowercase characters - uppercase
characters (which, by definition can not also be lowercase)
were just "ignored" in our rotate function.
We now need to modify our rotate function to additionally
detect uppercase characters,
and to convert them.
Our rotations by 5 will also convert (A->F, B->G, ...).
Two small extensions are required -
we detected lowercase characters with the standard function islower.
It's a reasonable guess (and a correct one) to guess
that there's also a function named isupper,
which determines if a character is uppercase.
We also need to follow how lowercase characters were converted,
and apply the same logic to uppercase ones.
This time, instead of specifically considering the first lowercase
character, 'a', we'll use the first uppercase one, 'A'.
Our whole rotate function now becomes:
static char rotate(char c)
{
if (islower(c)) // Check if c is lower-case or not
{
return ('a' + (c - 'a' + ROT) % 26);
}
else if (isupper(c)) // Check if c is upper-case or not
{
return ('A' + (c - 'A' + ROT) % 26);
}
else
{
return c;
}
}
|
|
and we should modify the block comment about rotate to correctly
describe what the function does.
- Extend the program so that it can accept multiple arguments,
performing the ciphering process on each argument to the program.
Our starting program only dealt with a single command-line argument.
All through our main() function we can find references to
the variable argv[1] and, if we wish to process more command-line
arguments, we'll need that value '1' to become '1', '2', '3',....
In the original program, we'll need to add a new loop "around" all lines
accessing argv[1] and make them access argv[a],
where a will now range from 1 up to the number of command-line
arguments, argc.
We'll use a new for loop "around" the old lines 43..50 inclusive:
for (int a = 1 ; a < argc ; a++)
{
// Calculate the length of each argument
int length = strlen(argv[a]);
// Loop for every character in this argument
for (int i = 0; i < length; i++)
{
// Determine and print the ciphered character
char ch = argv[a][i];
printf("%c %c %d\n", rotate(ch), ch, i);
}
}
|
|
|
Top of Page
|
|
CRICOS Provider Code: 00126G
|
|
|