0

Pointers (and pointers to pointers) are relatively easy to understand when you have simple types such as int, char etc but I've always found it tricky to understand dynamic memory allocation and pointers (to pointers) when you have structs.

That's why I posted an earlier thread trying to understand dynamically allocating memory for a simple struct. I have recently started studying the use of pointers to pointers, and was trying to do a practical exercise shown in another thread: Why use double pointer? or Why use pointers to pointers? especially the part where it says

"Pointers to pointers also come in handy as "handles" to memory where you want to pass around a "handle" between functions to re-locatable memory."

Following the code template provided in the thread, I created the following functions (LIB_OBJECT is a simple struct as defined in the template in the thread):

void lib_free_memblock(LIB_OBJECT** memblock)
{
if (*memblock) {
    free(*memblock);
    *memblock = NULL;
}
}

LIB_OBJECT **lib_create_memblock(void) 
{
LIB_OBJECT *memblock = (LIB_OBJECT*) malloc(10 *   
                          sizeof(LIB_OBJECT));

LIB_OBJECT **ptr_to_memblock = (LIB_OBJECT **) malloc(sizeof(LIB_OBJECT*));

*ptr_to_memblock = memblock; 

if (ptr_to_memblock == NULL)
     {
     printf("Memory block allocation (memblock) failed!\n");
     exit(1);
     }

printf("%d bytes of memory block successfully allocated starting at address %x\n", NUM_OBJECTS, *ptr_to_memblock); 

return ptr_to_memblock; 

}

void lib_resize_memblock (LIB_OBJECT **b, int new_size)
{ 
    *b = (LIB_OBJECT*) realloc ((void *)*b, new_size);if (b == NULL)
    {
    printf("Resizing memory block failed!\n");
    exit(1);
    }

    printf("Memory block at start address %x has been resized to %d bytes\n", *b, new_size);
 }

The program works successfully:

LIB_OBJECT **my_memblock = lib_create_memblock();
lib_resize_memblock(my_memblock, 20);
lib_free_memblock(my_memblock);

10 bytes of memory block successfully allocated starting at address 233c010
Memory block at start address 233c010 has been resized to 20 bytes

However, when I change

*ptr_to_memblock = memblock;

to

 ptr_to_memblock = &memblock;

I can succesfully allocate the 10 bytes using lib_create_memblock:

10 bytes of memory block successfully allocated starting at address 1c2b010

but the program crashes at lib_resize_memblock:

Breakpoint 2, lib_resize_memblock (b=0x7fffffffdf40, new_size=20)
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7aa3c01 in realloc_hook_ini () from /lib64/libc.so.6

Now isn't

*ptr_to_memblock = memblock;

and

 ptr_to_memblock = &memblock;

same?

Can someone please clarify?

If I understand correctly, *b is the start address of the memory block allocated and **b is the pointer to the start address of the memory block allocated thus allowing the programmer to do modifications on the allocated memory block. Then, how does the compiler understand what *b mean in lib_resize_memblock where we send **b as argument to the function? From the following initialization?

   *ptr_to_memblock = memblock;

I guess the compiler follows a chain connection, from the pointer to pointer into pointer and finally into the address that the pointer points to, all I have to do is to make the right connection, am I right? Thanks in advance..

Community
  • 1
  • 1
c_b
  • 111
  • 1
  • 1
  • 4
  • You have a bunch of unnecessary casts surrounding and inside your `alloc` calls; remove them all. Also, the `create` function makes 10 *objects* but the resize function makes the number of *bytes* which is an inconsistent interface; I'd suggest changing the resize function to take the number of objects as argument – M.M Feb 03 '15 at 02:45
  • I remember reading a post saying casting malloc was unnecessary, I guess that's what you mean (by "inside", I do not get what exactly you mean by "surrounding"). Regarding the bytes argument, I missed that - I actually had NUMBER_OF_OBJECTS defined in the header file, I will correct it in the function.. – c_b Feb 03 '15 at 03:00
  • That's what I meant by "surrounding"; the "inside" was `realloc ((void *)*b` – M.M Feb 03 '15 at 03:09
  • Before you bite off memory-block management I would strongly suggest examining how different algorithms that use, or don't use, pointer-to-pointer logic work, and can be implemented with *either*. One of the simplest is a single-linked list kept order on insertion of new nodes. The algorithms are fundamentally the same in that the "place" where you insert must be found. The implementations, however, are considerably different (you'd be surprised how short the pointer-to-pointer approach is). Challenge yourself and try coding it. – WhozCraig Feb 03 '15 at 04:00

2 Answers2

1

