tags:

views:

589

answers:

6

I have a function that takes pointer to pointer to struct.

struct ABC;

void func(ABC ** ptr);  //func is provided by someone else and i don't know about the implementation.
{


}

Now, i have following code.

ABC xyz[2];
ABC * ptr = xyz;

ABC **dptr1 = &ptr;     //pointer to ponter

ABC ** dptr2 = (ABC **)malloc(2*sizeof(struct abc*)); //pointer to arrary of pointers

dptr2[0] = &xyz[0];
dptr2[1] = &xyz[1];

if i pass dptr1 to func, and func does something like *ptr[0] and *ptr[1] to access the actual values, it fails.

So, what is the standard way of passing around a double pointer.

+1  A: 

I've written a guide on how to use Pointers and Double Pointers, you can find it here, this should explain some fundamentals about it and help you forward.

Filip Ekberg
A: 

Well, you tagged this as C++, so I'm going to new instead of malloc in the example. Malloc's a bit plain C. If you really want to C++ proper I'd suggest you actually use a std::vector instead.

struct ABC
{
  // some members;
};


void func( ABC* firstElemOfArray, int countOfElementsInArray)
{
  for( int i = 0; i < countOfElementsInArray; ++i )
  {
     ABC* currentElement = firstElemOfArray + i;
     // do something with current elements
  }
}

void useFunc()
{
   ABC array[2] = { new ABC(), new ABC() };

   func( array, sizeof(array/sizeof(array[0]) );
}

Basically, you don't need to do anything too fancy, just pass the pointer to the first element of the array with the length of the array.

Scott Langham
Scott, shouldn't ABC array[2] be ABC *array[2];Also, i can't change the func function. it is provided by someone else. I just know the signature of it.
chappar
Does func assume the array always contains 2 elements. If the number of elements isn't fixed, this needs to be passed to it somehow.
Scott Langham
+5  A: 

When you pass ptr1 to func, you are actually passing a pointer to the memory occupied by ptr. The problem is that func seems to be assuming that ptr1 is actually pointing the beginning of an array of pointers, but you've passed it a pointer to an "array" of only one pointer, ptr. func will probably do major damage on your stack.

What you should do when passing a pointer into a function is to determine whether that function will be treating the pointer as just that, or as a pointer into an array (which seems to be the case with func here). In the latter case, you have to be careful to indeed provide an array for the function.

In your code, I would write

ABC xyz[2];     // two ABC's
ABC * ptrs[2];  // array of pointers to ABC's
ptrs[0] = &xyz[0];
ptrs[1] = &xyz[1];
func(ptrs);    // C compiler convers an array of pointers to
               // a pointer to pointers when passing it to a function

As an aside, because C doesn't have a notion of array parameters, it is usually a good idea to specify an additional parameter with the array length in question, or you may leave yourself open to buffer overflow vulnerabilities. Your func function could have been written func(ABC ** ppabc, int elements) to better reflect how it intends to use its parameters. The current function signature implies that func in reality will only use the pointer pointed to by the pointer to pointer (try saying that fast three times!)

John Källén
In case func is provided by someone else, what should i assume?
chappar
Unfortunately, due to the nature of C, you can't assume much if all you have is the code. Hopefully, `func` has been documented properly so that you know what it will be doing with that pointer you're passing.
John Källén
C(99?) accepts array's as parameters just fine (a pointer will be passed, but the size information will be retained); C99 even allows for variably-sized array parameters! –
Christoph
A: 

Use type definitions to make code reading easier:

typedef ABC* ABC_ptr;

Then use new instead of malloc:

ABC_ptr *dptr2 = new ABC_ptr[2];

You should be able to access *ptr[0] inside func. Are you sure that the initial ABC table is initialized?

kgiannakakis
That's a matter of taste - I really dislike it when people do stuff like this; the only valid reason for this is information hiding, e.g. if you only ever pass a pointer to ABC and don't want to expose implementation details, do `typedef struct ABC * ABC_t;`
Christoph
+2  A: 

Arrays in C are basically pointers. The only real difference is that the compiler knows to how many elements it points to.

That means that wherever your code expects a pointer, you can use an array's name instead as it will implicitly be converted to a pointer to its first element.

As mentioned, the important difference between arrays and pointers is the result of the sizeof() operator: For pointers, it will return the number of bytes needed to store the pointer, for arrays, it will return the number of bytes needed to store the elements!

Therefore, it's possible to determine the number of elements in an array with the following macro:

#define count(ARRAY) (sizeof(ARRAY)/sizeof(*ARRAY))

Instead of *ARRAY, you could use ARRAY[0] as well.

Anyway, to pass an array to a function, just treat it as a pointer. Because the compiler won't know the number of elements it contains (it looses this information during the conversion), pass another argument specifying this as well:

void func(struct ABC * array, size_t elementCount) {...}

struct ABC anArray[2] = {...};
func(anArray, count(anArray));

We use our count() macro so that we need to change the array's size only in one place.


So, why does your function expect a double pointer? Most likely, to allow the elements of the array to be arbitrarily placed in memory. So if you stored your elements in a plain old array, you have to convert it:

struct ABC array[2] = {...};
struct ABC * parray[count(array)];

for(size_t i = 0; i < count(array); ++i)
    parray[i] = &array[i];

Also, if func really doesn't accept a second argument for the array's size, this could mean that it either only accepts arrays of a certain size (then, the function's signature should have been something like func(struct ABC * array[5])), or, that it expects a certain element to signify the end of the array (most likely NULL). In that case, you have to do

struct ABC * parray[count(array) + 1];
parray[count(array)] = NULL;


Also, another solution would be to use a two-dimensional array:

struct ABC parray[][1] = {
     { {/* first struct's initializer */} },
     { {/* second struct's initializer */} },
     { {/* third struct's initializer */} }
};

func(parray, count(parray));
Christoph
+2  A: 

From the function interface:

void func(ABC** ptr)
{
}

This could be one of two things.

  • You are passing the pointer to a pointer
  • You are passing a pointer to a two dimensional array.

The differences are subtle.
A pointer to a pointer indicates that func() is going to change (*ptr) to point at some object. This could be some memory allocation or other operation.

If it is a two dimensional array (as you are eluding too in your question)

ABC   xyz[2];
ABC*  ptr = xyz;

ABC** dptr1   = &ptr;     //pointer to ponter
// or 
ABC*  dptr1[] = {xyz};    //pointer to an array.
                          //Thus making dptr the 'logical' equivalent of ABC[1][2]


> if i pass dptr1 to func, and func does something like *ptr[0] and *ptr[1] to access the actual values, it fails.

If the value passed is a two dimensional array. Do not get this confused with this:

ABC  value[1][2];

Technically this is also a two dimensional array, but the memory layout for the two forms is completely different. The original is really an array of arrays of ABC while this is a true two dimensional array.

ABC   row1[3] = {/*STUFF 1*/};
ABC   row2[3] = {/*STUFF 2*/};
ABC   row3[3] = {/*STUFF 3*/};
ABC*  data[3] = {row1,row2,row3};  // logical ABC data[3][3];

func(data);
Martin York