Search code examples
cpointersreallocsingly-linked-list

_CrtIsValidHeapPointer(pUserData) Error: Realloc() pointer


I got the _CrtIsValidHeapPointer(pUserData) Message when compiling my Code in VS2012 (C).

I realized I was trying to use the realloc on the pointer so when I read other posts, many suggested using memcpy and const char **. I really love to do so but I just do not know how.

First Fragment of Code: Typedef struct of the Pointer that I am using to realloc

int clientcounter = 0; //Global variable since this will be needed in other functions

typedef struct client{
    char name[254];
    int birthday;
    int landline;
    int cellphone;
    int clientnum;
    struct client *next; 
}clientdata;

My program reads the content of the file and stores it to these variables through Singly linked-list.

Second Fragment of Code:

void loading_file(clientdata curr[])
{
    clientdata *dummy[10000] = {0};
    clientdata *head;
    clientdata *tail;

    head = NULL;
    tail = NULL;

    //.... Asking for roster file

    //.. memory allocation of dummy[clientcounter]

    //.... Storing data to variables

    if (head == NULL)
    {
        head = dummy[clientcounter];
    }
    else
    {
        tail->next=dummy[clientcounter];
    }
    tail = dummy[clientcounter];

    if (head!=NULL)
    {
        dummy[clientcounter-1] = head;
        do {
            printf("\n ::%s:: Name\n",dummy[clientcounter]->name);
            curr[clientcounter] = dummy[clientcounter];
            dummy[clientcounter] = realloc(dummy,sizeof(item*) * clientcounter); //Error appears Here
            dummy[clientcounter] = NULL;
            clientcounter++;
            dummy[clientcounter] = dummy[clientcounter-1]->next;
        }while(dummy[totalitem] != NULL);
    }
    free(dummy[totalitem-1]);
}

Someone told me that everytime when clientcounter++ happens, I have to reallocate the memory so that it won't leak. My problem is, I know what causes the error (reallocating the pointer) but I do not know how to fix it.

Can anyone teach me how to fix the code?


Solution

  • If the end-goal is to simply load a file of data into a linked list, most of the posted code is extraneous and simply not needed. Following is a very simple implementation of loading a data file into a forward linked list.

    What Is Needed

    The general algorithm is this:

    • Read a single line from the input file.
    • Parse the line into our structure members.
    • If all the members properly parsed, create a new item and add it to the list.

    The last step is the core of the above code, and I'll explain how it works after the code. this modified function takes the file name being processed as the only input parameter, and returns a linked list of clientdata items, the parsing of which you still have to write, so don't think you're getting all of this for free:

    The Code

    clientdata* load_file(const char fname[])
    {
        FILE *fp = fopen(fname, "r");
        char line[512]; // suitable value
    
        // pp always holds the *address* of the pointer where the
        //  next node will be added to the list. notice how it first
        //  holds the address of our list-head pointer.
        clientdata *result = NULL;
        clientdata **pp = &result;
    
        while (fgets(line, sizeof(line), fp))
        {
            clientdata val = {0};
    
            //
            // TODO: parse line into val-members using things like
            //  sscanf(), strtok(), or others, etc.
            //
    
            if (parsed-successfully-condition)
            {
                // allocate a new list node, copy over the data
                *pp = malloc(sizeof(**pp));
                if (*pp)
                {
                    memcpy(*pp, &val, sizeof(**pp));
    
                    // load pp with the address of the 'next' member for
                    //  the node we just allocated and saved. it will be
                    //  the link to the next new node if we need one
                    pp = &(*pp)->next;
                }
                else
                {   // malloc() failed. not good. no sense in continuing.
                    perror("Failed to allocate new node.");
                    break;
                }
            }
        }
    
        // required. this terminates the list.
        *pp = NULL; 
        return result;
    }
    

    The Explanation

    We keep two pointers. One is the return result pointer (result) and is initially NULL. The other is a pointer-to-pointer, pp, that initially holds the address of our return result pointer. This is a special variable. It is a pointer-to-pointer that will always hold the address of the next pointer we need to set when making a new allocation. A picture speaks a thousand words. The initial configuration looks like this:

    result = NULL;
    ^---- pp
    

    When we insert a new node we populate whatever pointer who's address is being held in pp. The first time that address will be pointing to result. Note the dereference, *pp. So after adding a new node, then moving the address of the new node's next pointer in to pp, the picture now looks like this:

    result --> item0
               next
    pp --------^
    

    And after the second insertion

    result --> item0
               next --> item1
                        next
    pp -----------------^
    

    And finally one more...

    result --> item0
               next --> item1
                        next --> item2
                                 next
    pp --------------------------^
    

    The algorithm finishes by terminating the list. This is done by setting the pointer addressed by pp to NULL. Note the wording there. We are NOT setting pp to NULL (that would be pointless). We're setting the pointer it addresses to NULL.

    *pp = NULL;

    The resulting picture would finish like this:

    result --> item0
               next --> item1
                        next --> item2
                                 next --> NULL
    

    In each case the insertion mantra is always the same.

    • Store a new allocation in the node pointed to by *pp
    • copy over the data to the new allocation
    • store the next pointer to receive the next new allocation in pp
    • loop until we finish reading the file.
    • upon exiting the loop, set *pp to NULL, since it is the last next pointer in our chain, and should be NULL to terminate the linked list.

    There are some interesting base-conditions that are worth understanding. A notable one is "What happens when the input file has no items? Does this still work?" Yes, indeed it does. In that case the while-loop will never add a new item, pp will still contain the address of result, and *pp = NULL; will (redundantly) set it to NULL. The result will be a NULL return value, exactly what you want for a linked list that is "empty".

    I strongly suggest running this in a debugger to see how pp, *pp, and **pp work in this algorithm.