C Memory management

Discussion in 'C' started by manaila, Jan 31, 2012.

  1. manaila

    manaila New Member

    Joined:
    Jul 6, 2010
    Messages:
    11
    Likes Received:
    0
    Trophy Points:
    0
    Location:
    On Earth
    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)" ?
     
  2. xpi0t0s

    xpi0t0s Mentor

    Joined:
    Aug 6, 2004
    Messages:
    3,009
    Likes Received:
    203
    Trophy Points:
    63
    Occupation:
    Senior Support Engineer
    Location:
    England
    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.
     
    jovy_WOG and manaila like this.
  3. manaila

    manaila New Member

    Joined:
    Jul 6, 2010
    Messages:
    11
    Likes Received:
    0
    Trophy Points:
    0
    Location:
    On Earth
    Hmm, what an excellent and very well explained lecture!
    Thank you, thank you, thank you very much xpi0t0s, I think I owe you:)
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice