Search code examples
cmultidimensional-arraydeclarationdefinitionvariable-length-array

What is the best way to make 2 dimensional array in C


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.


Solution

  • 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.