2

I want to make a 2 dimensional array in C.

I know 1 way to make it like this.

#include <stdlib.h>

void    my_func(int **arr)
{
        printf("test2: %d\n", arr[0][1]);
}

int     main(void)
{
        const int row = 3;
        const int col = 4;

        int **arr = (int **)malloc(sizeof(int *) * 3);
        arr[0] = (int *)malloc(sizeof(int) * 4);
        arr[1] = (int *)malloc(sizeof(int) * 4);
        arr[2] = (int *)malloc(sizeof(int) * 4);

        arr[0][0] = 1;
        arr[0][1] = 2;
        arr[0][2] = 3;
        arr[0][3] = 4;
        arr[1][0] = 3;
        arr[1][1] = 4;
        arr[1][2] = 5;
        arr[1][3] = 6;
        arr[2][0] = 5;
        arr[2][1] = 6;
        arr[2][2] = 7;
        arr[2][3] = 8;

        printf("test1: %d\n", arr[0][1]);

        my_func(arr);

}

In this case, the array can be passed to the function well as an argument. But it's not that pretty. If the array has lots of values (e.g 20*20), I need to type every single value line by line.

So I searched it and found out a way to make an array like this.

#include <stdio.h>

void    my_func(int **arr)
{
        printf("test2: %d", arr[0][1]);
}

int     main(void)
{
        const int row = 3;
        const int col = 4;

        int arr[row][col] = {
                {1,2,3,4},
                {3,4,5,6},
                {5,6,7,8}
        };
        printf("test1: %d", arr[0][1]);

        my_func(arr);
}

It's concise and don't make me exhausted. But something is wrong when array is passed to a function. And when compiling, there is a warning as below

test_2D_array.c:20:11: warning: incompatible pointer types passing 'int [3][4]' to
      parameter of type 'int **' [-Wincompatible-pointer-types]
                my_func(arr);
                        ^~~
test_2D_array.c:3:20: note: passing argument to parameter 'arr' here
void    my_func(int **arr)
                      ^
1 warning generated.

and Even the function can't access the array argument. There is a segmentation fault.

So I want to know the best way to make array which can be passed toany function as an argument and less exhausting than my first code.

