Question

I do not quite understand the difference between passing to a function *mode1 or **mode2.

I wrote some examples. (note: type can be any type)

[Code 1]

#include <stdio.h>

void function (type *vet)
{
    /* other */
}

int main ()
{
    type *vet;

    function (vet)

    /* other */

    return 0;
}

[Code 2]

#include <stdio.h>

void function (type **vet)
{
    /* other */
}

int main ()
{
    type *vet;

    function (&vet)

    /* other */

    return 0;
}

I know: in the first case is a pointer, in the second case is a pointer to a pointer. But why for example in the second case if I pass &vet can I allocate memory in function() and free it in main() and in the first one not?

I search someone who explain me the differences well. What can I do in the two cases? How and where to malloc or realloc? And free? And modify vet in the function?

Was it helpful?

Solution

Original questions

The main (most significant) difference is whether the value in the calling function can be changed.

  1. In the first example, the called function gets a copy of the pointer in the calling code, and cannot modify the pointer in the calling code.
  2. In the second example, the called function gets a pointer to the pointer, and by assigning to *vet in the called function, you can modify the value in the called function.

Why in the second case if I pass &vet can I allocate memory in function() and free it in main() and in the first one not?

In the second case, the code in function() can modify the actual pointer in main(), so the value of vet in main() ends up with the allocated pointer value, which can therefore be freed. In the first case, the value in main() is not modified by the called function, so the data can't be freed by main().

How and where to malloc or realloc? And free?

In the first case, you can use malloc() or realloc() in the function, but you should also free the allocated memory before return unless your code stores the value in a global variable (in which case you can delegate to some other code to handle the free(), but it had better be very clear which code has the responsibility, and in any case using global variables is probably not a good idea). Or unless you change the function signature and return a pointer to the allocated data which should be freed by the calling code.

In the second case, you can allocate or reallocate memory in the called function and leave it allocated to be used by other functions and to be freed by the calling function.

And modify vet in the function?

In both functions, you can modify the local vet variable as you see fit; this is true of any parameter to any function. What you can't necessarily do is modify values in the calling function; you have to have a pointer to value in the calling function to do that. In the first function, you can't change the value of vet in main(); in the second, you can. In both functions, you can change what vet points at. (One minor problem is the conflation of the name vet in the three contexts — the main() and the two different functions. The name vet in the two functions points at different types of things.)


Extension questions

But is it freed in function() like, for example, this?

#include <stdio.h>
#define NUM (10)

struct example
{
    /* ... */
}

void dealloc (struct example *pointer)
{
    free (pointer);
}

int main()
{
    struct example *e;
    e = malloc (NUM * sizeof(struct example));
    if (e == NULL)
        return -1;
    struct example *e_copy = e;
    dealloc (e_copy);
    return 0;
}

This code is legitimate. You pass (a copy of) the value of the pointer to the dealloc() function; it passes that pointer to free() which releases the allocated memory. After dealloc() returns, the pointer values in e and e_copy are the same as before, but they no longer point to allocated memory and any use of the value leads to undefined behaviour. A new value could be assigned to them; the old value cannot be dereferenced reliably.

And what is the difference from this?

#include <stdio.h>
#define NUM (10)

struct example
{
    /* ... */
}

int main()
{
    struct example *e;
    e = malloc (NUM * sizeof(struct example));
    if (e == NULL)
        return -1;
    struct example *e_copy = e;
    free (e_copy);
    return 0;
}

The difference between this example and the last is that you call free() directly in main(), rather than from a function dealloc().

What would make a difference is:

void dealloc(struct example **eptr)
{
    free(*eptr);
    *eptr = 0;
}

int main()
{
    ...
    dealloc(&e_copy);
    return 0;
}

In this case, after dealloc() returns, e_copy is a null pointer. You could pass it to free() again because freeing the null pointer is a no-op. Freeing a non-null pointer twice is undefined behaviour — it generally leads to problems and should be avoided at all costs. Note that even now, e contains the pointer that was originally returned by malloc(); but any use of that pointer value leads to undefined behaviour again (but setting e = 0; or e = NULL; or e = e_copy; is fine, and using e = malloc(sizeof(struct example)); or such like also works.

OTHER TIPS

As you said, the argument in the first case is a pointer, and a copy of the pointer vet is passed. We can modify the value that vet pointed to (e.g., *vet = new value). But we cannot modify the value of the pointer vet because it is just a copy of the original vet pointer. Therefore, after first function, value of *vet may be changed, but value of vet will not.

So how could we modify the value of the pointer vet? We use the pointer to pointer. In the second function, we can allocate memory for *vet, and this modified value will be kept after the second function. So we can free it in main.

We cannot do this in first case because if we try to allocate memory in function, we just allocate memory for the copy of the pointer vet, not the original vet.

You are correct in your understanding that type *var; is a pointer to data of type and type **var; is a pointer to a pointer to data of type.

The difference you asked about, allocating memory in the function and keeping track of it, is because of the ability to assign the value to the pointer.

In C, any time you want to modify a value in a function, you must provide it a pointer the data it is to modify.

If you want to allocate memory, you must know where it is in order to use it, and later free it. If you pass only a pointer to a function, and allocate memory to it, it cannot change the value of the pointer it is passed (rather, when your program returns to the function that called this allocation function, the stack will have unloaded the address you needed); it can only read from it (which in this use is rather pointless).

Consider this, the pointer variable

type *vet;

is created on the stack of function

main()

when "function_1()" is called from main, a stack for this new function is created. Any argument passed to this function is saved on the stack of this function. In this case, the argument is a pointer variable. Now function_1() can very well change the value of this pointer variable but as soon as the function returns the stack of this function is released and any changes are lost.

But when you are passing a pointer to pointer, what you pass is actually an address of a pointer variable and not a pointer variable. So when you work on this pointer variable inside the called function, you are actually working on the memory of the stack of the calling function. And since this memory is on the stack of the calling function, any changes made by the calling function will persist even after the stack of the called function is released.

First thing is, there is no garbage collector in C. So if you don't explicitely free an allocated memory block, it will eat up memory until the process exits. That is a mighty source of memory leaks.

So, once you've allocated a memory block using malloc or similar functions, you must keep a pointer to it in order to free it someday.

If you allocate a block within a function and you plan this block to remain useable after function termination, you must pass its value to some higher level piece of code that will eventually free it, long after the function that created it has exited.

To do so, you have three basic choices:

  1. store the pointer in some global variable
  2. return the pointer as the function's result
  3. have one of the function arguments specify where the pointer is to be stored

case 1

void * global_address_of_buffer;
void alloc_a_buffer (int size)
{
    global_address_of_buffer = malloc (size); // block reference in global var
}

alloc_a_buffer ();
// ...  
free (global_address_of_buffer);

This is clearly impractical. If you call your function twice, you will lose the address of the first buffer.
One of the innumerable illustrations of why using globals will drag you screaming right into hell.

Nevertheless, it's a possibility.

case 2

void * alloc_a_buffer (int size)
{
    return malloc (size); // block reference as return value
}

void * new_buffer;
new_buffer = alloc_a_buffer (10); // retrieve pointer through return value
// ...
free (new_buffer);

This is not always possible. For instance you might want all your functions to return a status indicating success or failure, in which case the return value will not be available for your pointer.

case 3

void alloc_a_buffer (int size, void ** buffer)
{
    *buffer = malloc (size); // block reference set through 2nd parameter
}

void * new_buffer;
alloc_a_buffer (10, &new_buffer); // pass pointer address to the function
// ...
free (new_buffer);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top