0

The code

#include<stdio.h>

// 2D array of 5column && 5rows
int** fivearray(){
    static int arr[5][5];
    for(short i = 0; i<25; ++i){
        **(arr+i) = i;
        printf("%d\t",**(arr+i));
    }

    return arr;
}



int main()
{
    int **array = fivearray();
    
    return 0;
}

Does this way of setting an array and returning it acceptable? what am I missing?

ERROR message

tester.c:12:12: warning: return from incompatible pointer type
[-Wincompatible-pointer-types] return arr;

BUT... now that I think

If I change the code to (*fivearray())[5], it does work, but why is that so? Is an 2D array an array of 5 pointer, pointing to arrays of 5 integers?

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • You also need to change the array variable in `main` to `int (*array)[5]` to match the updated function return type. The return type `int (*)[5]` is "pointer to array of 5 `int`". – Ian Abbott Sep 03 '21 at 10:40
  • Question: Is an 2D array an array of 5 pointer, pointing to arrays of 5 integers? Answer: No, it is an array of 5 arrays of 5 integers. – Ian Abbott Sep 03 '21 at 10:42
  • 1
    In memory, `int array[4][4]` is the same as `int array[16]`. You access it differently but in memory it is the same. – 12431234123412341234123 Sep 03 '21 at 11:04

5 Answers5

4

Is an 2D array an array of 5 pointer, pointing to arrays of 5 integers?

No.

A 2D int array is an array of array of int. There are no pointers involved.

But.. In most cases where you use the name (aka identifier) of an array, it's converted to a pointer to the first element of the array.

As a 2D array is an array of array, the first element is actually an array.

So the name of a 2D array will (in most cases) be converted to a pointer to an array. That's exactly what happens when you do: return arr;

In other words - in your case it's not 5 pointer but just a single pointer to an array of 5 int.

Therefore

int (*fivearray())[5] {...}

is the correct signature for your function and you can use it like:

int (*array )[5]  = fivearray();
array[3][2] = 42;

Then you have this code:

for(short i = 0; i<25; ++i){
    **(arr+i) = i;
    printf("%d\t",**(arr+i));
}

where **(arr+i) is all wrong. You probably want:

for(short i = 0; i<25; ++i){
    arr[i/5][i%5] = i;
    printf("%d\t", arr[i/5][i%5]);
}

but it would IMO be more clear as:

for(int i = 0; i<5; ++i){
   for(int j = 0; j<5; ++j){
        arr[i][j] = i*5 + j;
        printf("%d\t", arr[i][j]);
    }
}
Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
2

I find making typedefs helps when dealing with more complex return types.

Here, this might help:

typedef int fivebyfivearray_t[5][5];

It could then be used like this:

#include <stdio.h>

typedef int fivebyfivearray_t[5][5];

fivebyfivearray_t* fivearray(){
    static fivebyfivearray_t arr;
    for(short i = 0; i<25; ++i) {
        arr[i/5][i%5] = i;   // to avoid undefined behavior
    }
    return &arr;
}

int main() {
    fivebyfivearray_t* array = fivearray();

    for(short i = 0; i<25; ++i) {
        printf("%d\n", (*array)[i/5][i%5]);
    }
}

A user of the fivearray() function is still free to call it without using the fivebyfivearray_t typedef:

int(*array)[5][5] = fivearray();

But, since the function is made to return the address of the 2D array (instead of letting the pointer decay into int(*)[5]), a user would not be allowed to do this mistake:

int(*array)[4][5] = fivearray(); // compilation error

The only downside with this is that you need to dereference the returned value: (*array)[y][x].


A clearer alternative would probably be to package the complex data in a struct and return a pointer to that:

#include <stdio.h>

typedef struct {
    int data[5][5];
} fivebyfivearray_t;

fivebyfivearray_t *fivearray(){
    static fivebyfivearray_t arr;
    for(short i = 0; i<25; ++i) {
        arr.data[i/5][i%5] = i;   // to avoid undefined behavior
    }
    return &arr;
}

int main() {
    fivebyfivearray_t* array = fivearray();

    for(short i = 0; i<25; ++i) {
        printf("%d\n", array->data[i/5][i%5]);
    }
}
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • 1
    No, please don't hide arrays behind typedef, that just makes the code harder to read. If you need to wrap an array in something then use a struct. Or alternatively pass the array as a parameter to be modified. – Lundin Sep 03 '21 at 11:45
  • @Lundin I actually had a struct in my answer for a few minutes but was worried that it'd be received badly. :-) Oh, well - I added that as an option. – Ted Lyngmo Sep 03 '21 at 11:49
  • 1
    It's a bit subjective perhaps. In real programs you rarely ever need to pass around multi-dimensional arrays that aren't part of some more high level data structure though. – Lundin Sep 03 '21 at 11:50
  • @Lundin True! While I've got your attention. I was looking for something I _think_ you (or Peter Cordes) wrote about why accessing sub arrays out-of-bounds is UB (other than "just because the standard says so"). I wanted to add a link to what (I think) you wrote to [an answer](https://stackoverflow.com/a/68909068/7582247) - but I couldn't find it. Do you remember having written anything detailed on this topic? – Ted Lyngmo Sep 03 '21 at 12:04
  • I have some vague memory about that, but I can't find the post. – Lundin Sep 03 '21 at 14:23
  • @Lundin :-) Darn. I remember thinking I should have bookmarked it to use as a reference whenever that discussion pops up but then it was too late. – Ted Lyngmo Sep 03 '21 at 14:26
