How to realloc properly? - c

I wrote a little function to return a string made from the input given to the program, it worked fine until i traded constant size for dynamic memory allocation. After i tested with a few printf() it looks like the program crashes when realloc() is called. Am i doing something wrong with realloc(), or is it something else?
char* get_line()
{
size_t ptr_pos = 0, size = 50;
int c;
char* line = malloc(size * sizeof *line);
char* temp;
while((c = getchar()) != EOF)
{
if(++ptr_pos >= size)
{
size += 50;
temp = realloc(line, size * sizeof *line); // The program crashes on this intruction.
if(temp != NULL)
{
line = temp;
printf("Reallocation success.\n");
}
else
{
printf("Reallocation error.\n");
free(line);
exit(1);
}
}
*line++ = c;
if(c == '\n')
break;
}
if(ptr_pos == 0)
return NULL;
*line = '\0';
return line - ptr_pos;
}
Thanks for your help.

When you call realloc, you must give it the address of the beginning of the allocated memory, the same address as was originally returned by malloc. The same is true of free.
But you are modifying the value of line, so it is no longer pointing to the beginning of the block when realloc is called.
That is Undefined Behaviour, so a segfault is definitely possible.

Related

Free() doesn't work as expected after calling malloc()

I wrote the following function in C but have 2 problems:
I am asked to free the dynamically allocated memory to prevent using too much RAM but the line free(word) just causes bugs to my program.
if I delete it everything works fine, why is that happening? I used free after I finished using word as suggested in many different articles.
I am requested to use malloc with the minimum needed space but how could I do that?
currently my code allocates max_str_len blocks of RAM but if the word was much shorter like a letter I don't want to allocate 200 blocks for that.
Any suggestions please?
int read_words(char *words[], int size, int max_str_len) {
char *word;
char ch;
int i;
for (i = 0; i < size; ++i) {
word = (char *)malloc((max_str_len + 1) * sizeof(char));
if (word == NULL)
return -1;
for (int j = 0; j < max_str_len; ++j) {
scanf("%c", &ch);
if (ch == '\n') break;
if (ch == EOF || ch == 'R') return i;
word[j] = ch;
}
words[i] = word;
free(word);
}
return i;
}
You put a malloc'd char* into a caller-provided words[i] and then you free it. That doesn't make sense. If you free it, the caller can't do anything with it.
If you want the malloc'd strings to be minimal you could realloc them or you could read into a large buffer (allocated perhaps at the start of the function) and then copy the result into a malloc'd buffer that's sized just right.
Note that you're also failing to check scanf for errors and that you're leaking memory if you get a memory failure in the middle of the function—by returning -1, you effectively lose info on how many elements of words have been filled with owning pointers. You might want to return that info (return i;) or to free all pointers allocated by the function before the malloc failure.
There are multiple problems in your code:
you free the memory allocated for each word, yet you return pointers to the freed blocks in the array supplied by the caller, causing undefined behavior when the caller will dereference these pointers.
your test for end of file is incorrect: scanf() will return EOF then, bu the character will not be set to EOF, which might not be appropriate for a char anyway. You should use getchar() and make ch an int.
you should set a null terminator at the end of the string read.
you could use realloc to shrink the block of memory once you know the string length.
Here is a modified version:
int read_words(char *words[], int size, int max_str_len) {
char *word, *p;
int i, j, ch;
for (i = 0; i < size;) {
word = (char *)malloc(max_str_len + 1);
if (word == NULL) {
/* free the words allocated so far and return a failure code */
while (i-- > 0)
free(words[i];
return -1;
}
for (j = 0; j < max_str_len; ++j) {
ch = getchar();
if (ch == '\n' || ch == EOF || ch == 'R') break;
word[j] = ch;
}
if (j == 0 && (ch == EOF || ch == 'R'))
break;
word[j] = '\0';
p = (char *)realloc(word, j + 1);
if (p != NULL)
word = p;
words[i++] = word;
if (ch == EOF || ch == 'R')
break;
}
return i;
}

Dynamic buffer fgets in C

I've been searching on how to allocate a dynamic buffer using fgets, but I can't seem to get it on this example. The file has two numbers of unknown length separated by a white-space. For every line it reads each character until ' ' and \n and prints it.
char *ptr;
char line[MAX];
while(fgets(line, sizeof line , fp) != NULL){
ptr = line;
for(i=0; i<2; i++){
while(*ptr && (*ptr) != ' '){
if(*ptr == ' ')
break;
k = (*ptr) - '0';
if(k != -38) // wont print '\n'
printf("%d", k);
ptr++;
}
while(*ptr && (*ptr) != '\n') {
if(*ptr == ' '){
ptr++;
continue;
}
k = (*ptr) - '0';
printf("%d", k);
ptr++;
}
}
}
Can someone give me an idea on how to make line dynamic while still using ptr that way?
I think what you want is something like this:
size_t linelen = 80;
char *line = malloc(linelen);
while(magic_reallocating_fgets(&line, &linelen, fp) != NULL) {
/* ... do whatever you want with line ... */
}
But then, of course, the $64,000 question is, what does magic_reallocating_fgets look like? It's something like this:
char *magic_reallocating_fgets(char **bufp, size_t *sizep, FILE *fp) {
size_t len;
if(fgets(*bufp, *sizep, fp) == NULL) return NULL;
len = strlen(*bufp);
while(strchr(*bufp, '\n') == NULL) {
*sizep += 100;
*bufp = realloc(*bufp, *sizep);
if(fgets(*bufp + len, *sizep - len, fp) == NULL) return *bufp;
len += strlen(*bufp + len);
}
return *bufp;
}
That's not really complete code, it's almost pseudocode. I've left two things for you as exercises:
It has no error-checking on the malloc and realloc calls.
It's kinda grossly inefficient, in that it makes not one but two extra passes over each line it reads: to count the characters, and again to look for a '\n'. (It turns out fgets's interface isn't ideal for this kind of work.)
On systems with glibc >= 2.7, or POSIX.1-2008 support, what I think you want can be accomplished using:
char *line;
while (fscanf(f, "%m[^\n]\n", &line) == 1) {
/* do stuff with line */
free(line);
}
This works great on my Linux systems, but over in the Windows universe, Microsoft Visual C++ supports neither %m, nor any equivalent that I can find.
You cannot change the size of an array during run time in C. It is illegal. This is because arrays are allocated from the stack. To have the size be dynamic you would have to declare a pointer, and allocate the memory for it dynamically. This data is allocated from the heap.
You can change the size of allocated memory by using realloc.
int lineLen = 80;
char *line;
line = (char *)malloc(sizeof(char) * 80);
if (line == NULL) {
// Something went horribly wrong
exit(1);
}
while (fgets(line, lineLen, fp)) {
// Do something to find the size
line = (char *)realloc(line, sizeof(char) * newLen);
if (line == NULL) {
// Something went horribly wrong
exit(1);
}
}
However, allocating and reallocating memory is a rather expensive operation. As a result, you may be more effective by just choosing a big buffer size, if you can do that safely. If you have a short loop then it may not be significant enough to worry about, but it is probably not advisable to be constantly changing your buffer's size.

realloc: invalid next size and malloc: memory corruption (fast)

I am doing an exercise for fun from K and R C programming book. The program is for finding the longest line from a set of lines entered by the user and then prints it.
Inputs:
This is a test
This is another long test
this is another long testthis is another long test
Observation:
It runs fine for the first two inputs but fails for the larger string (3rd input)
Errors:
Error in `./longest': realloc(): invalid next size: 0x000000000246e010 ***
Error in `./longest': malloc(): memory corruption (fast): 0x000000000246e030 ***
My efforts:
I have been trying to debug this since 2 days (rubber duck debugging) but the logic seems fine. GDB points to the realloc call in the _getline function and shows a huge backtrace with glibc.so memory allocation calls at the top.
Here is what I have written (partially, some part is taken from the book directly):-
#include <stdio.h>
#include <stdlib.h>
int MAXLINE = 10;
int INCREMENT = 10;
char* line = NULL, *longest = NULL;
void _memcleanup(){
free(line);
free(longest);
}
void copy(char longest[], char line[]){
int i=0;
char* temp = realloc(longest,(MAXLINE)*sizeof(char));
if(temp == NULL){
printf("%s","Unable to allocate memory");
_memcleanup();
exit(1);
}
longest = temp;
while((longest[i] = line[i]) != '\0'){
++i;
}
}
int _getline(char s[]){
int i,c;
for(i=0; ((c=getchar())!=EOF && c!='\n'); i++){
if(i == MAXLINE - 1){
char* temp = realloc(s,(MAXLINE + INCREMENT)*sizeof(char));
if(temp == NULL){
printf("%s","Unable to allocate memory");
_memcleanup();
exit(1);
}
s= temp;
MAXLINE += INCREMENT;
}
s[i] = c;
}
if(c == '\n'){
s[i++] = c;
}
s[i]= '\0';
return i;
}
int main(){
int max=0, len;
line = malloc(MAXLINE*sizeof(char));
longest = malloc(MAXLINE*sizeof(char));
while((len = _getline(line)) > 0){
printf("%d%d", len, MAXLINE);
if(len > max){
max = len;
copy(longest, line);
}
}
if(max>0){
printf("%s",longest);
}
_memcleanup();
return 0;
}
You´re reallocating on copied addresses (because parameters).
A parameter in C is a copy of the original value everytime; in case of
a pointer it will point to the same location but the address itself is copied.
realloc resizes the buffer asociated with the address, everything fine so far.
But it can relocate the whole thing and assign a completely new address,
and this new address (if it happens) will be lost after the function returns to main.
Use a double pointer:
Pass a char **s instead of char *s (==char s[]) as formal parameter,
pass &xyz intead of xyz as actual value, and inside the function,
use *xyz and **xyz (or (*xyz)[index]) for address and value.
Other things:
Global variables are ugly (and confusing when named same as parameters),
multiplying with sizeof(char) is nonsense because it´s be 1 everytime,
and names in capitals should be used for #define´s rather than variables.
The double pointer alone, isn't the solution to your problems. You have 2 primary issues. You can see them by entering your strings as a string of characters and will notice you problem occurs when you pass the 20th character. (e.g. 01234567890123456789)
You have declared both line and longest globally. So while you can rewrite _getline (char **s), you can also simply update line at the end of _getline with memcpy (include string.h). For example:
memcpy (line, s, (size_t)i);
return i;
}
That cures your _getline issue. Issue two is fairly straight forward. You are not null-terminating longest in copy. (your choice of arguments with the same name as the globals presents challenges as well) Including the following fixes copy:
++i;
}
longest[i] = '\0';
}
If you incorporate both changes, then I believe you will find you routine works. You can then rewite _getline (char **s) and pass &line as another exercise. For example, you can rewrite _getline as:
int
_getline (char **s) {
int i, c;
for (i = 0; ((c = getchar ()) != EOF && c != '\n'); i++) {
if (i == MAXLINE - 1) {
char *temp = realloc (*s, (MAXLINE + INCREMENT) * sizeof (char));
if (temp == NULL) {
printf ("%s", "Unable to allocate memory");
_memcleanup ();
exit (1);
}
*s = temp;
MAXLINE += INCREMENT;
}
(*s)[i] = c;
}
if (c == '\n') {
(*s)[i++] = c;
}
(*s)[i] = '\0';
return i;
}
And then modify your call in main to:
while ((len = _getline (&line)) > 0) {

double free or corruption error happens when call free in c

I'm a newer in c, for learning it, i'm trying to write a function to manually read characters from std input. The program will read lines from std and output them, ant it will end when meets an empty line.
But it works well if the input stream only contains three lines or lesser, but it always stopped with an error if the input contains 4+ lines. The error happens when call realloc and free function: 'double free or corruption (fasttop): 0x0000000001f46030 *', why?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *readline(int *length) {
char ch, *s = NULL, *temp = NULL;
int UNIT = 3, size = 0, index = 0;
while ((ch = getchar()) != EOF) {
if (size == 0 || index >= size) {
size += UNIT;
temp = realloc(s, sizeof(char) * size);
if (s != NULL && temp != s) free(s);
s = temp;
temp = NULL;
}
s[index++] = (ch == '\n') ? '\0' : ch;
if (ch == '\n') break;
}
*length = index - 1;
return s;
}
char **readlines(int *count) {
char **lines = NULL, **tempLines = NULL;
int UNIT = 1, size = 0, index = 0;
int length = 0;
char *line = NULL;
while ((line = readline(&length)) != NULL) {
if (strlen(line) == 0) break;
if (size == 0 || index >= size) {
size += UNIT;
tempLines = realloc(lines, size * sizeof(char *));
if (lines != NULL && tempLines != lines) free(lines);
lines = tempLines;
tempLines = NULL;
}
lines[index++] = line;
}
*count = index;
return lines;
}
int main(int argc, char *argv[]) {
int length = 0, index = 0;
char **lines = readlines(&length);
printf("The lines you typed are: \n");
for (; index < length; index++) {
printf("%5s %s.\n", "-", lines[index]);
}
return 0;
}
The execute result is:
xxx#xxx:~/vmshared$ ./mylib2
abc
def
hij
The lines you typed are:
- abc.
- def.
- hij.
xxx#xxx:~/vmshared$ ./mylib2
11
22
33
44
*** Error in `./mylib2': double free or corruption (fasttop): 0x00000000017f1030 ***
your problem is here:
temp = realloc(s, sizeof(char) * size);
if (s != NULL && temp != s) free(s);
in case realloc succeeded, you free s after realloc already freed it.
you can see this answer for more details.
there's a problem with your readlines and readline function. your error is caused by freeing a pointer after realloc call.
tempLines = realloc(lines, size * sizeof(char *));
if (lines != NULL && tempLines != lines) free(lines); // wrong
temp = realloc(s, sizeof(char) * size);
if (s != NULL && temp != s) free(s); //wrong
if memory content was moved to another location, realloc frees the old pointer for you.
in your main function you never free your lines pointer.
Because you are freeing your data and then using it:
temp = realloc(s, sizeof(char) * size);
if (s != NULL && temp != s) free(s);
which then means that you are writing to freed memory - which is bad.
The function realloc can be seen as doing:
void *realloc(void *ptr, size_t new_size)
{
void* newptr = malloc(size);
size_t oldsize = find_size(ptr);
memcpy(newptr, ptr, oldsize);
free(ptr);
return newptr;
}
Of course, the REAL realloc is a lot more complex (because it checks the current block to see if it can be expanded before it allocates new data), and probably doesn't call regular malloc, but the functionality is roughly this.
The reason for storing the result of realloc in a different variable than the old pointer is for the case where it returns NULL - it couldn't expand to the new size - at that point, you need a temp and the original pointer, so you don't leak the old pointer's memory.
When you call realloc() and it is successful, the old memory location has already been freed and the new location is returned. There is a chance that the old and new locations are the same. However, either way, it is incorrect to release the old pointer. It would be eccentric to immediately release the new pointer.
Thus, this code is incorrect:
temp = realloc(s, sizeof(char) * size);
if (s != NULL && temp != s)
free(s);
s = temp;
temp = NULL;
It should probably be:
temp = realloc(s, size);
if (temp == NULL)
…report error and exit function…
s = temp;
There's no need to set temp = NULL; after the assignment, though it does no particular harm beyond marginally (immeasurably) slowing the program down.
You should not free the original memory area after a successful call to realloc.
temp = realloc(s, sizeof(char) * size);
if (s != NULL && temp != s) free(s); // This is wrong!
If realloc moves your data, it will also free the old area. You don't need to do that yourself.

C Programming getting input

How do I constantly get user input (strings) until enter is pressed in C just like string class in C++?
I don't know the input size so I can't declare a variable of fixed size or even I can't allocate memory dynamically using malloc() or calloc().
Is there any way to implement this as a separate function?
As H2CO3 said, you should allocate a buffer with malloc(), then resize it with realloc() whenever it fills up. Like this:
size_t bufsize = 256;
size_t buf_used = 0;
int c;
char *buf = malloc(bufsize);
if (buf == NULL) { /* error handling here */ }
while ((c = fgetc(stdin)) != EOF) {
if (c == '\n') break;
if (buf_used == bufsize-1) {
bufsize *= 2;
buf = realloc(buf, bufsize);
if (buf == NULL) { /* error handling here */ }
}
buf[buf_used++] = c;
}
buf[buf_used] = '\0';
Use exponential storage expansion:
char *read_a_line(void)
{
size_t alloc_size = LINE_MAX;
size_t len = 0;
char *buf = malloc(LINE_MAX); // should be good for most, euh, *lines*...
if (!buf)
abort();
int c;
while ((c = fgetc(stdin)) != '\n' && c != EOF) {
if (len >= alloc_size) {
alloc_size <<= 1;
char *tmp = realloc(buf, alloc_size);
if (!tmp)
abort(); // or whatever
buf = tmp;
}
buf[len++] = c;
}
if (len >= alloc_size) {
alloc_size++;
char *tmp = realloc(buf, alloc_size);
if (!tmp)
abort(); // or whatever
buf = tmp;
}
buf[len] = 0;
return buf;
}
In C, you have little choice: If you want to input a string of unbounded length, have to use allocations in a loop. Whether you use realloc() or a linked list of buffers, it comes down to reading (usually through fgets()), reading some more, and so on until the buffer you've just read contains a \n.
Then, depending on the method, you either already have a contiguous buffer (the realloc method) or just need to concatenate them all (the linked list method). Then you can return.
If you're lucky, your platform comes with the extension function getline() that does the realloc method for you. If not, you'll have to write it yourself.

Resources