Using realloc to Allocate More Memory
In the previous exercise, and in many programming contests, you know exactly how big the input is going to be, e.g. the first number in the input will tell you how much memory to allocate to store all the data.
Sometimes it's not possible to know the size when you start reading. Let's imagine you don't know how many numbers you are going to read. You made your best bet and allocated the memory for 100 integers:
int *p;
int allocated = 100;
p = malloc(allocated * sizeof(*p));
/* not going to check the return value in this exercise */
What do we do if we reached p[99] and need to store more? Well, we can allocate another array and copy the data into it by doing something like
if (count == allocated) {
int *bigger;
int i;
allocated *= 2; /* twice as many! */
bigger = malloc(allocated * sizeof(*bigger));
/* again, not checking the return value to save space */
/* copy all the data we have into the new array */
for (i = 0; i < count; ++i) {
bigger[i] = p[i];
}
/* use the new array and free the old one */
free(p);
p = bigger;
}
That's a lot of code. The standard C library has a special function that does this, and even something better: if it's possible to extend the allocated memory block, it might do that, so that no data needs to be copied. If it's impossible to extend the allocated block, it will allocate a new block elsewhere in memory and will copy the data for you.
Meet realloc:
char *bigger;
bigger = realloc(p, new_size);
With realloc we can rewrite the above fragment like this:
if (count == allocated) {
int *bigger;
allocated *= 2; /* twice as many! */
bigger = realloc(p, allocated * sizeof(*bigger));
if (!bigger) {
perror("realloc");
/* realloc failed: what do we do? see below */
}
p = bigger;
}
Note that realloc can return either of:
NULL, if it failed. We'll talk more about it below;- same pointer as
p, if it managed to keep the data in the same place; - a new pointer, in which case
pis freed and you cannot use it anymore.
If realloc succeeded (that is, returned not NULL), you should always use the pointer it returned, and forget the pointer you passed to it.
Check the return value or not?
The question whether to check the return value of malloc and realloc or not depends on what kind of program you are creating.
If you are thinking about this, ask yourself a few questions: is there any reasonable way for your program to continue working if the memory allocation or reallocation has failed? Do you want to add checks to crash immediately and report a reasonable error, or are you OK with crashing later when accessing an invalid pointer?
For any “real” program, you would probably want to check the return values, and report a nice error message if you got NULL. For exercises or throw-away code, it might be just fine to assume that malloc or realloc always work, because if not, you will still crash, just a few lines later.
With that said, in this course I will omit the checks to save space.
Now exercise!
You will need to print all the numbers from the input, in the same order, twice: same as the previous exercise, but now you don't know how many numbers you will get.
Remember that scanf returns number of values it has read, so you can loop while scanf returns 1:
int value;
while (scanf("%d", &value) == 1) {
/* store value */
}
Here is the sample input for this exercise:
1 2 3 4 5
Expected output for this input:
1
2
3
4
5
1
2
3
4
5
Try writing the code! First allocate some memory, and then use realloc to allocate more as needed.
#include <stdio.h>
#include <stdlib.h>
int main() {
/* your code here! */
return 0;
}