Manually Allocating a Pointer Array

A few Lessons ago, I mentioned that a variable such as **months couldn’t be used to declare an array. That’s true because the **months construction doesn’t use array notation. Duh. That doesn’t mean that **months is totally out of luck.

Before venturing any further, I understand that this topic is insanely advanced. It’s utterly acceptable if you utterly want to eschew any variable in the C language that begins with two asterisks. Feel free to merrily skip this Lesson. La-di-da.

Buried in the bosom of digital storage, an array of strings is really a collection of memory locations or addresses. To code those addresses in the C language, you can create a pointer array. For example:

char *dwarfs[7] = {
    "Bashful",
    "Doc",
    "Dopey",
    "Grumpy",
    "Happy",
    "Sleepy",
    "Sneezy" };

The *dwarfs[] array isn’t a two-dimensional string array; it’s an array of pointers. Each pointer shows the starting location in memory of the seven strings.

If you were to examine the *dwarfs[] array in memory, it would look something like Figure 1, which is how the Code::Blocks debugger displays such a beast.

Figure 1. A dump of the *dwarfs[] array. It lists the memory locations (pointers) to each string in the array.

Figure 1. A dump of the *dwarfs[] array. It lists the memory locations (pointers) to each string in the array.

For comparison purposes, there is the same data constructed as a two-dimensional char array:

char dwarfs[7][8] = {
    "Bashful",
    "Doc",
    "Dopey",
    "Grumpy",
    "Happy",
    "Sleepy",
    "Sneezy" };

The string Bashful is the longest, requiring 8 bytes of storage (7 characters and one place for the \0). Therefore, the second dimension of the array must be 8 characters for all seven elements. A memory dump of how the array is stored is shown in Figure 2.

Figure 2. Storage for of an array of strings.

Figure 2. Storage for of an array of strings.

As you can see, it’s more wasteful to store the strings as an array of text characters. In Figure 2, you see a lot of null characters (\0, which appears as code 00 in the memory dump). It’s far more efficient to store memory locations, which reference the strings stored one after the other elsewhere in memory.

Of course, these days, conserving a few bytes of storage isn’t a major concern.

Actually, to get back to the point, when storing an array of strings as memory locations, you can — if you’re crazy enough — build those strings first and then pack their memory locations into an array. The entire operation can be done without using array notation. That’s where the ** notation comes into play:

The variable type **dwarfs is similar to *dwarfs[] in many respects. One of the big differences is that storage for **dwarfs must be allocated, either by assigning that variable to an already-declared pointer array or by using the malloc() function to set aside storage. The following code demonstrates how that second solution works:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    char *dwarf0 = "Bashful";
    char *dwarf1 = "Doc";
    char *dwarf2 = "Dopey";
    char *dwarf3 = "Grumpy";
    char *dwarf4 = "Happy";
    char *dwarf5 = "Sleepy";
    char *dwarf6 = "Sneezy";
    char **dwarfs;
    int x;

    dwarfs = (char **)malloc(sizeof(dwarf0) * 7);
    if(dwarfs == NULL)
    {
        puts("Unable to allocate pointer array");
        exit(1);
    }

    *(dwarfs+0) = dwarf0;
    *(dwarfs+1) = dwarf1;
    *(dwarfs+2) = dwarf2;
    *(dwarfs+3) = dwarf3;
    *(dwarfs+4) = dwarf4;
    *(dwarfs+5) = dwarf5;
    *(dwarfs+6) = dwarf6;

    for(x=0;x<7;x++)
        printf("Dwarf %d is %s\n",x+1,*(dwarfs+x));

    return(0);
}

In this code, the strings are declared by themselves in Lines 6 through 14. Each of the string variables represent memory locations — pointers.

At Line 16, memory is allocated to store 7 pointer variables. The sizeof operator obtains the storage required for a char pointer. That value is multiplied by 7. The result of the malloc() function is typecast to reference an array of pointers, which is what the (char **) thingy does. The result is assigned to the dwarfs variable, which is also a ** type of variable.

Yeah, it’s messy, but it does work and if you know your pointers, it makes sense.

Once the storage is allocated, the pointers from each individual string can be assigned to **dwarfs. Array notation isn’t used because (once again) **dwarfs isn’t an array; it’s a pointer-to-a-pointer thing. Therefore the pointer equivalent notation is used in Lines 23 through 29. (By “pointer equivalent” I mean pointer notation that’s equivalent to array notation.)

Finally, to prove that it all worked, a for loop displays the “array” at Line 31.

Beyond this example, and code similar to it, you really don’t see the ** type of variable used that often. Next Lesson I’ll present perhaps the most common and practical example.

Leave a Reply