How to update other pointers when realloc moves the memory block? - c

The realloc reference says:
The function may move the memory block
to a new location, in which case the
new location is returned.
Does it mean that if I do this:
void foo() {
void* ptr = malloc( 1024 );
unsigned char* cptr = ( unsigned char* )ptr+256;
ptr = realloc( ptr, 4096 );
}
then cptr may become invalid if realloc moves the block?
If yes, then does realloc signal in any way, that it will move the block, so that I can do something to prevent cptr from becoming invalid?

Yes, cptr will become invalid as realloc moves the block! And no, there is no mention of signalling to you to tell that it is moving the block of memory. By the way, your code looks iffy...read on... please see my answer to another question and read the code very carefully on how it uses realloc. The general consensus is if you do this:
void *ptr = malloc(1024);
/* later on in the code */
ptr = realloc(ptr, 4096);
/* BAM! if realloc failed, your precious memory is stuffed! */
The way to get around that is to use a temporary pointer and use that as shown:
void *ptr = malloc(1024);
/* later on in the code */
void *tmp = realloc(ptr, 4096);
if (tmp != null) ptr = tmp;
Edit: Thanks Secure for pointing out a gremlin that crept in when I was typing this earlier on.

This is coming a bit late, but the solution to this problem (which nobody has mentioned) is not to use pointers into allocated blocks that will need to be allocated. Instead, use integer-valued offsets from the base pointer or (better) use a struct type and member elements to address specific locations in the allocated object.

Yes, cptr will become invalid if realloc moves the block.
No, there is no signal. You would have to check the return value against the original ptr location.

Yes.
Best thing to do is compare ptr before and after the reallocation, and see if it has been moved. You shouldn't assign a pointer to the offset value, instead you should store the offset and then index the original operator with it.
i.e.
Instead of
void* newPtr = ptr + 10;
*newPtr = something;
Use
int new = 10;
ptr[new] = something;

Yes, the cptr becomes invalid if realloc moves the block.

Related

C - Storing pointers with malloc() in an array, can't free() them afterwards

I want to store pointers that have been allocated using malloc() in an array and then free all of them after. However even though the program doesn't complain it doesn't work. Below cleanMemManager() won't actually free the memory as when tested inside main() the char* pointer is not NULL and it will print ???.
code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void **ptrList = NULL;
void tfree(void** ptr)
{
free(*ptr);
*ptr = NULL;
}
void* talloc(int size)
{
void* ptr = malloc(size);
ptrList[0] = ptr; ///No clue if this actually does what I think it does
return ptrList[0];
}
void initMemManager()
{
ptrList = (void**)malloc(sizeof(void**) * 3);
memset(ptrList, 0, sizeof(void**) * 3);
}
void cleanMemManager()
{
tfree(&ptrList[0]); //Doesn't free the right pointer it seems
}
int main()
{
initMemManager();
char* ptr = (char*)talloc(3);
cleanMemManager();
if (ptr != NULL) //This will trigger and I'm not expecting it to
printf("???");
getchar();
return 0;
}
I don't understand the syntax to use for this, does the pointer not actually get touched at all? What is it freeing then since it doesn't throw any errors?
In main, char *ptr = (char*)talloc(3); declares ptr to be a local variable. It contains a copy of the value returned by talloc, and none of your subroutines know about ptr or where it is. So none of them change the value of ptr. Thus, when you reach if (ptr != NULL), the value of ptr has not changed.
Additionally:
In initMemManager, you should use sizeof(void *) in two places where you have sizeof(void**). In these places, you are allocating and copying void * objects, not void ** objects.
It looks like you are trying to implement a sort of smart memory manager that automatically sets pointers to NULL when they are freed. To do that in C, you would have to give up having copies of pointers. For example, ptr is a copy of ptrList[0], but tfree only sets whichever copy it is passed to NULL. We could give advice on building such a system, but it would quickly become cumbersome—your memory manager needs to keep a database of pointers and their copies (and pointers derived from them, as by doing array arithmetic). Or you have to refer to everything indirectly through that ptrList array, which adds some mess to your source code. Unfortunately, C is not a good language for this.
Freeing doesn't guarantee that pointers pointing to the allocated block will be set to NULL. If you actually try doing
if (ptrList[0] != NULL)
printf("ptrList[0] != NULL");
you will see that the program won't output and if you remove the cleanMemManager() function call, it will output. This means tfree function is working as intended, it's freeing the memory that was allocated.
Now as to why ptr variable being not set to NULL, it's simply because ptr is still storing the old address. cleanMemManager() has no way of mutating the variable ptr. This is commonly called dangling pointer or use after free.
Also free() doesn't clean/zero out the the allocated space, the block is simply marked as "free". The data will most likely remain in the memory for a moment until the free block is overwritten by another malloc request.

