简体   繁体   中英

realloc fails to reallocate a previously malloc-ed pointer

I'm working on a function to read a full line from stdin into a char* using getchar() , and it mostly works, but when I input a longer string I get

realloc(): invalid next size: 0x00000000007ca010

Here's the function:

char *readCMDLine() {
    char *cmdline = malloc(sizeof(char));
    int counter = 0;
    char c;
    while (c != 10) {
        c = getchar();
        cmdline = realloc(cmdline, sizeof(cmdline) + sizeof(char));
        cmdline[counter] = c; 
        counter++;
    }
    return cmdline;
}

Is there a way to fix this so that I could read larger lines with it?

Using sizeof(cmdline) is not doing what you expect. When used with pointers it will return the size of the pointer, not the allocated memory. This is different for arrays defined at compile time.

You can use the counter variable to keep track how many bytes are already allocated.

Also one style thing: sizeof(char) is always 1 so it is unnecessary.

It is not performant to realloc after each character. It would be better to allocate a buffer of decent size and when it's full allocate a bigger one.

The problem here is,

 sizeof(cmdline)

does not work the way you think it is. You cannot get the size of the allocated memory using sizeof on a pointer. You have to keep track of the size yourself.

Now, to add a bit on the error,

realloc(): invalid next size: 0x00000000007ca010

basically what happens is,

cmdline = realloc(cmdline, sizeof(cmdline) + sizeof(char));

does not actually resize the allocated memory as you would have thought. So, when the counter value is large enough to point to an out-of-bound memory in case of cmdline[counter] , you essentially invoke undefined behavior . So, due to the UB (and subsequent memory corruption), realloc() emits the error and throws a segmentation fault as a side effect.

Also, to add

  • Point 1:

      pointer = realloc(pointer....) 

    is a very bad syntax to use. In case realloc() fails, you'll end up losing the actual memory, too, as it will get replaced by NULL.

  • Point 2:

    getchar() returns an int and all the return values may not fit into a char (ex. EOF ). Change the type of c to int .

  • Point 3:

    You may want to null terminate your string before returning it to make it usable with string manipulation functions and a perfect suit for %s in printf() .

  • Point 4:

    You should be checking for the success of malloc() and realloc() before using the returned pointer.

There are multiple problems with your code:

  • You test while (c != 10) , but c is not initialized the first time and you do not allow for the file to end before the next '\\n' . You also assume ASCII, some systems still use EBCDIC today, where '\\n' is not 10 .
  • You realloc using sizeof(cmdline) instead of counter . sizeof(cmdline) is the size of the pointer, not the currently allocated array size.
  • You store getchar() into a char variable, EOF is not a value that can fit there, nor possibly values between 128 and UCHAR_MAX if char is signed on your platform. Use int for this.
  • You do not null terminate the allocated string. Neither malloc not realloc initialize the allocated memory.
  • You never check for malloc or realloc failure.
  • You should probably return NULL upon end of file, otherwise there is no way to tell end of file from sequences of empty lines.
  • You reallocate the buffer for each character, this is inefficient, but can be optimized at a later stage, fix the code first.

Here is a corrected version:

#include <stdlib.h>

char *readCMDLine(void) {
    char *cmdline = malloc(1);
    size_t counter = 0, size = 1;
    int c;

    if (cmdline == NULL)
        return NULL;

    while ((c = getchar()) != EOF && c != '\n') {
        char *p = realloc(cmdline, counter + 2);
        if (p == NULL) {
            free(cmdline);
            return NULL;
        }
        cmdline = p;
        cmdline[counter++] = c;
    }
    cmdline[counter] = '\0';
    if (c == EOF && counter == 0) {
        free(cmdline);
        cmdline = NULL;
    }
    return cmdline;
}

Here is an optimized version that calls realloc much less:

#include <stdlib.h>

#define ALLOCATE_INCREMENT  100
char *readCMDLine(void) {
    char *p, *cmdline = NULL;
    size_t counter = 0, size = 0;
    int c;

    while ((c = getchar()) != EOF && c != '\n') {
        if (counter > size - 2) {
            p = realloc(cmdline, size += ALLOCATE_INCREMENT);
            if (p == NULL) {
                free(cmdline);
                return NULL;
            }
            cmdline = p;
        }
        cmdline[counter++] = c;
    }
    if (counter == 0) {
        if (c == EOF || (cmdline = malloc(1)) == NULL)
            return NULL;
    } else {
        p = realloc(cmdline, counter + 1);
        if (p != NULL)
            cmdline = p;
    }
    cmdline[counter] = '\0';
    return cmdline;
} 

Combining all the suggestions:

#define BYTES_TO_ADD 80     /* a rather arbitrary number */

char * readCMDLine ( void ) {
    char * cmdline = NULL;
    size_t cmdSize = 0;
    size_t cmdLen = 0;
    int    c;

    while ((c = getchar()) != EOF) {
        if (cmdLen == cmdSize) {
            char * tmp = realloc(cmdline, (cmdSize += BYTES_TO_ADD));  
            if (tmp == NULL) { free(cmdline); return NULL; } 
            cmdline = tmp;
        }
        cmdline[cmdLen++] = (c == '\n') ? '\0' : c;
        if (c == '\n') return cmdline;
    }
    return NULL;   /* no newline */
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM