Using realloc to increase the size of an array - c

I am trying to scan a bunch of characters into an array. I have used malloc to set the original size of the array, but I want to use realloc to increase the size if the user enters more characters than the initial size allows. I am not quite sure where to put the realloc, or if it should be within a conditional statement.
char *strscan(void) {
int size = sizeof(char) * 10;
char *a = malloc(size);
// Below I try to read character input from the user and store it in the array.
while (a != EOF) {
scanf("%c", a);
if (trace) printf("%c", *a);
++a;
a = realloc(a, 2 * size);
}
return a;
}
As of now I still get heap buffer overflow upon entering, for example, 15 characters.

++a;
a = realloc(a, 2 * size);
This is the problem. The first argument of realloc must be a pointer that is returned by the malloc family functions, or a null pointer. a used to be one, but by ++a;, it's not any more.

I see two problems here.
The first is that you're incrementing a, then passing the incremented value to realloc. Since the pointer passed to realloc was not a value returned from malloc, calloc, or realloc, or is NULL, this can cause errors.
The second problem is that you're not increasing the size of your memory buffer after the first call to realloc, since you're always passing it 2 * size and size never changes. So you eventually run past the end of the buffer.
You need a separate pointer to keep track of where the next character should go, and you need to keep track of how big your buffer currently is and realloc only when your existing buffer is almost full.
char *strscan(void) {
size_t size = sizeof(char) * 10;
char *a = malloc(size);
char *current; // The current character
ptrdiff_t diff;
current = a;
do {
scanf("%c", current);
if (trace) printf("%c", *current);
if (current - a >= size - 1) {
size *= 2;
diff = current - a;
a = realloc(a, size);
current = a + diff; // Since "a" could change, we need to modify "current" as well
}
} while (*current++ != '\n');
*current = '\x0';
return a;
}

Remove that ++a . It is the culprit here .
realloc() accepts the either the NULL pointer or the malloc returned pointer.
Read from the man page.
Hope you understand . Happy Coding..

You need two pointers. One is the location of the buffer. The other is the location of the current character. When the difference between them is large enough, reallocate the buffer and reset the pointers.

Related

Set exact size of char array using dynamic memory allocation

