-1

I have searched and searched for a solution to this, can find plenty of answers about multidimensional arrays of int, float, double, but no char*. I think I have a grasp of the principles of pointers, know the difference between char, char* and char[] etc but a pointer to a 2D array of char pointers has got the better of me. I'm trying to parse a csv file and fill my 2D array with strings (char*). Here's my code:

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

#define COLS 10
#define ROWS 1000

int main (void) {
        char***myarray;
        FILE *fp;
        char charTemp[100];
        char *varTemp = NULL;
        char *strTemp = NULL;
        int i, j;

        // allocate memory to hold array ROWS
        if (( myarray = (char***) malloc(ROWS * sizeof(char**))) == NULL )
            return -1;

        // then allocate memory to hold array COLS
        for (i = 0; i < ROWS; i++)
        {
            if (( myarray[i] = (char**) malloc(COLS * sizeof(char**))) == NULL )
                return -2;
        }

        // read file
        if ((fp = fopen ("myfile.csv", "r")) == NULL)
            return -3;

        // parse and fill 'myarray'
        i = 0;
        j = 0;

        while (!feof(fp) && fgets(charTemp, sizeof charTemp, fp)) {
                strTemp = strtok(charTemp, ",");
                while (strTemp != NULL) {
                    sscanf(strTemp, "%s", &varTemp);
                    myarray[i][j] = varTemp;
                    printf("%s ", myarray[i][j]);
                    j++;
                    if (j > COLS - 1)
                        j = 0;
                    strTemp = strtok( NULL, "," );
                }   
                printf("\n");
                i++;
            }
        return 0;
}

myfile.csv looks something like this:

ABCD,1,0.2,0.5,0,A123,ZZ,1,120,1
BCDE,1.038,0,0.525,0,B321,YY,1.25,100,0.7
CDEF,1,0.2,0.5,0,C3P0,XX,1,120,1
DEFG,,,,,,,,,
EFGH,1,0.3,0.8,0,R2D2,WW,1.25,120,1
FGHI,,,,,,,,,
etc.....

I know some are ints and floats etc but I want them all to go in as char* then I can atoi or whatever when I need to use them.

The printf is just to view what I have loaded to test. If I use token %.*s it displays, if I use %s it segfaults at the printf line. I assume this means I am missing null pointers at the end of my strings??

Debugging suggests varTemp is using memory out of bounds. Additionally, when using printf with %.*s on rows with no data after first key, it prints COL 0 in COL 1 position as well where there should be a NULL pointer. i.e.:

ABCD 1 0.2 0.5 0 A123 ZZ 1 120 1
BCDE 1.038 0 0.525 0 B321 YY 1.25 100 0.7
CDEF 1 0.2 0.5 0 C3P0 XX 1 120 1
DEFG DEFG
EFGH 1 0.3 0.8 0 R2D2 WW 1.25 120 1
FGHI FGHI
etc.....

I'm confused, any ideas?!

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
Rincewind
  • 138
  • 1
  • 8
  • `char*** myarray;`... no, i'm not a three-star programmer. I'm also confused :-) – Sourav Ghosh Feb 11 '15 at 11:27
  • 1
    To start with, [in C you should not cast the result of `malloc`](http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc). To continue, take a look at the second loop allocating memory, and *what* it allocates. Then realize that all `myarray[i][j]` pointers you set, you set to exactly the same. – Some programmer dude Feb 11 '15 at 11:28
  • 1
    Oh, and you don't need the `!feof(fp)` check in the loop condition, `fgets` will return `NULL` if you hit end of file and exit the loop *before* `feof(fp)` returns true. – Some programmer dude Feb 11 '15 at 11:30
  • @SouravGhosh this is one situation where the three stars appear to be a good solution. – Iharob Al Asimi Feb 11 '15 at 11:36

1 Answers1

2

You never allocate space for varTemp, you need space for the scanf()d string to be stored, so you could try this

char varTemp[100];

and scanf() this way

sscanf(strTemp, "%99s", varTemp);

and then copy the varTemp string to the array by malloc()ing and then strcpy()ing.

The reason you need to copy the string is because you will overwrite it on subsequent calls to sscanf() so you copy it and use varTemp as a buffer to store the scanf()ed string.

Also Don't cast malloc(), and this !feof(fp) check in the while loop is superfluous, it will never be true, because when you reach the end of file fgets() will return NULL and then after that feof() will be true, so it's never evaluated when it would return true.

It doesn't matter in this case because sizeof(char *) == sizeof(char **) but as a rule, you should malloc() with one star less than that of the pointer you are malloc()ing for, so

if ((myarray[i] = malloc(COLS * sizeof(char *))) == NULL)

would be easier to understand, and also on failure you just return from main() without freeing the previously allocated pointers.

Finally if COLS and ROWS are fixed values there is absolutely no need for malloc() unless your array will be resized later or they are too big for the stack to hold them.

char *varTemp is a pointer, for it to be valid it should point somewhere, you can make it point somewhere by requesting that somewher from the OS via malloc(), like

char *varTemp;
varTemp = malloc(NumberOfBytesIWant);
if (varTemp == NULL)
    ohNo_TheSystemDidNotGiveMeMemory_PerhapsThereIsNoMemoryLeft_IShouldNotContinue();
/* now varTemp is accessible and you are allowed to write NumberOfBytesIWant
 * into it. But you must remember to calll 'free(varTemp)' later when you no
 * longer need the data.
 */

I'ts not the only way you can make it point somewhere, this way you can dynamically allocate space and that is very often the appropriate solution when you have found out how many bytes you need, then just ask for that amount, nothing more and nothing less, but this would also work

char array[100];
char *varTemp;

varTemp = array;

arrays in c decay to pointers, so the above is valid and in this example varTemp is also accessible and you can for instance sscanf(sourceString, "%99s", varTemp);.

But when you are not pointing to any valid memory address with your pointer, trying to access it is undefined behavior, since it's not defined where the pointer is pointing to.

Community
  • 1
  • 1
Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
  • Thanks iharob, very informative. For clarification, how come char *varTemp is no good and char varTemp[100] is required? Does sscanf require an array of chars to use as buffer rather than a linked list? – Rincewind Feb 11 '15 at 13:37
  • @user4554380 I think I made it clear now, if I didn't please let me know. – Iharob Al Asimi Feb 11 '15 at 13:52