Pictured is a peer learning day occuring

 

Arrays in C


You may have heard before that arrays are pointers, and vice versa. Allow me to clarify. Arrays are not pointers. Pointers are not arrays.

Arrays are contiguous blocks of memory containing values of a particular, common data type. Pointers are used to reference specific memory blocks of a given array.

For instance, in the following, the variable arr is declared and defined to refer to an array of four integers.

int arr[4] = {1, 2, 3, 4};

This is stored in memory like so (note the 4-byte jump between blocks, because integers take up 4 bytes of memory each):

Address 0x00 0x04 0x08 0x12
Variable arr
Value 1 2 3 4

We can access elements of this array using indexing syntax (keep in mind that indices of arrays begin at 0).

$ cat main.c
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int arr[4] = {1, 2, 3, 4};

    printf("The first element of the array is %d\n", arr[0]);
    printf("The last element of the array is %d\n", arr[3]);

    return (EXIT_SUCCESS);
}
$ gcc main.c -o array
$ ./array
The first element of the array is 1.
The last element of the array is 4.

We can declare a pointer ptr to refer to the first memory address of this array.

int arr[4] = {1, 2, 3, 4}
int *ptr = arr;

Now we have a pointer in memory like so:

Address 0x00 0x04 0x08 0x12
Variable arr
Value 1 2 3 4
Address 0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27
Variable ptr
Value 0x00

Using this pointer, we can access elements of the array with an alternative method of indexing - pointer arithmetic.

$ cat main.c
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int arr[4] = {1, 2, 3, 4};
    int *ptr = arr;

    printf("The first element of the array is %d\n", *ptr);
    printf("The last element of the array is %d\n", *(ptr + 3));

    return (EXIT_SUCCESS);
}
$ gcc main.c -o array
$ ./array
The first element of the array is 1.
The last element of the array is 4.

In the above, the arithmetic *(ptr + x) is equivalent to accessing the xth element after the given memory block pointed by ptr. In other words, *(ptr + 3) == arr[3].

Alright, nothing too crazy so far. The pointer ptr is merely referencing the memory address of the first element of the array arr, right? The array variable arr is a completely separate variable from ptr, referring to the entire contiguous memory block of four integers... right?

$ cat main.c
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int arr[4] = {1, 2, 3, 4};
    int *ptr = arr;

    printf("The address of the first value of the array is: %p\n", &(*arr));
    printf("The address of the value referenced by the pointer is: %p\n", &ptr);
    printf("The first value of the array is: %d\n", *arr);
    printf("The value referenced by the pointer is: %d\n", *ptr);
    
    return (EXIT_SUCCESS);
}
$ gcc main.c -o array
$ ./array
The address of the first value of the array is: 0x7ffc23848100
The address of the value referenced by the pointer is: 0x7ffc23848100
The first value of the array is: 1
The value referenced by the pointer is: 1

Well, that’s misleading. Didn’t I just say, no, emphasize, no, repeatedly emphasize, that arrays are not pointers? The array above looks an awfully lot like a pointer to me.

I did, and I meant it. In fact, I’ll say it again - arrays are not pointers.

Despite the above output suggesting that the array variable arr is nothing but a pointer, it is not. The variable arr is just that, a variable referring to the entire array of four integers. The pointer ptr is an independent variable in and of itself referring to the memory address of the first integer in the array.

The above behavior is the result of a special kind of conversion in C, called array type decay - when a variable referring to an array is used in an expression like a printf call, it is implicitly converted to a pointer in almost all contexts.

What do I mean by almost all contexts? In the context of sizeof and &, a variable name referring to an array refers to the entire array, composed of the entirety of its elements.

$ cat main.c
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int arr[4] = {1, 2, 3, 4};

    printf("The size of the array is: %ld\n", sizeof(arr));
    printf("The address of the array is: %p\n", &arr);

    return (EXIT_SUCCESS);
}
$ gcc main.c -o array
$ ./array
The size of the array is: 16.
The address of the array is: 0x7ffe4107c4b0

In the above, and only the above, contexts, the array variable arr refers to the entire contiguous array. Note that pointers to arrays will not behave similarly.

$ cat main.c
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int arr[4] = {1, 2, 3, 4};
    int *ptr = arr;

    printf("The size of the array is: %ld\n", sizeof(arr));
    printf("The address of the array is: %p\n", &arr);
    printf("The size of the pointer is: %ld\n", sizeof(ptr));
    printf("The address of the pointer is: %p\n", &ptr);

    return (EXIT_SUCCESS);
}
$ gcc main.c -o array
$ ./array
The size of the array is: 16
The address of the array is: 0x7fff26704fd0
The size of the pointer is: 8
The address of the pointer is: 0x7fff26704fc8

I do promise, the above differences in behavior are specific to these two contexts. Otherwise, arrays are not pointers - they are merely implicitly converted to pointers in expression use.

Written by:

Brennan Baraban, Cohort 7 (SF Campus)