Search code examples
cstringreallocpass-by-value

realloc() fails when passing pointer to char *. Why? (Because it was by-value, not reference!)


EDIT Thanks Joachim. Used the function signature as you pointed out, and passed the address of my strings in. Inside the function, I did the realloc() using *currBuffer, and placed the values into the string using (*currBuffer)[lenBuffer] .... :)

Passing the value of the pointer by-value was working fine until realloc() decided it needed to assign a different memory space. I must have only checked the pointer's address on entry to the function and not noticed that it changed later.
.........................................................

My program gets user input from stdin, and then parses it into tokens. As I read each character entered, I make a call addChrToLine(userInput, charCount-1, (char) inputChr); to void addChrToLine (char *currBuffer, int lenBuffer, char inputChr) where I add the new character to the string. Later, when I'm parsing the input, I using the same addChrToLine function while I build the parsed string. However, when the parser calls the function, it gives an error on the 25th character.

My code to read user input:

    char * userInput = malloc(sizeof(char));
    int charCount = 0;

LOOP
    if (!(inputChr == LF)) { // ignore the LF character as it denotes end of input line
        charCount++;
        addChrToLine(userInput, charCount-1, (char) inputChr);
        continue;
    }

My code to add the char to the current line:

void addChrToLine (char *currBuffer, int lenBuffer, char inputChr) {

    currBuffer = realloc(currBuffer, sizeof(char) * (lenBuffer +2));
    if (currBuffer == NULL) {
        perror(NULL);
        exit(ENOMEM);
    }
    currBuffer[lenBuffer] = (char) inputChr;
    currBuffer[lenBuffer+1] = (char) '\0';
}

My code where I parse the input:

    char * parsedCmd = malloc(sizeof(char));
    int ndxOutput = 0;

Switch statement inside a loop to handle variety of cases
    char currChr = command[ndxInput];

    if (addChr) {
        ndxOutput++;
        addChrToLine2(parsedCmd,ndxOutput-1,currChr);
//      parsedCmd = realloc(parsedCmd, sizeof(char) * (ndxOutput+2));
//      parsedCmd[ndxOutput] = command[ndxInput];
//      parsedCmd[ndxOutput+1] = '\0';
        addChr = FALSE;
    }

For input = alphabet, eg "abcde...yz", the user input string is read correctly and everything is as expected on entry to the parsing stage.

While parsing "a" to "w", the char * pointer parsedCmd correctly shows the contents. When I pass "x" to addChrToLine, it correctly places the character and adjusts the position of the null. BUT on return to the parser's next line, parsedCmd shows a null string.

The memory location of the pointer is the same during addChrToLine and after the call as it was before (so realloc() hasn't moved my data). However, when I look at the memory, the first 8 characters of the string are now 0x00 and the the string is intact inclusively after "i", all the way to "w" (no "x"). When the code tries to add "y" to parsedCmd, the realloc() statement issues an error double free or corruption (fasttop): 0x0000000000605160 ***

I'm a bit puzzled why this is happening.

And what puzzles me even more is that if I comment out the call to addChrToLine and uncomment the next three lines (which do the same thing), there is no error and the parser finishes fine.

Clearly I'm doing something wrong with my pointers or memory allocations, but I can't see what's wrong.

Can someone please help me understand what's going on here?


Solution

  • The error is that C passes arguments by value, meaning they are copied. So when you assign to currBuffer in the function you only assign (and modify) the local copy. When the function returns, the old userInput is still unchanged.

    To fix this you have to pass the pointer by reference which is done with pointers in C, that is you have to pass a pointer to the pointer:

    void addChrToLine (char **currBuffer, int lenBuffer, char inputChr);
    
    ...
    
    addChrToLine(&userInput, charCount-1, (char) inputChr);
    

    Another solution is to return the new pointer:

    char *addChrToLine (char *currBuffer, int lenBuffer, char inputChr)
    {
        ...
        return currBuffer;
    }
    
    ...
    
    userInput = addChrToLine(userInput, charCount-1, (char) inputChr);