-
Goals for this week:
-
Reminder about Unix resources and vimtutor.
-
Practice compiling and running C programs: gcc and using make.
-
Learn the basics of printing variables in the GDB debugger.
-
Set up our Swarthmore GitHub accounts.
-
Introduction to Lab 1 and C syntax.
-
1. Unix resources and vim
2. Weekly Lab Directories
Each week you will copy over some example code that we will talk through and practice during lab. These examples we go over in lab each week are related to the assigned lab, so remember to refer back the weekly lab page and the examples you copy over as helpful resources when working on lab assignments.
Start by creating a weeklylab subdirectory in your cs31
directory. In
this directory
you will create subdirectories for each week, into which you will copy files
that we will work on in lab. This week you will create a week01
subdirectory.
Your directory structure for cs31 will look something like this:
/ | -------------- / | \ bin home usr ... | --------------- / | \ ... you ... | ---------------------- / | \ ... cs31 ... | ------------------------- / \ labs weeklylab / / \ ... week01 week02 ... / Files to copy over
Let’s start by creating this directory structure and copying over some files (you may have some of this directory structure already created):
cd # cd with no argument switches to your home directory
mkdir cs31 # create a 'cs31' directory
cd cs31
mkdir labs weeklylab # create a 'labs' and 'weeklylab' directory
cd weeklylab
mkdir week01 # create a 'week01' directory
cd week01
pwd # print the current directory name
# cp <source> <destination>: dot refers to your current directory
cp ~newhall/public/cs31/week01/* . # <-- note the dot at the end of the line!
ls
Makefile README printtypes.c testprog.c testprog_nocoments.c
There is a guide on the CS Swarthmore webpage to Using Unix.
3. C Programming
3.1. Compiling and running C programs
C is a compiled language. Compiling is a process that translates C language
code text to a machine code program that the system knows how to execute
(the computer doesn’t understand high-level languages like C or Python or
Java, instead it understands low-level machine code). If compilation succeeds
(there are no syntax errors in your code), the compiler creates an executable
file named a.out
that you can run. For more info, refer to the
basics of
compiling and running C programs.
We’ll use the gnu compiler, gcc
, to translate C to an executable form:
gcc testprog.c
Then enter the path name of executable file on the command line to run it:
./a.out
gcc supports command line options to include debug information in the executable file (-g) and to specify the name of the executable file (-o filename) rather than use the default "a.out". Let’s try it out on one of the files you copied over:
gcc -g -o testprog testprog.c
./testprog
Along with the code you copied over, there’s a Makefile. In this file are rules
for compiling executables from the .c source files that are executed by typing
in the make
command. make is very convenient way to compile without having
to type in a long gcc command every time, you just need to type make
:
make # this will compile all files as specified by the all: rule
To clean up all the files that were compiled by make
, you can run
make clean
:
make clean # this removes all files generated by make (the can be rebuilt)
3.2. C types and printf
The files testprog.c
and printtypes.c
have some examples of printf
format strings for printing out values of different types. Use it as a reference
for printing out values in the lab assignment. Below is just a little info
about printf and C types.
Let’s open testprog.c
and look for examples the following things.
-
C comments
-
How to import a library (stdio.h)
-
The main function definition, function bodies ( { } ), and C statements (end in ;)
-
Declaring variables
-
printf function (similar to Python formatted print stmt)
-
Note that all the code is inside the body of a function!
Now let’s compile and run the program:
make
./testprog
C has many different types for storing integer values. These types differ by the number of bytes they use to store values, and by whether or not they store both positive and negative values.
1 byte: |
2 bytes: |
4 bytes: |
4 or 8 bytes: (depends on architecture) |
8 bytes: |
char |
short |
int |
long |
long long |
unsigned char |
unsigned short |
unsigned int |
unsigned long |
unsigned long long |
When you allocate a variable of a specific type, you get a storage location of the appropriate number of bytes associated with that variable name:
// 4 bytes of storage space to store a signed integer value
int x;
// 1 byte of storage space to store an unsigned integer value
unsigned char ch;
3.3. C functions
The syntax for functions in C is similar to that in Python, except that C function definitions must define the return type of the function and type and name of each parameter.
// sum: a function that computes the sum of two values
// x, y: two int parameters (the values to add)
// returns: an int value (the sum of its 2 parameter values)
int sum(int x, int y) {
int z; // a local variable declaration
z = x + y; // an assignment statement
return z; // return the value of the expression z
}
// a function that does not return a value has return type void.
void blah( ) {
printf("this function is called just for its side effects\n");
}
int main() {
int p; // local variable declaration
p = sum(7, 12); // call to function that returns an int value
printf("%d\n", p);
blah(); // call to void function
return 0;
}
3.4. Try out a simple function!
Try adding a simple function to testprog.c
, something like sum
above,
then make a call to it from the main
function. Compile testprog.c
and
try running it to see what happens.
Save and Recompile
If you have used Python you may be used to making changes in your editor and saving, and running it immediately. In C, there is one extra step between saving the file and running which is recompiling it. Every time you change your program you will have to recompile it to see the changes take effect! |
3.5. printf formatted output
printf
uses placeholders for specifying how a value should be printed
(how its series of bytes be interpreted). See printtypes.c
for examples.
Here is a brief summary:
### Specifying the numeric representation:
%d: print out value in decimal (base 10)
%u: print out value in unsigned decimal (base 10)
%x: print out value in hexadecimal (base 16)
%o: print out value in octal (base 8)
(there is no formatting option to display the value in binary)
### Specifying the type:
%d: int (to print numeric values of int, short, and char args)
%ld: long int
%lld: long long int
%u: unsigned
%lu: long unsigned
%llu: long long unsigned
%p: an address value
%f: float or double
%lf: double
%e: float or double in scientific notation
%g: float in either %e or %f format
%c: char (ex. 'x')
%s: string (ex. "hello there")
### Specifying field width:
%5d: print out the value in decimal in a field of width 5
%-5d: print out the value in decimal in a field of width 5, left justified
%6.4f: print out a float in a field with of 6 with a precision of 4
4. C debugger: gdb
The GNU debugger, gdb
, is the C debugger we will use in this class.
Usually, we will use gdb
to debug a program, but this week we are going to use
gdb
as calculator.
gdb’s print
command can be used to print the value of a expression in
different representations (binary, decimal, hex); you can use it as a simple
calculator to verify answers to hex, binary, and decimal arithmetic. For this
use of gdb
, we don’t have to have an executable to run gdb
on. We can just
run gdb
, and then call its print command:
$ gdb
# print an expression in different representations:
# (/t in binary, /x in hexadecimal, default is decimal):
(gdb) print/t 1234 # print/t: print decimal value 1234 in binary format
# p is shorthand for print
(gdb) p/x 1234 # p/x: print value in hexadecimal format
(gdb) p/d 1234 # p/d: print value in decimal format (the default)
# 0x is the prefix for a hexadecimal literals
# 0b is the prefix for a binary literals
# no prefix: for decimal literals
(gdb) p 0xabf1 # print the hex value abf1 as decimal
(gdb) p 0b0101 # print the binary value 0101 as decimal
(gdb) p/t 0x1234 # print the hex value 0x1234 as binary
# (note: leading 0's are not printed out)
(gdb) p/d 0b1010 # print the binary value 01010 as decimal
(gdb) p/x 0b10100000 # print a binary value in hex
# add a binary and a hex value, print result in binary
(gdb) p/t 0b101001001 + 0xa2
# you can re-cast a value as a specific C type:
# tell gdb that the value 12 is a char (1 byte) and print as binary
(gdb) p/t (char)(12)
(gdb) p/t (char)(-12) # print -12 char value (1 byte) in binary
5. One-time git / GitHub configuration
Before using Swarthmore’s GitHub Enterprise, you’ll need to complete some one time setup steps. and follow steps to clone your Lab1 repo using git: git hub for lab checkout
We will help students who did not get this to work for Lab 0 as we introduce Lab 1.
6. Lab 1 Intro
Next, we’ll take a look at the first lab assignment.