CIT 593 | Assignment 10: Intro to C and its Development Environment | 1
Setting up Codio for this assignment
1) Open the Codio assignment via Coursera
2) From the Codio File-Tree click on: program1.c
Problems #1-3: Basic I/O in C
Recall in Assembly that performing I/O involved calling a TRAP. TRAP_PUTS – wrote an ASCII
string out to the screen and TRAP_GETS – read in an ASCII string from the keyboard. In C,
there are two functions: printf() and scanf() that perform the same basic task (printing output to
the screen and reading a string in from the keyboard). They are actually “wrapper” functions –
like the one you created in the last HW – that invoke OS TRAPs to perform I/O on your behalf.
Let’s examine your first proper C-program (that does I/O) together and compile it on the Intel
x86 (Codio runs on an intel x86 processor). We won’t use LC4 because the function “printf()
and scanf()” don’t exist yet…you would have to write them! The C-compiler we’ll use on the Intel
x86 has many premade functions you are welcome and encouraged to use. So for this HW, we
will use the Intel x86 compiler called: clang, instead of our LC4 compiler: lcc.
On Codio, open the starter file called: program1.c in examine the following code: #include
int main() {
printf (“Hello World!\n”) ;
return (0) ;
}
Open up a terminal window in Codio and compile the above code by typing the following:
clang program1.c –o program1
What does this mean?
clang -is the name of our Intel x86 C-compiler
program1.c -is the name of the file you want to compile
-o program1 -tells the compiler you’d like to create an OBJECT file called program1
This compiler will internally generate the Assembly of your program1.c, but then automatically
call the Intel x86 Assembler and have it make an OBJECT file for you. So you don’t need to
manually “assemble” the output file like you did in the last assignment.
CIT 593 | Assignment 10: Intro to C and its Development Environment | 2
Now let’s “run” your program on the Intel x86. You would do that by typing in the name of the
file directly into the terminal as follows:
./program1
If things went well, you should see output to the ASCII display (aka – the terminal window):
Hello World
Congratulations, your first C-program works! You’ve performed the most basic of C-I/O –
working with the ASCII display. All the same steps of a TRAP being called were done in the
blink of an eye (privilege being elevated, vector table being called, trap subroutine being called,
returning back to the user, etc), and your string is outputted on the ASCII display.
Recall that a compiler should take the high-level language and convert it to assembly. While clang
does this step internally, it is possible to force it to show you the assembly it produces. Type in
the following command:
clang –S program1.c
This will generate a file called: program1.s It will contain the x86 Assembly translation for this
program! Open this file and you’ll see what the Assembly Language looks like for the Intel x86
ISA. Next, re-compile this same program to our LC4 ISA, using the old LCC compiler. The only
trouble is that LCC doesn’t have the stdio.h library, so open up the file and comment out the first
line (#include
lcc program1.c
Even through our compiler doesn’t have the library, it will create the JSR for printf(), it assumes
you’ll make it someday and you’ll link it to create the final object file. Open up program1.asm
and compare it to program1.s. Can you find the equivalent of the LC4’s JSR command in the
program1.s file? Hint: look for the call to printf! (Want to know more about Intel’s x86 ISA:
http://www.cs.virginia.edu/~evans/cs216/guides/x86.html )
http://www.cs.virginia.edu/~evans/cs216/guides/x86.html
CIT 593 | Assignment 10: Intro to C and its Development Environment | 3
Let’s try reading from the keyboard
Make a new file, called program2.c and type in the following C-code (do NOT copy/paste this,
the quotation marks do not copy properly from the PDF):
#include
int main () {
char name [50] ;
printf (“What is your name? “) ;
scanf (“%s”, name) ;
printf (“Hello %s\n”, name ) ;
return (0) ;
}
Save the file as program2.c and compile the above code by typing the following in the Codio’s
terminal:
clang program2.c –o program2
Then run it & try it out:
./program2
CIT 593 | Assignment 10: Intro to C and its Development Environment | 4
Analyzing what we’ve done:
A few things to notice about our program1 & program2:
• You didn’t have to “write” printf() and scanf(), the assembly that implements these two
functions is “linked” to your program (just like in the last assignment – recall ‘linking’). You
told the compiler to link them when you placed the line: #include
C-file. STDIO – stands for “standard input/output” functions.
• Printf() and scanf() actually stand for “print formatted string” and “scan formatted string”
What does “formatted” mean? Things like new-line characters are considered formatting
information – they aren’t visible ASCII character exactly, they are instructions for how to
‘format’ the output. Notice in program1 we said:
• Printf (“Hello World\n”) ; the “\n” is tip to the printf() function that you’d like a new-line
inserted. There are others… \t for tabs:
(https://en.wikipedia.org/wiki/Escape_sequences_in_C#Table_of_escape_sequences)
Doing more with printf() and scanf()
Your textbook, pages 485-490 does an excellent job explaining the details of printf() and
scanf(). Read these pages and try out the examples they give. Once you have mastered the
examples, write the following program and place it in a file called: program3.c:
1) Print “Welcome to CIT 593” to the ASCII display using printf(), followed by a newline
2) Ask the user to type their name, their HW1, HW2, HW3, HW4 scores (out of 100), their
midterm grade, and their expected final exam grade using a SINGLE scanf() statement
(not in a loop)
3) Have your program average the HW scores and compute their final weighted average
(using the CIT 593 syllabus as your guide).
4) Display their statistics using this example format:
Name: Thomas
HW Average : 100.00%
Midterm Grade : 100.00%
Final Exam Grade : 100.00%
Final Average : : 100.00%
(Notice my use of tabs, the alignment of the decimal points and only two decimal places)
Make certain to compile your program often and test it before submitting program3.c
https://en.wikipedia.org/wiki/Escape_sequences_in_C%23Table_of_escape_sequences
CIT 593 | Assignment 10: Intro to C and its Development Environment | 5
Problem #4 – Functions and Pointers in C
As you have learned well in the last assignment, functions in C are translated to sub-routines in
Assembly that obey the use of the stack. All implementations of C use the stack to pass
data/hold data/return data to and from functions in C. This is true for the Intel x86’s
implementation of C as well. In this section of the HW, you’ll experiment with setting up
functions in C and seeing how data is passed to & from the function.
Create a new file: program4a.c, and type the following program into it:
#include
void swap (int a, int b) {
int c = 0 ;
c = a ; /* swap values of a and b */
a = b ;
b = c ;
printf (“a= %d\n”, a) ;
printf (“b= %d\n”, b) ;
return ;
}
int main() {
int a = 5 ;
int b = 10 ;
printf (“a= %d\n”, a) ;
printf (“b= %d\n”, b) ;
swap (a, b) ;
printf (“a= %d\n”, a) ;
printf (“b= %d\n”, b) ;
return (0) ;
}
Compile the program with clang and view the results. Do “a” and “b” swap values in the
function: swap()? Do “a” and “b” swap values back in main()?
CIT 593 | Assignment 10: Intro to C and its Development Environment | 6
Create a new file: program4b.c, and copy and paste the program from program4a.c into this
file. Fix the program to perform the swap. You won’t need to add any lines of code to this file.
Instead, you will only need to use pointers instead. You’ll want the variables inside swap to
“point” to the variables in main() – instead of being copies (as they are in program4a).
Compile the program with clang and view the results. Do “a” and “b” swap values in the
function: swap()? Do “a” and “b” swap values back in main()? Can you explain why? Drawing
out the stack may help…
Available on Coursera is a file: CIT593_Assignment_C-Basics_spreadsheet.xlsx. Download
and open the file and pretend for a moment your program is running on the LC4, using the
stack on the LC4 and the LC4’s register file. Show why these programs differ and explain that
using the spreadsheet included.
You will upload the completed version of this spreadsheet (after completing problem 7)
to the root folder of your Codio workspace for submission
CIT 593 | Assignment 10: Intro to C and its Development Environment | 7
Problem #5 – Multifile development in C
Often in C, we like to not work in one giant file! Much like you did in your previous assignment,
you can separate functions from one another into separate files. This makes development of
programs much easier when you have more than one programmer on a project. But it’s also a
good way to setup a project in your own development environment, so we’ll do that here as
well.
Create a new file called: program5_swap.c, and place the first part of program4a.c inside it:
#include
void swap (int a, int b) {
int c = 0 ;
c = a ; /* swap values of a and b */
a = b ;
b = c ;
printf (“a= %d\n”, a) ;
printf (“b= %d\n”, b) ;
return ;
}
Save the file and try to compile it using the following command:
clang program5_swap.c –o program5_swap
You’ll immediately get an error. Why? Because you don’t have a main() function inside this file.
Without a main() function, the c-program could not start. But we have another option, try
compiling with this command:
clang –c program5_swap.c
This should work properly (assuming your code is correct), and if you look in your folder you’ll
see a new file called: program5_swap.o. This new file is called a “partially compiled” object file.
It is an object file, but it cannot run on its own, because it doesn’t have a main(). This is just like
your SUB_FACTORIAL.asm file in the last assignment after you modified it. It cannot run by
itself, because it didn’t have a main().
program5_swap.o is also considered a library. It is a library of code with only 1 function, but
now it’s separate from its main(), so anyone could call it from their own program. If you can
imagine, there is a library called: stdio.o, it contains the implementation for printf() and scanf()
(among other functions). It is just waiting for someone to “link” their program to it and call the
functions that are within the library.
CIT 593 | Assignment 10: Intro to C and its Development Environment | 8
So, how do we link a program with program5_swap.o? Create the following file called:
program5.c:
#include
void swap (int a, int b) ;
int main() {
int a = 5 ;
int b = 10 ;
printf (“a= %d\n”, a) ;
printf (“b= %d\n”, b) ;
swap (a, b) ;
printf (“a= %d\n”, a) ;
printf (“b= %d\n”, b) ;
return (0);
}
Notice that new line at the top:
void swap (int a, int b) ;
This is known as a function “declaration”, it tells the compiler all about the function swap(): its
return type, its name, and the type and order of its arguments. Now, the compiler could actually
write the assembly for main, including the call to swap (packing the arguments properly on the
stack), even if swap() did not yet exist (this is what your LCC compiler does!).
Save the file and try to compile it using the following command:
clang –c program5.c
You’ll see that program5.o gets created. It too is a “partially” compiled file, because it contains
the full assembly implementation of main(), but it doesn’t contain the actual implementation of
swap()!
We must now “link” the two .o files together, which can be done by typing: clang
program5_swap.o program5.o –o program5
This will final produce: program5, an object file that we can run by typing: ./program5 Try it now
and make sure your program is properly “linked” together.
CIT 593 | Assignment 10: Intro to C and its Development Environment | 9
Problem #6 – Headers Files and Makefiles:
Instead of placing “declarations” of the functions in the file with our main() function, there is a
better way to decouple the files a bit more. We can create a separate file, called a “header” file.
A header file is a simple file that contains only function declarations. Let’s create one for our
program5_swap.o library:
Create a new file (called program5_swap.h) and add only this one line:
void swap (int a, int b) ;
Notice, the extension: “.h” it stands for header. We are creating a simple header file. Copy the
file: program5.c, and call it program6.c. Now, edit program6.c
#include
#include “program5_swap.h”
int main() {
int a = 5 ;
int b = 10 ;
printf (“a= %d\n”, a) ;
printf (“b= %d\n”, b) ;
swap (a, b) ;
printf (“a= %d\n”, a) ;
printf (“b= %d\n”, b) ;
return (0);
}
Notice, that we put quotes around “header” files we create, and we use <> – angle brackets, for
header files that come with our compiler. Next, try compiling program6 with the following
command:
clang program5_swap.o program6.c –o program6
Notice inside program6.c, we didn’t need to put the function declaration, because it is contained
inside program5_swap.h. The compiler will open this file (when it is compiling your program6.c)
and substitute in the contents of the header file into your program6.c. Try running your program:
./program6 and make sure it’s working properly.
CIT 593 | Assignment 10: Intro to C and its Development Environment | 10
Makefiles:
As you can see, the commands that make the compiler work get a bit cumbersome after a
while. And the more files you create as part of a large project, the more difficult it becomes to
manage all the compiling. So on Linux based systems, we like to use a utility that comes with
Linux called: “make” ; It’s a simple utility that can automate some of our compiling duties. It
even works with java files, actually any language – it’s language independent.
Open up the file entitled: “Makefile” that was preloaded onto Codio:
Scroll down in the file to the line:
program5: program5.o program5_swap.o
clang program5_swap.c program5.o -o program5
This is called the “program5” rule. A Makefile is like a cookbook, it contains a recipe for how to
create targets: program5. “program5” is also called the “target” something to make.
The items after the colon: program5.o program5_swap.o, are the items that “program5’s”
recipe depends upon. These things must exist before the “recipe” for making the program5
target can ever be run. Let’s say the file: program5_swap.o doesn’t exist. The make utility will
look for the “target” program5_swap.o. So go ahead and look for that rule yourself:
program5_swap.o: program5_swap.c program5_swap.h
clang -c program5_swap.c
If the file: “program5_swap.o” didn’t exist when we were trying to follow the program5 recipe,
the make utility will run the rule for program5_swap.o, following the recipe to make the file:
program5_swap.o first. Then it will go back to the program5 recipe and continue making it.
try running it as follows. From the terminal type:
make program5
The make utility automatically looks for a file named: “Makefile” and looks inside that for the
target you’ve typed: program5. It tries following the program5 recipe. If any of the “prerequisite”
files – aka the files that must exist first, don’t exist, it will follow the recipe for creating them. So
it should run the program5_swap.o recipe if it doesn’t yet exist, before it follows the program6
recipe. Once it’s complete, you should have the file: program5 and you can then run it.
The general format for a Makefile rule is as follows:
target: prerequisite
recipe
CIT 593 | Assignment 10: Intro to C and its Development Environment | 11
You can go target by target and run each of them. OR you can run the target at the top: all:
program1 program2 program3 program4a program4b program5 program6
This is called a “phony” target, because a file called “all” doesn’t exist, nor will it ever (because
you’ll notice it has no recipe underneath it). Instead, if you type: make all then the make
utility will look for its prerequisite files: program1 program2, etc. and if it doesn’t find them, it will
follow the recipes to make them
Try typing:
make all
You should see it create all of programs we’ve made so far: program1-6. It may also say they
are “up to date” if no changes in the .C files (or the .H) files have occurred since the targets
were created.
Next, type in:
make clean
The job of this phony target (as you will see if you look inside the Makefile) it to delete all the
“.o” files. The .o files are necessary while you are compiling your program, but they aren’t
needed when you are running your program. So this target deletes intermediate files when you
wish to clean up your folder
Lastly, if you type in:
make clobber
This phone target runs the “make clean” recipe, and it also deletes the OBJECT files themselves!
This just leaves your .C files and .H files behind. It’s handy if you just have too many files in your
folder and you want to clean things up a bit. To bring them all back, simply type:
make all
And it will compile all your files fresh! Before you turn in your HW, be sure to type in: make
clobber and it turn in only those files that remain! We will use your Makefile to regenerate
your object files and test them accordingly.
Now that you understand rules, targets, and prerequisites; create a rule for program6.
You can use program5’s rule as an example but there are some differences. Remember
that program6.c depends upon program5_swap.h. So you’ll need to incorporate that into
the prerequisite. Once you get the rule working, update the “all” and “clobber” rules
accordingly.
CIT 593 | Assignment 10: Intro to C and its Development Environment | 12
Problem #7 – Pointer Basics
Included in Codio is a file: program7.c. Examine the contents of the file and compile this
program and then run it in a Codio terminal window. You’ll see it prints out the actual memory
addresses in the x86 stack! They are in hex, so you’ll get a feeling for how the x86’s memory
and its stack are organized. Each time you run this, your program will be given a different place
in the x86’s data memory to use. So you’ll get different addresses each time your run it (that’s
normal).
Task #1: Complete the program
Notice that the second part of program7.c is incomplete, the print statements don’t actually print
the values and memory addresses that they should. Using the first part of the program as an
example, update these print statements to print out the things they are supposed to. Hint
“deref’ed” means “dereferenced.” It’s just my short hand.
Task #2: Complete the spreadsheet
The spreadsheet that you worked on in problem #4 actually has two “tabs” within it. Switch to
the problem7 tab at the bottom of Excel. The Excel spreadsheet needs to be completed. Use
the output of the execution of your program (after you complete Task #1) and reason through
what must be on the stack and where it must be on the x86 stack. This is a little tricky, but the
memory addresses will help you get a good picture of the Intel x86 stack!
Task #3: Update the Makefile
Add a rule to the Makefile to compile program7 before you turn it in.
CIT 593 | Assignment 10: Intro to C and its Development Environment | 13
Problems #8 & #9 – Debugging Basics
Included in codio are two files: program8.c and program9.c. While each of these files will
compile, they contain bugs that will prevent them from running correctly. The purpose of these
two files is not to have you debug the programs visually, but instead using a debugging tool
called: GDB (for GNU DebBugger). To use this debugger, one must compile their files using the
“-g” flag. The -g flag compiles your program as it normally would, except it keeps all the line
numbers and variable names in the final executable file, which is very helpful for debugging!
1) Watch the following tutorial on GDB:
2) Compile program #8 with the -g flag as follows (note, the tutorial uses gcc, we use clang)
clang -g program8.c -o program8
3) Add the above recipe to make program8 to your makefile and include program8 in the
clean recipe
4) Try running the program, you will see that it goes into an infinite loop and doesn’t appear
to stop, press
5) Start program8 inside gdb using the following command:
(we need to startup a little differently than the tutorial):
gdb -tui ./program8 (-tui brings up the “text user interface”)
6) Important for Grading: Turn on the gdb logger with the following command inside gdb:
set logging on
This will output all the debugging output into a file called gdb.txt. Do not delete this file.
You must do this command each time you launch gdb.
Alternatively, you can start gdb with logging enabled automatically:
gdb -tui -ex “set logging on” ./program8
7) Now follow the steps in the tutorial to debug program8.c. Make the necessary changes
such that program8.c produces the correct output.
8) Once you finish, repeat this debugging process for program.9.c
As a side note for future HW’s, if you happen to write a program that requires command line
arguments, you can pass arguments to your program like this:
gdb -tui –args ./program8 argument1 argument2 …
A second wonderful tip for debugging is to have the compiler warn you of poor programming
choices! You should always use the “Warnings All” flag when compiling your programs in C by
using the following flag:
clang -Wall -g program8.c -o program8
Important Note on Plagiarism:
• We will scan your HW files for plagiarism using an automatic plagiarism detection tool.
• If you are unaware of the plagiarism policy, make certain to check the syllabus to see the
possible repercussions of submitting plagiarized work (or letting someone submit yours)