Reputation: 448
I'm currently working through Zed Shaw's Learn C the Hard Way tutorials and I'm trying to understand what is on the stack and what is on the heap in the following example, and how free() is working.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
/** our old friend die from ex17. */
void die(const char *message)
{
if(errno) {
perror(message);
} else {
printf("ERROR: %s\n",message);
}
exit(1);
}
// a typedef creates a fake type,
// in this case for a function pointer
typedef int (*compare_cb)(int a, int b);
// by declaring this pointer to a function
// we can use this like an int/char etc. in a function's arguments
/**
* A classic bubble sort function that uses the
* compare_cb to do the sorting.
*/
int *bubble_sort(int *numbers, int count, compare_cb cmp)
{
int temp = 0;
int i = 0;
int j = 0;
int *target = malloc(count * sizeof(int));
if(!target) die("Memory error.");
memcpy(target, numbers, count * sizeof(int));
for(i = 0; i < count; i++) {
for(j = 0; j < count - 1; j++){
if(cmp(target[j], target[j+1]) > 0) {
temp = target[j+1];
target[j+1] = target[j];
target[j] = temp;
}
}
}
return target; // returns address of target(I think)
} // target will persist even after function
// exit because we created on the heap (memcopy)
// so what happens if we run more than once?
int sorted_order(int a, int b)
{
return a - b;
}
int reverse_order(int a, int b)
{
return b - a;
}
int strange_order(int a, int b)
{
if(a == 0 || b == 0) {
return 0;
} else {
return a % b;
}
}
/**
* used to test that we are sorting things correctly
* by doing the sort and printing it out.
*/
void test_sorting(int *numbers, int count, compare_cb cmp)
{
int i = 0;
int *sorted = bubble_sort(numbers, count, cmp);
if(!sorted) die("failed to sort as requested.");
for(i = 0; i < count; i++){
printf("%d ", sorted[i]);
}
printf("\n");
free(sorted);
sorted = NULL;
}
int main(int argc, char *argv[])
{
if(argc < 2) die("USAGE: ex18 4 3 1 5 6");
int count = argc - 1;
int i = 0;
char **inputs = argv + 1;
int *numbers = malloc(count * sizeof(int));
if(!numbers) die("Memory error.");
for(i = 0; i < count; i++) {
numbers[i] = atoi(inputs[i]);
}
test_sorting(numbers, count, sorted_order);
test_sorting(numbers, count, reverse_order);
test_sorting(numbers, count, strange_order);
free(numbers);
numbers = NULL;
return 0;
}
In the function bubble_sort
, an array of ints target
is created on the heap. My understanding is that since this is on the heap, it will persist after the function exits.
int *target = malloc(count * sizeof(int));
the function then returns target
return target;
I believe that means that the function returns the address of target
later, in test_sorting
the result of the bubble_sort
function is passed
int *sorted = bubble_sort(numbers, count, cmp);
so, if I'm right, the pointer sorted
has been set to the same address as target
at the end of test_sorting
*sorted
is freed but the data pointed to by *target
is never freed.
But when I run the program in Valgrind I get no memory leaks, so this can't be true.
Am I right, then, in saying that when I free a pointer, the thing it points to is freed? I think probably not... I can't find any reference to this online so I'm assuming I'm mistaken at some point above, but I can't see where.
Upvotes: 0
Views: 1590
Reputation: 96937
You're not running free
to release the memory used to store the pointer value, but to free up the chunk of memory that pointer references, and the pointers sorted
and target
reference the same chunk of memory (within scope). When you free
that memory, neither pointer can be legally dereferenced to that chunk.
Upvotes: 2
Reputation: 5557
The pointer itself is allocated on the stack and - as you already mentioned - holds an address. You also return the pointer by value and pass it by value unless you pass a pointer to the pointer. Malloc allocates memory somewhere and returns a pointer to this location(the address). If you don't want this memory to become unusable, you have to tell the system that you don't need it any longer later. Therefore you pass the address (i.e. the pointer) of the previously allocated chuck to free. This call actually frees the memory pointed to. The pointer itself will go out of scope when the function returns.
Upvotes: 1
Reputation: 106012
Does Freeing a Pointer Free the Memory it References?
Yes. Quoting from: Usage of free
:
When we want to free a memory chunk previously allocated by
malloc()
, we use the free function. This function accepts a char pointer to a previously allocated memory chunk, and frees it - that is, adds it to the list of free memory chunks, that may be re-allocated. Several notes aboutfree()
:
The size of the chunk was stored by
malloc()
previously in its memory map, and that is howfree()
knows how many bytes to free.The freed memory is not being cleared or erased in any manner. This is why accessing memory that was just freed often does not cause a crash - any data in it is still the same as before calling
free()
.The free() function cannot nullify pointers to the given memory chunk that might still exist in our program. After we call
free()
, it is up to us (the programmers) not to try and dereference pointers that still point to that memory chunk. Such pointers are known as 'dangling pointers' - they point to memory that was already freed, and thus they should NOT be dereferenced again, unless they are assigned the address of a different (not-freed) memory chunk.
As you can see, free()
only marks the memory chunk as free - there is no enforcement of this freeing operation.
Upvotes: 2
Reputation: 1250
Yes, sorted and target will have the same address.
You can see this by printing both values.
In bubble_sort: printf("target address %X\n", target);
In test_sorting: printf("sorted address %X\n", sorted);
These should be the same, so the address is returned, then freed.
Upvotes: 0