Search code examples
carraysstringdynamic-arraysrealloc

Create a dynamic array of strings that will scale upon being filled up


Okay, so I'm reading though a txt file and storing each item by new line into an array of strings. The only problem is, I need to resize this array by 5 whenever it is close to filling up.

Input looks something like this, except a lot larger

CSE 1104
CSE 1105
CSE 1310
CSE 2320
IE 2308
PHYS 1443
MATH 1426

Here is my code

void printFileError(char *s) {
    printf("unable to open %s\n", s);
    exit(1);
}

void reallocTaken(char **taken, int *size, int newSize) {
    (*size) = newSize;
    taken = realloc(taken, (*size) * sizeof(char *));
    printf("Realocateding---- \n");
}

int main(int argc, char *argv[]) {
    char **coursesTaken;
    int coursesTakenSize = 5;

    int i;

    char *filePlan, *fileMyCourses;
    FILE *fp;

    /* Open the file of completed courses */
    if((fp = fopen(argv[2], "r")) != NULL) {
        char buffer[255];
        int lineCount = 0;

        coursesTaken = malloc(sizeof(char *) * coursesTakenSize);

        while(fgets(buffer, sizeof(buffer), fp) != NULL) {
            char token[] = "\n";
            char *split = strtok(buffer, token);

            while(split != NULL) {

                /* Check if array is full */
                if((lineCount + 1) == coursesTakenSize) {
                    printf("Needs to allocate \n");
                    /* Realoc Memory */
                    reallocTaken(coursesTaken, &coursesTakenSize, coursesTakenSize + 5);
                }

                coursesTaken[lineCount] = malloc((sizeof(split) + 1) * sizeof(char));
                strcpy(coursesTaken[lineCount], split);

                printf("%s\n", split);

                lineCount++;
                split = strtok(NULL, token);
            }
        }

        /* Cut out exessive memory */
        reallocTaken(coursesTaken, &coursesTakenSize, lineCount);

        printf("Line Count: %d\n", lineCount);
        printf("Size: %d\n", coursesTakenSize);
        printf("Final Size: %lu\n", sizeof(coursesTaken));
        fclose(fp);
    } else {
        printFileError(argv[2]);
    }

    for(i = 0; i < coursesTakenSize; i++) {
        printf("%d:\t%s\n", i+1, coursesTaken[i]);
    }

    return 0;
}

The plan here was to first allocate the 5 slots of memory and when the line count becomes equal to the size, add 5 more slots to it. After the file is done, resize the whole thing to the number of lines. At the same time, allocate each index of the array, the fill it with the data from the text file.

The code successfully reallocates the first two blocks, then gives the error

a.out(9410,0x7fff7f20a300) malloc: *** error for object 0x7fcb9b404bb0: pointer being realloc'd was not allocated 

That happens here

CSE 1104
CSE 1105
CSE 1310
CSE 2320
Needs to allocate 
Realocateding---- 
IE 2308
PHYS 1443
MATH 1426
MATH 1302
MATH 1323
Needs to allocate 

I noticed by commenting coursesTaken[lineCount] = malloc((sizeof(split) + 1) * sizeof(char)) the program fully runs until the end when print each

I'm not really too sure what's going on here.


Solution

  • You make a confusion between sizeof, which can give the size on a single element, of a struct, or of an array whose size is known, and strlen which gives the len of a null terminated string.

    The line coursesTaken[lineCount] = malloc((sizeof(split) + 1) * sizeof(char)), is indeed the culprit. As in next line you copy split into that, you need that it can contains all characters in split plus the terminating null.

    But as split is a char *, sizeof(split) is the size of a pointer (that is for example 4 on a 32 bits system, and 8 on a 64 one).

    You should write this line :

    coursesTaken[lineCount] = malloc((strlen(split) + 1) * sizeof(char));