Search code examples
carrayspointerspointer-to-pointer

storing data and printing a double pointer


char** splitInput = malloc(255 * sizeof(char*));
...
while(token != NULL)
{
    splitInput[i] = token;
    token = strtok(NULL, " ");
    i++;
}

I don't know why this code works. In my previous version,

while(token != NULL)
{
    *splitInput = token;
    token = strtok(NULL, " ");
    *splitInput++;
}

But It did't store anything. Why? What the difference between these two code?

That how I print the content of splitInput:

for(; *splitInput != NULL; *splitInput++){
    printf("%s\n", *splitInput);
}

I can print the content of splitInput in my first code but fail in the second. Why?


Solution

  • If you have not got it sorted out yet, the difference is due to the difference in using array indexing versus pointer arithmetic. When you use array indexes, the address pointed to by splitInput never changes.

    However, when using the post increment operator you are changing the address of splitInput each time you call *splitInput++. It is the same as calling *splitInput = token; splitInput += 1; So when you are done tokenizing your input, splitInput points to the next pointer address after your last assigned token.

    Whenever you allocate memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be accessed and then freed when it is no longer needed. You violate rule (1) which prevents ever being able to access the beginning of the block you allocated (and creates a permanent memory leak)

    How can you handle this? Either keep a count of the number of tokens assigned and then reset (back up) to the beginning of your block by that number of pointers, or better, simply use a separate pointer with the post-increment operator, thereby preserving the original address for the memory block in splitInput.

    A short example will illustrate:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define MAX 4096
    
    int main (void) {
    
        /* allocate & initialize */
        char **splitInput = calloc (255, sizeof *splitInput),
            **sp = splitInput,  /* create pointer to splitInput */
            buf[MAX] = "", *delim = " \n";
        int n = 0;
    
        if (!splitInput) {  /* validate all allocations */
            fprintf (stderr, "error: virtual memory exhausted.\n");
            return 1;
        }
    
        while (fgets (buf, MAX, stdin)) {
            for (char *p = strtok (buf, delim); p; p = strtok (NULL, delim)) {
                *sp++ = p;
                if (++n == MAX)  /* always protect against writing beyond bounds */
                    goto full;
            }
        }
        full:
    
        for (int i = 0; i < n; i++)
            printf ("splitInput[%3d] : %s\n", i, splitInput[i]);
    
        free (splitInput);  /* you allocate it --> you free it */
    
        return 0;
    }
    

    Example Input

    $ cat dat/fleas
    my dog has fleas
    

    Example Use/Output

    $ ./bin/ptrinc < dat/fleas
    splitInput[  0] : my
    splitInput[  1] : dog
    splitInput[  2] : has
    splitInput[  3] : fleas
    

    Of course, using array indexes, you can drop sp completely and simply use:

        while (fgets (buf, MAX, stdin)) {
            for (char *p = strtok (buf, delim); p; p = strtok (NULL, delim)) {
                splitInput[n++] = p;
                if (n == MAX)
                    goto full;
            }
        }
    

    Look things over and let me know if you have any further questions.