C++ Pointers And Arrays

Chris Tralie

As a continuation of our tricky examples on pointers, I'm going to show some examples below on pointers and arrays. I'll also briefly show some info on dynamic memory.

Array Example 1

As we saw in class, this code will print out 100. This is because in C++, arrays are actually pointers! (by contrast, in Java, arrays are objects). In particular, an array points to the first address of a chunk of memory with all of the elements in the array

This explains why we don't ever know the length of the array unless we store it separately.

Array Example 2

Below is another example

Since an int type is 4 bytes, and since pointers address memory at the byte level, we might be tempted to say that arr+4 will jump to the very next element and that x will be 200. However, pointer arithmetic in C/C++ takes into account the size of the type it's pointing to. So when we say arr+4, this is actually jumping by 4 integer places in memory, so by a total of 4x4 = 16 bytes. This means that we can actually translate array syntax in C/C++ to pointer arithmetic:

Array SyntaxPointer Syntax
arr[i]*(arr+i)

Although we rarely do this in practice.

Array Example 2 Continued

Let's actually examine the addresses of different elements in the array so you can see I'm not lying about ints taking up 4 bytes:

On my machine, this prints out

Though this may vary on your computer since the stack moves around for security reasons. But note how 104-88 = 16, so we see that the element at index 4 is indeed 16 bytes ahead of the element at index 0;

Array Bug 1

Below is a slight tweak on the example we just did

The issue here is that an array with 4 elements isn't big enough to hold the 5 elements we're trying to assign to it! So our code will actually just print something random that happens to be left in memory directly after our array.

Character Arrays Example 1

Below is an example of an array of char variables, which forms a string

The output of this is

A few things to point out

  • This prints out "Hello world", as expected, but notice how we have an extra character \0 at the end. This is the so-called null terminator that's used to indicate that we've reached the end of a string. Since C/C++ arrays don't have a length stored with them, we need something like this to tell us when to stop.
  • When we look at the addresses of x and x+11, we see that the last element of the array is 11 bytes ahead of the first element. This makes sense, because a char is a single byte in size.
  • As a sanity check, x+11 and &x[11] are the same address. This should make sense to you given what you now know about the relationship between array syntax and pointer arithmetic in C/C++.

Character Arrays Example 2

The above example is kind of an insane and cumbersome way to initialize a character array. We can initialize it instead with a "string literal"

Rather than being stored on the stack, this string literal is actually stored in the "data" region further up in memory, and we just set x to point to the beginning of it.

Character Arrays Example 3

Below is a slightly bizarre example of a character array. Actually, I start off declaring an array of 3 integers (12 bytes long), and then I create another pointer to the beginning of this array, but cast it as the char* type. I then fill in the characters 'h' and 'i' in the first two bytes of this chunk of memory

If you run this, you will get the following output:

The first two characters look right, but I never put in a null terminator, so it keeps going through the bytes taken up by the ints I declared in the array between lines 4 and 6 until it encounters as 0 (which is the null terminator). It ends up printing some weird characters in between corresponding to the encoding of the ints I made. Furthermore, since I used the same address as the int array to store my string, I corrupted the first int in the array when I wrote "hi" there, so the number is slightly off.

Dynamic Memory Example

Below is a simple example showing how to allocate and de-allocate dynamic memory into an array at runtime. This is very useful if we don't know how large of an array we will need until runtime. We can't take this memory from the stack because C++ needs to know at compile time how big the stack frames are for each method, so the program instead takes it from a region known as the heap.