On RHEL6, I'm facing a strange problem with realloc(). At some point in the program, realloc() returns NULL (the old pointer has an address and there's plently of memory available). What's being allocated is 200 structure elements (structure below). For some reason, when I do a realloc() instead, it works, but I then have to assign the old pointer to the new one. Below is a simplified version of my code.
This is perhaps a server tuning issue more than a programming one. What is your opinion?
Thanks.
//hearder file
typedef struct { /* Variable Node Detail Record */
long next;
long mask;
char *value;
// more stuff...
} NODETEST;
extern NODETEST *oldNodes;
extern NODETEST *newNodes;
//program
#define MAXSIZE 200
// do some stuff with oldNodes....
int alloc_nodes (void)
{
// Allocate or grow the table
oldNodes = (NODETEST *) malloc(MAXSIZE * sizeof(NODETEST));
if( oldNodes == NULL ) {
//handle exception...
exit(1);
}
//oldNodes = (NODETEST *) realloc(oldNodes,MAXSIZE * sizeof(NODETEST)); // *** FAILS
newNodes = (NODETEST *) realloc(oldNodes,MAXSIZE * sizeof(NODETEST)); // *** WORKS
if( newNodes == NULL ){
printf("errno=%d\n", errno );
}else{
oldNodes = newNodes; }
}
Your first call malloc with a size S and then realloc with the same size S. This is wrong: you have to pass to realloc the new wanted size (independently of the current size - it is not an increment). Here, there is a big chance realloc returns exactly the same pointer it received. BTW it is not clear why you want to do with a malloc immediately followed by a realloc. Gives us more detail.
If you want a dynamic table whose size auto-adjusts, you need to allocate an initial size storing its size in a variable (e.g. alloc_size) and keep the current number of occupied elements in another variable (e.g. n_elem) . When you add an element you increment this number. When the table is full reallocate it. Here is a sketch
NODETEST *newNodes = NULL;
int allocated_elem = 0;
int n_elem = 0;
#define ALLOC_INCR 200
then at each addition:
if (n_elem >= alloc_size) { // the first time realloc is as malloc since nodes == NULL
alloc_size += ALLOC_INCR;
nodes = (NODETEST *) realloc(nodes, alloc_size * sizeof(NODETEST));
if (nodes == NULL) {
//handle exception...
exit(1);
}
}
// add your element at nodes[n_elem]
n_elem++;
Recall that realloc acts like malloc when the received pointer is NULL (case of the first call). Thus it allocates the initial table. Subsequent calls reallocate it by adjusting the size with a constant increment (here 200). Some other schemes are possible for the enlargement of the table, for instance you can multiply the size by a factor (e.g. 2) starting from 32:
if (n_elem >= alloc_size) { // the first time realloc is as malloc since nodes == NULL
alloc_size = (alloc_size == 0) ? 32 : alloc_size * 2;
Regarind the FAIL and WORKS comments: it is clear that if you assign oldNodes (in the FAIL code) then newNodes is not assigned and keeps its initial value which is zero (NULL) since it is declared as a global variable and not initialized (well I suppose, it is extern here). Thus the test if (newNodes == NULL) will probably fail.
Related
The question is in the title
void deleteLastValue(dynIntArray* t){ //function should responds to unexpected values
if(t->tab == NULL){ //if the array is empty we should return an error
fprintf(stderr,"The array is empty");
exit(1);
}
t->tab[(t->size)-1] = 0; /**I'm not sure what to do here should I free the pointer using free function**/
//t->tab[(t->size)-1] = NULL; is it necessary ?
//free(t->tab[(t->size)-1]);
t->tab = (int*)realloc(t->tab,((t->size)-1)*sizeof(int)); //we reallocate but with the new size
t->size--;//size - 1; but capacity stills the same
}
typedef struct{ //structure of an dynamic array
int * tab; //an array of
unsigned int capacity; // the space in memory
unsigned int size;// size of array
} dynIntArray;
If you can review my code, I had issues with what to do with the last element of the array. And should I do more if I want the program to respond to unexpected inputs to make it work properly. Thank you.
You are awesome!
Apparenty it's not quite clear to you how capacity and size are related.
realloc can be a pretty expensive function that may take quite some time. So if you call realloc each and every time the size changes your code will work, but it might be pretty slow. Therefore the capacity is used.
Roughly it is done like this:
You initialize the dynamic array with a size = 0 and capacity = 100 (or some appropriate value larger than 0).
Now you can add 100 elements to the array without realloc ever being called.
If you add one element more, you add 100 to capacity and you call realloc
Now the capacity is 200 and you can add another 100 elements without realloc being called.
If you want to decrease the size of the dynamic array, you just can decrease size without calling realloc leaving capacity alone.
If the difference between size and capacity becomes larger than some value or maybe if the capacity is larger than twice the size, then you can decrease the capacity and call realloc
There are several strategies about how size and capacity are related and when to increase/decrease capacity.
void deleteLastValue(dynIntArray* t) {
if (t->tab == NULL) {
fprintf(stderr, "The array is empty");
exit(1);
}
t->size--; // that's all you need to do
// now optionally you might want to decrease the capacity
// if the capacity is larger than twice the size
// or based on some other strategy
if (t->capacity > 2 * t->size)
{
t->capacity = t->size;
t->tab = realloc(t->tab, t->capacity);
if (t->tab == NULL) {
fprintf(stderr, "realloc error in deleteLastValue");
exit(1);
}
}
}
Be aware that you still need to check if the array is empty (IOW if size is 0. I leave this as an exercise.
If you want more information, you should show us the other functions.
Review of your function:
void deleteLastValue(dynIntArray* t) {
if (t->tab == NULL) {
fprintf(stderr, "The array is empty");
exit(1);
}
t->tab[(t->size) - 1] = 0; // this is useless, the element is no longer part of the array
// you don't need to overwrite it with 0
// the call to free below is completly wrong, t->tab[(t->size) - 1] is not a pointer but an int
// free(t->tab[(t->size) - 1]);
// this reallocation is correct (except there is no error checking and the (int*) cast is useless)
t->tab = (int*)realloc(t->tab, ((t->size) - 1) * sizeof(int));
t->size--;
// you leave the capacity alone which is maybe what you want
}
As you realloc every time, you only need the size of the array. You can also use a flexible array simplifying the allocation and deallocation.
typedef struct{ //structure of an dynamic array
unsigned int size;// size of array
int tab[]; //an array of
} dynIntArray;
dynIntArray *deleteLastValue(dynIntArray* t)
{
if(t && t -> size)
{
t = realloc(sizeof(*t) + (t -> size - 1) * sizeof(t -> tab[0]));
if(t) t -> size--;
}
return t;
}
Thank y'all! It worked perfectly fine. I submit the answer here for posterity. (For the sake of the exercise we don't need to change the capacity)
void deleteLastValue(dynIntArray* t){
if(t->tab == NULL || t->size == 0){
fprintf(stderr,"The array is empty");
exit(1);
}
t->size--;
}
Edit: solved by kaylums little comment. Thank you!
good morning,
I am relatively new to C still and I'm trying to make a doubly linked list.
I got my program to run properly with all the functions with this kind of element:
the program crashes after either 2 or 3 inserted elements in the list in the calloc() call of my insertElement() function. I don't get any SIGSEGV or anything, the program just stops with a random negative return.
I'll try to give a minimum code example of the function and the function call:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct Element {
char name[30];
}Element;
typedef struct List {
int size;
Element* first;
Element* last;
}List;
Element* insertElement(List* List, char name[30]) {
Element* element;
element = (Element*)calloc(0, sizeof(Element));
strncpy_s(element->name, name, 30);
return element;
}
List globalList;
char name[30];
int main() {
while (true) {
printf("insert the name >>");
if (fgets(name, 30, stdin) != NULL)
name[strcspn(name, "\n")] = 0;
insertElement(&globalList, name);
}
}
is there already something obvious wrong with that basic stuff?
Thank you very much in advance! Any advice would be very much appreciated, have a good day!
element = (Element*)calloc(0, sizeof(Element));
what is 0 in first argument?
actually you ask for 0 number of your type from memory!
here is some explanation about dynamic memory allocation:
Dynamic memory allocation is a process of allocating memory at run time. There are four library routines, calloc(), free(), realloc(), and malloc() which can be used to allocate memory and free it up during the program execution. These routines are defined in the header file called stdlib.h.
What is malloc() ?
It is a function which is used to allocate a block of memory dynamically. It reserves memory space of specified size and returns the null pointer pointing to the memory location.
The pointer returned is usually of type void. It means that we can assign malloc function to any pointer. The full form of malloc is memory allocation.
What is calloc() ?
Calloc() function is used to allocate multiple blocks of memory. It is a dynamic memory allocation function which is used to allocate the memory to complex data structures such as arrays and structures. If this function fails to allocate enough space as specified, it returns will null pointer. The full form of calloc function is contiguous allocation.
Why use malloc() ?
Here are the reasons of using malloc()
You should use malloc() when you have to allocate memory at runtime.
You should use malloc when you have to allocate objects which must exist beyond the execution of the current memory block.
Go for malloc() if you need to allocate memory greater than the size of that stack.
It returns the pointer to the first byte of allocated space.
It enables developers to allocate memory as it is needed in the exact amount.
This function allocates a memory block size of bytes from the heap.
Why use calloc() ?
Here are the reasons of using calloc()
When you have to set allocated memory to zero.
You can use calloc that returns a pointer to get access to memory heap.
Used when you need to initialize the elements to zero to returns a pointer to the memory.
To prevent overflow that is possible with malloc()
Use calloc() to request a page that is known to already be zeroed.
Syntax of malloc()
Here is a Syntax of malloc()
ptr = (cast_type *) malloc (byte_size);
n above syntax, ptr is a pointer of cast_type. The malloc function returns a pointer to the allocated memory of byte_size.
Example of malloc() in C
In the bellow code, sizeof(*ptr) is used to allocate a memory block of 15 integers. In the printf statement, we are finding the value of the 6th integer.
#include<stdlib.h>
#include<stdio.h>
int main(){
int *ptr;
ptr = malloc(15 * sizeof(*ptr));
if (ptr != NULL) {
*(ptr + 5) = 480;
printf("Value of the 6th integer is %d",*(ptr + 5));
}
}
Output:
Value of the 6th integer is 480
Syntax of calloc()
Here is a Syntax of malloc()
ptr = (cast_type *) calloc (n, size);
The above syntax is used to allocate n memory blocks of the same size. After the memory space is allocated, all the bytes are initialized to zero. The pointer, which is currently at the first byte of the allocated memory space, is returned.
Example of calloc() in C
The C language program below calculates the sum of the first ten terms. If the pointer value if null, then the memory space will not be allocated.
For loop is used to iterate the value of a variable "i" and print the sum. Lastly, function free is used to free-up the pointer.
#include <stdio.h>
#include <stdlib.h>
int main() {
int i, * ptr, sum = 0;
ptr = calloc(10, sizeof(int));
if (ptr == NULL) {
printf("Error! memory not allocated.");
exit(0);
}
printf("Building and calculating the sequence sum of the first 10 terms \n");
for (i = 0; i < 10; ++i) { * (ptr + i) = i;
sum += * (ptr + i);
}
printf("Sum = %d", sum);
free(ptr);
return 0;
}
Output:
Building and calculating the sequence sum of the first 10 terms n Sum = 45
I will not extend on the actual problem (specifying 0 as the number of elements requested to calloc()). I will point you to several other things found in your code.
The first problem in reading your code is that you lack to include the file <stdbool.h>, necessary to use the constants true and false and the type bool. I have added it in the first line.
#include <stdbool.h>
Next, you use at several places the value 30 as the size of several objects that are all related. If you decide in the future to change that value, it will be difficult to find all the ocurrences of the constan 30 and change all of them (and the risk you have used also 30 for anything else and it gets changed in the middle)
I have included a constan with the following lines:
#define NAME_LENGTH (30)
and all the definitions:
...
char name[NAME_LENGTH];
in the structure...
Element* insertElement(List* List, char name[NAME_LENGTH]) {
in the prototype of insertElement (you don't need as name is actually defined as char *, not as an array of NAME_LENGTH elements...
On other side, you need to include a pointer on each Element to link each to the next element of the list. This is done right after name:
struct Element *next; /* we need to include struct as the type Element is not yet defined */
Next, include sizeof *element as the second parameter to calloc() and 1 to the first. Better, if you are going to initialize all fields in the Element structure, then it is better to call malloc() (see the final code , posted at the end)
NEVER, NEVER, NEVER cast the value returned by malloc()
(and friends) This is a legacy that causes a lot of
errors, that get undetected (and very difficult to find),
due to the cast. When you cast you tell the compiler:
leave it in my hands, as I know what I'm doing. And this
makes the compiler silent, when it should be complaining.
The problem mainly has to do with forgetting to include
the header file where malloc (and friends) are declared
(<stdlib.h>) and you will take long time to detect and
see why your program has crashed.
For the same reason, don't use the size of the type, when
you can use the pointed to expression as template of the
type. This is because if you change the type of the
pointed to object, you need to remember that here you have
put the type of the object (and you need to change it too)
This way, this expression
will only be bad if you change the object into a non
pointer object. Also, you have requested for 0 elements
of the specified type, which has already been noticed in other answers. This will make calloc() to return NULL, value you don't check in your code, and you try to use it later on. This will crash your program, but in the best case, it is Undefined Behaviour (and a very difficult error to find, so be careful and always check the value returned by malloc()).
Next, don't use strncpy_s() as it is Microsoft specific routine, and isn't included in any standard. A proper substitute has been provided by strncpy():
strncpy(element->name, name, sizeof element->name);
also use the sizeof operator, as it protects you if you decide in the future to change the type of the pointer.
Finally, it is better to use fgets() as the test expression for the while statement in main(). The reason is that you can end the loop when the end of file is detected.
Finally, you code ends as (including the linking of Elements in the linked list):
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define NAME_LENGTH (30)
typedef struct Element {
char name[NAME_LENGTH];
struct Element *next;
} Element;
typedef struct List {
int size;
Element* first;
Element* last;
} List;
Element* insertElement(List* List, char name[NAME_LENGTH]) {
Element* element;
/* NEVER, NEVER, NEVER cast the value returned by malloc
* (and friends) This is a legacy that causes a lot of
* errors, that get undetected (and very difficult to find),
* due to the cast. When you cast you tell the compiler:
* leave it in my hands, as I know what I'm doing. And this
* makes the compiler silent, when it should be complaining.
* The problem mainly has to do with forgetting to include
* the header file where malloc (and friends) are declared
* (<stdlib.h>) and you will take long time to detect and
* see why your program has crashed. */
/* for the same reason, don't use the size of the type, when
* you can use the pointed to expression as template of the
* type. This is because if you change the type of the
* pointed to object, you need to remember that here you have
* put the type of the object. This way, this expression
* will only be bad if you change the object into a non
* pointer object. Also, you have requested for 0 elements
* of the specified type. */
element = malloc(sizeof *element);
/* don't use strncpy_s as it is not standard. Use the sizeof
* operator again, to protect the expression if you change
* the type of element->name */
strncpy(element->name, name, sizeof element->name);
element->next = NULL;
if (List->last) {
List->last->next = element;
List->last = element;
} else {
List->first = List->last = element;
}
return element;
}
List globalList;
char name[NAME_LENGTH];
int main() {
/* if you put the fgets() call as the test of the while
* statement below, you will process each line until you get
* an end of file condition. Then you can do both things: to
* null the occurence of the \n char, and the call to
* insertElement() I have not corrected because it's a
* question of taste. */
printf("insert the name >> ");
while (fgets(name, sizeof name, stdin) != NULL) {
/* sizeof name is better than the constant, as if you
* change the type definition of object name, you have to
* remember that you are using here its size. sizeof
* does the job for you. */
name[strcspn(name, "\n")] = 0;
insertElement(&globalList, name);
printf("insert the name >> ");
}
Element *p;
char *sep = "\n\n{ ";
for (p = globalList.first; p; p = p->next) {
printf("%s\"%s\"", sep, p->name);
sep = ", ";
}
printf(" };\n");
}
This question already has answers here:
Using realloc to shrink the allocated memory
(5 answers)
Closed 2 years ago.
I have a question about the realloc function. Will the content of old pointer be changed after apply realloc function?
The code is
main () {
int *a, *b, i;
a = calloc(5, sizeof(int));
for (i = 0; i < 5; i++)
a[i] = 1;
for (i = 0; i < 5; i++)
printf("%d", a[i]);
printf("\n%p\n", a);
b = realloc(a, 200000 * sizeof(int));
if(b == NULL)
printf("error\n");
for (i = 0; i < 5; i++)
printf("%d", a[i]);
printf("\n");
for (i = 0; i < 10; i++)
printf("%d", b[i]);
printf("\n%p %p\n", a, b);
}
The output is
11111
0x2558010
00111
1111100000
0x2558010 0x7f29627e6010
Pointer a still point to the same address, but the content is changed.
Pointer a still point to the same address, but the content is changed.
That's because realloc() may first try to increase the size of the block that a points to. However, it can instead allocate a new block, copy the data (or as much of the data as will fit) to the new block, and free the old block. You really shouldn't use a after calling b = realloc(a, 200000 * sizeof(int)) since the realloc call may move the block to a new location, leaving a pointing to memory that is no longer allocated. Use b instead.
The value returned by realloc tells you whether it succeeded or failed.
b = realloc(a, 200000 * sizeof(int));
If it fails, it returns a null pointer, and a still points to the original unmodified chunk of memory (and of course b is a null pointer).
If it succeeds, then b points to a (possibly newly allocated) chunk of memory, and the value of a is indeterminate. If it was able to allocate the new chunk in the same place as the old one (by growing or shrinking the chunk in place), then b will be equal to a -- but testing that, or even referring to the value of a, has undefined behavior. If it has to relocate the chunk, then realloc will have done the equivalent of free(a) after copying the data. In either case, it's probably best to set a to NULL to avoid accidentally referring to its (now indeterminate) value.
Note that realloc can relocate chunk even if the new size is smaller.
A simple realloc implementation should answer your questions:
void * realloc(void * ptr, size_t desired_size) {
size_t allocated_size = _allocated_size_of(ptr);
if (allocated_size < desired_size) {
void * new_ptr = malloc(desired_size);
memcpy(new_ptr, ptr, allocated_size);
free(ptr);
ptr = new_ptr;
}
return ptr;
}
malloc and related functions don't always allocate exactly the desired size. Very often they allocate more than the desired size. There is some hidden data kept up with by the memory allocation functions which allows for a pointer that was allocated by malloc or related functions to be used to look up the memory block size that was allocated. How this is kept up with isn't necessary to understand, but some very simple implementations simply store the size in the space just before the pointer returned *(((size_t)ptr)-1).
If realloc() returns a pointer different from the one you passed in (as it will most of the time), then the pointer you passed in no longer belongs to you, and you have no business knowing or caring what becomes of it. It might change its contents, it might not. But you are no longer allowed to access it, so it can be no concern of yours.
If 'a' points a valid block of memory (from a previous malloc/realloc/calloc), then a realloc call will attempt to provide a block of memory with the new size you requested
The realloc call should be of the form *tmp = realloc (a ...
The return value from realloc must be tested
If it is NULL, realloc was unable to allocate the requested memory, and this leaves 'a' as a valid pointer
You are then responsible for handling any data pointed to by 'a' (save it / discard it) and you are responsible for free ing the memory pointed to by 'a'
If the realloc call was successful make b = tmp and now 'b' is the new pointer to the block of memory - it does not matter whether the start location is the same as 'a' or different. 'a' is no longer a valid memory allocation pointer, although further errors will depend on whether 'a' points to memory owned by your program or not - basically if a == b, 'a' can be accessed without obvious errors.
After a valid *tmp = realloc(a ... & b = tmp;:
1) If the start location of the reallocated memory was unchanged: (a == b)
it will allocate the requested memory
but run it under valgrind and you will see error messages:
Invalid free() / delete / delete[] / realloc()
Address 0x51fc040 is 0 bytes inside a block of size 256 free'd
In this case realloc could not free the memory pointed to by 'a'
and again in this case 'a' can still be accessed as it is a pointer to memory that is allocated to your progam
2) If the start location of the reallocated memory was changed: (a != b)
it will fail and Valgrind shows output like this:
address of a: 0x1e89010
address of b: 0x7f2c5893c010
a after realloc: 0x1e89010
Error in `./test15': realloc(): invalid old size: 0x0000000001e89010
and trying to access 'a' will fail - even trying to print it's value as a pointer fails, presumably because it no longer points to memory owned by the program
In other words, using 'a' after b = realloc(a ... is undefined behaviour.
The above commentary was based on using the following code:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int *a = NULL, *b = NULL, *c = NULL;
/* initial allocation */
a = malloc(256);
if( a == NULL) return (1);
printf("address of a: %p\n", a);
/* reallocation 'b' MAY be same as 'a' - try much larger allocations */
void *tmp = realloc(a, 512);
if ( !tmp ) {
free(a);
return (1);
} else {
b = tmp;
}
printf("address of b: %p\n", b);
/* see what 'a' is now - this MAY crash the program*/
printf("a after realloc: %p\n", a);
/* 'a' may not be a valid pointer - try using it for another realloc */
c = realloc(a, 256);
/* Valgrind shows that memory could not be free'd or 'a' was not valid allocated memory */
printf("return value of c: %p\n", c);
if (c != NULL) {
free(c);
printf("'c' allocated\n");
} else {
free(b);
printf("'c' not allocated\n");
}
return 0;
}
Reading the man page is key here, but the TLDR is if there isn't enough memory to enlarge at the back end of the previous block, it will get a new block of memory, copy the old data into it, and return the address of the new block. The old address should not be used, and most typical realloc statement looks like this
a = realloc(a, 200000 * sizeof(int));
That way you won't accidentally use the possibly wrong old value.
It can't change the address in the pointer, since it is passed by value, so changing it in the function is only changing the local copy.
EDIT : Per Weather Vane's absolutely correct comment, the safer route would be
void * b = realloc(a, 200000 * sizeof(int));
if ( b ) {
a = b;
} else {
;;; /* error handler here */
}
Practice
#include <stdio.h>
#include <stdlib.h>
int main(void){
int i=0,z=2;
char *p=(char *)calloc(z,(sizeof(char)));
if(!(p)){
printf("\nMemory NOT Enough\n");
goto END;
}
*p='V';
z+=2;
p=realloc(p,z*(sizeof(char))); ----A
*(p+3)='S';
for(i=0;i<z;++i)
printf("\n%d\n",p[i]);
END:free(p);p=NULL;
return 0;
}
As you can see, the line marked by A uses realloc.
In line A the p on the LHS of the = is assigned the new address generated by realloc(p,z*(sizeof(char)));.
My question is :
What happens to the previously stored address in p? Previously stored address is replaced, so does it lead to memory leak?
If the return value from realloc() is not NULL all is well;
if realloc() returns NULL you have a memory leak.
You need to use a helper variable to use realloc() safely.
char *tmp;
tmp = realloc(p, z);
if (tmp == NULL) {
fprintf(stderr, "Unable to realloc.\n");
// p still points to the old memory and its contents are valid
exit(EXIT_FAILURE); // or some other error recovery
} else {
// tmp points to a (possibly new) block of memory with the same contents
// as what p used to point to (to the maximum of the old size and z)
// p (very probably) points to an invalid address
p = tmp; // now p points to a valid address (also tmp)
// ignore tmp for now on
}
No that memory is taken care by realloc if the realloc finds sufficient memory adjascent to p then its okay else realloc moves memory to new location.note that it moves it, so old memory is freed.
realloc will attempt for the extension of the available memory range only when sufficient memory is available in the heap.
From the C standard:
size == 0
realloc might free old and return 0
alternatively realloc behaves as for size != 0 but cannot return 0
size != 0
realloc might return 0. old is not touched
if the block pointed to by old is >= size, realloc might return old
alternatively realloc allocates a block >= size, copies all bytes from old up to size, frees old and returns this new block
Using what I have learned here: How to use realloc in a function in C, I wrote this program.
int data_length; // Keeps track of length of the dynamic array.
int n; // Keeps track of the number of elements in dynamic array.
void add(int x, int data[], int** test)
{
n++;
if (n > data_length)
{
data_length++;
*test = realloc(*test, data_length * sizeof (int));
}
data[n-1] = x;
}
int main(void)
{
int *data = malloc(2 * sizeof *data);
data_length = 2; // Set the initial values.
n = 0;
add(0,data,&data);
add(1,data,&data);
add(2,data,&data);
return 0;
}
The goal of the program is to have a dynamic array data that I can keep adding values to. When I try to add a value to data, if it is full, the length of the array is increased by using realloc.
Question
This program compiles and does not crash when run. However, printing out data[0],data[1],data[2] gives 0,1,0. The number 2 was not added to the array.
Is this due to my wrong use of realloc?
Additional Info
This program will be used later on with a varying number of "add" and possibly a "remove" function. Also, I know realloc should be checked to see if it failed (is NULL) but that has been left out here for simplicity.
I am still learning and experimenting with C. Thanks for your patience.
Your problem is in your utilisation of data, because it points on the old array's address. Then, when your call realloc, this area is freed. So you are trying to access to an invalid address on the next instruction: this leads to an undefined behavior.
Also you don't need to use this data pointer. test is sufficient.
(*test)[n-1] = x;
You don't need to pass data twice to add.
You could code
void add(int x, int** ptr)
{
n++;
int *data = *ptr;
if (n > data_length) {
data_length++;
*ptr = data = realloc(oldata, data_length * sizeof (int));
if (!data)
perror("realloc failed), exit(EXIT_FAILURE);
}
data [n-1] = x;
}
but that is very inefficient, you should call realloc only once in a while. You could for instance have
data_length = 3*data_length/2 + 5;
*ptr = data = realloc(oldata, data_length * sizeof (int));
Let's take a look at the POSIX realloc specification.
The description says:
If the new size of the memory object would require movement of the object, the space for the previous instantiation of the object is freed.
The return value (emphasis added) mentions:
Upon successful completion with a size not equal to 0, realloc() returns a pointer to the (possibly moved) allocated space.
You can check to see if the pointer changes.
int *old;
old = *test;
*test = realloc(*test, data_length * sizeof(int));
if (*test != old)
printf("Pointer changed from %p to %p\n", old, *test);
This possible change can interact badly because your code refers to the "same" memory by two different names, data and *test. If *test changes, data still points to the old chunk of memory.