2

Let's see what the C Standard says about using array designators in expressions (6.3.2.1 Lvalues, arrays, and function designators)

3 Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.

You declared a two-dimensional array with static storage duration within the function fivearray

static int arr[5][5];

and are using it as an expression in the return statement

return arr;

So the array arr is implicitly converted in this return statement to pointer to the array element type.

What is the element type of the array? It is the type int[5]. So the pointer to an object of this type will have the type int ( * )[5].

But you specified the return type as int **.

int** fivearray(){

However there is no implicit conversion between the pointer types int ( * )[5] and int **.

So the return type of the function is incorrect. You have to write

int ( *fivearray( void ) )[5]{
    static int arr[5][5];
    // ...
    return arr;
}

Now let's consider the for loop within the function

for(short i = 0; i<25; ++i){
    **(arr+i) = i;

What is interesting in this loop for us is the expression **( arr + i ).

As it was already noted the array used in this expression is converted to pointer of the type int ( * )[5].

Dereferencing the pointer expression *( arr + i ) you will get i-th "row" of the array that is its i-th element of the type int[5]. So you may rewrite the expression like

arr[i]

It is a one-dimensional array. Applying the second dereferencing operator to this expression

*( arr[i] )

that is the same as **( arr + i ) you will get the first element of the array because again the array arr[i] is implicitly converted to pointer to its first element.

Thus this statement

**(arr+i) = i;

sets the first element of each "row" of the two-dimensional array to the value i.

But the array has only 5 rows. On the other hand, the variable i is being changed from 0 to 25. So this statement

    **(arr+i) = i;

tries to access memory beyond the array when the variable i is greater than 4.

As a result the loop invokes undefined behavior.

To make it visually clear consider the following demonstrative program. I changed the first dimension of the array to 25 to guarantee that there will not be an access beyond the array.

#include <stdio.h>

int main(void) 
{
    int arr[35][5] = { 0 };
    
    for ( size_t i = 0; i < 25; i++ )
    {
        **( arr + i ) = i;
    }
    
    for ( size_t i = 0; i < 25; i++ )
    {
        for ( size_t j = 0; j < 5; j++ )
        {
            printf( "%d\t", arr[i][j] );
        }
        
        putchar( '\n' );
    }
    
    return 0;
}

The program output is

0   0   0   0   0   
1   0   0   0   0   
2   0   0   0   0   
3   0   0   0   0   
4   0   0   0   0   
5   0   0   0   0   
6   0   0   0   0   
7   0   0   0   0   
8   0   0   0   0   
9   0   0   0   0   
10  0   0   0   0   
11  0   0   0   0   
12  0   0   0   0   
13  0   0   0   0   
14  0   0   0   0   
15  0   0   0   0   
16  0   0   0   0   
17  0   0   0   0   
18  0   0   0   0   
19  0   0   0   0   
20  0   0   0   0   
21  0   0   0   0   
22  0   0   0   0   
23  0   0   0   0   
24  0   0   0   0   

As you can see this expression **( arr + i ) = i; changed only the first element of each "row" of the array.

What you need is to use two loops like

for(short i = 0; i < 5; ++i ){
    for ( short j = 0 j < 5; j++ ){
        arr[i][j] = 5 * i + j;
        printf( "%d\t", arr[i][j] ); 
    }
}

Or if to use only one for loop then you need to write

for(short i = 0; i < 25; ++i){
    arr[i / 5][i % 5] = i;
    printf( "%d\t", arr[i / 5][i % 5] );
}

Thus the function will look like

int ( *fivearray( void ) )[5] {
    static int arr[5][5];

    for( short i = 0; i < 25; ++i){
        arr[i / 5][i % 5] = i;
        printf( "%d\t", arr[i / 5][i % 5]);
    }

    return arr;
}

At last in main you should write

int ( *array )[5] = fivearray();

Pay attention to that it is not a good idea to use magic number like 5 throughout the program.

You could introduce a named constant for the magic number 5 like for example

enum { N = 5 };

int ( *fivearray( void ) )[N] {
    static int arr[N][N];

    for( short i = 0; i < N * N; ++i){
        arr[i / N][i % N] = i;
        printf( "%d\t", arr[i / N][i % N]);
    }

    return arr;
}

int main( void )
{
    int ( *array )[N] = fivearray();
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
0

I think int** is not the one notation suitable for your case, but for more information refer to this other question: Difference between the int * i and int** i.

0

When you have an array of a fixed size for the function, then I think it is best to pass it as a pointer to the function. This way you do not have to deal with (de)allocation and neither with static memory from the function. Because for the static solution, if you call this function again you will get the same address as before.

#include<stdio.h>

// 2D array of 5column && 5rows
int fivearray(int arr[5][5]){
    if(!arr) return -1; //NULL pointer check
    for(int i = 0; i<5; ++i){
        for(int j = 0; j<5; ++j){
            arr[i][j] = i*5 + j;
        }
    }
    return 0;
}

int main()
{
    int array[5][5] = {0};
    int retVal = fivearray(array);
    printf("%d\n",retVal);
    for(int i = 0; i<5; ++i){
        for(int j = 0; j<5; ++j){
            printf("%d\t", array[i][j]);
        }
    }

    return 0;
}

The declaration in of arr[5][5] in the function, is just a hint and the compiler will not check if the size is matching as it is still just a pointer that is passed. But it documents what should be passed.

Kami Kaze
  • 2,069
  • 15
  • 27