Safer way to realloc

I am implementing a function to safely realloc a structure in order not to lost the information if any allocation error occurs, something like this:
int foo (someStruct_t *ptr, int size)
{
someStruct_t *tmp_ptr;
tmp_ptr = realloc(ptr, size);
if (tmp_ptr == NULL)
return -1;
ptr = tmp_ptr;
return 0;
}
My doubt resides in the following: Am I not duplicating the allocated memory for the structure everytime I run this function? In my line of thought I should free one of the pointers before exiting, correct?
The primary and major problem here is, after a call to foo(), in the caller, the passed argument for ptr will not be changed, as it itself is passed by value.
You need to pass a pointer to the pointer which you want to be reallocated, in case you don't want to return the new pointer.
That said, there is no "duplication" of memory here.
From realloc() point of view
realloc(), if successful, returns the pointer to the new memory and handles the job of de-allocating the older one. You don't need to worry about any duplication or memory leaks.
Quoting C11, chapter §7.22.3.5 (emphasis mine)
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. [....]
[....] If memory for the new object cannot be
allocated, the old object is not deallocated and its value is unchanged.
From the assignment point of view
A statement like ptr = tmp_ptr; does not duplicate the memory or memory contents the pointer points to, it is just having two copies of the same pointer. For example, You can pass either of them to free().
So, bottom line, to answer the "question" in the question,
In my line of thought I should free one of the pointers before exiting, correct?
No, you should not. You need to have the newly allocated pointer to be useful in the caller, free()-in it inside the called functions makes the whole function pointless. You should free the pointer from the caller, though.
int foo (someStruct_t **ptr, int size)
{
someStruct_t *tmp_ptr;
tmp_ptr = realloc(*ptr, size);
if (tmp_ptr == NULL)
return -1;
*ptr = tmp_ptr;
return 0;
}
the posted code contains several problems:
cannot change where a caller's pointer points without the address of that pointer, so use ** in the parameter list and call the function using: foo( &ptr, size);
each reference to the callers' pointer must now be dereferenced via a *
usually the size refers to the number entries room to be allocated, so size needs to be multiplied by sizeof( someStruct_t )
note the use of size_t for the size parameter because realloc() is expecting the second parameter to have the type: size_t
and now the proposed code:
#include <stdlib.h> // realloc()
int foo (someStruct_t **ptr, size_t size)
{
someStruct_t *tmp_ptr;
tmp_ptr = realloc( *ptr, sizeof( someStruct_t) * size );
if (tmp_ptr == NULL)
return -1;
*ptr = tmp_ptr;
return 0;
} // end function: foo

Proper usage of realloc()

