views:

114

answers:

3

This is not so much a question on, "How do I pass it into the function?" but rather, "Is this acceptable?"

void func( int **ptr );

int main( int argc, char* argv[] )
{
    int arr[][3] = {{1, 2,}, {3, 4}, {5, 6}};
    int *pArr = *arr;

    (&pArr[0])[1] = 3;

    func(&pArr);

    cin.get();
    return 0;
}

void func( int **ptr )
{
    cout << "In func()" << endl;
    ptr[0][1] = 5;
}

This works as far as I can tell. It doesn't really feel safe to me, but I like it more than passing a 2D array into a function. Instead, a pointer that does the work for me.

Would this be very confusing for people who had to read my code? Should I use other methods instead? Is it a bad idea to work with pointers to arrays?

Also, question a little bit off-topic. Why can I write:

int arr[] = { 1, 2, 3, 4, 5 };
int *pArr = *arr;

but why can't I write

int arr[][3] = {{1, 2,}, {3, 4}, {5, 6}};
int *pArr = **arr;

or even use a **pArr?

+6  A: 

You are getting confused here. int arr[][3] = {{1, 2,}, {3, 4}, {5, 6}}; is not a pointer to arrays of length 3. It's a single solid memory block 9 ints long, which decays into a single int*. The compiler does a 3*first+second mapping for you to this single block of memory.

The reason the subscripting in the function is not valid is because the function has no way of determining what the dimensions of the array are. With a single dimension array, you don't need this information -- you just add the index to the given base pointer. However, with a two dimensional array, you need the dimensions to calculate the final index.

ptr[0][1] = 5; in your function is being translated into *((*(ptr + 0)) + 1) = 5;, which is obviously not what you want given that arr is not an array of pointers.

EDIT: In response to comment:

ptr[0][1] does overwrite the information in cell [0][1] of the array

Yes, you got lucky here -- here's why. When you passed the array into your function, you passed (&pArr[0])[1], which is a pointer to a pointer to the first element of the array. When you did *((*(ptr + 0)) + 1) = 5;, ptr+0 becomes a no-op, leaving *(*ptr + 1) = 5;, which does have well defined behavior given your input data (it's looking at arr as a single-dimension array). If you'd try to change the first dimension of the subscript, however, it would blow up.

Billy ONeal
Thanks for the information, but I'm not quite understanding what you mean. ptr[0][1] does overwrite the information in cell [0][1] of the array, so how is that not what I want? And then, if the array is simply a block of 9 units, then could I really use a pointer that simply points to element 0..7 of the array instead of doing this? So I basically overcomplicated the entire thing?
SoulBeaver
@SoulBeaver: Yes. `arr` decays into an `int *`, not an `int **`. I have responded to the first part of your comment in my answer.
Billy ONeal
Johannes Schaub - litb
@Johannes: Better?
Billy ONeal
+4  A: 

Let's dissect this carefully, since array-to-pointer conversions are sometimes confusing.

int arr[][3] = {{1, 2,}, {3, 4}, {5, 6}};

arr is now an array of 3 arrays of 3 ints.

int *pArr = *arr;

*arr uses the array arr in expression, so it decays to a pointer to the first element of arr -- that is pointer to array of 3 ints (the array containing {1,2,0}). Dereferencing that pointer (with *) gives you the array of 3 ints. Now you're using that array in an expression and it decays to a pointer to int, which is assigned to pArr.

(&pArr[0])[1] = 3;

pArr[0] gives the integer at which pArr is pointing (the number 1). &pArr[0] makes a pointer at that integer (which is actually equal to pArr). Indexing that pointer with [1] gives a reference to the next integer after the number 1, which is the number 2. To that reference you're assigning 3. Here's the catch: pointer to an element of an array can only be used to access other elements of the same array. Your pointer points at an element of the array {1, 2, 0}, which you've changed to {1, 3, 0}, and that's fine, but

func(&pArr);

Now you're creating a pointer to pointer to int (since pArr was a pointer to int), and passing that to your function.

ptr[0][1] = 5;

And now you've taken ptr[0], which evaluates to the pointed-to object, which is your original pointer pArr. This line is equivalent to pArr[1] = 5;, and that is still valid (changing your {1,2,0} array to {1,5,0}). However, ptr[1][... would be invalid, because ptr is not pointing at an element of an array of any kind. It's pointing at a standalone pointer. Incrementing ptr will make it point at uninitialized memory and dereferencing that will be undefined behavior.

And for the additional questions:

You should not be able to write this:

 int arr[] = { 1, 2, 3, 4, 5 };
 int *pArr = *arr;

The array arr decays to a pointer-to-int (pointing at the number 1), dereferencing that gives the integer, and an integer cannot be assigned to the pointer pArr. gcc says error: invalid conversion from 'int' to 'int'*. Likewise, you cannot write this:

int arr[][3] = {{1, 2,}, {3, 4}, {5, 6}};
int *pArr = **arr;

for the same reason: *arr is the array of 3 ints {1, 2, 0}, **arr is the integer 1, and an integer cannot be assigned to a pointer.

Cubbi
A: 

I find multi-dimensional arrays in C++ always a bit confusing and tend to avoid them. There are several solutions that are easier to understand, such as the classes in the Boost.MultiArray or Boost.UBlas libraries, or a vector of vectors, depending on your needs.

Philipp