views:

396

answers:

9
#include<stdio.h>
void print(int *arr[], int s1, int s2) {
    int i, j;
    for(i = 0; i<s1; i++)
        for(j = 0; j<s2; j++)
            printf("%d, ", *((arr+i)+j));
}

int main() {
    int a[4][4] = {{0}};
    print(a,4,4);
}

This works in C, but not in C++.

error:

cannot convert `int (*)[4]' to `int**' for argument `1' to 
`void print(int**, int, int)'

Why does it not work in C++? What change is needed to be made?

+8  A: 

This code will not work in neither C nor C++. An array of type int[4][4] is not convertible to a pointer of type int ** (which is what int *arr[] stands for in parameter declaration). If you managed to compile it in C, it is simply because you probably ignored a C compiler warning of basically the same format as the error message you got from C++ compiler. (Sometimes C compilers issue warnings for what is essentially an error.)

So, again, don't make assertions that are not true. This code does not work in C. In order to convert a built-in 2D array into a int ** pointer you can use a technique like this one

http://stackoverflow.com/questions/1584100/converting-multidimensional-arrays-to-pointers-in-c

(See the accepted answer. The problem is exactly the same.)

EDIT: The code appears to work in C because another bug in the printing code is masquerading the effects of the bug in array passing. In order to properly access an element of an int ** pseudo-array, you have to use expression *(*(arr + i) + j), or better a plain arr[i][j] (which is the same thing). You missed the extra * which made it print something that has absolutely nothing to do with the content of your array. Again, initialize your array in main to something else to see that the results you are printing in C have absolutely nothing to do with the your intended content of the array.

If you change the printf statement as shown above, your code will most likely crash because of the array-passing bug I described initially.

One more time: you cannot pass a int[4][4] array as an int ** pseudo-array. This is what the C++ is telling you in the error message. And, I'm sure, this is what your C compiler told you, but you probably ignored it, since it was "just a warning".

AndreyT
http://codepad.org/ZKBvaGiW
Amoeba
@cambr: One more time: the code does not work. Whatever results you are getting from it in C is just garbage, which just accidentally happens to look like the correct output. Initialize your array to something else (not zeros) and try again.
AndreyT
Well. THe type is different, but the underlying memory address is the same. Hence it is rational to treat this as a warning for a C compiler.
kriss
@kriss: Er... I don't know what "the underlying memory address is the same" is supposed to mean. The physical structure of `int[4][4]` array is not even remotely similar to an `int **` pseudo-array. Any attempts to access `int[4][4]` array through a `int **` pointer normally lead to a segfault. Or to meaningless results. In this case the meanigless results accidentally happen to look as meaningful results, which is why the OP decided that the code works.
AndreyT
kriss
In other words, the OP did not test their broken code well enough. Hence the baseless assumption that it "works", while in reality it doesn't.
AndreyT
@kriss: Yes, but I don't see what relevance that fact is supposed to have in this case.
AndreyT
That's also only meaningful if it holds true for every value in the array- not just for the primary pointer. Adding to pointers like he has done is undefined behaviour, too. The solution with the templates is the best solution - if the OP can't use the STL.
DeadMG
@DeadMG: not if you ever have to maintain this code, I much prefer the C99 version using variadic array.
kriss
@AndreyT: the relevance is that as the internal layout of arrays is sensibly defined in C standards (contiguity), when you now it, it become both simpler and faster to use 1d arrays for such cases.
kriss
@kriss; Well, firstly, pedantically speaking, reinterpreting 2D array as a 1D array is illegal from the standard point of view, even if the internal layout would allow that. Secondly, in order to do that you'd have to convert `int[4][4]` to `int *`, not to `int **` as in OP's case.
AndreyT
@Kriss: The code maintains itself. That's why it's such great code.
DeadMG
+3  A: 
#include<stdio.h>
void print(int arr[][4], int s1, int s2) {
    int i, j;
    printf("\n");
    for(i = 0; i<s1; i++) {
        for(j = 0; j<s2; j++) {
            printf("%d, ", *((arr+i)+j));
        }
    }
    printf("\n");
}

int main() {
    int a[4][4] = {{0}};
    print(a,4,4);
}

This will work, where by work I mean compile. @AndreyT explained why your version doesn't work already.

This is how you should pass a 2d array.

For clarity, you can also specify both sizes in the function declaration:

#include<stdio.h>
void print(int arr[4][4], int s1, int s2) {
    int i, j;
    printf("\n");
    for(i = 0; i<s1; i++) {
        for(j = 0; j<s2; j++) {
            printf("%d, ", *((arr+i)+j));
        }
    }
    printf("\n");
}

int main() {
    int a[4][4] = {{0}};
    print(a,4,4);
}

Both will work.

You should also change *((arr+i)+j) to either a[i][j] (preferably) or *(*(arr+i)+j) if your intention is to access the jth element of row i.

IVlad
ok. But what's the point in passing s1 and s2 if they are fixed constants defined in function header ? You can go the full way and replace them by 4.
kriss
@kriss: ask the OP, I only made his program work :). One reason would be to print only a subset of the array.
IVlad
@|V|lad I understand you fix (see my answer), but I believe he is trying to write a general purpose print function working for any array, so I proposed another one.
kriss
@IVlad: There's no point in passing array sizes if they are already fixed at compile time. It is a question that one should answer first. If you need a function for 2D array of fixed size, don't pass the sizes. If you need a function that works with 2D arrays of any size, use the proper array passing technique. Your variants strikes me as a weird mix of the two. I don't see the point of doing it that way.
AndreyT
+2  A: 
#include<cstdio>
template <size_t N, size_t M>
struct DataHolder
{
    int data[N][M];
    DataHolder()
    {
       for(int i=0; i<N; ++i)
           for(int j=0; j<M; ++j)
               data[i][j] = 0;
    }
};

template <size_t N, size_t M>
void print(const DataHolder<N,M>& dataHolder) {
    printf("\n");
    for(int i = 0; i<N; i++) {
        for(int j = 0; j<M; j++) {
            printf("%d, ", dataHolder.data[i][j]);
        }
    }
    printf("\n");
}

int main() {
    DataHolder<4,4> a;
    print(a);
}
Notinlist
Really, what's the point of doing this?
IVlad
Preventing unnecessary copy. Secondly if the size is fixed at compile time it should be a template parameter or a define, instead of "magic number 4 everywhere".
Notinlist
is there no such thing as template abuse ? ;-)
kriss
@Notinlist: nothing will be copied anyway. I agree with the size being a define / constant, but I think you're overcomplicating this.
IVlad
@IVlad. It is easily possible :-). The whole thing should be approached in an entirely different way (see STL comment at question), so this answer remains awkward anyway. :-)
Notinlist
@Notinlist: precisely :).
IVlad
It's definitely far preferable to the OP's original solution, although it could still be improved by using std::cout over printf.
DeadMG
+1: this is a perfectly valid solution; the initialization code could be shortened to `memset(data, 0, sizeof data)` and you might want to make `print()` a method or add `operator<<()` for C++-style printing
Christoph
A: 

Short answer, you may change the program as following

void print(int arr[], int s1, int s2) {
...
printf("%d,", *(a+i + s2*j));
...
print((int*)a,4,4);

This would need a better answer explaining the differences between pointer and pointer arithmetic and arrays in C and C++. I won't launch into that now. Maybe someone else ?

I obviously am not shocked by the same point as other posters in your code. What bother me most in the print function header is that you use a double indirection for an array where you do not intend to change initial pointer back (in fact it can't be done as it is a constant). @|V|lad answer fix this by setting one or two dimensions to a fixed constant, but then passing s1 and s2 become useless.

All depends of what you really want to do. Is print a general purpose array printing function or a specialized one for some array types ?

kriss
this won't work if he changes `*((arr+i)+j)` to `arr[i][j]` however. And if he doesn't, I'm getting weird (and different) results if I init `a[i][j] = i + j` with both your and my solution. So yeah, I dunno what the OP is trying to do...
IVlad
Won't work if he changes his printing to `*(*(arr+i)+j)` either. WILL work if he changes to `*((arr+i)+s1*j)` however. Basically if I understand this right, you're using a 1d array to represent a 2d array...
IVlad
@|V|lad: yes, you are right. Yes, representing 2d or higher dimension arrays with 1d array is an old common C trick. That's the same logic that when you copy a structure using memcpy. That is ??? (what is the equivalent of "pythonic" for C) because C was designed like a very low level language, a kind of portable assembly. The drawback is that doing so you lose some type checking. C99 added some cool alternatives like variadic arrays, and you also have the C++ way of doing things. But I feel that going into the high level side, C++ has lost some of this low levelness of C.
kriss
John Bode
@John: that's the point, `int*` remove type checking. Hence the print function can be used with non [][4] arrays. I agree removing type checking is dangerous, but it's not forbidden (and following argument with Robert above, I wonder now if using `a[0]` could not lead to problems like boundary restriction when trying to access `a[1]` inside function, even if I know of no compiler doing that, if feels like a Java thing).
kriss
+2  A: 

Here's a version which is both working, but theoretically invalid (see below) C90 and C++98:

#include <stdio.h>

static void print(int *arr, size_t s1, size_t s2)
{
    size_t i, j;
    printf("\n");
    for(i = 0; i < s1; i++) {
        for(j = 0; j < s2; j++) {
            printf("%d, ", arr[i * s2 + j]);
        }
    }
    printf("\n");
}

int main(void) {
    int a[4][4] = {{0}};
    print(a[0], 4, 4);
    return 0;
}

A C++ version using templates (adapted from Notinlist's answer) could look like this:

#include <iostream>
#include <cstring>

using namespace std;

template <size_t N, size_t M>
struct IntMatrix
{
    int data[N][M];
    IntMatrix() { memset(data, 0, sizeof data); }
};

template <size_t N, size_t M>
ostream& operator<<(ostream& out, const IntMatrix<N,M>& m)
{
    out << "\n";
    for(size_t i = 0; i < N; i++) {
        for(size_t j = 0; j < M; j++) {
            out << m.data[i][j] << ", ";
        }
    }
    out << "\n";
    return out;
}

int main()
{
    IntMatrix<4,4> a;
    cout << a;
    return 0;
}

Alternatively, you could use nested STL containers - ie vector< vector<int> > - instead of a plain array.

With C99, you could do

static void print(size_t s1, size_t s2, int arr[s1][s2]) {
    printf("\n");
    for(size_t i = 0; i < s1; i++) {
        for(size_t j = 0; j < s2; j++) {
            printf("%d, ", arr[i][j]);
        }
    }
    printf("\n");
}

and call it as

print(4, 4, a);

As Robert pointed out in the comments, the first snippet actually involves undefined behaviour. However, assuming that pointer arithmetics will always result in a pointer even when undefined behaviour is involved (and not blow up your computer), there is only a single possible result because of other restrictions within the standard, ie this is an instance of where the standard leaves something unnecessarily undefined.

As far as I can tell, substituting

print(a[0], 4, 4);

with

union m2f { int multi[4][4]; int flat[16]; } *foo = (union m2f *)&a;
print(foo->flat, 4, 4);

will make it legal C.

Christoph
@Christoph: nice to show off C99 variadic arrays, they are not yet very well known by C programmers.
kriss
The first version invokes undefined behavior as the object to which arr points is the 4 element array of int and you index outside that bound in your printf call. Despite the fact that you know that the memory must be adjacent it is technically illegal, this is to allow for implementations which may check array bounds. See Question #16 and the response at http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_017.html for the details.
Robert Gamble
@Christoph: Subscripting is defined in terms of accessing elements within a single array object. You cannot use subscripting on a pointer that points into an array to access an element outside of that array. This is all clearly spelled out in c99 6.5.6p8. The defect report I referenced clarifies how a pointer to an element in a multidimensional array is handled. In your example, arr points to the first int in an array of 4 ints, attempting to use the pointer to access an element outside of this array is undefined, even though it will *probably* work as expected on every implementation.
Robert Gamble
@Robert: as function parameter is defined as `int*` there is no problem with the function internals. The only problem is if compiler will accept `a[0]` to be of the same type as `int*` at function call.
kriss
@Robert: Q16 does not apply because `print()` never sees a multi-dimensional array; undefined behaviour might occur because pointer arithmetics is defined only within a given array (see C99 6.5.6), so technically, you are correct; practically, this doesn't matter, though, because `(int *)foo + offset == (char *)foo + offset * sizeof (int)` and a `char *` may point anywhere within the multi-dimensional array; I'd consider this a bug within the standard
Christoph
@kriss: I'm sorry but I don't understand what you are saying.@Christoph: It is not what print() "sees" that is the problem, the compiler/runtime could well be tracking the assignment to the pointer all the way into the function and determine that an out-of-bounds exception has occurred inside the function, the standard allows implementations to do this. I also don't think your `char *` example is any more valid than the `int *` example, the `char *` pointer can point anywhere within one of the arrays but you still cannot use it with pointer arithmetic to access memory outside that array.
Robert Gamble
But again I will acknowledge that this will probably work on any implementation you are likely to encounter, it just isn't guaranteed.
Robert Gamble
@Robert: a `char *` can cross the sub-array boundaries if it's derived from the original multi-dimensional array in accordance with C99 6.3.2.3 §7; one could also argue that in principle, you can always create a `int (*)[16]` pointing to the `int [4][4]` to circumvent the undefined pointer arithmetics; afaik, this is legal as the conversion from `int (*)[4][4]` to `int (*)[16]` doesn't violate any alignment requirements
Christoph
@Christoph: Okay, I see what you are saying about the char pointer pointing to the entire array of arrays and agree that it can traverse that entire object which includes all the sub-arrays. This special provision for character pointers does not extend to other types though. Lastly, `int (*)[4][4]` and `int (*)[16]` are certainly not compatible types and such a conversion is not allowed.
Robert Gamble
@Robert: I will try to be simple. If you define a function with header `void f(int * i)` that implies there is no boundary checking on i. The compiler/runtime can't be tracking the assignment to the pointer as it would defeat compilation unit purpose (to do that you would have to break C calling convention). That implies using a compiler generating code another can't link to. There was such compilers in the past (I even used a C compiler with Pascal calling convention). But what's the point of making a compiler standard compliant if you can't link with it's code ?
kriss
@kriss: There isn't a *single* C calling convention but several common, incompatible ones and it isn't difficult to produce code with different compilers that cannot be linked. But despite any reasons you may come up with as to why an implementation might not want to perform bounds checking by carrying extra type information around, the fact is that the standard does allow it.
Robert Gamble
@Robert: I wonder where you did read that ? You seems to be saying that standard states that cast operator does not work any-more. If you define a 2d array `int a[5][5]` then cast it like `(int *)a` I understand the standard as allowing you to access the full 5x5 cells blocks, guaranteed contiguous block of 25 int, etc. Boundary checks if any should be performed outside the aggregate. Are you implying that C99 changed the semantic of casting ? As far as I can read `int (*)[4][4]` can be cast to `int (*)[16]` or to `int*` for that matter, it is allowed with precise semantic.
kriss
@kriss: You can't just cast anything you want and get a defined result, that do you think would happen if you tried to cast a `float` to `int *`? The standard defines which conversions (a cast is an explicit conversion) are valid (have defined behavior) in section 6.3 (6.3.2.3 for pointers). An implementation may support additional conversions but it doesn't have to. The conversions you specified are invalid and if you try it out with a decent compiler it should tell you as much (gcc gives *assignment from incompatible pointer type* for example).
Robert Gamble
@Robert: rules for casting pointers are clear in standard, the rules are about pointer *alignement*, and gcc gives the above warning only if I use implicit cast. With explicit cast to `int*` you won't get warning (and this is exactly why explicit cast is for). On this point I totally agree with @Christoph, rules of pointer aithmetic, array contiguity, explicit casting, etc. implies specific behavior. For instance array contiguity is ensured by 6.5.3.4 : you can compute array length using `sizeof array / sizeof array[0]`. There is just no room for additional runtime information.
kriss
@kriss: There is no such thing as an implicit cast or an explicit cast. All casts are explicit conversions. You can cast whatever you want but that doesn't mean that you are going to get a useful or defined result when you try to access the the data through the pointer. I have provided plenty of references to the standard, committee responses on the topic, and (in my answer) links to the comp.lang.c FAQ that addresses the same topic. You seem to have made up your mind despite the facts so I don't think anything I say is going to convince you otherwise.
Robert Gamble
@Robert: did you ever bothered to *read* the material you provided link to. I suggest rereading the following in your link. `Q16: The standard refers to a pointed-to object. There does not appear to be any concept of a slice of an array being an independent object.` Here, you are just using `authority argument` and hide behind standard to state things that just aren't there (or avoid seeing things that are). But indeed this is not the place to speak of that, I shall open a full question on it.
kriss
@Robert: correct me if I'm wrong, but 6.3.2.3 §7 seems to imply that as long as you don't violate alignment requirements, any object pointer conversion is a priori valid as long as no other constraints are violated (eg the aliasing rules provided by 6.5 §7); this means converting `int (*)[4][4]` to `int (*)[16]` via explicit cast is allowed; as 6.3.2.3 §7 says nothing about indirection, you'll have to check 6.5.3.2 for what happens during subscription of the latter: according to §4, indirection only yields undefined behaviour 'If an invalid value has been assigned to the pointer'...
Christoph
...which is not the case here, making `int a[4][4] = {{0}}; (*(int (*)[16])` legal
Christoph
on further thought, `*(int (*)[16]) apart from that, all behaviour is defined; you can work around this via type punning with a `union`
Christoph
@Robert: see last edit for a version which - as far as I can tell - is legal C
Christoph
A: 

You can use int** instead. Its much more flexible:

#include <stdio.h>
#include <stdlib.h>
void print(int **a, int numRows, int numCols )
{
  int row, col ;
  for( int row = 0; row < numRows; row++ )
  {
    for( int col = 0; col < numCols ; col++ )
    {
      printf("%5d, ", a[row][col]);
    }
    puts("");
  }
}

int main()
{
  int numRows = 16 ;
  int numCols = 5 ;
  int **a ;

  // a will be a 2d array with numRows rows and numCols cols

  // allocate an "array of arrays" of int
  a = (int**)malloc( numRows* sizeof(int*) ) ;

  // each entry in the array of arrays of int
  // isn't allocated yet, so allocate it
  for( int row = 0 ; row < numRows ; row++ )
  {
    // Allocate an array of int's, at each
    // entry in the "array of arrays"
    a[row] = (int*)malloc( numCols*sizeof(int) ) ;
  }

  int count = 1 ;
  for( int row = 0 ; row < numRows ; row++ )
  {
    for( int col = 0 ; col < numCols ; col++ )
    {
      a[row][col] = count++ ;
    }
  }

  print( a, numRows, numCols );
}

Another thing which you may be interested in is a structure like D3DMATRIX:

typedef struct _D3DMATRIX {
    union {
        struct {
            float        _11, _12, _13, _14;
            float        _21, _22, _23, _24;
            float        _31, _32, _33, _34;
            float        _41, _42, _43, _44;

        };
        float m[4][4];
    };
} D3DMATRIX;

D3DMATRIX myMatrix ;

The sweet thing about this little tidbit is you can use both myMatrix.m[0][0] (to access the first element), or you can use myMatrix._11 to access that same element as well. The union is the secret.

bobobobo
+2  A: 

The problem is, that

int a[4][4];

will actually be stored in a physically continuos memory. So, to access an arbitrary part of your 4x4 array, the function "print" needs to know the dimensions of the array. For example the following little piece of code, will access the same part of the memory in two different ways.

#include <iostream>

void print(int a[][4]){
    for (int i = 0; i <4; i++){
        for (int j = 0; j < 4; j++){
            //accessing as 4x4 array
            std::cout << a[i][j] <<std::endl;        

            //accessing corresponding to the physical layout in memory
            std::cout <<  *(*(a)+ i*4 + j) << std::endl;  

        }
    }
}

int main(){
    int a[4][4];

    //populating the array with the corresponding indices from 0 to 15
    int m = 0;
    for (int i = 0; i<4; i++){
        for (int j= 0; j < 4; j++){
            a[i][j] =  m;
            m++;
        }
    }
    print(a);
}

So the memory layout doesn't change but the way of accessing does. It can be visualized like a checkerboard.

   0  1  2  3
  ----------
0| 1  2  3  4
1| 5  6  7  8
2| 9 10 11 12
3|13 14 15 16

But the real physical memory looks like this.

0*4+0 0*4+1 0*4+2 0*4+3 1*4+0 1*4+1 1*4+2 1*4+3 2*4+1   etc.
-----------------------------------------------------
1      2       3    4     5     6      7     8     9    etc.

In c++ the data of an array is stored row-by-row and the length of a row (in this case 4) is always necessary to get to the proper memory offset for the next row. The first subscript therefore only indicates the amount of storage that is needed when the array is declared, but is no longer necessary to calculate the offset afterwards.

Lucas
@Lucas: Nice explanation, but in your example code, you should define the function passing in the limit value for i, or it is dangerous to loop until 4 is reached (If I call with an a[1][4] array I will probably get a segfault, but will be ok for compiler).
kriss
More precisely, it needs to know the number of columns, not necessarily the number of rows.
JohnMcG
A: 

First thing to do is get the types right. If C++'s rules are the same as C's with respect to array types (I'm pretty sure they are), then given the declaration

int a[4][4];

the expression a has type int [4][4], which is implicitly converted ("decays") to a pointer type of int (*)[4] (pointer to 4-element array of int) when passed to print, so you need to change print to

void print(int (*arr)[4], int s1, int s2)
{
  int i, j;        
  for(i = 0; i<s1; i++)        
    for(j = 0; j<s2; j++)        
      printf("%d, ", arr[i][j]);        
}        

The expression arr[i] implicitly dereferences arr, so you don't need to mess with an explicit dereference.

The drawback is that print can only handle Nx4 arrays of int; if you want to handle other array sizes, you'll need to take a different approach.

One thing you can do is instead of passing the array, pass the address of the first element, and have print manually compute the offsets, as so:

int main() {                    
  int a[4][4] = {{0}};                    
  print(&a[0][0],4,4);  // note how a is being passed                  
}  

void print(int *arr, int s1, int s2)  // note that arr is a simple int *
{
  int i, j;
  for (i = 0; i < s1; i++)
    for (j = 0; j < s2; j++)
      printf("%d, ", arr[i * s2 + j]);
}
John Bode
A: 

Aside from using variable-length arrays in C99, you can't really portably write a function to accept a multidimensional array if the sizes of the arrays are not known at compile-time, See Question 6.19 of the C-FAQ. The best way to handle this is to simulate multidimensional arrays using dynamically allocated memory. Question 6.16 does a very good job of explaining the details of doing this.

Robert Gamble