CS31 Weekly Lab Week 1

C programming, C types, printf, gdb
Start by creating a cs31/weeklylab/week01 subdirectory in your home directory into which you will copy over some files. The result will look something like this:
         /home/you 
               /
             cs31  
            /     
      weeklylab  
          / 
     week01 
       / 
  Makefile  printtypes.c  testprog.c 
Here are the commands to run to create the directory structure:
    cd                  # move into your home directory
    mkdir cs31          # make a new directory named cs31
    cd cs31             # move into your cs31 directory
    pwd                 # print current working directory
    mkdir weeklylab
    cd weeklylab
    mkdir week01
    cd week01
    pwd                 # should be: /home/you/cs31/weelylab/week01
This cp command copies everything (*) from my week01 sudirectory into your current working directory (.):
    cp ~newhall/public/cs31/week01/*  .   

Goals for this week:

  1. Learn how to compile and run a C program
  2. Learn basic C program syntax, types, and printf formats
  3. Learn how to use the gdb debugger to print different representations of values
  4. Introduction to Lab 1 Assignment and using git

Compiling and Running C programs
C is a compiled language. Compiling is a process that translates a C language program to an machine code executable 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.

We will 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 infortmation in the executable file (-g) and to specify the name of the executable file (-o filename) rather than use the default a.out. For example:
  gcc -g -o testprog testprog.c
  ./testprog
We will often use make to compile code. make executes the gcc commands listed in the Makefile to compile the two programs:
  make        # compiles testprog.c and printtypes.c 
If you run make clean it will remove the executable files (they can always be re-compiled from .c code):
  make clean  # removes executable files testprog and printtypes

Here is some more information about the basics of editing, compiling and running C programs on our system

C Program, C Types, printf
In an editor to open testprog.c:
  vim testprog.c
Notice the following in this program: Let's compile and try running the program:
  make
  ./testprog

C types and printf

The file printtypes.c has some examples of printf format strings for printing out values of different types. C has many different types for storing numeric 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. For example:
1 byte: 4 bytes:
char, unsigned char int, unsigned int, float
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:
int x;             // 4 bytes of storage space to store a signed integer value 
unsigned char ch;  // 1 byte of storage space to store an unsigned integer value

printf formatted output

printf has different placeholders for specifying how a value should be printed (how its series of bytes be interpreted). You can print a value in different ways using printf. printtypes.c for examples. Here is a brief summary of some formatting options:
### Specifying the numberic representation:
   %c: print out value as a character  
   %d: print out value as a signed decimal  (base 10)
   %u: print out value as an unsigned decimal  (base 10)
   %x: print out value in hexidecimal  (base 16) 
See more information about printf and C types for more information about printf and formatting options.

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 statment
   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;  
}

Try out:

Try adding a simple function to testprog.c, something like sum above, then make a call to in from the main function. Compile testprog.c and try running it to see what happens.

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 hexidecimal, 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 hexidecimal format
(gdb) p/d 1234           # p/d: print value in decimal format (the default)

# 0x is the prefix for a hexidecimal 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  
(gdb) p/t 0b101001001 + 0xa2  # add a binary and a hex value, print result in binary

# you can re-cast a value as a specific C type:
(gdb) p/t (char)(12)    # tell gdb that the value 12 is a char (1 byte) and print as binary
(gdb) p/t (char)(-12)   # print -12 char value (1 byte) in binary