2

I want to use another function to print the contents of an array. When I run the code I get "IntelliSense: argument of type "int (*)[3][3]" is incompatible with parameter of type "int *(*)[3]"

This is my function:

void display(int *p[N_ROWS][N_COLS]) {
    int i, j;
    for (i = 0; i < N_ROWS; i++) {
        for (j = 0; j <N _COLS; j++) {
            printf("%i", p[i][j]);
        }
    }
}

I defined N_ROWS and N_COLS and in my main function I declare my array and then call the function

{
    int Array[N_ROWS][N_COLS] = { {1,2,3},{4,5,6},{7,8,9} };
    display(&Array);
}

Aren't both my parameters type int(*)[3][3] or am I missing something?

chqrlie
  • 131,814
  • 10
  • 121
  • 189
j.yang29
  • 65
  • 9
  • You probably meant the function parameter to be `int (*p)[N_ROWS][N_COLS]` – M.M Mar 23 '16 at 00:14
  • 1
    Your function is declared to take an array of pointers, and you're passing it a pointer to an array. – Lee Daniel Crocker Mar 23 '16 at 00:18
  • @M.M: I suspect OP just should remove the `*` from `p`. – too honest for this site Mar 23 '16 at 00:22
  • Look at the declaration of `Array` and the function argument `p`. Then think what you pass to `p`. (But be aware that the **semantics** differ). An array is not a pointer (and vice versa). – too honest for this site Mar 23 '16 at 00:24
  • Remove the `*` from the function, and remove the `&` from the call. – user3386109 Mar 23 '16 at 00:30
  • @Olaf In that case the `&` would also be removed from `&Array`, and also it would be possible to call the function with an array with a different number of rows, silently causing undefined behaviour if not enough rows are given. OP may want the compiler to diagnose attempt to pass an array that is not the exact size of both row and column. – M.M Mar 23 '16 at 00:31
  • So I removed the * and & and that error went away, but now it's saying that expression must have a pointer to object type (in my printf statement) as well as function returning array is not allowed? (No idea what this means) @Olaf Array is declared as a 3x3 array, if I changed the code to void display(int p)[N_ROWS][N_COLS] wouldn't I be only referencing for an integer and not an array? – j.yang29 Mar 23 '16 at 00:37
  • @vvid solely removing `*` and `&` from the code you posted would not cause that error message. You must have also made some other change. Perhaps you messed with `p[i][j]`. – M.M Mar 23 '16 at 00:49
  • Yeah, I also had to change the () so the prototype read void display(int p[N_ROWS][N_COLS]) I did not have to change with the p[i][j] – j.yang29 Mar 23 '16 at 00:53
  • What did you mean by "I removed the `*`" then? The only `*` in your code was the one in that prototype. – M.M Mar 23 '16 at 01:04
  • You pre-last comment is not clear. Something like `void display(int p)[N_ROWS][N_COLS]` is invalid syntax. C follows a strict syntax&grammar. Please be exact what you mean - beginner or not. – too honest for this site Mar 23 '16 at 08:59

2 Answers2

4

Your prototype for display is incorrect, as well as your call syntax: you define display to take a 2D array of pointers to int, whereas you just want a 2D array if int, as you pass a pointer to the array in main where you just want to pass the array, decaying as a pointer to its first element.

Here is a simpler version:

void display(int p[N_ROWS][N_COLS]) {
    int i, j;
    for (i = 0; i < N_ROWS; i++) {
        for (j = 0; j < N_COLS; j++) {
            printf("%i", p[i][j]);
        }
    }
}

Note however that p above can have any number of rows, N_ROWS is ignored in the prototype, equivalent to void display(int (*p)[N_COLS]).

Note also that your printf will output the matrix values without any separation. This might not be your intent.

And from main:

{
    int Array[N_ROWS][N_COLS] = { {1,2,3},{4,5,6},{7,8,9} };
    display(Array);
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • Is there a specific reason as to why N_ROWS is ignored in the prototype? That's the only thing I find confusing. – j.yang29 Mar 23 '16 at 00:51
  • 2
    @vvid: the size information for an array specified as a function parameter is not used by the compiler at all, neither for type checking, nor for `sizeof()`, because the array is passed as a pointer to its first element and the size is not deemed to be part of the type. It is accepted for historical reasons: the C inventors did not think of how this would become misleading and error prone. They instead must have thought it was a good way for the programmer to tell the next reader of the code what size is *expected* for the array, which itself is passed as a pointer to its first element. – chqrlie Mar 23 '16 at 01:01
  • 1
    @vvid to explain why `N_ROWS` is ignored, [see here](http://stackoverflow.com/questions/22677415/why-do-c-and-c-compilers-allow-array-lengths-in-function-signatures-when-they/22677793#22677793). Note that the alternative solution suggested by jxh means that `N_ROWS` would *not* be ignored any more, so if you want it to be significant you could consider that approach. – M.M Mar 23 '16 at 01:06
  • If you pass an array to `sizeof()`, the size information is not ignored. But, C defines array semantics to mostly allow them to be treated "by reference" so that passing an array to a function doesn't cause tons of data to be pushed onto the stack. Their approach was to define array semantics so that it decays to a pointer type, and this decayed type is what the function uses. So the function parameter looks like an array but is actually a pointer. – jxh Mar 23 '16 at 01:07
  • @jxr: indeed *the argument looks like an array but is actually a pointer*, it is very misleading, especially for `sizeof()` and any similar macros to compute the number of elements of this false array. – chqrlie Mar 23 '16 at 01:13
  • @chqrlie how are you making those grey boxes? I try Ctrl+K and 5 spaces but it doesn't turn gray – j.yang29 Mar 23 '16 at 01:25
  • @vvid: use the backquotes glued to the text: \`code\` will appear as `code` – chqrlie Mar 23 '16 at 01:29
3

Much of this has already been explained in comments to your question.

Your definition of display:

void display(int*p[N_ROWS][N_COLS])
{

This says p will be an array N_ROWS of array N_COLS of pointers to int.

When you call display:

int Array [N_ROWS][N_COLS] = { {1,2,3},{4,5,6},{7,8,9} };
display(&Array);

You are passing in the address of Array, thus it is a pointer to an array N_ROWS of array N_COLS of int.

To make the definition of display match the way you are calling it:

void display(int (*p)[N_ROWS][N_COLS])
{

The parentheses make the * associate with p first. Since p is a pointer to an array of array of int, getting to the int requires an extra dereference:

printf("%i\n", (*p)[i][j]);

Defining display to take a pointer to an array means that the size of the array is bound to the type parameter, and thus display knows exactly the dimensions of the array.

An alternative means would be to define display to take the dimension of the array as a second parameter.

void display(int p[][N_COLS], int n_rows)
{

In this case the p parameter is a pointer to an array N_COLS of int. An array of T when used in most expressions will decay to a value of type pointer to T equal to the address of its first element. Thus, you could call this second version of display like this:

int Array [N_ROWS][N_COLS] = { {1,2,3},{4,5,6},{7,8,9} };
display(Array, N_ROWS);

The advantage of this second approach is that display can work with arrays that have fewer or more than N_ROWS. The disadvantage is that it is up to the caller to pass in the right number of rows.

You might think that the following declaration would give you complete type safety:

void display(int p[N_ROWS][N_COLS])
{

But, the array syntax in C for function parameters cause the size information for p to be ignored, and becomes equivalent to int p[][N_COLS], which in turn is treated as int (*p)[N_COLS].

jxh
  • 69,070
  • 8
  • 110
  • 193
  • Your points are correct, but the OP is confused on how to pass a simple 2D array, your explanation just adds to this confusion. – chqrlie Mar 23 '16 at 00:32
  • 2
    @user3386109: That would defeat the type checking and allow the calling code to pass, say, an `int [1][3]` array as an argument. If the array size is always the same, i.e. fixed at compile time, it is a much better idea to enforce the sizing by doing the above, i.e. pass a pointer to the entire array. – AnT stands with Russia Mar 23 '16 at 00:32
  • 1
    @user3386109: C99-specific `static` keyword in array size does not enforce anything in this case. It works as a hint to the compiler, but it does not trigger compile-time size checking. If you consider this syntax "complicated", you can always greatly simplify it by using an intermediate `typedef` for array type. Just do `typedef int A[N_ROWS][N_COLS]` and then declare `A *` parameter. – AnT stands with Russia Mar 23 '16 at 00:35
  • 1
    @jxr: as noted by AnT, your approach does improve type checking, but you must also correct the OP's code and use `printf("%i", (*p)[i][j]);` – chqrlie Mar 23 '16 at 00:42
  • @jxr: for completeness, you might also add the generalized C99 syntax `void display(int n_rows, int n_cols, int p[nrows][n_cols])` – chqrlie Mar 23 '16 at 00:44
  • @chqrlie: C.2011 makes VLA an optional feature. I think that means the array syntax support you are advising is also optional. – jxh Mar 23 '16 at 00:45
  • 1
    @user3386109 using the `static` keyword does not enforce the constraint. It helps the compiler optimize , and the compiler *could* emit a warning; but the standard requires that the compiler still compile the code in either case , and in practice no compilers that I'm aware of actually do emit a warning there. – M.M Mar 23 '16 at 00:47
  • 1
    @chqrlie: A good compiler would warn on the `printf` usage error. – jxh Mar 23 '16 at 00:47
  • I understood up to the part about "An alternative means" but the first part was very helpful and agree with the user, it's easier to just remove the * and & – j.yang29 Mar 23 '16 at 00:49
  • @jxr: what a bummer! Complete VLA support as specified in C99 was indeed a headache, but making the whole thing optional is killing the feature. – chqrlie Mar 23 '16 at 00:52
  • @user3386109 what compiler? gcc provides no warning. See http goo.gl/XheF1S . Also it is hard to see why you would advocate a solution where the compiler may optionally warn; vs. one where the compiler must give an error – M.M Mar 23 '16 at 00:54
  • 1
    @user3386109: Firstly, there's no requirement to generate a diagnostic message in such cases and many compilers don't. I don't know what specific compilers you are talking about. Secondly, and more importantly, a parameter of type, say, `int [static 5]` is supposed to accept argument of type `int *`, i.e. something that is declared as a pointer (not as an array), in which case no compile-time size checking is possible at all. – AnT stands with Russia Mar 23 '16 at 00:55
  • @jxr: *A good compiler would warn on the printf usage error.* is a bit of a lame excuse. – chqrlie Mar 23 '16 at 00:55
  • @chqrlie: It's not an excuse, it's the truth. The question was more or less about array function parameters. The `printf` was already broken in the original code. – jxh Mar 23 '16 at 00:58
  • @user3386109: You claim I am advocating a particular approach, when that is inaccurate. I am explaining what the asker passed to `display`, and how to make `display` accept that type. Specifically, the last sentence is the actual question: *Aren't both my parameters type `int(*)[3][3]` or am I missing something?*. I explain what is missed. – jxh Mar 23 '16 at 01:29
  • That error can be detected trivially within the function by anyone looking at the code, whereas the bounds check error cannot be (and in fact it may not even be known at compile-time what size array is passed in). A ridiculous comparison by you, really – M.M Mar 23 '16 at 01:30