8

The following C snippet:

[...] 
void f1(void* a){
  printf("f(a) address = %p \n",a);
  a = (void*)(int*)malloc(sizeof(int));

  printf("a address = %p \n",a);
  *(int*)a = 3;

  printf("data = %d\n",*(int*)a);
}

void f(void){
  void* a1=NULL;
  printf("a1 address = %p \n",a1);

  f1(a1);

  printf("a1 address = %p \n",a1);
  printf("Data.a1 = %d\n",*(int*)a1);
}
[...]

results in

a1 address = (nil) 
f(a) address = (nil) 
a address = 0xb3f010 
data = 3
a1 address = (nil) 
Segmentation fault (core dumped)

Why doesn't a1 keep the address that has been assigned to it in the function?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Will
  • 155
  • 1
  • 2
  • 5

4 Answers4

10

As this is C, you cannot pass the pointer by reference without passing in a pointer to the pointer (e.g., void ** rather than void * to point to the pointer). You need to return the new pointer. What is happening:

f(a1);

Pushes the value of the pointer (NULL) as the stack parameter value for a. a picks up this value, and then reassigns itself a new value (the malloced address). As it was passed by value, nothing changes for a1.

If this were C++, you could achieve what you want by passing the pointer by reference:

void f(void *&a);
pb2q
  • 58,613
  • 19
  • 146
  • 147
pickypg
  • 22,034
  • 5
  • 72
  • 84
10

Passing a pointer to a1 to your function, you can't change where a1 points. The pointer is passed by value, so in f1 you're only changing a copy of the address held by a. If you want to change the pointer, i.e. allocate new memory for the pointer passed in, then you'll need to pass a pointer to a pointer:

void f1(void **a)
{
    // ...
    *a = malloc(sizeof(int));
    // ...
pb2q
  • 58,613
  • 19
  • 146
  • 147
5

To change a variable via a function call, the function needs to have reference semantics with respect to the argument. C doesn't have native reference variables, but can implement reference semantics by means of taking addresses and passing pointers.

Generally:

void mutate_thing(Thing * x)    // callee accepts pointer
{
    *x = stuff;                 // callee derefences ("*")
}

int main()
{
    Thing y;
    mutate_thing(&y);           // caller takes address-of ("&")
}

In your case, the Thing is void *:

void f(void ** pv)
{
    *pv = malloc(12);   // or whatever
}

int main()
{
     void * a1;
     f(&a1);
}
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
1

Based on Kerrek SB's example I came with this to demonstrate void pointer-to-pointer as an argument and how it may be used.

#include <stdio.h>

void test(void ** jeez)
{
  *jeez = (void *) (int) 3;
}


int main (int argc, char* argv[])
{
void *a;
test(&a);

int b = *(int *)&a;
printf("value returned = %d\n", b);

return 0;
}
enthusiasticgeek
  • 2,640
  • 46
  • 53