Search code examples
carrayspointersfree

Function for freeing array of strings


In my program I have a function called freeFx();

this function is fed two arrays and a count of records to free.

I am getting an invalid pointer error when this function is called. I don't quite understand where this error is coming from, any help would be awesome!

here is the code:

/* ---- LIBRARIES ---- */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* ---- PROTOTYPES ---- */
int readFx(char** charArr, int* intArr);

/* int sortFx(char** arr, int arg2);
int printFx(char** arr, int arg2); */

int freeFx(char** charArr, int* intArr, int cnt);
char* getToken(char arr1[], int loc);
void makeRoom(char*** t, int** z, int size);

/* ---- MAIN ---- */
int main(void)
{
    char** charPntrArr;
    int* intPntrArr;
    char* fileText;
    int iniArrSize = 10;
    int recCnt = 0;
    int i = 0;

    /* array to store addresses of arrays forming the rows */

    charPntrArr = malloc(iniArrSize * sizeof(char*));
    intPntrArr = malloc(iniArrSize * sizeof(int));

    recCnt = readFx(charPntrArr, intPntrArr);
    printf("%d\n", recCnt);

    /*sortFx(pntrArr, recCnt);
     printFx(pntrArr, recCnt); */
    freeFx(charPntrArr, intPntrArr, recCnt);

    return;
}

/* ---- FUNCTIONS ---- */

int readFx(char** charArr, int* intArr)
{
    /*
    input: csv file of string arrays
    output: count of records received
    purpose: read file, store values in array and populate pointer array
    */

    char buffer[350];
    char temp[350];
    char temp2[350];
    char*** reallocTemp;
    char* token;
    int counter;
    int subLoc = 4;
    int enrLoc = 9;
    int arrSize = 10;

    /* Clear headers */
    fgets(buffer, sizeof(buffer), stdin);

    counter = 0;

    /* While file stream is not null */
    while (fgets(buffer, sizeof(buffer), stdin) != NULL)
    {

        /* Populate array within array if pntr arr has room */
        if (counter < arrSize)
        {

            /* buffer copy*/
            strcpy(temp, buffer);
            strcpy(temp2, buffer);

            /* create array for token values*/
            charArr[counter] = malloc(10 * sizeof(char));

            /* Get first token */
            token = getToken(temp, subLoc);

            strcpy(charArr[counter], token);

            /* Get second token */
            token = getToken(temp2, enrLoc);

            intArr[counter] = atoi(token);

            counter++;
        }
        else
        {
            /* Reallocate memory due to necessary expansion */
            makeRoom(&charArr, &intArr, arrSize);

            /* Realloc was successful */
            if (temp != NULL)
            {
                arrSize = arrSize * 2;

                /* Print Reallocation info */
                printf("reallocating to %d\n", arrSize);

                /* Populate values for current buffer now that you have realloc'd */

                /* buffer copy*/
                strcpy(temp, buffer);
                strcpy(temp2, buffer);

                /* create array for token values */
                charArr[counter] = malloc(10 * sizeof(char));

                /* Get first token */
                token = getToken(temp, subLoc);

                strcpy(charArr[counter], token);

                /* Get second token */
                token = getToken(temp2, enrLoc);

                intArr[counter] = atoi(token);

                counter++;
            }
            else
            {
                printf("unable to reallocate\n");
                exit(1);
            }
        }



    }
        return counter;
}

    char* getToken(char arr1[], int loc)
    {

        /*
        input: string array & location of desired string
        output: string of token at position
        purpose: grab string (char*) of certain position in given array
        */

        int loopCnt;
        char* del = ",\n";

        /* Grab first token */
        char* token = strtok(arr1, del);

        /* Loop through array to grab value at given location */
        for (loopCnt = 1; loopCnt < loc; loopCnt++)
        {
            token = strtok(NULL, del);
        }

        return token;
    }

    int freeFx(char** charArr, int* intArr, int cnt)
    {
        int i;

        printf("INSIDE FREE FX\n");
        for (i = 0; i < cnt; i++)
        { 
            printf("%d\n", i);
            free(charArr[i]);
        }
        printf("FREED ARRAYS WITHIN ARRAY\n");

        free(charArr);
        printf("CHAR ARR FREE\n");

        free(intArr);
        printf("INT ARR FREE\n");

        return 0;
    }

    void makeRoom(char*** t, int** z, int size)
    {   
        *t = (char**)realloc(*t, size * 2 * sizeof(char*));

        *z = (int*)realloc(*z, size * 2 * sizeof(int*));

    }

HERE IS A SAMPLE FROM THE TEXT FILE:

