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;
}