Почему моя функция не может распечатать элемент многомерного массива с помощью указателей? - PullRequest
3 голосов
/ 22 октября 2019

У меня есть многомерный массив 3 X 3, и я хочу напечатать все 3 элемента 0-й строки с помощью указателей.

Вот код, который я написал:

#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выдает следующие ошибки:

Main.cpp: 5: 26: ошибка: подписанное значение не является массивом, указателем или вектором

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

Main.cpp: 16: 4: ошибка: нет подходящей функции для вызова 'print'

print(arr);
   ^~~~~

Main.cpp: 3: 6: note: функция-кандидат недопустима: нет известного преобразования из 'int [3] [3]' в 'int* 'для 1-го аргумента

void print(int *p){

Из того, что я понимаю, arr - указатель на первый элемент массива, который сам является массивом. Так почему я не могу найти массив [i] значения, возвращаемого указателем arr ?

Ответы [ 3 ]

1 голос
/ 22 октября 2019

Параметр функции print имеет тип int *.

print(int *p)

Таким образом, разыменовав указатель в выражении (*p)[i], вы получите скалярный объект типа int. Вы не можете применять оператор индексации к скалярным объектам типа int.

С другой стороны, в этом вызове

print(arr);

аргумент, имеющий тип int[3][3], имеет видпреобразован в указатель на свой первый элемент. Элементы массива имеют тип int[3]. Таким образом, тип выражения после неявного преобразования массива в указатель на его первый элемент: int ( * )[3].

И сообщение об ошибке указывает на эту проблему

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

print(arr);
   ^~~~~

, поскольку компиляторне может найти функцию с именем print, которая принимает аргумент типа int ( * )[3].

Таким образом, параметр функции print должен быть объявлен как

print( int p[][3] )

или

print( int ( *p )[3] )

И так как массив не изменяется в функции, он должен быть объявлен с квалификатором const.

Определение функции в этом случае будет выглядеть (если вы хотитеиспользуйте указатели)

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

Вот демонстрационная программа.

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

Его вывод

1 2 3 
4 5 6 
7 8 9

Однако этот подход имеет серьезный недостаток. Функция использует магическое число 3.

Лучше переписать функцию по крайней мере, как

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

Также вы можете добавить еще один параметр с аргументом по умолчанию. Например,

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

Например,

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

И наконец вы можете написать шаблонную функцию.

#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;
}
1 голос
/ 22 октября 2019

первый элемент массива arr получил тип int[3]. Поскольку так называемый двумерный массив в терминах языка - это массив массивов .

Таким образом, arr не может быть эквивалентом int*, только int[][3] или int(*)[3].

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

или

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

будут правильными сигнатурами вашей функции. Любая попытка представить многомерный массив в качестве указателя требует явного приведения и отмывания его обратно к нужному типу внутри функции, в противном случае это считается неопределенным поведением, даже если большинство реализаций позволяют этому работать.

0 голосов
/ 22 октября 2019

По моему мнению, все предыдущие ответы не решают основную проблему.

Проблема в том, как передать простой старый C-массив в функцию.

Если вы хотите передатьпростой старый C-массив для функции, у вас есть 2 возможности.

  • Передача по ссылке
  • Передача по указателю

Кажется, что вы хотитепередать по ссылке. Но вы используете неправильный синтаксис.

Пожалуйста, смотрите:

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

То, что вы передаете в функцию - это потерянный указатель на массив int.

Просто исправьтесинтаксис, и он будет работать.

Дополнительная подсказка:

Не используйте простые массивы в стиле C в C ++. Никогда. Пожалуйста, используйте контейнеры STL.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...