Go4Expert

Go4Expert (http://www.go4expert.com/)
-   C (http://www.go4expert.com/forums/c/)
-   -   C Memory management (http://www.go4expert.com/forums/c-memory-management-t27694/)

manaila 31Jan2012 19:11

C Memory management
 
Hi,
I'm struggling with pointers with regard to memory allocation and deallocation.
In the below code, I have two functions, func() and func2(); in func I allocate memory using malloc and in func2 I assign the pointer returned from func to the newly defined pointer.
So since I have allocated memory, I also have to deallocate it when done using it to avoid memory leaks. The problem is I am not sure where I have to deallocate it (well, I understand it has to be after when done with using that memory), but... where..?
I have commented out the statement where I thought it was necessary to free the memory but when executing the program I get the "...double free or corruption (out)..." message.

Code:

#include <stdio.h>
#include <stdlib.h>

int* func(int var)
{
    int *a = (int*)malloc(sizeof(int));
    a = &var;
    return a;
}

void func2()
{
    int *b = func(5);
    printf("*b = %d\n", *b);
    //free(b);   
}

int main()
{
    func2();
    return 0;
}

Can anyone please explain to me this phenomena of memory allocation/deallocation based on the above code...Do I "free(b)" or "free(a)" ?

xpi0t0s 1Feb2012 14:29

Re: C Memory management
 
Neither, because your code leaks memory, and in particular it leaks the value returned by malloc, so you cannot free it because you don't have the address any more.

The value you pass to free() is the value that is returned to you by malloc(). You initially store this in a, but then you overwrite a with the address of var, which is only valid within the stack frame of that function and invalid after the function returns. So this is what's known as undefined behaviour. However, the memory won't actually have changed yet, which is why you get *b=5, from which you have incorrectly deduced that the assignment worked correctly. Proof:

Code:

int *test48a(int var)
{
        int *a=(int*)malloc(sizeof(int));
        a=&var;
        return a;
}

void test48b()
{
        int *b=test48a(5);
        int *c=test48a(21);
        printf("*b=%d\n",*b);
        //free(b);
}

void test48()
{
        test48b();
}

Output: *b=21.

and if you still need convincing, try this one:
Code:

void test48b()
{
        int *b=test48a(5);
        printf("*b=%d\n",*b);
        printf("Hello world!\n");
        printf("*b=%d\n",*b);
}

Output:

*b=5
Hello world!
*b=19865967

or even this one:
Code:

void test48b()
{
        int *b=test48a(5);
        printf("*b=%d\n",*b);
        printf("*b=%d\n",*b);
}

Output:
*b=5
*b=17637720

The garbage value displayed for *b the second time is caused by the fact that the stack frame from the first call to test48a() that contained the address of var has been overwritten by the printf() function call. The expressions are evaluated before the function is called, so *b is unchanged right until the function is called and therefore evaluates correctly in the first printf (although this is still undefined behaviour).

What you probably meant in func() (my test48a()) is *a=var; This would fix the undefined behaviour above, i.e. even with the int *c line you get *b=5 output, and it would leave the correct value in b for you then to pass into free().

Here's a good rule of thumb: wherever possible, always malloc and free within the same function. So your code would become:
Code:

void test48a(int *b_ptr, int var)
{
        *b_ptr = var;
}

void test48b()
{
        int *b=(int*)malloc(sizeof(int));
        test48a(b, 5);
        printf("*b=%d\n",*b); // displays *b=5 both times
        printf("*b=%d\n",*b);
        free(b);
}

void test48()
{
        test48b();
}

There are always going to be cases where this isn't possible; strdup() for example has no choice in the matter and must malloc without calling free, but certainly this is a minority of cases and in most cases there's nothing stopping you keeping it simple and doing both within the same function.


Remember that a pointer is a variable that contains a memory location. In order to access that memory location you have to DEREFERENCE the pointer, which in C means you have to stick a * in front of it. This tells the compiler not to modify the address in the variable, but to modify the contents of the memory location contained in that variable.

If you stick an & in front of something, that does the opposite of dereferencing, and takes the memory address of that something. So &var is the memory location that var is stored in; it is NOT the value in var.

So let's try a text-mode memory map, starting at memory location 1000 and going up in 2's, cos we're going to assume this is a 16-bit machine:

Location : value
1000 : 5
1002 : 21
1004 : 1000
1006 : 1002

So in our program, int *a=malloc(sizeof int) might return 1000. a itself is stored at 1004, so memory location 1004 contains the value 1000, which by variable semantics we know to be a pointer to location 1000 and NOT the value 1000.

We can write 7 into a (note I said a here and not *a), and that will have the following result:

Location : value
1000 : 5
1002 : 21
1004 : 7
1006 : 1002

But that's not what we wanted; we wanted to modify location 1000. So let's reset 1004 to 1000 then write 7 into *a, and we will get the following:

Location : value
1000 : 7
1002 : 21
1004 : 1000 (unchanged by the assignment to *a)
1006 : 1002

Pointers can be multiple, so consider:

Location : value
1000 : 5
1002 : 21
1004 : 1000
1006 : 1004
1008 : 1006

int a: a=1000, value=5
int *a: a=1004, points to 1000, which contains the int 5
int **a: a=1006, points to an int* at 1004, points to an int at 1000
int ***a: a=1008, points to an int** at 1006 and so on.
This is nothing to be frightened of, in fact it's an extremely powerful feature of C. Just about anything can have its address taken, then later that address can be dereferenced to get at the original thing, whether that thing is a real thing or a pointer to the real thing. The good thing about this is that you don't have to think too hard about how many *'s you need; just look at the definition. If you have int ***a, then to dereference that fully to the final address, and modify the int itself, you just need to use ***a.

Address-of cannot be multiplied. &a is the address of a, but this is not stored anywhere, so &&a is not possible because you can't take the address of something that doesn't have an address. (So similarly you can't do int *a = &5;) It is only stored somewhere when you store it somewhere, e.g. int *b=&a; &&a still isn't possible but you can do int **c=&b.

manaila 2Feb2012 12:20

Re: C Memory management
 
Hmm, what an excellent and very well explained lecture!
Thank you, thank you, thank you very much xpi0t0s, I think I owe you:)


All times are GMT +5.5. The time now is 06:23.