0
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?

Hugo
  • 129
  • 1
  • 3
  • 14
  • 4
    I suspect you never restore `splitInput` to it's original value but without an MCVE it's impossible to tell. – Karoly Horvath Apr 08 '17 at 16:10
  • 2
    Please read [mcve]. – anonymoose Apr 08 '17 at 16:11
  • Please add additional context information about what your question actually is. Not sure what you mean by "I don't know why this code works" because there is not description as to what the desired or expected behavior nor the actual behavior observed. Also not sure as to what the pronoun "it" refers to in "But it didn't store anything." There are obvious differences between the two code snippets so your question as to "What the difference between these two code?" doesn't make much sense either. – Richard Chambers Apr 08 '17 at 16:16
  • splitInput is just a pointer to an array of pointers. in the first while loop, you put each token into each slot of the array. in the second while loop, you never advanced the pointer. you just increased what it pointers to. you save the pointer first before the while loop, e.g. char **sp = splitInput; then in the loop, just do splitInput++ instead of *splitInput++. then after the loop, you may do splitInput = sp to restore the pointer if you still need to use it in its normal way. – Shiping Apr 08 '17 at 16:19
  • I seem to remember having troubles with portability of the pointer-array duality. As far as I vaguely remember, C standard is not definite enough. I.e. two compilers can both be standard-conforming, even if one compiler has "char array[]" representend as/compatible to "char* array" and the other "char** array". Can anybody confirm? Or put me right? The relevance to the question is: One compiler might treat OPs two versions identical, while another (equally correct) treats them differently. – Yunnosch Apr 08 '17 at 16:45
  • I'm update my question with more details. And, I would reply any further comments tomorrow. Thanks for your help. – Hugo Apr 08 '17 at 17:00

1 Answers1

1

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.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • Glad I could help. C is a fantastic language that gives YOU the freedom and flexibility to craft very efficient solutions to any programming problem in a number of different way. It provides that freedom and flexibility by giving you bit-level access to memory and hardware. All of that flexibility means there is quite a bit to learn in C so YOU exercise that freedom and flexibility properly. The whole key to learning C is simply to ***slow down*** and take the time to fully understand each aspect of the language, particularly those dealing with pointers and memory addressing `:)` – David C. Rankin Apr 09 '17 at 22:46