CS44 Weekly Lab: week 5

C and C++ strings, C++ operator overloading, mapping types into bytes


Start by creating a w05 subdirectory in your cs44/weeklylabs subdirectory into which you will copy over some files:
  cd ~/cs44/weeklylabs
  mkdir w05 
  cd w05
  pwd
  cp ~newhall/public/cs44/week05/* .
  ls

Strings in C and C++ string
Let's look at some example code that uses both C strings (char arrays) and C++ string types that are created in different ways and then compares them in different ways.

Some more information about C strings, and other C and C++ links

C++ Operators
In C++ you can overload the behavior of most built-in C++ operators by defining them for different classes or structs. For example you can overload == to compare two objects based on your own defined "equal-to" operator function. If, for example the Auto class overloads the != operator, then the expression combining car1 and car1 with != calls the overloaded operator function rather than using the C++ default != operation:
Auto car1, car2;
if(car1 != car2) { ...
Let's look at some example code and try it out.

Here is some more information about C++ operator overloading In general, don't use C++ style reference parameters in code you write. However, reference parameters are used in some of the library code that we will use in subsequent lab assignments.

Let's look at the example code in refparams.cpp and talk about the semantics and syntax of C++ Reference Parameters. Then try running it to see what is going on.

Some more information about references see Chapt. 11

Mapping Structures into arrays of bytes
C and C++ allow a programmer to map any type onto a chunk of memory (an array of bytes). Low-level File I/O is where you may have seen something like this before---calls to read and write take a char buffer and a size, but you can write any value (ex. int value) by re-casting its address as a (char *) and passing sizeof(int) as the number of bytes to write to the file.

In general, you can treat any chunk of memory as storing any type of values by recasting the address to the appropriate type, and dereferencing values from it using the syntax of the re-casted type. Here is a simple example

struct foo {
  int x;
  int y;
};
char byte_arr[100]; 
struct foo *next;
int i =0;

next = (struct foo *)(&byte_arr[i]);
next->x = 16;
next->y = 100;

// compute next valid index into space for an array of foo 
// (also in general we need to be careful about alignment and 
// padding if storing an array of structs into an array of bytes)
i = i + sizeof(struct foo);
next = (struct foo *)(&byte_arr[i]);
next->x = 200;
next->y = 18;

// get value out of buffer by mapping type on to buffer
// (re-casting as a type) and then dereference field values
int val = ((struct foo *)(&byte_arr[i]))->x;

// or re-cast the bytes it as what-ever type I want
int val2 = *((int *)(&byte_arr[i]));
char val3 = *((char *)(&byte_arr[i]));
In gdb you can use the same syntax to re-cast a chunk of memory as a type and then access field values of that type.

Let's try out an example.