0

I recently encountered an interesting issue in a piece of code I had to write. Although I fixed the issue using a different approach to my original one, I would like to see if anybody has any idea as to why the original didn't work. Here are the condensed code snippets.

Original code:

int i, j, k;
for (i=0; i!=10, (result = fgets(newline, 1024, stream))!=NULL; i++)
{
    result = strtok(newline, " ");
    for (j=0; j!=x; j++) {
        for (k=0; k!=y; k++) {
            score_matrix[i][j][k] = result;
             printf("%d ", atoi(score_matrix[i][j][k]));
            result = strtok(NULL, " ");
        }
    }
     printf("\n");
}

In the code above, `stream' is a CSV file. Anyway, the code above prints out the CSV file as it is supposed to be:

19 17 20 18 
9 6 10 9 
12 11 10 16 
3 7 9 10 
0 5 8 6 
15 13 15 15 
20 18 18 16 
17 19 19 18 
13 15 14 12 
10 13 18 15 

However, when I take the same exact code as above but only remove a few irrelevant lines:

int i, j, k;
for (i=0; i!=10; i++) {
    for (j=0; j!=x; j++) {
        for (k=0; k!=y; k++) {
            printf("%d ", atoi(score_matrix[i][j][k]));
        }
    }
    printf("\n");
}

It prints out:

10 13 18 15 
10 0 3 8 
10 13 18 15 
10 0 3 18 
10 0 3 18 
10 13 18 15 
10 13 18 15 
10 13 18 15 
10 13 18 15 
10 13 18 15 

Which is very wrong. Fixed code (which does nothing more than convert the matrix to int, and removes the atoi):

int i, j, k;
for (i=0; i!=10, (result = fgets(newline, 1024, stream))!=NULL; i++)
{
    result = strtok(newline, " ");
    for (j=0; j!=x; j++) {
        for (k=0; k!=y; k++) {
            score_matrix[i][j][k] = atoi(result);
            printf("%d ", score_matrix[i][j][k]);
            result = strtok(NULL, " ");
        }
    }
    printf("\n");
}

and

int i, j, k;
for (i=0; i!=10; i++) {
    for (j=0; j!=x; j++) {
        for (k=0; k!=y; k++) {
            printf("%d ", score_matrix[i][j][k]);
        }
    }
    printf("\n");
}

now both print the right thing:

19 17 20 18 
9 6 10 9 
12 11 10 16 
3 7 9 10 
0 5 8 6 
15 13 15 15 
20 18 18 16 
17 19 19 18 
13 15 14 12 
10 13 18 15

Maybe I'm missing something obvious here, but I'm very curious as to why this is happening.

EDIT:

  • x --> passed by argument. In this case it's 2.
  • score_matrix --> In bad code is char* score_matrix[10][x][y];, in good it's int score_matrix[10][x][y];
  • newline --> char newline[1024];
  • result --> char* result;
  • Also, result has been validated with printf statements.
13
  • check the array bounds of score_matrix carefully, remembering that each index is zero based. Commented Apr 9, 2014 at 7:12
  • 1
    How are score_matrix, newline and x declared ? Commented Apr 9, 2014 at 7:12
  • ... and how is x calculated ? Commented Apr 9, 2014 at 7:14
  • 1
    result = strtok(NULL, " "); what does it mean?? Commented Apr 9, 2014 at 7:18
  • 1
    @wyas great, now read my comment and understand why reusing the same line buffer and storing offsets within it (which is all strotok does; return terminated substrings in the provided buffer) is wrong. It is no coincidence those rows seem to be reporting the same data as the last row. If you need the data as int anyway, just use the "fixed" version and avoid what will only be solvable with dynamic allocation (and later dynamic cleanup). Commented Apr 9, 2014 at 7:23

1 Answer 1

1

Your repeated use of the same row-buffer is wrong. The first sample's output is lulling you into a false sense of success when in reality it is anything-but-correct. Modifying the loop to dump the address of the token extracted and being saved will show evidence of this:

score_matrix[i][j][k] = result;
printf("%p ", result);
result = strtok(NULL, " ");

You will see many of your tokens in the output cube are sharing the same buffer addresses, which of course is because you're passing the same line buffer newline for each tokenization, and in the process obliterating the content in any prior loops.

If you want to store actual char * you can, but unless you have to, don't. Instead do what one of your fixed versions does: convert to int and store the int as the matrix value. Otherwise you'll need something like this for load:

int i, j, k;
for (i=0; i!=10, (result = fgets(newline, 1024, stream))!=NULL; i++)
{
    result = strtok(newline, " ");
    for (j=0; j!=x; j++) {
        for (k=0; k!=y; k++) {
            score_matrix[i][j][k] = strdup(result); // NOTE: POSIX strdup() function
            printf("%d ", atoi(score_matrix[i][j][k]));
            result = strtok(NULL, " ");
        }
    }
     printf("\n");
}

Which will later require a hella-cleanup for all those dynamic allocations. You could make a single mondo-allocation that loads the entire file into memory as a single dynamic char buffer and considerably alter your parsing loops, but I advise that even less than I advise the single-token dynamic allocation approach.

Sign up to request clarification or add additional context in comments.

2 Comments

@wyas glad it helped. Seriously. use the int solution if you have any say in the matter (and <cough> check your return values from your lib-calls before using them =P)
int solution is there to stay, but I was just curious as to why the heck this all went downhill with chars. Glad to know C is as dangerous as always :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.