From man realloc:The realloc() function returns a pointer to the newly allocated memory, which is suitably aligned for any kind of variable and may be different from ptr, or NULL if the request fails.
So in this code snippet:
ptr = (int *) malloc(sizeof(int));
ptr1 = (int *) realloc(ptr, count * sizeof(int));
if(ptr1 == NULL){ //reallocated pointer ptr1
printf("Exiting!!\n");
free(ptr);
exit(0);
}else{
free(ptr); //to deallocate the previous memory block pointed by ptr so as not to leave orphaned blocks of memory when ptr=ptr1 executes and ptr moves on to another block
ptr = ptr1; //deallocation using free has been done assuming that ptr and ptr1 do not point to the same address
}
Is it sufficient to just assume that the reallocated pointer points to a different block of memeory and not to the same block.Because if the assumption becomes false and realloc returns the address of the original memory block pointed to by ptr and then free(ptr) executes(for the reason given in the comments) then the memory block would be erased and the program would go nuts.
Should I put in another condition which will compare the equality of ptr and ptr1 and exclude the execution of the free(ptr) statement?
Just don't call free() on your original ptr in the happy path. Essentially realloc() has done that for you.
ptr = malloc(sizeof(int));
ptr1 = realloc(ptr, count * sizeof(int));
if (ptr1 == NULL) // reallocated pointer ptr1
{
printf("\nExiting!!");
free(ptr);
exit(0);
}
else
{
ptr = ptr1; // the reallocation succeeded, we can overwrite our original pointer now
}
Applying fixes as edits, based on the good comments below.
Reading this comp.lang.c question, reveals 3 cases:
"When it is able to, it simply gives you back the same pointer you handed it."
"But if it must go to some other part of memory to find enough contiguous space, it will return a different pointer (and the previous pointer value will become unusable)."
"If realloc cannot find enough space at all, it returns a null pointer, and leaves the previous region allocated."
This can be translated directly to code:
int* ptr = (int*)malloc(sizeof(int));
int* tmp = (int*)realloc(ptr, count * sizeof(int));
if(tmp == NULL)
{
// Case 3, clean up then terminate.
free(ptr);
exit(0);
}
else if(tmp == ptr)
{
// Case 1: They point to the same place, so technically we can get away with
// doing nothing.
// Just to be safe, I'll assign NULL to tmp to avoid a dangling pointer.
tmp = NULL;
}
else
{
// Case 2: Now tmp is a different chunk of memory.
ptr = tmp;
tmp = NULL;
}
So, if you think about it, the code you posted is fine (almost). The above code simplifies to:
int* ptr = (int*)malloc(sizeof(int));
int* tmp = (int*)realloc(ptr, count * sizeof(int));
if(tmp == NULL)
{
// Case 3.
free(ptr);
exit(0);
}
else if(ptr != tmp)
{
ptr = tmp;
}
// Eliminate dangling pointer.
tmp = NULL;
Note the extra else if(ptr != tmp), which excludes Case 1, where you wouldn't want to call free(ptr) because ptr and tmp refer to the same location. Also, just for safety, I make sure to assign NULL to tmp to avoid any dangling pointer issues while tmp is in scope.
OP: ... may be different from ptr, or NULL if the request fails.
A: Not always. NULL may be legitimately returned (not a failure), if count is 0.
OP: Is it sufficient to just assume that the reallocated pointer points to a different block of memory and not to the same block.
A: No
OP: Should I put in another condition which will compare the equality of ptr and ptr1 and exclude the execution of the free(ptr) statement?
A: No.
If realloc() returns NULL (and count is not 0), the value of ptr is still valid, pointing to the un-resized data. free(ptr) or not depends on your goals.
If realloc() returns not NULL, do not free(ptr), it is all ready freed.
Example: https://codereview.stackexchange.com/questions/36662/critique-of-realloc-wrapper
#include <assert.h>
#include <stdlib.h>
int ReallocAndTest(char **Buf, size_t NewSize) {
assert(Buf);
void *NewBuf = realloc(*Buf, NewSize);
if ((NewBuf == NULL) && (NewSize > 0)) {
return 1; // return failure
}
*Buf = NewBuf;
return 0;
}
realloc will return the same address to ptr if it have enough space to extend the actual chunk of memory pointed by ptr. Otherwise, it will move the data to the new chunk and free the old chunk. You can not rely on ptr1 being different to ptr. Your program behaves undefined.
If realloc returns another address, it first deallocates the old one so you don't have to do it yourself.
By the way, never cast the return of malloc/realloc :). Your code should be like this:
ptr=malloc(sizeof(int));
ptr=realloc(ptr,count*sizeof(int));
if(ptr==NULL)
{
// error!
printf("\nExiting!!");
// no need to free, the process is exiting :)
exit(0);
}
If realloc moves your data, it will free the old pointer for you behind the scenes. I don't have a copy of the C11 standard, but it is guaranteed in the C99 standard.
You should not free your original pointer if the realloc succeeds. Whether you free that pointer if the realloc fails depends on the needs of your particular application; if you absolutely cannot continue without that additional memory, then this would be a fatal error and you would deallocate any held storage and exit. If, OTOH, you can still continue (perhaps execute a different operation and hope that memory will come available later), the you'd probably want to hold on to that memory and a attempt a another realloc later.
Chapter and verse:
7.22.3.5 The realloc function
Synopsis
1 #include <stdlib.h>
void *realloc(void *ptr, size_t size);
Description
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.
3 If ptr is a null pointer, the realloc function behaves like the malloc function for the
specified size. Otherwise, if ptr does not match a pointer earlier returned by a memory
management function, or if the space has been deallocated by a call to the free or
realloc function, the behavior is undefined. If memory for the new object cannot be
allocated, the old object is not deallocated and its value is unchanged.
Returns
4 The realloc function returns a pointer to the new object (which may have the same
value as a pointer to the old object), or a null pointer if the new object could not be
allocated.
Emphasis added. Note clause 4; the returned pointer may be the same as your original pointer.