Your original version of the code works (but has some unnecessary guff as I'll explain later). The problem with:

ptr_to_memblock = &memblock;

is that memblock is a local variable to the create function, and its lifetime ends when the function returns. So that line would end up with you returning a pointer to a variable that no longer exists.

Also this would leak the memory that you just malloc'd because there is no longer anything pointing to that memory.


In fact the create function has an unnecessary level of indirection, and your whole code has a memory leak because you never have a free that corresponds to the malloc. It would be much better to use the following:

LIB_OBJECT *lib_create_memblock(void) ;

which just returns memblock and do not bother with ptr_to_memblock at all. Your main function then has a couple of options, you can automatically allocate the "handle":

LIB_OBJECT *my_memblock = lib_create_memblock();
lib_resize_memblock(&my_memblock, 20);
lib_free_memblock(&my_memblock);   

or you can malloc the handle (which is unnecessarily complicated unless you really have a good reason):

LIB_OBJECT **pp_memblock = malloc(sizeof *pp_memblock);
*pp_memblock = lib_create_memblock();
lib_resize_memblock(pp_memblock, 20);
lib_free_memblock(pp_memblock);
free(pp_memblock);
M.M
  • 138,810
  • 21
  • 208
  • 365
  • I now see the problem with the create function, I seem to have used the double pointer unnecessarily. I now corrected the code returning just memblock. I also seem to have forgotten to free(my_memblock), thanks for the heads up! – c_b Feb 03 '15 at 03:19
  • We are only initializing **pp_memblock in your example, then how does the compiler extract what *pp_memblock or pp_memblock mean in the following lines? Can you explain the thinking way of the compiler here? – c_b Feb 03 '15 at 03:25
  • @c_b the second line of the second example initializes `*pp_memblock` . after that, `pp_memblock` points to (a copy of) the pointer returned by `lib_create_memblock` just as `&my_memblock` does in the first example – M.M Feb 03 '15 at 03:29
  • Wouln't it be better to swap the first and second lines? I mean, shouldn't we define *pp_memblock first and then **pp_memblock? – c_b Feb 03 '15 at 04:00
  • @c_b I'm not sure what you mean, that isn't even possible. The first line declares `pp_memblock` (not `**pp_memblock`) and makes it point to a small block of memory we just allocated with `malloc`. The second line puts a value in that block of memory we just allocated. Maybe you misunderstand what variable is where in memory, try drawing a diagram on paper (use a box to represent a variable (named or not), and an arrow to represent where a pointer is pointing) – M.M Feb 03 '15 at 04:04
  • I was using two different variables, one for the pointer to memory and another for the pointer to this pointer. I guess I got a bit confused when you used the same variable for both.. – c_b Feb 03 '15 at 12:20
  • @c_b I used `my_memblock` to point to your block of objects, and `pp_memblock` to point to a pointer which points to your block of objects .. was hoping to avoid that confusion :) – M.M Feb 03 '15 at 20:13
  • You are right, I think I did not understand the second example well. You are allocating the pointer to pointer, then allocating the pointer that it points to - both with the same pp_memblock. Is this the same logic as using pointers to pointers for creating 2D arrays? Except here the row in the array becomes the pointer to the memory block while the column becomes the memory block itself - is this analogy correct? – c_b Feb 03 '15 at 23:12
  • @c_b `pp_memblock` is just one variable. It points to another pointer variable which doesn't have a name. (Memory allocated by malloc is not named). That pointer variable points to your block of objects. The `pp_memblock` example is the same memory layout as your original code except that the `malloc` call has been moved to a different place. You could think of this as an jagged array with 1 row, yes. – M.M Feb 03 '15 at 23:17
0

*ptr_to_memblock = memblock; and ptr_to_memblock=&memblock are not same in this case.

When you look at

LIB_OBJECT **ptr_to_memblock = (LIB_OBJECT **)malloc(sizeof(LIB_OBJECT*));

and

*ptr_to_memblock = memblock;

you are creating a pointer to a pointer which actually contains 10 continuous location of whatever date. The second expression implies the address stored in ptr_to_memblock is the address to this data. This is also the same value in memblock. But the equations implies there are 2 things pointing to data. One is memblock , another is a pointer whose address is in ptr_to_memblock. Hence two separate pointers.

Now considering,ptr_to_memblock=&memblock: ptr_to_memblock stores the address of memblock pointer. Hence ptr_to_memblock points to memblock which in turn points to data. Hence there is a single pointer to data.

Hope this helps!!

  • So you are saying ptr_to_memblock and memblock are pointing to different things in *ptr_to_memblock = memblock? "One is memblock" - I get it, "another is a pointer whose address is in ptr_to_memblock" - I guess you mean address of memblock is in ptr_to_memblock. If that's so, then isn't this the same as what you wrote in the following paragraph? – c_b Feb 03 '15 at 03:57
  • No. In *ptr_to_memblock = memblock, ptr_memblock doesnot containt the address of memblock. Here data is being pointed by memblock and a pointer whose address is in ptr_memblock. I mean to say there are two pointers pointing to same thing ,"not different things". – Saisujithreddy Feb 03 '15 at 04:56
  • "data is being pointed by memblock" - ok, but when you say "a pointer whose address is in ptr_memblock", what pointer are we talking about exactly? – c_b Feb 03 '15 at 12:25
  • I am talking about a pointer which is not declared explicitly. In LIB_OBJECT **ptr_to_memblock = (LIB_OBJECT**)malloc(sizeof(LIB_OBJECT*)); you are creating a memory(a pointer) and the address of that memory is in ptr_memblock. Let me take some values: say starting address of data is 10. 10 will be stored in memblock. Let the pointer which you are dynamically creating be created at address 70. Now ptr_memblock stores 70. And the pointer which you have created explicitly using malloc stores 10. Hence this dynamically created pointer points to data. – Saisujithreddy Feb 03 '15 at 23:31