Term Code,Session Code,Campus Code,Subject,Catalog Nbr,Section,Class Nbr,Class Component,Enrollment Total,Enrollment Cap,Enrollment Availability,Waitlist Total,Waitlist Cap,Instructor Name,Instructor Email,Building Code,Room Nbr,Start Time,End Time,M,T,W,Th,F,Sa,Su,Class Start Date,Class End Date
2152,1,MAIN,SOCW,6390,6,22913,IND  - Independent Study,0,1,1,0,0,,,,,12:00 AM,12:00 AM,N,N,N,N,N,N,N,1/20/2015,5/8/2015
2152,1,MAIN,MUSI,4460,3,21831,PRI  - Private Lesson,0,20,20,0,0,Michael J Drake,mjdrake@uta.edu,,,12:00 AM,12:00 AM,N,N,N,N,N,N,N,1/20/2015,5/8/2015
2152,1,MAIN,MAE,4301,1,27674,LEC  - Lecture,0,5,5,0,3,,,NH,109,7:00 PM,8:20 PM,Y,N,Y,N,N,N,N,1/20/2015,5/8/2015
2152,1,MAIN,EE,2403,101,25557,LAB  - Laboratory,11,24,13,0,0,Jonathan W Bredow,jbredow@uta.edu,NH,148A,5:30 PM,8:20 PM,Y,N,N,N,N,N,N,1/20/2015,5/8/2015
2152,1,MAIN,SOCW,6451,68,26055,PRA  - Practicum,1,1,0,0,0,Laura S Frank,laura.frank@mavs.uta.edu,,,12:00 AM,12:00 AM,N,N,N,N,N,N,N,1/20/2015,5/8/2015
2152,1,MAIN,ARCH,3331,1,20182,LEC  - Lecture,47,61,14,0,0,Edward R Nelson,nelsone@uta.edu,ARCH,401,5:30 PM,6:50 PM,Y,N,Y,N,N,N,N,1/20/2015,5/8/2015
2152,1,MAIN,BIOL,6291,7,26391,IND  - Independent Study,0,5,5,0,0,Matthew Fujita,mkfujita@uta.edu,,,12:00 AM,12:00 AM,N,N,N,N,N,N,N,1/20/2015,5/8/2015
2152,1,MAIN,BE,6194,12,30366,IND  - Independent Study,6,6,0,0,0,Young-Tae Kim,ykim@uta.edu,,,,,N,N,N,N,N,N,N,1/20/2015,5/8/2015
2152,1,MAIN,BIOL,5698,21,27536,THE  - Thesis Research,0,5,5,0,0,Laura D Mydlarz,mydlarz@uta.edu,,,12:00 AM,12:00 AM,N,N,N,N,N,N,N,1/20/2015,5/8/2015
2152,1,MAIN,EDAD,6399,7,20089,DTN  - Dissertation,2,10,8,0,0,Daniel B Saunders,saunders@uta.edu,,,12:00 AM,12:00 AM,N,N,N,N,N,N,N,1/20/2015,5/8/2015
2152,1,MAIN,BE,3344,14,26082,LEC  - Lecture,6,10,4,0,0,Baohong Yuan,baohong@uta.edu,ERB,131,11:00 AM,12:20 PM,N,Y,N,Y,N,N,N,1/20/2015,5/8/2015
2152,1,MAIN,EDAD,6390,11,26017,LEC  - Lecture,0,10,10,0,0,Yi Zhang,lyzhang@uta.edu,,,12:00 AM,12:00 AM,N,N,N,N,N,N,N,1/20/2015,5/8/2015
2152,1,MAIN,BIOL,3454,2,20468,LAB  - Laboratory,31,30,-1,0,0,Nicholas A Long,nicholas.long@mavs.uta.edu,LS,133,1:00 PM,4:50 PM,Y,N,N,N,N,N,N,1/20/2015,5/8/2015
2152,1,MAIN,CHEM,1451,1,22411,LEC  - Lecture,118,140,22,0,0,Seiichiro Tanizaki,tanizaki@uta.edu,SH,121,9:00 AM,9:50 AM,Y,N,Y,N,Y,N,N,1/20/2015,5/8/2015
2152,1,MAIN,ME,6297,39,30394,IND  - Independent Study,1,5,4,0,0,Ashfaq Adnan,aadnan@uta.edu,,,,,N,N,N,N,N,N,N,1/20/2015,5/8/2015
2152,1,MAIN,MUSI,1243,2,21463,PRI  - Private Lesson,1,20,19,0,0,Young-Hyun Cho,yhcho@uta.edu,,,12:00 AM,12:00 AM,N,N,N,N,N,N,N,1/20/2015,5/8/2015
2152,1,MAIN,MUSI,4242,2,21728,PRI  - Private Lesson,0,20,20,0,0,Young-Hyun Cho,yhcho@uta.edu,,,12:00 AM,12:00 AM,N,N,N,N,N,N,N,1/20/2015,5/8/2015
2152,1,MAIN,EVSE,6399,44,25290,DTN  - Dissertation,1,5,4,0,0,Merlynd K Nestell,nestell@uta.edu,,,12:00 AM,12:00 AM,N,N,N,N,N,N,N,1/20/2015,5/8/2015