Problems Using memset and memcpy

So I am trying to create a Memory Management System. In order to do this I have a set amount of space (allocated by malloc) and then I have a function myMalloc which will essentially return a pointer to the space allocated. Since we will then try and free it, we are trying to set a header of the allocated space to be the size of the allocated space, using memset.
memset(memPtr,sizeBytes,sizeof(int));
We then need to be able to read this so we can see the size of it. We are attempting to do this by using memcpy and getting the first sizeof(int) bytes into a variable. For testing purposes we are just trying to do memset and then immediately get the size back. I've included the entire method below so that you can see all declarations. Any help would be greatly appreciated! Thanks!
void* FirstFit::memMalloc(int sizeBytes){
node* listPtr = freelist;
void* memPtr;
// Cycle through each node in freelist
while(listPtr != NULL)
{
if(listPtr->size >= sizeBytes)
{
// We found our space
// This is where the new memory allocation begins
memPtr = listPtr->head;
memset(memPtr,sizeBytes,sizeof(int));
void *size;
memcpy(size, memPtr, sizeof(int));
// Now let's shrink freelist
listPtr->size = listPtr->size - sizeBytes;
int *temp = (int*)listPtr->head + (sizeBytes*sizeof(int));
listPtr->head = (int*) temp;
return memPtr;
}
listPtr = listPtr->next;
}
::Edit::
Sorry! When running this, we keep getting a seg fault when attempting to run the memcpy line. We have been playing with different ideas for the past hour or so and honestly just have no idea where the error is occurring.
::Edit2::
I also posted this as a comment, but figured I'd put it here as well, so it was easier to find...
Our problem is that we have an allocated space that we are allowed to work with, specified by one malloc call for 128MB. We can only use this, so we can't initialize size to anything using malloc. I guess, is there a way to do this without initializing size. If not, is there anyway to get the int that the header is set to without using memcpy.
The prototype for memcpy is
void * memcpy ( void * destination, const void * source, size_t num );
The problem lies here:
void *size; /* you haven't initialized this variable, and then you're writing to what it points to*/
memcpy(size, memPtr, sizeof(memPtr)); /* because size points to uninitialized memory it seg faults*/
EDIT1:
Please review this tutorial on pointers in C and C++ Unless you understand pointers you will not understand why those two lines of code, back to back, are a bad pair.
You code has numerous bugs in it - rather than go through them one-by-one, I'll give you a commented version of what it should look like:
void* FirstFit::memMalloc(size_t sizeBytes) // size_t is the appropriate type for memory sizes
{
node* listPtr = freelist;
void* memPtr;
// The actual allocation needs to be bigger, to have space to hold the size itself.
size_t allocSize = sizeBytes + sizeof allocSize;
// Test to make sure that allocSize didn't wrap around to zero
if (allocSize < sizeBytes)
{
return NULL;
}
// Cycle through each node in freelist
while(listPtr != NULL)
{
if(listPtr->size >= allocSize)
{
// We found our space
// This is where the new memory allocation begins
memPtr = listPtr->head;
// Copy the size to the start of the memory region
memcpy(memPtr, &allocSize, sizeof allocSize);
// Increment the pointer to be returned past the size
char *tempPtr = (char *)memPtr;
memPtr = (void *)(tempPtr + sizeof allocSize);
// Shrink the block
listPtr->size -= allocSize;
tempPtr = (char *)listPtr->head;
listPtr->head = (void *)(tempPtr + allocSize);
// TODO: If the block is now zero-sized, remove it from the linked list
return memPtr;
}
listPtr = listPtr->next;
}
/* No space */
return NULL;
}
void *size; is an uninitialized pointer, when you try to memcpy into it, your process will try to write this invalid location resulting seg fault.
Your use of memset is very odd:
memset(memPtr,sizeBytes,sizeof(int));
is equivalent to (assuming a 32 bit integer):
*((char *)memPtr + 0) = (sizeByte & 0xFF);
*((char *)memPtr + 1) = (sizeByte & 0xFF);
*((char *)memPtr + 2) = (sizeByte & 0xFF);
*((char *)memPtr + 3) = (sizeByte & 0xFF);
As you can see, it's setting each byte to the same value which is the lower byte of sizeBytes.
I'm not sure what you are intending to do so I can't offer a fix.
if you are writing it in windows... you can use
IsBadWritePtr
To Verify that the calling process has write access to the specified range of memory.
There may be three reason
1>pointers is either garbage or NULL
2>the amount you're trying to copy is too much.
i.e. copying past the end of the block of memory. Potentially "backwards" copy of string-literal would also cause this behaviour
char *s = "Hello";
char t[10];
memcpy(s, t, 6);
In creating your own memory management system, now that you have learned what memset() does and doesn't, hopefully already knowing enough of the low level stuff that you know the difference between memcpy() and memmove(), your next step is to learn about "alignment" and about the guarantees that malloc() fulfills, but your code doesn't.

How to make the bytes of the block be initialized so that they contain all 0s

I am writing the calloc function in a memory management assignment (I am using C). I have one question, I wrote the malloc function and thinking about using it for calloc as it says calloc will take num and size and return a block of memory that is (num * size) which I can use malloc to create, however, it says that I need to initialize all bytes to 0 and I am confused about how to do that in general?
If you need more info please ask me :)
So malloc will return a pointer (Void pointer) to the first of the usable memory and i have to go through the bytes, initialize them to zero, and return the pointer to that front of the usable memory.
I am assuming you can't use memset because it's a homework assignment assignment, and deals with memory management. So, I would just go in a loop and set all bytes to 0. Pseudocode:
for i = 1 to n:
data[i] = 0
Oh, if you're having trouble understanding how to dereference void *, remember you can do:
void *b;
/* now make b point to somewhere useful */
unsigned char *a = b;
When you need to set a block of memory to the same value, use the memset function.
It looks like this: void * memset ( void * ptr, int value, size_t num );
You can find more information about the function at: http://www.cplusplus.com/reference/clibrary/cstring/memset/
If you can't use memset, then you'll need to resort to setting each byte individually.
Since you're calling malloc from your calloc function, I'm going to assume it looks something like this:
void *calloc (size_t count, size_t sz) {
size_t realsz = count * sz;
void *block = malloc (realsz);
if (block != NULL) {
// Zero memory here.
}
return block;
}
and you just need the code for "// Zero memory here.".
Here's what you need to know.
In order to process the block one byte at a time, you need to cast the pointer to a type that references bytes (char would be good). To cast your pointer to (for example) an int pointer, you would use int *block2 = (int*)block;.
Once you have the right type of pointer, you can use that to store the correct data value based on the type. You would do this by storing the desired value in a loop which increments the pointer and decrements the count until the count reaches zero.
Hopefully that's enough to start with without giving away every detail of the solution. If you still have problems, leave a comment and I'll flesh out the answer until you have it correct (since it's homework, I'll be trying to get you to do most of the thinking).
Update: Since an answer's already been accepted, I'll post my full solution. To write a basic calloc in terms of just malloc:
void *calloc (size_t count, size_t sz) {
size_t realsz, i;
char *cblock;
// Get size to allocate (detect size_t overflow as well).
realsz = count * sz;
if (count != 0)
if (realsz / count != sz)
return NULL;
// Allocate the block.
cblock = malloc (realsz);
// Initialize all elements to zero (if allocation worked).
if (cblock != NULL) {
for (i = 0; i < realsz; i++)
cblock[i] = 0;
}
// Return allocated, cleared block.
return cblock;
}
Note that you can work directly with char pointers within the function since they freely convert to and from void pointers.
Hints:
there is already a posix library function for zeroing a block of memory
consider casting the void * to some pointer type that you can dereference / assign to.

Resources