Reallocation of memory to empty reference - c

I was watching a lesson on malloc and while they were doing a specific example, it made no sense for why such code to print out the entire pointer.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *list = malloc(3 * sizeof(int));
if (list == NULL)
return 1;
list[0] = 1;
list[1] = 2;
list[2] = 3;
int *tmp = malloc(4 * sizeof(int));
if (tmp == NULL)
return 1;
for (int i = 0; i < 3; i++)
tmp[i] = list[i];
tmp[3] = 4;
free(list);
//From this line and below is the thing in question.
list = tmp;
for (int i = 0; i < 4; i++)
printf("%i\n", list[i]);
}
OUTPUT:
~/test/ $ ./malloc_test
1
2
3
4
From my understanding of free(), it de-allocates the memory allocated by allocation functions and free() will 'free' up the memory of the pointer.
If I were to go by this definition:
*list was allocated 12 bytes (3 * sizeof(int), sizeof(int) = 4
Hard code list with numbers
*tmp was allocated 16 bytes
Copy the numbers in list to tmp
Free up list making it have no allocated bytes
List is now equal to tmp
How can list now be equal to tmp when list doesn't have any allocated memory?
Is list pointing to the address of tmp? If yes, why do we need to not allocate memory for list since earlier in the code, we did this int *tmp = malloc(4 * sizeof(int));

*list was allocated 12 bytes (3 * sizeof(int), sizeof(int) = 4
To be precise, you allocated 12 bytes of memory and set list to point to that memory.
Hard code list with numbers
*tmp was allocated 16 bytes
Again, to be precise, you allocated 16 bytes of memory and set the pointer variable tmp to point to it.
Copy the numbers in list to tmp
Free up list making it have no allocated bytes
To be precise, you freed the object that list pointed to so now list points to garbage and must not be dereferenced.
list is now equal to tmp
To be precise, list now points to the same thing tmp points to, those 16 bytes you allocated that previous was pointed to only by tmp.
How can list now be equal to tmp when list doesn't have any allocated memory?
Previously, list didn't point to allocated memory. But you changed it to point to the 16 bytes you allocated second. The value of a pointer is what it points to, list and tmp now point to the same thing so they have the same value. So they're now equal.
Is list pointing to the address of tmp? If yes, why do we need to not allocate memory for list since earlier in the code, we did this int *tmp = malloc(4 * sizeof(int));
The code frees the first allocated block of memory. If it didn't allocate a second block of memory, there would be nothing valid for either pointer to point to!

When you copy a pointer you're just copying the pointer's value1:
list = tmp;
This means that list now contains a copy of the tmp pointer, or in other words, they both point to the same memory address. In this case that address is an allocation.
Once they're made identical you can reference either of them interchangeably.
1 Of course this presumes the pointers are of the same type, like int* to int*, and not int to int**. You could also assign to void* and convert back again later, that works as well.

a pointer is a data type that is only assigned memory addresses of the corresponding data type, in your case in list = tmp; you assign the first position of that memory block that you created with int * tmp = malloc (4 * sizeof (int)); , when you make the assignment to list it will point to that memory address. (in your case at the beginning of that memory block).

Related

C11 - Realloc on array of structs fails when doing realloc twice

I'm trying to use malloc and realloc to hold an array of structs. The array should dynamically grow, size should increase by 10 struct elements every time.
Struct:
typedef struct
{
unsigned char foreign_word_[100] = {0};
unsigned char native_word_[100] = {0};
} VocabularyCouple;
In my main, I initialize the array with malloc:
VocabularyCouple* VocStruct = (VocabularyCouple*)malloc(sizeof(*VocStruct) * 10);
Increasing the size of the struct-array seems to work fine in main...
VocabularyCouple* temp = (VocabularyCouple*)realloc(VocStruct, (sizeof(VocabularyCouple) * 20));
if (temp == NULL)
{
printf("ERROR: Out of Memory\n");
return 4;
}
else
{
VocStruct = temp;
free(temp);
temp = NULL;
}
However, if I put the realloc-part into a function like this:
uint8_t resizeVoc(uint32_t new_size, VocabularyCouple **VocStruct)
{
VocabularyCouple *temp = (VocabularyCouple*)realloc(*VocStruct, (sizeof(VocabularyCouple) * new_size));
...
}
I can only call the function once. Every other call will result in this error:
HEAP[VocTest.exe]: Invalid address specified to RtlValidateHeap( 01300000, 01308500 )
Unless I'm missing something, this should be the same problem as c - Realloc an array of Structs, but I just can't get it to work.
Thank you for your help!
VocStruct = temp;
free(temp);
This is wrong, you free all memory as soon as you have allocated it. VocStruct and temp point at the same memory area. Just remove the free().
To clarify, the temp pointer is just there in case realloc fails. Had you written VocStruct = realloc(VocStruct, ... and realloc fails, then you would have overwritten the only pointer to the allocated memory with NULL and created a memory leak. But you only ever have 1 chunk of memory - even though 2 pointers point at it at the same time.

Malloc and Realloc array of structs

I am defining a type of structures
typedef struct structs{
int freeSpace;
} structs;
I am creating a pointer to a structure
structs* arrayOfStructs;
I am allocating memory for array of size 5 of that structure
arrayOfStructs = malloc(5 * sizeof (arrayOfStructs));
arrayOfStructs[3].freeSpace = 99;
printf("\n%d", arrayOfStructs[3].freeSpace);
At some point I am reallocating that memory to array of size 10
arrayOfStructs = realloc(arrayOfStructs, 10 * sizeof (arrayOfStructs));
arrayOfStructs[8].freeSpace = 9;
printf("\n%d", arrayOfStructs[8].freeSpace);
And here I am setting freespace 17 at position 13 of the array which I expect to have only 10 positions.
arrayOfStructs[13].freeSpace = 17;
printf("\n%d", arrayOfStructs[13].freeSpace);
free(arrayOfStructs);
Why is this working ? What am I doing wrong ?
The behaviour of the program is undefined.
You are accessing memory that doesn't belong to you.
(What could well be happening is that realloc is obtaining more memory from the operating system than is actually required).

Implementing Malloc: case for splitting a block

The heap is a linked list of structs of the following definition:
struct block
{
/*header + block*/
bool freeSpace;
block * prev;
block * next;
size_t size;
char block_part[];
};
I'm working on the following case:
If the first block of memory I find is so big that it can accommodate both the newly allocated
block and another block in addition, then the block is split in two; one block to hold the newly
allocated block, and a residual free block.
(Note that if it’s a bit larger than what I need, but not big
enough for a new block (i.e. it’s not big enough to hold the metadata of a new block), I will have unused space at the end of the block.)
My code is the following. When I test specifically this case, my program crashes with a segfault. Could someone see what the problem is? Thanks.
do{
if (ptr -> freeSpace && ptr -> size >= size){
/*first suffient free block is found*/
ptr -> freeSpace = false;
if (ptr -> size > size + sizeof(struct block)){
/*if big enought for 'size' AND metadata of a new block, split the block*/
struct block * returnPtr = memset((ptr -> block_part), 0, size);
struct block * added = returnPtr + size;
added -> size = ptr -> size - size - sizeof(struct block);
added -> freeSpace = true;
added -> prev = ptr;
added -> next = ptr -> next;
(ptr -> next) -> prev = added;
ptr -> next = added;
ptr -> size = size;
return returnPtr;
}
ptr -> size = size;
return memset((ptr -> block_part), 0, size);
}
prevPtr = ptr;
ptr = ptr -> next;
}while (ptr);
Memset returns a void *. It doesn't give warning because void pointers are automatically casted by the Compiler (e.g. malloc (non-)cast).
struct block *returnPtr = memset((ptr->block_part), 0, size);
Why would you waste memory for a whole struct with multiple members to store a void pointer pointing to character?
After that you use the address of the now void pointer and move it size-number forwards. Afterwards treat it like an allocated struct. That results in added not being allocated at all (if size is bigger than strlen(ptr->block_part)+1 you are already at someone other's memory) or at least violating memory. Accessing such memory gives so an erroneous program.
Hence, you observe segmentation fault and you program crashes.
Note: So far, I haven't seen someone use in such way the memset return value.

What is the difference between malloc(sizeof(int)) and malloc(sizeof(int*))

I'd like to allocate memory for the 2d int ptr below, but I'm not 100% positive I've done it correctly, so any pointers (ha ha) on that would be great. Is the way I free the array and its indexes in the for loop correct? Also, what is the difference between the first malloc and the second malloc: (int *) and (int)?
int **array = NULL;
int mem_size = 0;
int i = 0, j = 0;
// leaving out how mem_size is calculated, but it can vary
array = malloc(sizeof(int *) * mem_size);
if (array == NULL) {
// some error message
return;
}
for (i = 0; i < mem_size; i++) {
array[i] = malloc(sizeof(int) * 2);
if (!(array[i])) {
// some error message
for (j = 0; j < i; j++)
free(array[j]);
free (array);
return;
}
}
This is only a section of the code I wrote. At the end, I am freeing the array:
for (i = 0; i < mem_size; i++)
free(array[i]);
free(array);
It is just a compile time constant - size of pointer in first case, size of int in second. It may vary between systems (e.g. if compiling for 32bit systems, pointer would be 4 bytes, while on 64bit systems it is 8 bytes).
In case any of the mallocs fail in the for loop, should I be freeing the array there
You should be freeing everything you've allocated so far - each array[0..(i-1)] and array itself.
malloc(sizeof(int *) * mem_size)
Allocates memory for array of mem_size pointers.
malloc(sizeof(int) * 2);
Allocates memory for 2 ints.
Also you should consider allocating ordinary 1D array and just calculating index when you want to access it.
sizeof(int) is equal to 4 bytes
sizeof(int *) is also equal to 4 bytes
... since a pointer only holds 4 bytes.
When you call malloc(int) and malloc(int *) - in both cases, the memory manager allocates 4 bytes on the heap and returns a pointer to the allocated memory.
Since you are going to store that address into the array (which is a double pointer and can thus only hold the address of another pointer), the following is illegal:
array = malloc(sizeof(int *) * mem_size); --- illegal to use
You may implement what you want in the following way:
int *ptr = NULL;
int **p_ptr = NULL;
ptr=(int *)malloc(sizeof(int *));
p_ptr = &ptr;
**p_ptr = 100 or any other value; now, whatever changes you made will be reflected in the allocated size of 4 bytes
Each one of them is determined according to a different characteristic within your platform.
The size of int is determined by the compiler, which is typically designated for a specific processor.
So it is effectively derived from the CPU architecture.
It is usually 4 bytes, but may be 2 bytes on some platforms.
The size of int* (or any other pointer) is determined by the size of the virtual memory address space.
So it is effectively derived from the MMU architecture.
It is 4 bytes on 32-bit systems and 8 bytes on 64-bit systems.

Are there any dangling pointers in my program?

char** removeDuplicateChromosomes(char* input[], int no_of_chromosomes)
{
char** result = (char**) malloc(sizeof(char* )*(no_of_chromosomes));
//some piece of code
result[count] = input[itr];
//some piece of code . I didn't free any pointers here in this function
return result;
}
Can someone help me to identify any dangling pointers (if present) and explain the same pls?
Memory leak - if no pointer points to unfreed memory.
Dangling pointer - a pointer that points to freed memory.
Your code has a significant risk of either / both, though without a complete code sample, it's impossible to tell whether either would occur. I'll just give some possible scenario's of when it could happen.
As an initial note, If you don't free result's memory in the calling function, you'd have a memory leak. Any malloc must have a corresponding free.
If you free input like this:
free(input);
There would not be a dangling pointer.
But then there may be a memory leak if each element of input doesn't have an element of result pointing to the same memory. And if multiple elements of result point to this memory, you'll probably end up with a dangling pointer when you try to free it, along with some undefined behaviour.
If you free input like this:
int i;
for (i = 0; i < inputSize; i++)
free(input[i]);
free(input);
There would be a dangling pointer.
Why?
input[itr]; is a pointer.
result[count] = input[itr]; just makes result[count] point to the same memory as what input[itr] points to.
So if we free input[itr], result[count] would point to freed memory, and thus be dangling.
If we don't free input[itr], result[count] will still point to valid memory.
If you want result[count] to point to its own memory, you'll have to use:
result[count] = malloc(inputItrSize);
memcpy(result[count], input[itr], inputItrSize);
Note - there's no way to tell how much memory input[itr] is pointing to, so you'll have to declare inputItrSize with the appropriate size yourself.
A dangling pointer is a pointer to a memory area that is no-longer allocated.
char* dangleMeBaby(char* obj) {
free(obj);
return obj;
}
int* localDangle() {
int i = 10;
return &i;
}
#define NUM_POINTERS 8
char** wholeLottaDangle() {
char* ptr = malloc(sizeof(char*) * NUM_POINTERS);
size_t i;
void* data = malloc(NUM_POINTERS);
for (i = 0; i < NUM_POINTERS; ++i) {
ptr[i] = data + i;
}
free(data); // all the pointers in ptr now dangle.
free(ptr); // and ptr itself is now a dangle
return ptr;
}
If you have allocated input as one big block of pointers + data, then freeing input before result will turn result into an array of dangling pointers.
size_t indexSize = sizeof(char*) * numChromosomes;
size_t chromosomeSize = (MAX_CHROMOSOME_LEN) * numChromosomes;
char* data = malloc(indexSize + chromosomeSize);
char** input = (char**)data;
char* chromosome = data + indexSize;
for (size_t i = 0; i < numChromosomes; ++i, chromosome += MAX_CHROMOSOME_LEN) {
input[i] = chromosome;
}
// no dangling pointers in result until you free input.
If you have allocated chromosomes individually and then allocated "input" to house all the pointers, then freeing a chromosome without removing it from "result" will cause a dangling pointer
result = removeDuplicateChromosomes(input, 64);
free(input[0]); // result still points to it, result is dangling.
But if input and the list of chromoes remain intact until you free input() and/or any chromosomes, you have no dangling pointers.

Resources