Thank you for reading.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
Terry
  • 23
  • 4
  • 1
    In your case: `arr` is not an `int **`; it is actually an `int [][4]` or `int *[4]`. – Fiddling Bits Apr 27 '20 at 17:26
  • Your first example is _not_ a 2D array in the sense that C does it [your second example _is_ a 2D array]. Your first is an array of _pointers_ to [separate] 1D arrays. This is what languages that _don't_ have 2D arrays do (e.g. `perl` and `python`). Your second: Change function to: `void my_func(int row,int col,int arr[row][col]) { ... }` and call [from `main`] with: `my_func(row,col,arr);` – Craig Estey Apr 27 '20 at 17:29
  • Are you sure that `int arr[row][col] = { {1,2,3,4},...};` is working? I'm getting `error: variable-sized object may not be initialized` – abelenky Apr 27 '20 at 17:34
  • *something is wrong when array is passed to a function* That's because `int **arr = (int **)malloc(sizeof(int *) * 3);` and then `arr[0] = (int *)malloc(sizeof(int) * 4); ...` **DOES NOT CREATE A 2-DIMENSIONAL ARRAY**. Sorry for shouting, but the "multiple `malloc()` 2-d array" is really a one-dimensional array of pointers to multiple and completely separate one-dimensional arrays. It's not actually an array. See [**Correctly allocating multi-dimensional arrays**](https://stackoverflow.com/questions/42094465/correctly-allocating-multi-dimensional-arrays) – Andrew Henle Apr 27 '20 at 17:43
  • @AndrewHenle The problem with your shouting is that the array can be accessed using the `arr[y][x]` syntax. So it behaves "as if" it *is* a 2-dimensional array. In other words, it does create a 2-dimensional array, just not your narrow definition of a 2-dimensional array. – user3386109 Apr 27 '20 at 18:10
  • @user3386109 Loose, imprecise definitions like that lead to buggy, unreliable code. Just like this very question. My standards are higher than that. Note that just because the syntax *appears* similar, that does not make the underlying data structure identical. For a true 2-dimensional array, the `arr[y]` portion of `arr[y][x]` refers to an actual one-dimensional array that the `[x]` then dereferences to an actual element. In the faux "array", the `arr[y]` part refers to a **pointer**. They are **not** the same. Deliberately conflating the two is worse than confusing. – Andrew Henle Apr 27 '20 at 18:16
  • *"Loose, imprecise definitions like that lead to buggy, unreliable code."* That is a blatant non-sequitur. Furthermore, I did not say that `arr[y]` was the same in both cases. I said that `arr[y][x]` will access a single element of the array in both cases. Therefore, it is illogical not to call the array a 2 dimensional array in both cases, and it is a lack of logic leads to buggy, unreliable code. – user3386109 Apr 27 '20 at 19:02

5 Answers5

4

This

int **arr = (int **)malloc(sizeof(int *) * 3);

is not a declaration or allocation of a two-dimensional array

Here a one-dimensional array with the element type int * is created. And then each element of the one-dimensional array in turn points to an allocated one dimensional array with the element type int.

This declaration of a two-dimensional array

    const int row = 3;
    const int col = 4;

    int arr[row][col] = {
            {1,2,3,4},
            {3,4,5,6},
            {5,6,7,8}
    };

is incorrect. Variable length arrays (and you declared a variable length array) may not be initialized in declaration.

You could write instead

    enum { row = 3, col = 4 };

    int arr[row][col] = {
            {1,2,3,4},
            {3,4,5,6},
            {5,6,7,8}
    };

When such an array is passed to a function it is implicitly converted to pointer to its first element of the type int ( * )[col].

You could pass it to a function that has a parameter of the type of a variable length array the following way

void    my_func( size_t row, size_t col, int arr[row][col] )
{
        printf("test2: %d", arr[0][1]);
}

Or if to place the definition of the enumeration before the function declaration

    enum { row = 3, col = 4 };

then the function could be also declared like

void    my_func( int arr[][col], size_t row )
{
        printf("test2: %d", arr[0][1]);
}

Here is a demonstrative program that shows three different approaches. The first one when an array is defined with compile-time constants for array sizes. The second one when a variable length array is created. And the third one when a one-dimensional array of pointer to one-dimensional arrays are allocated dynamically.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

enum { row = 3, col = 4 };

void output1( int a[][col], size_t row )
{
    for ( size_t i = 0; i < row; i++ )
    {
        for ( size_t j = 0; j < col; j++ )
        {
            printf( "%d ", a[i][j] );
        }
        putchar( '\n' );
    }
}

void output2( size_t row, size_t col, int a[row][col] )
{
    for ( size_t i = 0; i < row; i++ )
    {
        for ( size_t j = 0; j < col; j++ )
        {
            printf( "%d ", a[i][j] );
        }
        putchar( '\n' );
    }
}

void output3( int **a, size_t row, size_t col )
{
    for ( size_t i = 0; i < row; i++ )
    {
        for ( size_t j = 0; j < col; j++ )
        {
            printf( "%d ", a[i][j] );
        }
        putchar( '\n' );
    }
}


int     main(void)
{
        int arr1[row][col] = 
        {
                {1,2,3,4},
                {3,4,5,6},
                {5,6,7,8}
        };

        output1( arr1, row );
        putchar( '\n' );

        const size_t row = 3, col = 4;

        int arr2[row][col];

        memcpy( arr2, arr1, row * col * sizeof( int ) );

        output2( row, col, arr2 );
        putchar( '\n' );

        int **arr3 = malloc( row * sizeof( int * ) );

        for ( size_t i = 0; i < row; i++ )
        {
            arr3[i] = malloc( col * sizeof( int ) );
            memcpy( arr3[i], arr1[i], col * sizeof( int ) );
        }

        output3( arr3, row, col );
        putchar( '\n' );

        for ( size_t i = 0; i < row; i++ )
        {
            free( arr3[i] );
        }

        free( arr3 );
} 

The program output is

1 2 3 4 
3 4 5 6 
5 6 7 8 

1 2 3 4 
3 4 5 6 
5 6 7 8 

1 2 3 4 
3 4 5 6 
5 6 7 8 

Pay attention to that the function output2 can be used with the array arr1 the same way as it is used with the array arr2.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
1

The function can be declared as

void my_func(int arr[][4])
{
    printf("test2: %d", arr[0][1]);
}

Note that you don't have to specify the size of the first dimension.

CS Pei
  • 10,869
  • 1
  • 27
  • 46
1

Suppose there is no dynamic allocation.

1   #include <stdio.h>
  1
  2 void func(int *arr, int row, int col) {
  3     int i, j;
  4
  5     for (i = 0; i < row * col; i++) {
  6         if (i && (i % col == 0))
  7             printf("\n");
  8         printf("%d ", arr[i]);
  9     }
 10
 11     printf("\n");
 12 }
 13
 14 int main(int argc, char *argv[]) {
 15     // can be this
 16     int arr1[] = {
 17         1,2,3,  // row 0
 18         4,5,6   // row 1
 19     };
 20
 21     // or this way
 22     int arr2[2][3] = {
 23         {0,1,2},  // row 0
 24         {4,5,6}   // row 1
 25     };
 26
 27     func(arr1, 2, 3);
 28     func((int*)arr2, 2, 3);
 29     return 0;
 30 }
~
S Dao
  • 555
  • 4
  • 7
1

First you cannot initialize a 2D array with variable size, as 'Vlad from Moscow' mentioned in his answer. Instead, you just need to specify the size of your 2nd dimension, leaving the 1st dimension blank. Second your my_func(int **arr) is expecting pointer to pointer to int and you are just passing the address of an array, that's why compiler is throwing an error of incompatibility.

Your fixed code will look like this:

#include <stdio.h>

void    my_func(int **arr)
{
        printf("test2: %d", arr[0][1]);
}

int     main(void)
{
        int arr[][4] = {1,2,3,4,
                        3,4,5,6,
                        5,6,7,8};
        int *p = (int *)arr;
        int **p1 = &p;
        printf("test1: %d", arr[0][1]);

        my_func(p1);
}

Now there's no use of const int row = 3 & const int column = 4 so you can remove them.

Shubham
  • 1,153
  • 8
  • 20
  • Why are you using `#include` in your first code, BTW? – Shubham Apr 28 '20 at 14:47
  • It's for malloc. And I should have written `` as well. But I forgot it when copy and pasting. Thank you for your answer. – Terry Apr 28 '20 at 23:53
  • The expression `pointer to pointer to int` is really helpful for me to understand. Thank you so much. Can I ask a question? How can I read or understand `int [][4]`? – Terry May 01 '20 at 04:26
  • I think, you are asking **How can I read or understand `int arr[ ][4]`?** Declaring a 2D array takes this general form: `type varName[size][size]` When the array is completely initialized with all values, explicitly, we need not to specify the size of the first dimension. In our example, we declared and intialized an array `int arr[ ][4]`, that means we know the size of row and column. So, we can read it as an array of `3x4` elements. A 2D array is just like the matrix in mathematics. – Shubham May 02 '20 at 05:23
  • And if you are curious about why we specified the size of 2nd dimension, its because compiler will seperate 4 elements from all the values and store them in a row. So, it'll first seperate `1,2,3,4` and store them in a row, then next 4 elements and so on. – Shubham May 02 '20 at 05:27
  • If you want to explore more you can refer to this link: [2D Arrays in C](https://www.geeksforgeeks.org/multidimensional-arrays-c-cpp/) – Shubham May 02 '20 at 05:28
-2

It needs a funny looking typecast, but I did it this way:

#include <stdio.h>

void my_func(int **arr, int cols)
{
    int (*matrix)[cols] = arr;
    printf("test2: %d\n", matrix[0][1]);
}

int main(void)
{
        const int row = 3;
        const int col = 4;

        int arr[3][4] = {
                {1,2,3,4},
                {3,4,5,6},
                {5,6,7,8}
        };
        printf("test1: %d\n", arr[0][1]);

        my_func(arr, col);
}

IDEOne Link

abelenky
  • 63,815
  • 23
  • 109
  • 159
  • warning: incompatible pointer types passing `int [3][4]` to parameter of type `int *` You'd better off declaring `int *arr` as `void *arr`. Then you don't even need the cast in `my_func`. – user3386109 Apr 27 '20 at 17:43
  • warning: incompatible pointer types passing `int [3][4]` to parameter of type `int **` and warning: incompatible pointer types initializing `int (*)[cols]` with an expression of type `int **`. The edit made the problem worse. You either need to get the types right, or bypass the type checking with `void*`. – user3386109 Apr 27 '20 at 17:50