Why is my function unable to print an element of a multi-dimensional array using pointers?

I have a 3 X 3 multidimensional array and I want to print all the 3 elements of the 0th row using pointers.

Here's the code that i wrote:

#include <iostream>

void print(int *p){
    for(int i = 0; i < 3; i++){
        std::cout << (*p)[i];
    }
};


int main(void){
    int arr[3][3] = {
        {1,2,3} ,
        {4,5,6} ,
        {7,8,9}
    };
   print(arr);
}

This throws up these errors:

Main.cpp:5:26: error: subscripted value is not an array, pointer, or vector

std::cout << (*p)[i];
                 ~~~~^~

Main.cpp:16:4: error: no matching function for call to 'print'

print(arr);
   ^~~~~

Main.cpp:3:6: note: candidate function not viable: no known conversion from 'int [3][3]' to 'int *' for 1st argument

void print(int *p){

From what I understand, arr is a pointer to the first element of the array which is itself an array. So why am i unable to find array[i] of the value returned by the pointer arr ?

3 answers

  • answered 2019-10-22 08:32 Swift - Friday Pie

    first element of the array arr got type int[3]. Because so-called two-dimensional array in terms of language is an array of arrays.

    Thus arr cannot be an equivalent of int*, only of int[][3] or int(*)[3].

    void print(int p[][3]){
        for(int i = 0; i < 3; i++){
            std::cout << p[0][i];
        }
    };
    

    or

    void print(int (*p)[3]){
        for(int i = 0; i < 3; i++){
            std::cout << (*p)[i];
        }
    };
    

    would be correct signatures of your function. Any attempt to represent multidimensional array as a pointer requires explicit casting and laundering it back to the proper type within function, doing otherwise is considered as an undefined behavior, even while most implementations allow this to work.

  • answered 2019-10-22 08:55 Vlad from Moscow

    The parameter of the function print has the type int *.

    print(int *p)
    

    So dereferencing the pointer in the expression (*p)[i] you will get a scalar object of the type int. You may not apply the subscript operator to scalar objects of the type int.

    On the other hand, in this call

    print(arr);
    

    the argument that has the type int[3][3] is converted to pointer to its first element. Elements of the array have the type int[3]. So the type of the expression after the implicit conversion of the array to pointer to its first element is int ( * )[3].

    And the error message points to this problem

    Main.cpp:16:4: error: no matching function for call to 'print'
    
    print(arr);
       ^~~~~
    

    because the compiler is unable to find a function with the name print that accepts an argument of the type int ( * )[3].

    Thus the parameter of the function print should be declared like

    print( int p[][3] )
    

    or

    print( int ( *p )[3] )
    

    And as the array is not changed in the function it should be declared with the qualifier const.

    The function definition in this case will look like (if you want to use pointers)

    void print( const int p[][3] )
    {
        for( const int ( *row )[3] = p; row != p + 3; ++row ) 
        {
            for ( const int *col = *row; col != *row + 3; ++col )
            {
                std::cout << *col << ' ';
            }
            std::cout << '\n';
        }
    }
    

    Here is a demonstrative program.

    #include <iostream>
    
    void print( const int p[][3] )
    {
        for( const int ( *row )[3] = p; row != p + 3; ++row ) 
        {
            for ( const int *col = *row; col != *row + 3; ++col )
            {
                std::cout << *col << ' ';
            }
            std::cout << '\n';
        }
    }
    
    int main() 
    {
        const size_t N = 3;
        int arr[N][N] = 
        {
            { 1, 2, 3 } ,
            { 4, 5, 6 } ,
            { 7, 8, 9 }
        };
    
        print( arr );
    
        return 0;
    }
    

    Its output is

    1 2 3 
    4 5 6 
    7 8 9
    

    However this approach has a serious drawback. The function uses magic number 3.

    It is better to rewrite the function at least like

    #include <iostream>
    
    const size_t N = 3;
    
    void print( const int p[][N], size_t rows )
    {
        for( const int ( *row )[N] = p; row != p + rows; ++row ) 
        {
            for ( const int *col = *row; col != *row + N; ++col )
            {
                std::cout << *col << ' ';
            }
            std::cout << '\n';
        }
    }
    
    int main() 
    {
        int arr[][N] = 
        {
            { 1, 2, 3 } ,
            { 4, 5, 6 } ,
            { 7, 8, 9 }
        };
    
        print( arr, sizeof( arr ) / sizeof( *arr ) );
    
        return 0;
    }
    

    Also you could add one more parameter with a default argument. For example

    std::ostream & print( const int p[][N], size_t rows, std::ostream &os = std::cout )
    {
        for( const int ( *row )[N] = p; row != p + rows; ++row ) 
        {
            for ( const int *col = *row; col != *row + N; ++col )
            {
                os << *col << ' ';
            }
            os << '\n';
        }
    
        return os;
    }
    

    For example

    #include <iostream>
    
    const size_t N = 3;
    
    std::ostream & print( const int p[][N], size_t rows, std::ostream &os = std::cout )
    {
        for( const int ( *row )[N] = p; row != p + rows; ++row ) 
        {
            for ( const int *col = *row; col != *row + N; ++col )
            {
                os << *col << ' ';
            }
            os << '\n';
        }
    
        return os;
    }
    
    int main() 
    {
        int arr[][N] = 
        {
            { 1, 2, 3 } ,
            { 4, 5, 6 } ,
            { 7, 8, 9 }
        };
    
        print( arr, sizeof( arr ) / sizeof( *arr ) ) << '\n';
    
        return 0;
    }
    

    And at last you could write a template function.

    #include <iostream>
    
    template <typename T, size_t N>
    std::ostream & print( const T ( &p )[N][N], std::ostream &os = std::cout )
    {
        for( const int ( *row )[N] = p; row != p + N; ++row ) 
        {
            for ( const int *col = *row; col != *row + N; ++col )
            {
                os << *col << ' ';
            }
            os << '\n';
        }
    
        return os;
    }
    
    int main() 
    {
        const size_t N = 3;
    
        int arr[][N] = 
        {
            { 1, 2, 3 } ,
            { 4, 5, 6 } ,
            { 7, 8, 9 }
        };
    
        print( arr ) << '\n';
    
        return 0;
    }
    

  • answered 2019-10-22 11:55 Armin Montigny

    In my opinion all previous answers do not address the main problem.

    The problem is, how to pass a plain old C-array to a function.

    If you want to pass a plain old C-array to a function, you have 2 possibilities.

    • Pass by reference
    • Pass by pointer

    It seems that you want to pass by reference. But you are using the wrong syntax.

    Please see:

    void function1(int(&m)[3][4])   // For passing array by reference
    {}
    void function2(int(*m)[3][4])   // For passing array by pointer
    {}
    
    int main()
    {
        int matrix[3][4]; // Define 2 dimensional array
    
        function1(matrix);  // Call by reference
        function2(&matrix); // Call via pointer 
        return 0;
    }
    

    What you pass to the function is a decayed pointer to array of int.

    Simply correct the syntax and it will work.

    Additional hint:

    Do not use plain C-style arrays in C++. Never. Please use STL containers.