Labsheet 1: An Introduction To C
For the week commencing Monday 4th August.
An Introduction to Mac-OSX
The computers will we use in this unit are Apple iMac G5s, based on the
IBM PowerPC G5 chips with a Unix operating system. These computers can
be found in Lab 2.01 of the CSSE building.
You will have 24 hour, 7 days a week access to these labs provided your
security card has been enabled for access to the laboratory.
If you're having any difficulties accessing the lab, please see someone
in the reception area (Room 1.31) of the CSSE building.
Read and complete An
Introduction to Mac-OSX that introduces the Mac-OSX operating system, including both the GUI environment and the underlying Unix-based operating system.
For the first half of this unit, we will be predominately interfacing with the Macs through the Unix operating system via a command-line terminal window.
To access the operating system through this interface, click the Terminal program on the dock.
This will open up a terminal window containing an operating system shell.
The shell is the interface that will interpret your commands and execute them in the Unix operating system.
Your terminal window should say something like:
Last login: Wed Jul 23 12:14:01 on ttyp1
Welcome to Darwin!
csse2100%
Make sure you read the section called "Unix Basics" in the document An
Introduction to Mac-OSX thoroughly.
This material will give you a quick introduction to the command-line interface will we be using.
However, the learning curve for this type of command-line environment is steep - only practice and experience will improve your skills.
Don't be afraid to try things!
An Introduction to the C Compiler
Background
A compiler is a program responsible for converting high-level programming language instructions into machine-understandable execution instructions.
For the first half of this unit, the compiler we will use is the gcc command-line compiler.
Create in your personal work area a folder for this unit.
Name the folder CITS1210.
Within this folder, create a folder for this labsheet called Lab01.
You should get into the practice of maintaining a well-structured directory hierarchy - it will help you organise and separate your work into logical divisions.
Download and save in your Lab01 folder a copy of the rot.c simple C program that performs "rot13".
Rot13 is an old-school Internet term used to described ciphering a piece of text by replacing each character in the text with the character 13 positions further along the character sequence, allowing for wrap-around.
So, for example, "rot13-ing" the text hello produces the text uryyb, as character h is converted to the character u (13 characters beyond h), the character e is converted to the character r, and so on.
For this part of the labsheet, you do not need to know how this program works.
Basic Compilation
Launch a terminal and navigate to your Lab01 folder.
Ensure your copy of the rot.c file is located in this folder by typing ls at the command prompt:
csse2100% ls
rot.c
You shouldn't see any other files.
Compile your copy of rot.c file by typing:
csse2100% gcc -std=c99 -Wall -Werror -pedantic -o rot rot.c
at the command prompt.
Assuming everything went successfully (and it should have!), you should now see two files in your folder (type ls again at the command prompt):
csse2100% ls
rot rot.c
Depending on your setup, you might see an asterisk immediately following the rot file that indicates that file is executable.
Even if you don't see an asterisk, the rot file is indeed executable (see man chmod to find out about permissions), meaning we can execute or "run" it.
We will do this shortly.
But first, let's consider what has happened here when we compiled our rot.c program with the gcc compiler.
The compiler has read our program, parsed it for errors, and converted it to machine-understandable instructions in the form of an executable, generating the rot executable we now see.
Indeed, the compiler has converted our high-level program into a machine-understandable form that we can now execute on our computer.
Execution
Let's now execute the program at the command-prompt by typing:
csse2100% ./rot hello
The first part following the prompt is the name of the executable (the ./ part indicates in the current directory/folder).
The second and subsequent parts following the prompt form the arguments to the program.
In this case, we are supplying one argument to the rot program - the text hello.
Executing the command as detailed above should yield the following:
csse2100% ./rot hello
uryyb
meaning our executable generated the output uryyb in this case of a the text argument hello.
Try other arguments to ensure the program is behaving as expected.
The gcc -o <name> Option
Let's now explore the different arguments we supplied to the gcc compiler when we compiled our program.
Clearly, the last argument to gcc was the name of our program.
But what about the other arguments?
The -o rot part of the compiler command determined the name of the executable (in our case rot).
We can name the executable whatever we want, so typing:
csse2100% gcc -std=c99 -Wall -Werror -pedantic -o blah rot.c
at the command prompt, followed by ls should produce the following listing:
csse2100% ls
blah rot rot.c
We now have two (equal) executables (blah and rot) that do the same thing.
Let's delete these executables by typing:
csse2100% rm blah rot
at the command prompt.
Typing ls again should reveal we are back to one single file - our rot.c program:
csse2100% ls
rot.c
The -o <name> argument(s) to the compiler are actually optional.
If we omit them, the compiler still generates an executable, but gives the executable a default name called a.out.
Try this now by typing:
csse2100% gcc -std=c99 -Wall -Werror -pedantic rot.c
and you should see an executable called a.out along with the rot.c program when you type ls at the command prompt:
csse2100% ls
a.out rot.c
Generating executables with the default name is bad form and gets confusing, so you should always supply a name for the executable with the -o <name> syntax.
Delete the a.out executable with the rm command.
The gcc -std Option
The -std=c99 argument to the gcc compiler indicates that we wish to use the ISO C99 standard when compiling our program.
Try removing this argument and see what happens:
csse2100% gcc -Wall -Werror -pedantic -o rot rot.c
rot.c: In function 'main':
rot.c:42: error: 'for' loop initial declaration used outside C99 mode
When we do this, the gcc compiler indicates there was (at least one) error in the compilation process (on line 42) of our program due to some C99 specific code in our program.
That's okay - we want to ensure we're following the conventions laid out by the C99 standard, so use this argument (or flag) when compiling your code.
We'll talk more about errors in the next section.
Errors and Warnings
Errors During Compilation
In the previous section we generated an error during compilation due to a mis-match in language specifications.
Re-examine the output for gcc in this case and notice that the compiler has indicated the line number and given us a description of the error it encountered.
Indeed, the gcc compiler is generally very good at giving accurate and good feedback about what is wrong with our program.
However, sometimes even it gets confused and finding the true cause of the problem can be difficult.
As a general rule, look at the top-most (first reported) error and fix that error first before proceeding to later errors.
Indeed, sometimes later errors are erroneous (or "flow-ons" from the previous errors) due to the compiler guessing at what is wrong with our program.
Until you get a little more experience, you should re-compile your program after fixing the single top-most error.
Download this version (rot-error1.c) of our rot-13 program that contains one error.
Attempt to compile the program to determine the error:
csse2100% gcc -std=c99 -Wall -Werror -pedantic -o rot rot-error1.c
rot-error1.c: In function `main':
rot-error1.c:40: error: parse error before ';' token
It should be fairly obviously what the problem is (there is something missing before the ;).
Fix it now.
Download this version (rot-error2.c) of our rot-13 program that also contains one error.
Attempt to compile the program to determine the error:
csse2100% gcc -std=c99 -Wall -Werror -pedantic -o rot rot-error2.c
rot-error2.c: In function `main':
rot-error1.c:42: warning: ISO C forbids nested functions
rot-error2.c:42: error: syntax error before '==' token
rot-error2.c:42: error: `i' undeclared (first use in this function)
rot-error2.c:42: error: (Each undeclared identifier is reported only once
rot-error2.c:42: error: for each function it appears in.)
rot-error2.c:42: error: syntax error before ')' token
Again, it should still be fairly obvious what the problem is (is == correct?), but notice the multiple errors that result from this one error.
Again, fix the error now.
The gcc Warning Options
What of the other arguments to the gcc compiler?
What do they do?
These flags (-Wall -Werror -pedantic) all relate to the reporting of errors (or of potential errors, called warnings).
Warnings are not fatal to the compilation process - the compiler simply reports them and continues compiling the program.
It is up to the programmer to decide on the importance of warnings.
Poor programmers simply ignore warnings.
Good programmers do not.
Your program should never contain any warnings.
The -Wall flag indicates that the compiler should report all warnings.
The -Werror flag indicates that the compiler should treat all warnings as errors, aborting the compilation process when encountering a warning.
To emphasis the importance of these flags, download another version (rot-warning.c) of our rot-13 program that contains one warning.
Before compiling this version of the program however, ensure you have no executables in your work folder (and remove any if you do):
csse2100% ls
rot.c rot-error1.c rot-error2.c rot-warning.c
Attempt to compile the program to see the warning:
csse2100% gcc -std=c99 -Wall -Werror -pedantic -o rot rot-warning.c
rot-warning.c: In function `main':
rot-warning.c:32: warning: suggest parentheses around assignment used as truth value
Here we see that the gcc is reporting one warning, suggesting a potential problem on line 32 of our rot-warning.c program.
Notice also that no executable was constructed because the compiler stopped as soon as it encountered the warning:
csse2100% ls
rot.c rot-error1.c rot-error2.c rot-warning.c
Now let's try compiling the program without the -Werror flag:
csse2100% gcc -std=c99 -Wall -pedantic -o rot rot-warning.c
rot-warning.c: In function `main':
rot-warning.c:32: warning: suggest parentheses around assignment used as truth value
Again notice the warning generated by the compiler, but this time notice that the warning wasn't fatal and that an executable file called rot was indeed constructed:
csse2100% ls
rot rot.c rot-error1.c rot-error2.c rot-warning.c
Execute rot as before:
csse2100% ./rot hello
./rot: program expected 1 argument, but instead received 1
but notice the strange behaviour of the program (the program doesn't produce the expected answer and instead reports an inconsistent message).
This is a due to an "error" in our program that was reported as a warning during compilation.
However, this error is not like the errors we saw before - the erroneous statement actually makes syntactic sense (i.e. it is valid code), and is hence called a logical error because the code is not doing what we intended it to do.
The warning issued by the compiler hints at the problem (instead of performing a test for inequity on line 32, we're actually performing an assignment) because the compiler is "smart" enough to realise that this valid code is somewhat peculiar and is letting us know of a potential problem.
Remove the executable with the rm command and confirm you have no executables in the folder:
csse2100% ls
rot.c rot-error1.c rot-error2.c rot-warning.c
Now let's try compiling this program without the -Wall flag:
csse2100% gcc -std=c99 -Werror -pedantic -o rot rot-warning.c
This time, no warning is generated (after all the code is valid!) and an executable is constructed:
csse2100% ls
rot rot.c rot-error1.c rot-error2.c rot-warning.c
but of course the executable behaves strangely as before:
csse2100% ./rot hello
./rot: program expected 1 argument, but instead received 1
The -pedantic is similar, forcing the compiler to issue warnings about not following the ANSI standard and stopping the use of forbidden extensions.
Having completed this section, hopefully you see the importance of using these warning flags.
Of course, typing all these flags in every time you want to compile something is tiresome and prone to error.
In the next section, we will generate an alias (like a shortcut, but textual) to save us from typing in these flags every time we want to compile a program.
Aliases
An alias is a textual shortcut that replaces one piece of text on the command-prompt with another.
We can use it here to minimise the amount of typing we need to do to compile our programs.
Use you favourite editor to edit your .zshrc startup file in your home directory/folder.
For example:
csse2100% vi ~/.zshrc
(The ~/ part means home directory - so this command means edit the file called .zshrc in your home directory).
[In case the part before the / appears unclear on your screen, that character is the called the "tilde" character and can can be found with the back-quote key, left of the 1 key]
Add the line (even if the file is blank):
alias mygcc="gcc -std=c99 -Wall -pedantic -Werror"
into this file.
Now type:
csse2100% source ~/.zshrc
to load these changes.
This creates a textual alias called mygcc that you can now use to compile your programs with (when the computer sees the command mygcc, it will be replaced by the command gcc -std=c99 -Wall -pedantic -Werror).
Try it by typing:
csse2100% mygcc -o rot rot.c
and see that the compilation process still works.
Notice that you still need to provide the -o <name> arguments to determine the name of the executable, but all the other flags have now been captured in the mygcc alias.
Extending the Sample C Program
The following tasks are intended for those who already have some exposure
to the C programming language, or maybe to Java.
Absolute beginner C programmers may struggle in completing any of these tasks.
More advanced programmers should be able to complete a number,
if not all,
of these tasks.
Tasks:
| 1. |
Modify the program to perform rot-5, rotation by 5 characters (or Caesar-encoding).
|
| 2. |
Extend the program to print out each character of the ciphered text one per line instead of all the characters on one line.
|
| 3. |
Extend the program to print out on each line not only the ciphered character, but also the original character too.
|
| 4. |
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.
|
| 5. |
Extend the program to handle both upper-case and lower-case letters.
|
| 6. |
Extend the program so that it can accept multiple arguments, performing the ciphering process on each argument to the program.
|
7.
|
Extend the program so that it reads the rotation amount (the amount to rotate each character by) as the first argument to the program.
You will need to use the atoi function to convert the text argument into a number.
|
8.
|
Extend the program so that all arguments to the program can be either text or numbers.
If an argument is text, the text is ciphered using the current rotation amount (initialised to 13).
If an argument is a number, the rotation amount changes to this value and all subsequent cipherings (up to the next number argument) use this new rotation amount.
|
9.
|
Extend the previous version of the program so that textual arguments can represent files.
If a text argument can be opened as a file (see man fopen), perform the ciphering on the contents of the file.
Otherwise, treat the arguments as before: if an argument is text, the text is ciphered using the current rotation amount (initialised to 13), or if an argument is a number, the rotation amount changes to this value and all subsequent cipherings (up to the next number argument) use this new rotation amount.
|
|
Top of Page
|
|
CRICOS Provider Code: 00126G
|
|
|