C Multi-Level Pointers

I feel like I have read a lot of anti-C articles recently.

Don't worry, I'm not going to sit here and claim C is the greatest language ever invented. However, at this point, it has taken the unfairly assigned mantel of all the worst things about unprotected programming.

I thought it might be fun to take a break from that train and explore some of the interesting features and quirks of C. I'm going to start with multi-level pointers.

A good litmus test of someone's software development skills is to ask them to explain pointers. If they can, it shows they understand the difference between both passing by-reference and by-value and also how memory is allocated and accessed by a program. Regardless of the language used, this is a fundemental concept of computer science.

A pointer is a variable that stores the address of another variable. In C, they use the * and & syntax. For example:

int i = 5;
int *p = &i; //p now holds the address of i
*p = 7; //i is now 7

A common use case of pointers is when you have dynamic requirement with your data. Imagine a program where a user types a sentence that needs to be stored in a variable and the amount of characters in that sentence. How is this varaible declared? char sentence[amount_of_characters]? Unfortunately, this is invalid C, as arrays must be declared with a static dimension. sentence could be declared with a large size to try to account for all use cases, but this is a waste of memory and will eventually fail to a large enough sentence. A pointer is how this issue is solved:

//allocate a section of memory for us to use of length 
//amount_of_characters * the size of a character
char *sentence = calloc(amount_of_characters, sizeof(char)); 
//assign sentence
free(sentence); //release the memory section

Onto to the title of this article. Take the following code:

char **sentences;

This is a multi-level pointer, i.e., it is a pointer that points at a pointer. Building off the example above, this allows you to create a dynamic amount of dynamic length sentences. To refer to the first element, you would do:

sentences[0] = //some address

However, this is also possible:

sentences[0] = calloc(some_length, sizeof(char));
sentences[0][0] = 'c';

This is one of the fun quirks of C: arrays and pointers are interchangeable in a lot of ways. The title of this article could have also been "C Multi-Dimensional Arrays." For example:

int numbers[5] = {1,2,3,4,5};
numbers[0] = 4; //numbers = {4,2,3,4,5}
*numbers = 8; //numbers = {8,2,3,4,5}
*(numbers+sizeof(int)) = 0; // numbers = {8,0,3,4,5}

In C, arrays are simply pointers to contiguous areas of memory that can be assigned. For example, the previous example could be rewritten in an "opposite" manner like:

int *numbers = calloc(5, sizeof(int));
*numbers = 4;
numbers[0] = 8;
numbers[1] = 0;

This can be a helpful way of thinking about complex programs with lots of pointers and is also a good demonstration on how close C is to the raw hardware.