2.11. Arrays

Arrays in C share some similarities with arrays in Java. They are indexed starting at 0. Write A[i] to mean the variable at index i in array A.


Arrays and pointers

C treats arrays in a way that, while useful, is initially confusing.

When you allocate an array, whether in a stack frame or in the heap, the array is treated like a pointer to the first thing in the array.

As a consequence of that, an array of ints has type int*.

C does not distinguish a pointer to a single thing from a pointer to an array of things.


Allocating an array in a stack frame

Allocate an automatic array as in the following example.

  int A[20];

The entire array is stored in the frame of the current function, and it will be destroyed when the function returns.

I have said that an array is considered to be the same as a pointer to the first thing in the array. So

  int A[20];
  int* p = A;
should be allowed. And it is. After doing that, A[i] and p[i] are the same thing, since p is equal to A.

Notice that we do not say

  int* p = &A;
A is already a pointer.


Allocating an array in the heap

To allocate an array in the heap, use malloc. But tell malloc the total number of bytes that you need. Statement

  int* B = (int*) malloc(20*sizeof(int));
allocates a new array of 20 ints and makes B point to it. Now B[i] is the i-th member in the array.


The sizeof operator

I recommend that you only use sizeof(T), where T is a type. It is the number of bytes that something of type T occupies. For example, sizeof(double) is usually 8.

You are allowed to use sizeof(x), where x is a variable. It is intended to yield the number of bytes that variable x uses. But it can be confusing. Let's suppose that we are using a 64-bit machine. Then

  long A[10];
  long* p = A;
  int m = sizeof(A);
  int n = sizeof(p);
makes m = 80 and n = 8. The compiler knows that A is an array of 10 longs, which occupies 80 bytes. But variable p is a pointer, which occupies 8 bytes.

What sizeof does with an array depends on what the compiler knows about the array.

  long* B = (long*) malloc(10*sizeof(long));
  int k = sizeof(B);
makes k = 8, since B is a pointer, and a pointer occupies 8 bytes on a 64-bit machine.

Because the meaning of sizeof(x) for a variable x depends on context, avoid using it.


Passing an array to a function

An array is a pointer. To pass an array to a function, pass the pointer.

In Java, you get the length of array A as A.length. But in C, there is no way to ask an array how large it is. You must pass that information separately. For example,

  // arraysum(A,n) returns A[0] + ... + A[n-1],
  // the sum of the first n values in array A.

  int arraysum(int* A, int n)
  {
    int sum = 0;
    int k;
    for(k = 0; k < n; k++) {
      sum = sum + A[k];
    }
  }

  void test(void)
  {
    int q[3];
    int n;

    q[0] = 5;
    q[1] = 9;
    q[2] = 7;
    n = arraysum(q, 3);
    …
  }
creates and uses function arraysum.

Returning an array from a function

To return an array from a function, always return a pointer to an array in the heap. For example, the following returns an array of n integers, all set to 0. It uses memset, which sets a collection of bytes to a particular value.

  int* zeroedArray(int n)
  {
    int  nbytes = n*sizeof(int);
    int* p      = (int*) malloc(nbytes);

    memset(p, 0, nbytes);
    return p;
  }

Array bounds errors

Watch out for array bounds errors in C. There is no automatic testing for them!

If you use an invalid index in an array, there is no telling what will happen.

Do your own array bounds checking.