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:
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
. sizeof(cmdline)
instead of counter
. sizeof(cmdline)
is the size of the pointer, not the currently allocated array size. 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. malloc
not realloc
initialize the allocated memory. malloc
or realloc
failure. NULL
upon end of file, otherwise there is no way to tell end of file from sequences of empty lines. 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.