I have to read from stdin some characters (i don't know how many of them, but no more than MAX) and I want to store them in an array. Is this code snippet correct?
char *c1 = (char*) malloc(MAX * sizeof(char)); //may be too much
fgets(c, MAX, stdin);
int size = strlen(c1);
char *c2 = (char*) realloc(c1, size * sizeof(char));
free(c1);
Or is there a more elegant way of determining the right amount of memory to allocate for an array when you don't know how many elements to store?
In the fgets(), you probably meant c1, not c.
You could do it like this (if you insist on using realloc()):
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 50
int main() {
char *c1 = malloc(MAX * sizeof(char));
if(!c1) {
printf("malloc() failed\n");
return -1;
}
fgets(c1, MAX, stdin);
int size = strlen(c1);
char* c2;
if(c1[size - 1] == '\n') {
c1[size - 1] = '\0';
c2 = realloc(c1, (size) * sizeof(char));
} else {
c2 = realloc(c1, (size + 1) * sizeof(char));
}
if(!c2) {
printf("realloc() failed\n");
return -1;
}
c1 = NULL;
printf("Input was: %s\n", c2);
free(c2);
return 0;
}
Here are some comments:
You expect to read a string, thus you should use char*, not
int*.
Usually, you don't want the newline read by fgets() to be kept,
thus I used c1[size - 1] = '\0';, which overwrites it. However, if the user inputs the maximum allowed characters, then there will no room for it, thus we check if a newline was really stored in our buffer.
Do not cast what malloc() returns.
In the realloc(), you should allocate space for the size of the
string, PLUS THE NULL TERMINATOR. That's why it's now size+1. However, in the case we overwritten the newline, there is no need for that, since we have already decreased the size of the string by one, thus size would suffice.
Never forget to de-allocate your memory.
However, if I were you, I would print a message warning the user to input MAX characters and avoiding the reallocation.
You see, as the ref of fgets() states, the function will:
Reads characters from stream and stores them as a C string into str until (num-1) characters have been read or either a newline or the end-of-file is reached, whichever happens first.
So, even if the user inputs more, the program will not crash.
As Paul correctly commented, it is a really good idea to use a second pointer, so that if realloc() fails, we won't lose our data.
Also note that you should't free c1, because this pointer is invalidated and should be set to NULL. c1 will likely point where c2 points, thus if we free c1, then c2 will be pointing to garbage. Or it could be that the whole block of memory is transferred somewhere else, thus c1 points to garbage, we are trying to free.

realloc() invalid old size

I am doing an exercise for fun from KandR C programming book. The program is for finding the longest line from a set of lines entered by the user and then prints it.
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;
void copy(char longest[], char line[]){
int i=0;
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){
s = (char*)realloc(s,MAXLINE + INCREMENT);
if(s == NULL){
printf("%s","Unable to allocate memory");
// goto ADDNULL;
exit(1);
}
MAXLINE = MAXLINE + INCREMENT;
}
s[i] = c;
}
if(c == '\n'){
s[i] = c;
++i;
}
ADDNULL:
s[i]= '\0';
return i;
}
int main(){
int max=0, len;
char line[MAXLINE], longest[MAXLINE];
while((len = _getline(line)) > 0){
printf("%d", MAXLINE);
if(len > max){
max = len;
copy(longest, line);
}
}
if(max>0){
printf("%s",longest);
}
return 0;
}
The moment I input more than 10 characters in a line, the program crashes and displays:-
*** Error in `./a.out': realloc(): invalid old size: 0x00007fff26502ed0 ***
======= Backtrace: =========
/lib64/libc.so.6[0x3d6a07bbe7]
/lib64/libc.so.6[0x3d6a07f177]
/lib64/libc.so.6(realloc+0xd2)[0x3d6a0805a2]
./a.out[0x400697]
./a.out[0x40083c]
/lib64/libc.so.6(__libc_start_main+0xf5)[0x3d6a021b45]
./a.out[0x400549]
I also checked realloc invalid old size but could not follow the logic of passing a pointer to a pointer to the function modifying the array.
realloc call will re-allocate memory by taking a pointer to a storage area on the heap i.e a dynamically allocated memory result of a call to malloc.
In your case the problem is that you are allocating memory on the stack and not dynamically by a call to malloc which results memory allocation on the heap. And, passing the pointer of the automatic character array line to _getline which uses it for call to realloc. So, you got the error.
Try dynamically allocating the character array line :
char* line = (char *) malloc(MAXLINE);
char* longest = ( char *) malloc(MAXLINE);
You get an invalid old size error when your code writes the memory that malloc/realloc allocated for "housekeeping information". This is where they store the "old" allocated size. This also happens when the pointer that you pass to realloc has not been properly initialized, i.e. it's neither a NULL nor a pointer previously returned from malloc/calloc/realloc.
In your case, the pointer passed to realloc is actually an array allocated in automatic memory - i.e. it's not a valid pointer. To fix, change the declarations of line and longest as follows:
char *line = malloc(MAXLINE), *longest = malloc(MAXLINE);
To avoid memory leaks, make sure that you call free(line) and free(longest) at the end of your program.
You are trying to realloc() memory that was not allocated dynamically with malloc(). You cannot do that.
Also, if realloc() fails, the original memory is still allocated, and thus still needs to be freed with free(). So do not assign the return value of realloc() to the original pointer unless it is not NULL, otherwise you are leaking the original memory. Assign the return value of realloc() to a temp variable first, check its value, and then assign to the original pointer only if realloc() was successful.
if _getline() reads 10 or more characters, it will call realloc() on memory that was not allocated with malloc(). This is undefined behavior.
Additionally, the memory allocated from realloc() will be leaked at the end of the call to _getline().
Additionally, let's assume that the input string is "0123456789\n". Then you will attempt to write to longest that value, but you will never call realloc() before you do that, which is required.
Your error is here:
int _getline(char s[])
It means _getline is a function returning int getting a pointer to char by value.
You actually want to pass that pointer (which must point to memory allocated with malloc() or be NULL) by reference.
That means, you need to pass a pointer to a pointer to char.
Correcting that error will force you to correct all follow-on errors too.
Only use free / realloc on NULL resp. on pointers returned from malloc, calloc, realloc or a function specified to return such a pointer.

Reducing the memory assigned to an array

Let's say I have an array of char containing a sentence, and I want to copy that sentence in a different array but filtering a certain character:
char a[] = "hello everybody";
char b[sizeof(a)];
char idontwantyou = 'e';
int it = 0;
for (int i=0; i<sizeof(a); ++i)
if (a[i]!=idontwantyou)
b[it++] = a[i];
I had to allocate the same amount of memory for b than the size of a because I don't know the size of the sentence without the undesired characters right?
Now, I have my b array with the sentence hllo vrybody'#·&¡` right? I mean, there is "trash" at the end of the array.
Is there any way I could "cut" the trash in the tail so the size of b is the same value than it?
I just copied b in a new array I defined as char c[sizeof(b)] but this doesn't seem like a good practise.
Maybe realloc in b would do what I want?
In-place elimination of unwanted character, and allocated array size reduction:
char *a = strdup("hello everybody");
char idontwantyou = 'e';
char *b = a;
char *c = a;
Eliminate unwanted character:
while(*b)
{
if(*b != idontwantyou)
{
*c=*b;
++c;
}
++b;
}
*c='\0';
Reduce the size of the allocated array:
b=realloc(a, strlen(a) + 1);
if(NULL == b)
/*Handle error... */;
a=b;
Test results:
printf("array = \"%s\"\n", a);
Output should be:
array = "hllo vrybody"
sizeof(b) would be equal to sizeof(a), because that is the actual size of b. To use realloc, you have to allocate b using malloc in the first place.
That would be done in a way similar to this:
char a[] = "hello everybody";
/* better use strlen here -- it will also work with char*, and not only with
statically allocated arrays */
char *b = malloc(strlen(a)+1);
/* your code here */
char *tmp = realloc(b, it);
if (!tmp) {
free(b);
/* some error here! */
} else {
b = tmp;
/* continue working with b */
}
There are some important bits in here:
You have to manually free b after you’re done, because it is allocated on the heap instead of the stack, and thus you have to release the memory on your own instead of it getting released automatically on the function exit.
The return value of realloc may be NULL. In that case, the old memory is still allocated and you have to do some kind of error handling (as sketched with the if).
If realloc succeeds, tmp points to the new memory area and b is invalid, which is why we override b with tmp.
Another way to do it is to run two passes over the string, one to count and one to copy.
You should evaluate the problem in terms of how much this operation matters in the
overall cost and cpu+memory profile of the program. Most likely, it hardly matters at all.
In which case, the simplest is probably the best.

