21B. Mental Model of Arrays


Review

A program needs to be able to refer to large chunks of memory, and arrays are a mechanism to support that. We have seen the basics of arrays. This chapter looks at arrays in more detail, emphasizing the differences between arrays in Java and arrays in C++.

An array is a numbered sequence of variables, where the variable numbers are referred to as where you refer to indices (singular: index). You refer to the variable with index i of array A as A[i].

Remember that the indices start at 0. If array A contains 4 variables, then they are A[0], A[1], A[2] and A[3]. The last index in an array of size n is n−1.


An array is a pointer

C++ has an unusual way of treating arrays, and it takes a while to get used to. It is helpful to have a term for a piece of memory, possibly quite large. Let's call it a chunk. A chunk has a starting address and a size. A chunk of integers might contain memory for 1 integer (4 bytes, for 32-bit integers) or for 100 integers (400 bytes).

The unusual thing about C++ is that an array is not the same thing as a chunk. The rule is:

In C++, an array is considered to be the same thing as a pointer to the first item in a chunk of memory.

Not only is an array not a chunk, it is not even a pointer to a chunk. It is a pointer to the first variable in a chunk. For example, an array of integers is a pointer to the first integer variable in a chunk of some unspecified size. That rule has several consequences.

An array of integers has type int*.

C++ separates the issue of allocating an array from the issue of using an array. Once you have created a chunk, what you actually have is a pointer to the first variable in a chunk.

C++ does not distinguish between a pointer to one variable and a pointer to a chunk of many variables. The presence of other things after the first one in the memory is considered an allocation issue.

An array of ints has type int*, an array of characters has type char*, etc. You can think of * as meaning array, as long as you remember that the "array" sometimes only contains one variable.

There is no way for you to know just by looking at the type whether a pointer points to a single variable or to an array. When it does point to an array, there is no way for you to determine the size of the array. You need to obtain that information separately, somehow.


Pointer arithmetic

Suppose that p is a pointer of type T *, where T is a type. Keep in mind that a pointer is a memory address, which is just an integer from the computer processor's perspective.

If k is an integer, then C++ expression p + k is defined to be memory address p + k*sizeof(T ), where sizeof(T ) is the number of bytes that something of type T occupies.

For example, suppose that pointer p points to the first member of a chunk of ints, where an int occupies 4 bytes. Let's assume that p is memory address 1000. Then p+1 points to the second variable in that chunk; p + 1 is address 1004. Similarly, p+2 points to the third variable, at address 1008. By definition, p + k is computed as memory address p + 4k, when p has type int*.

If p has type T *, then expression p + k also has type T *.


p[k] abbreviates *(p+k)

Notation p[k] abbreviates *(p+k). That is, it
  1. computes the address p+k of the variable at index k in the chunk that is pointed to by p, then

  2. gets the actual variable at index k by following that pointer.

Suppose that p has type int*.

  1. p+1 also has type int*.

  2. Recall that, if p has type T *, then expression *p has type T. Since p+1 has type int*, expression *(p+1) has type int.

  3. Since p[1] abbreviates *(p+1), expression p[1] must have type int. It is the variable at index 1 in the array pointed to by p.


Comparing pointers

You can compare pointers. They work just like integers. If p and q are pointer variables, then expression p < q is true if p holds a smaller memory address than q. Expression p == q is true if p and q hold the same memory address.

Getting a pointer to an array

An array is a pointer, and you can store that pointer into any pointer variable of the correct type. An array of integers has type int*. So
  int  A[10];
  int* p = A;
  p[0] = 0;
makes variable p point to the first member of array A. Setting p[0] = 0 is equivalent to setting A[0] = 0, since pointers p and A are the same.

Watch out: array bounds errors

If your program uses a nonexistent index in an array, the error will not automatically be detected. It cannot possibly be detected automatically because an array is a pointer; it says where the array begins, but does not provide any information about where the array ends.

If you use a nonexistent index in an array, the program just computes the memory address that you refer to and uses that memory as if it is part of the array. That is a dangling pointer error, and can cause your program to devolve into chaos.

Make sure that you are alert. If you are not absolutely sure that a given index is correct, do your own array bounds check. For example, if array A has size n, then code

  if(i < 0 || i ≥ n)
  {
    printf("funname: bad array index %d\n", i);
    exit(1);
  }
checks that index i is acceptable for array A, and aborts the program if not. (The function containing this code is assumed to be funname.)


Arrays are not automatically copied

Consider the following C++ code.

  int A[10];
  int* B = A;
  A[1] = 5;
  B[1] = 7;
What is the value of A[1]? It must be 7. That is clear from the following pointer diagram.

An array is a pointer, and statement B = A only copies the pointer, not the chunk to which the pointer points.



Summary

An array is a pointer to the first item in a chunk of memory that might contain any number of items. An array of integers has type int*. An array of doubles has type double*.

It is important to keep the pointer nature of arrays in mind. Copying an array in an assignment statement such as B = A only copies the pointer, not the chunk of memory that the pointer points to. This is in the spirit of Java, where all objects are pointers to chunks of memory, and objects are not implicitly copied.

You can perform arithmetic with pointers. A + i is the same as &(A[i]), the address where A[i] is stored.

C++ has a major difference from Java when it comes to array indices that are out of bounds. In Java, such indices are automatically caught. In C++, they are not. It is up to you to ensure that your arrays are large enough and that your array indices are sensible.


Exercises

  1. How do you refer to the variable at index k in array W ? Answer

  2. What happens if you use an index that is not an allowed index for an array? Answer

  3. If array B is created by

      double B[4];
    
    what are the allowed indices for B? Answer

  4. Write statements that create an array of 100 ints and store 0 into each variable in the array. Answer

  5. Is the following allowed?

      long  B[20];
      long* p = B;
    
    Answer

  6. What does A[i] abbreviate? Answer

  7. Suppose that p has type int*. Is the following allowed?

      int k = p[4];
    
    Answer

  8. Suppose that you are using a 64-bit machine and a variable of type long occupies 8 bytes. If pointer variable p holds memory address 2000, what memory address does expression p+2 yield? Answer