38
votes

I have some code in a couple of different functions that looks something like this:

void someFunction (int *data) {
  data = (int *) malloc (sizeof (data));
}

void useData (int *data) {
  printf ("%p", data);
}

int main () {
  int *data = NULL;

  someFunction (data);

  useData (data);

  return 0;
}

someFunction () and useData () are defined in separate modules (*.c files).

The problem is that, while malloc works fine, and the allocated memory is usable in someFunction, the same memory is not available once the function has returned.

An example run of the program can be seen here, with output showing the various memory addresses.

Can someone please explain to me what I am doing wrong here, and how I can get this code to work?


EDIT: So it seems like I need to use double pointers to do this - how would I go about doing the same thing when I actually need to use double pointers? So e.g. data is

int **data = NULL; //used for 2D array

Do I then need to use triple pointers in function calls?

11
Yes, you would need triple pointers thenvpram86

11 Answers

76
votes

You want to use a pointer-to-pointer:

void someFunction (int **data) {
  *data = malloc (sizeof (int));
}

void useData (int *data) {
  printf ("%p", data);
}

int main () {
  int *data = NULL;

  someFunction (&data);

  useData (data);

  return 0;
}

Why? Well, you want to change your pointer data in the main function. In C, if you want to change something that's passed in as a parameter (and have that change show up in the caller's version), you have to pass in a pointer to whatever you want to change. In this case, that "something you want to change" is a pointer -- so to be able to change that pointer, you have to use a pointer-to-pointer...

Note that on top of your main problem, there was another bug in the code: sizeof(data) gives you the number of bytes required to store the pointer (4 bytes on a 32-bit OS or 8 bytes on a 64-bit OS), whereas you really want the number of bytes required to store what the pointer points to (an int, i.e. 4 bytes on most OSes). Because typically sizeof(int *)>=sizeof(int), this probably wouldn't have caused a problem, but it's something to be aware of. I've corrected this in the code above.

Here are some useful questions on pointers-to-pointers:

How do pointer to pointers work in C?

Uses for multiple levels of pointer dereferences?

9
votes

A common pitfall especially if you moved form Java to C/C++

Remember when you passing a pointer, it's pass by value i.e the value of the pointer is copied. It's good for making changes to data pointed by the pointer but any changes to the pointer itself is just local since it a copy!!

The trick is to use pass the pointer by reference since you wanna change it i.e malloc it etc.

**pointer --> will scare a noobie C programmer ;)

4
votes

You have to pass a pointer to the pointer if you want to modify the pointer.

ie. :

void someFunction (int **data) {
  *data = malloc (sizeof (int)*ARRAY_SIZE);
}

edit : Added ARRAY_SIZE, at some point you have to know how many integers you want to allocate.

2
votes

That is because pointer data is passed by value to someFunction.

int *data = NULL;
//data is passed by value here.
someFunction (data); 
//the memory allocated inside someFunction  is not available.

Pointer to pointer or return the allocated pointer would solve the problem.

void someFunction (int **data) {
  *data = (int *) malloc (sizeof (data));
}


int*  someFunction (int *data) {
  data = (int *) malloc (sizeof (data));
return data;
}
2
votes

someFunction() takes its parameter as int*. So when you call it from main(), a copy of the value you passed created. Whatever you are modifying inside the function is this copy and hence the changes will not be reflected outside. As others suggested, you can use int** to get the changes reflected in data. Otherway of doing it is to return int* from someFunction().

2
votes

Apart from using the doublepointer technique, if there's only 1 return param needed rewrite is as following:

 int *someFunction () {
   return (int *) malloc (sizeof (int *));
 }

and use it:

 int *data = someFunction ();
1
votes

Here's the general pattern for allocating memory in a function and returning the pointer via parameter:

void myAllocator (T **p, size_t count)
{
  *p = malloc(sizeof **p * count);
}
...
void foo(void)
{
  T *p = NULL;
  myAllocator(&p, 100);
  ...
}

Another method is to make the pointer the function's return value (my preferred method):

T *myAllocator (size_t count)
{
  T *p = malloc(sizeof *p * count);
  return p;
}
...
void foo(void)
{
  T *p = myAllocator(100);
  ...
}

Some notes on memory management:

  1. The best way to avoid problems with memory management is to avoid memory management; don't muck with dynamic memory unless you really need it.
  2. Do not cast the result of malloc() unless you're using an implementation that predates the 1989 ANSI standard or you intend to compile the code as C++. If you forget to include stdlib.h or otherwise don't have a prototype for malloc() in scope, casting the return value will supress a valuable compiler diagnostic.
  3. Use the size of the object being allocated instead of the size of the data type (i.e., sizeof *p instead of sizeof (T)); this will save you some heartburn if the data type has to change (say from int to long or float to double). It also makes the code read a little better IMO.
  4. Isolate memory management functions behind higher-level allocate and deallocate functions; these can handle not only allocation but also initialization and errors.
0
votes

Here you are trying to modifying the pointer i.e. from "data == Null" to "data == 0xabcd"some other memory you allocated. So to modify data that you need pass the address of data i.e. &data.

void someFunction (int **data) {
  *data = (int *) malloc (sizeof (int));
}
0
votes

Replying to your additional question you edited in:

'*' denotes a pointer to something. So '**' would be a pointer to a pointer to something, '***' a pointer to a pointer to a pointer to something, etc.

The usual interpretation of 'int **data' (if data is not a function parameter) would be a pointer to list of int arrays (e.g. 'int a [100][100]').

So you'd need to first allocate your int arrays (I am using a direct call to malloc() for the sake of simplicity):

data = (int**) malloc(arrayCount); //allocate a list of int pointers
for (int i = 0; i < arrayCount; i++) //assign a list of ints to each int pointer
   data [i] = (int*) malloc(arrayElemCount);
0
votes

Rather than using double pointer we can just allocate a new pointer and just return it, no need to pass double pointer because it is not used anywhere in the function.

Return void * so can be used for any type of allocation.

void *someFunction (size_t size) {
    return  malloc (size);
}

and use it as:

int *data = someFunction (sizeof(int));
0
votes

For simplicity, let me call the above single pointer parameter p and the double pointer pp (pointing to p).

In a function, the object that p points to can be changed and the change goes out of the function. However, if p itself is changed, the change does not leave the function.

Unfortunately, malloc by its own nature, typically changes p. That is why the original code does not work. The correction (58) uses the pointer pp pointing to p. in the corrected function, p is changed but pp is not. Thus it worked.