tags:

views:

466

answers:

10

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?

+4  A: 

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.

Ben
The `sizeof` should be `sizeof(**data)` (or simply `sizeof(int)`); this bug was already in the original code.
Martin B
gnud
right and right, corrected.
Ben
+6  A: 

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?

Martin B
I suggest not calling it a "double pointer" - too easily confused with a "pointer to double".
Nefrubyr
"double pointer" and "triple pointer" are actually quite common terms
a_m0d
Thanks for the comments -- even though I agree "double pointer" is quite common, I've eliminated it to avoid confusion.
Martin B
Personally I'd use `sizeof **data` as opposed to `sizeof (int)`, but otherwise this nails it.
John Bode
+2  A: 

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;
}
aJ
+1  A: 

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().

Naveen
+2  A: 

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 ();
Toad
No, I just did that to simplify things - can't return it.
a_m0d
I was guessing as much.... but you never know. ;^)
Toad
A: 

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));
}
Learner
A: 

If someFunction does nothing of its own, then why does it even exist ?

Because it has been extremely simplified for pasting on the net - we don't like full code samples pasted on here.
a_m0d
A: 

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);
karx11erx
A: 

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));

GG
A: 

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.
John Bode