Same structure objects memory overlap?

struct integer
{
int len;
char* str;
int* arr;
}int1, int2;
int main(void) {
printf("Please enter 1st number\n");
int1.str= str_input();
int1.len=chars_read-1;
int1.arr= func2(int1.len, int1.str);
printf(("\%c\n"), *int1.str);
printf("Please enter 2nd number\n");
int2.str = str_input();
int2.len=chars_read-1;
printf(("\n %c\n"), *int1.str );
int2.arr= func2(int2.len, int2.str);
if the input is 4363 and 78596 , the output is 4 and 7 respectively.
The output is not 4 and 4. Given that both are different objects, shouldn't both have different memory allocation?
Please note: this is NOT a typographical error. I have used the same *int1.str both times. the problem is that although I have made no changes in it, its value is changing. How?
I do not think that str_input() can make a difference.
char* str_input(void) {
char cur_char;
char* input_ptr = (char*)malloc(LINE_LEN * sizeof(char));
char input_string[LINE_LEN];
//while ((cur_char = getchar()) != '\n' && cur_char<='9' && cur_char>='0' && chars_read < 10000)
for(chars_read=1; chars_read<10000; chars_read++)
{
scanf("%c", &cur_char);
if(cur_char!='\n' && cur_char<='9' && cur_char>='0')
{
input_string[chars_read-1]= cur_char;
printf("%c\n", input_string[chars_read-1]);
}
else{
break;
}
}
input_string[chars_read] = '\n';
input_ptr = &input_string[0]; /* sets pointer to address of 0th index */
return input_ptr;
}
//chars_read is a global variable.
Thanks in advance.
you have printed the same variable, *int1.str
It will be helpful have the source code of str_input(), but it's probably that it returns a pointer to the same buffer in each call, so the second call to str_input() updates also the target of int1.str (beacuse it's pointing to the same char* than int2.str)
As noted elsewhere, both of the printf calls in your question pass *int1.str to printf.
However, if that is merely a typographical error in your question, and the second printf call passes *int2.str, then most likely the problem is that str_input returns the address of a fixed buffer (with static or, worse, automatic storage duration). Instead, str_input should use malloc or strdup to allocate new memory for each string and should return a pointer to that. Then the caller should free the memory.
Alternatively, str_input may be changed to accept a buffer and size passed to it by the caller, and the caller will have the responsibility of providing a different buffer for each call.
About the newly posted code
The code for str_input contains this line:
char* input_ptr = (char*)malloc(LINE_LEN * sizeof(char));
That declares input_ptr to be a char * and calls malloc to get space. Then input_ptr is set to contain the address of that space. Later, str_input contains this line:
input_ptr = &input_string[0];
That line completely ignores the prior value of input_ptr and overwrites it with the address of input_string[0]. So the address returned by malloc is gone. The str_input function returns the address of input_string[0] each time it is called. This is wrong. str_input must return the address of the allocated space each time.
Typically, a routine like this would use input_ptr throughout, doing its work in the char array at that address. It would not use a separate array, input_string, for its work. So delete the definition of input_string and change str_input to do all its work in the space pointed to by input_ptr.
Also, do not set the size of the buffer to LINE_LEN in one place but limit the number of characters in it with chars_read < 10000. Use the same limit in all places. Also allow one byte for a null character at the end (unless you are very careful never to perform any operation that requires a null byte at the end).