Solution

  • From what I see, besides the brittleness of the code in general (there is MUCH I would change), the biggest problem is by-value arrays you're passing in to your reader function.

    You initially size them here in main():

    charPntrArr = malloc(iniArrSize * sizeof(char*));
    intPntrArr = malloc(iniArrSize * sizeof(int)); // note: fixed this. also fix in your resize fn
    

    Then their values (the addresses held by each of those pointers) are passed into your reader here:

    recCnt = readFx(charPntrArr, intPntrArr);
    

    At various times the reader can (and does) resize those buffers, including relocating data if needed. There is no guarantee the base address stays the same. Thus when readFX returns, the pointers still hold the original values, but the resize efforts have long-since made those locations no longer defined as accessible.

    A quick way to address this is to do the following:

    • Change your readFX function to take its pointer parameters by address (pointers to pointers).
    • Rename the input parameters in the actual function (it will make sense when you see the code below).
    • Declare local vars that have the same original names, and are initialized with the dereferenced initial values of your params.
    • Save the locals back to the dereferenced parameters when the function finishes.

    Something like this:

    int readFx(char*** ppCharArr, int** ppIntArr)
    {
        char **charArr = *ppCharArr;
        int *intArr = *ppIntArr;
    
        /*
         input: csv file of string arrays
         output: count of records received
         purpose: read file, store values in array and populate pointer array
         */
    
        char buffer[350];
        char temp[350];
        char* token;
        int counter;
        int subLoc = 4;
        int enrLoc = 9;
        int arrSize = 10;
    
    
        counter = 0;
    
        /* Clear headers */
        fgets(buffer, sizeof(buffer), stdin);
    
        /* While file stream is not null */
        while (fgets(buffer, sizeof(buffer), stdin) != NULL)
        {
    
            /* Populate array within array if pntr arr has room */
            if (counter >= arrSize)
            {
                /* Reallocate memory due to necessary expansion */
                arrSize = makeRoom(&charArr, &intArr, arrSize);
    
                /* Realloc was successful */
                if (charArr == NULL || intArr == NULL)
                {
                    printf("unable to reallocate\n");
                    exit(1);
                }
            }
    
            /* buffer copy*/
            strcpy(temp, buffer);
    
            /* Get first token */
            token = getToken(buffer, subLoc);
            if (token != NULL)
                charArr[subLoc] = strdup(token);
    
            /* Get second token */
            token = getToken(temp, enrLoc);
            intArr[counter] = atoi(token);
    
            counter++;
    
        }
    
        *ppCharArr = charArr;
        *ppIntArr = intArr;
        return counter;
    }
    

    Invoked from main() like this:

    charPntrArr = malloc(iniArrSize * sizeof *charPntrArr);
    intPntrArr = malloc(iniArrSize * sizeof *intPntrArr);
    recCnt = readFx(&charPntrArr, &intPntrArr);
    

    The freeFX call can stay as it is. That's about the quickest way I can see for you to resolve this specific issue. Note: I did some hacking up on this code, so some things will not work with a simple cut/paste back to your code base (I have makeRoom returning the new size, for example), but you can hopefully still see what the root problem was.

    Hope it helps.


    Update

    A dumbed down version with integrated allocation in the read-array and bubble-sorting of the content. I hope the OP finds it useful. This is considerably cleaner, imho, than the original version.

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    /* ---- PROTOTYPES ---- */
    int readFx(char*** ppCharArr, int** ppIntArr);
    int freeFx(char** charArr, int* intArr, int cnt);
    void sortFx(char** const charArr, int* const intArr, int cnt);
    
    /* ---- MAIN ---- */
    int main(void)
    {
        char** charPntrArr = NULL;
        int* intPntrArr = NULL;
        int recCnt = 0;
    
        /* array to store addresses of arrays forming the rows */
    
        /* read file and get record count */
        recCnt = readFx(&charPntrArr, &intPntrArr);
        sortFx(charPntrArr, intPntrArr, recCnt);
        freeFx(charPntrArr, intPntrArr, recCnt);
    }
    
    /* ---- FUNCTIONS ---- */
    
    int readFx(char*** ppCharArr, int** ppIntArr)
    {
        const int subLoc = 4;
        const int enrLoc = 9;
    
        char **charArr = *ppCharArr;
        int *intArr = *ppIntArr;
    
        char line[350];
        char* token = NULL;
        int arrSize = 0;
        int counter = 0;
        int i=0;
    
        /* Clear headers */
        fgets(line, sizeof(line), stdin);
        while (fgets(line, sizeof(line), stdin) != NULL)
        {
            // check for reallocation prior to insertion
            if (counter == arrSize)
            {
                // need to expand
                int newSize = (arrSize ? (2*arrSize) : 1);
                void *tmp = realloc(charArr, newSize * sizeof *charArr);
                if (tmp == NULL)
                {
                    fprintf(stderr, "Failed to expand charArr to %d elements", newSize);
                    exit(1);
                }
                charArr = tmp;
    
                // expand intArr likewise
                tmp = realloc(intArr, newSize * sizeof(*intArr));
                if (tmp == NULL)
                {
                    fprintf(stderr, "Failed to expand intArr to %d elements", newSize);
                    exit(1);
                }
                intArr = tmp;
                arrSize = newSize;
    
                printf("Resized arrays to %d slots\n", newSize);
            }
    
            // get tokens
            for (token = strtok(line, ",\n"), i=1; token && (i<subLoc); ++i)
                token = strtok(NULL, ",\n");
            if (token)
            {
                charArr[counter] = strdup(token);
    
                // next token
                for (; token && i<enrLoc; ++i)
                    token = strtok(NULL, ",\n");
                if (token)
                {
                    intArr[counter] = atoi(token);
                    printf("%s %d\n", charArr[counter], intArr[counter]);
                    ++counter;
                }
                else
                {
                    free(charArr[counter]);
                }
            }
    
        }
    
        *ppCharArr = charArr;
        *ppIntArr = intArr;
    
        return counter;
    }
    
    int freeFx(char** charArr, int* intArr, int cnt)
    {
        while (cnt--)
            free(charArr[cnt]);
        free(charArr);
        free(intArr);
        return 0;
    }
    
    
    void sortFx(char** const charArr, int* const intArr, int cnt)
    {
        int swapped = 1, i, j=cnt;
    
        // simple bubblesort algorithm. note there is no string copying cone here
        //  we compare strings, and if swapping is needed, swap *pointers* in the
        //  charArr pointer array (and intArr side by side, but that is unrelated)
        while (swapped && j--)
        {
            swapped = 0; // reset swap detection
    
            for (i = 0; i < j; ++i)
            {
                int cmp = strcmp(charArr[i], charArr[i+1]);
                if ( cmp > 0)
                {
                    char *strTmp = charArr[i];
                    int intTemp = intArr[i];
    
                    // do the swaps
                    charArr[i] = charArr[i+1];
                    charArr[i+1] = strTmp;
                    intArr[i] = intArr[i+1];
                    intArr[i+1] = intTemp;
    
                    // something swapped so set flag
                    swapped = 1;
                }
                else if (cmp == 0 && intArr[i] > intArr[i+1])
                {
                    int intTemp = intArr[i];
                    intArr[i] = intArr[i+1];
                    intArr[i+1] = intTemp;
    
                    // something swapped so set flag
                    swapped = 1;
                }
            }
        }
    
        printf("\nSORTED RESULTS\n");
        for (i=0; i<cnt; ++i)
            printf("%s %d\n", charArr[i], intArr[i]);
    }
    

    Output

    The following output is from the pasted sample data.

    Resized arrays to 1 slots
    SOCW 0
    Resized arrays to 2 slots
    MUSI 0
    Resized arrays to 4 slots
    MAE 0
    EE 11
    Resized arrays to 8 slots
    SOCW 1
    ARCH 47
    BIOL 0
    BE 6
    Resized arrays to 16 slots
    BIOL 0
    EDAD 2
    BE 6
    EDAD 0
    BIOL 31
    CHEM 118
    ME 1
    MUSI 1
    Resized arrays to 32 slots
    MUSI 0
    EVSE 1
    
    SORTED RESULTS
    ARCH 47
    BE 6
    BE 6
    BIOL 0
    BIOL 0
    BIOL 31
    CHEM 118
    EDAD 0
    EDAD 2
    EE 11
    EVSE 1
    MAE 0
    ME 1
    MUSI 0
    MUSI 0
    MUSI 1
    SOCW 0
    SOCW 1