What does memcpy do exactly in this program?

I am writing a program where the input will be taken from stdin. The first input will be an integer which says the number of strings to be read from stdin.
I just read the string character-by-character into a dynamically allocated memory and displays it once the string ends.
But when the string is larger than allocated size, I am reallocating the memory using realloc. But even if I use memcpy, the program works. Is it undefined behavior to not use memcpy? But the example Using Realloc in C does not use memcpy. So which one is the correct way to do it? And is my program shown below correct?
/* ss.c
* Gets number of input strings to be read from the stdin and displays them.
* Realloc dynamically allocated memory to get strings from stdin depending on
* the string length.
*/
#include <stdio.h>
#include <stdlib.h>
int display_mem_alloc_error();
enum {
CHUNK_SIZE = 31,
};
int display_mem_alloc_error() {
fprintf(stderr, "\nError allocating memory");
exit(1);
}
int main(int argc, char **argv) {
int numStr; //number of input strings
int curSize = CHUNK_SIZE; //currently allocated chunk size
int i = 0; //counter
int len = 0; //length of the current string
int c; //will contain a character
char *str = NULL; //will contain the input string
char *str_cp = NULL; //will point to str
char *str_tmp = NULL; //used for realloc
str = malloc(sizeof(*str) * CHUNK_SIZE);
if (str == NULL) {
display_mem_alloc_error();
}
str_cp = str; //store the reference to the allocated memory
scanf("%d\n", &numStr); //get the number of input strings
while (i != numStr) {
if (i >= 1) { //reset
str = str_cp;
len = 0;
}
c = getchar();
while (c != '\n' && c != '\r') {
*str = (char *) c;
printf("\nlen: %d -> *str: %c", len, *str);
str = str + 1;
len = len + 1;
*str = '\0';
c = getchar();
if (curSize/len == 1) {
curSize = curSize + CHUNK_SIZE;
str_tmp = realloc(str_cp, sizeof(*str_cp) * curSize);
if (str_tmp == NULL) {
display_mem_alloc_error();
}
memcpy(str_tmp, str_cp, curSize); // NB: seems to work without memcpy
printf("\nstr_tmp: %d", str_tmp);
printf("\nstr: %d", str);
printf("\nstr_cp: %d\n", str_cp);
}
}
i = i + 1;
printf("\nEntered string: %s\n", str_cp);
}
return 0;
}
/* -----------------
//input-output
gcc -o ss ss.c
./ss < in.txt
// in.txt
1
abcdefghijklmnopqrstuvwxyzabcdefghij
// output
// [..snip..]
Entered string:
abcdefghijklmnopqrstuvwxyzabcdefghij
-------------------- */
Thanks.
Your program is not quite correct. You need to remove the call to memcpy to avoid an occasional, hard to diagnose bug.
From the realloc man page
The realloc() function changes the size of the memory block pointed to
by ptr to size bytes. The contents will be unchanged in the range
from the start of the region up to the minimum of the old and new
sizes
So, you don't need to call memcpy after realloc. In fact, doing so is wrong because your previous heap cell may have been freed inside the realloc call. If it was freed, it now points to memory with unpredictable content.
C11 standard (PDF), section 7.22.3.4 paragraph 2:
The realloc function deallocates the old object pointed to by ptr and returns a pointer to a new object that has the size specified by size. The contents of the new object shall be the same as that of the old object prior to deallocation, up to the lesser of the new and old sizes. Any bytes in the new object beyond the size of the old object have indeterminate values.
So in short, the memcpy is unnecessary and indeed wrong. Wrong for two reasons:
If realloc has freed your previous memory, then you are accessing memory that is not yours.
If realloc has just enlarged your previous memory, you are giving memcpy two pointers that point to the same area. memcpy has a restrict qualifier on both its input pointers which means it is undefined behavior if they point to the same object. (Side note: memmove doesn't have this restriction)
Realloc enlarge the memory size where reserved for your string. If it is possible to enlarge it without moving the datas, those will stay in place. If it cannot, it malloc a lager memory plage, and memcpy itself the data contained in the previous memory plage.
In short, it is normal that you dont have to call memcpy after realloc.
From the man page:
The realloc() function tries to change the size of the allocation pointed
to by ptr to size, and returns ptr. If there is not enough room to
enlarge the memory allocation pointed to by ptr, realloc() creates a new
allocation, copies as much of the old data pointed to by ptr as will fit
to the new allocation, frees the old allocation, and returns a pointer to
the allocated memory. If ptr is NULL, realloc() is identical to a call
to malloc() for size bytes. If size is zero and ptr is not NULL, a new,
minimum sized object is allocated and the original object is freed. When
extending a region allocated with calloc(3), realloc(3) does not guaran-
tee that the additional memory is also zero-filled.

Resources