Как должна выглядеть функция подписи в двухмерных массивах произвольного размера? [Дубликат]

Если вы получаете эту ошибку в сборке где-то в другом месте, в то время как ваша среда IDE говорит, что все в порядке, проверьте, используете ли вы одни и те же версии Java в обоих местах.

Например, Java 7 и Java 8 имеют разные API, поэтому вызов несуществующего API в старой версии Java приведет к этой ошибке.

224
задан Peter Mortensen 25 February 2015 в 19:36
поделиться

12 ответов

Существует три способа передать 2D-массив функции:

  1. Параметр представляет собой 2D-массив
    int array[10][10];
    void passFunc(int a[][10])
    {
        // ...
    }
    passFunc(array);
    
  2. Параметр представляет собой массив, содержащий указатели
    int *array[10];
    for(int i = 0; i < 10; i++)
        array[i] = new int[10];
    void passFunc(int *a[10]) //Array containing pointers
    {
        // ...
    }
    passFunc(array);
    
  3. Параметр является указателем на указатель
    int **array;
    array = new int *[10];
    for(int i = 0; i <10; i++)
        array[i] = new int[10];
    void passFunc(int **a)
    {
        // ...
    }
    passFunc(array);
    
327
ответ дан Peter Mortensen 18 August 2018 в 20:51
поделиться
  • 1
    @Overflowh Вы можете получить элементы array с array[i][j] :) – shengy 27 May 2013 в 04:15
  • 2
    Для 1-го случая параметр может быть объявлен как int (*a)[10]. – Zachary 13 June 2013 в 15:38
  • 3
    Для второго случая параметр может быть объявлен как int **. – Zachary 14 June 2013 в 04:14
  • 4
    @ Zack: Вы правы, есть только два случая; один - указатель на указатель, а другой - единственный указатель на целочисленный массив размером n i.e. int (*a) [10]. – legends2k 10 July 2013 в 11:44
  • 5
    Случай 2 и 3 не являются двумерными массивами, поэтому этот ответ вводит в заблуждение. См. это . – Lundin 16 June 2015 в 08:05

Вы можете создать шаблон функции следующим образом:

template<int R, int C>
void myFunction(double (&myArray)[R][C])
{
    myArray[x][y] = 5;
    etc...
}

Затем у вас есть размеры размеров через R и C. Для каждого размера массива будет создана другая функция, поэтому, если ваша функция большая и вы называете это множеством различных размеров массивов, это может быть дорогостоящим. Вы можете использовать его как оболочку над такой функцией, хотя:

void myFunction(double * arr, int R, int C)
{
    arr[x * C + y] = 5;
    etc...
}

Он обрабатывает массив как одномерный и использует арифметику для вычисления смещений индексов. В этом случае вы должны определить шаблон следующим образом:

template<int C, int R>
void myFunction(double (&myArray)[R][C])
{
    myFunction(*myArray, R, C);
}
19
ответ дан Benjamin Lindley 18 August 2018 в 20:51
поделиться
  • 1
    size_t - лучший тип для индексов массива, чем int. – Andrew Tomazos 10 July 2013 в 12:42

Фиксированный размер

1. Передача по ссылке

template <size_t rows, size_t cols>
void process_2d_array_template(int (&array)[rows][cols])
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < rows; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < cols; ++j)
            std::cout << array[i][j] << '\t';
        std::cout << std::endl;
    }
}

В C ++ передача массива по ссылке без потери информации о размерах, вероятно, является самой безопасной, так как не нужно беспокоиться о том, что вызывающий абонент передает неправильное измерение (флаги компилятора при несоответствии). Однако это невозможно с динамическими (freestore) массивами; он работает только для автоматических ( обычно стек-живых ) массивов, т. е. размерность должна быть известна во время компиляции.

2. Pass by pointer

void process_2d_array_pointer(int (*array)[5][10])
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < 5; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < 10; ++j)
            std::cout << (*array)[i][j] << '\t';
        std::cout << std::endl;
    }    
}

С-эквивалент предыдущего метода передает массив по указателю. Это не следует путать с передачей по разлагаемому типу указателя массива (3), который является распространенным, популярным методом, хотя и менее безопасным, чем этот, но более гибким. Как и (1), используйте этот метод, когда все размеры массива фиксированы и известны во время компиляции. Обратите внимание, что при вызове функции адрес массива должен быть передан process_2d_array_pointer(&a), а не адрес первого элемента путем распада process_2d_array_pointer(a).

Размер переменной

Они наследуются от C но менее безопасны, компилятор не имеет возможности проверить, гарантируя, что вызывающий абонент передает необходимые размеры. Функция только банков на том, что вызывающий абонент входит в качестве измерения (измерений). Они более гибкие, чем предыдущие, поскольку массивы различной длины могут быть переданы им неизменно.

Следует помнить, что нет такой вещи, как передача массива непосредственно функции в C [в то время как в C ++ они могут быть переданы как ссылка (1)]; (2) передает указатель на массив, а не сам массив. Всегда передавать массив as-is становится операцией копирования-указателя, которая облегчается природой массива для разложения в указатель .

3. Pass by (value) указатель на затухающий тип

// int array[][10] is just fancy notation for the same thing
void process_2d_array(int (*array)[10], size_t rows)
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < rows; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < 10; ++j)
            std::cout << array[i][j] << '\t';
        std::cout << std::endl;
    }
}

Хотя разрешено int array[][10], я бы не рекомендовал его над указанным выше синтаксисом, поскольку приведенный выше синтаксис дает понять, что идентификатор array является единственным указателем на массив из 10 целых чисел, в то время как этот синтаксис выглядит , как будто это 2D-массив, но является тем же самым указателем на массив из 10 целых чисел. Здесь мы знаем количество элементов в одной строке (т. Е. Размер столбца, здесь 10), но количество строк неизвестно и, следовательно, должно быть передано в качестве аргумента. В этом случае есть определенная безопасность, поскольку компилятор может указывать, когда передается указатель на массив со вторым измерением, не равным 10. Первое измерение является изменяющейся частью и может быть опущено. См. здесь обоснование о том, почему разрешено только первое измерение.

4. Пропустить указатель на указатель

// int *array[10] is just fancy notation for the same thing
void process_pointer_2_pointer(int **array, size_t rows, size_t cols)
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < rows; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < cols; ++j)
            std::cout << array[i][j] << '\t';
        std::cout << std::endl;
    }
}

Опять есть альтернативный синтаксис int *array[10], который совпадает с int **array. В этом синтаксисе [10] игнорируется, поскольку он распадается на указатель, тем самым становясь int **array. Возможно, это всего лишь сигнал вызывающему, что переданный массив должен иметь не менее 10 столбцов, даже если требуется подсчет строк. В любом случае компилятор не указывает на какие-либо нарушения длины и размера (он проверяет, является ли переданный тип указателем на указатель), поэтому для определения значения параметра строки и столбца требуется значение параметра

Примечание: (4) является наименее безопасным вариантом , поскольку он вряд ли имеет проверку типа и наиболее неудобен. Нельзя законно передать 2D-массив этой функции; C-FAQ осуждает обычное обходное решение для выполнения int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10);, поскольку оно может потенциально привести к неопределенному поведению из-за сглаживания массива. Правильный способ передачи массива в этом методе приводит нас к неудобной части, то есть нам нужен дополнительный (суррогатный) массив указателей, каждый из которых указывает на соответствующую строку фактического, подлежащего передаче массива; этот суррогат затем передается функции (см. ниже); все это для того, чтобы выполнить ту же работу, что и вышеупомянутые методы, которые более безопасны, чище и, возможно, быстрее.

Вот программа драйвера для проверки вышеперечисленных функций:

#include <iostream>

// copy above functions here

int main()
{
    int a[5][10] = { { } };
    process_2d_array_template(a);
    process_2d_array_pointer(&a);    // <-- notice the unusual usage of addressof (&) operator on an array
    process_2d_array(a, 5);
    // works since a's first dimension decays into a pointer thereby becoming int (*)[10]

    int *b[5];  // surrogate
    for (size_t i = 0; i < 5; ++i)
    {
        b[i] = a[i];
    }
    // another popular way to define b: here the 2D arrays dims may be non-const, runtime var
    // int **b = new int*[5];
    // for (size_t i = 0; i < 5; ++i) b[i] = new int[10];
    process_pointer_2_pointer(b, 5, 10);
    // process_2d_array(b, 5);
    // doesn't work since b's first dimension decays into a pointer thereby becoming int**
}
128
ответ дан Community 18 August 2018 в 20:51
поделиться
  • 1
    Как насчет передачи динамически распределенных массивов в функции на C ++? В стандарте C11 это можно сделать для статически и динамически распределенных массивов, таких как fn (int col, int row, int array [col] [row]): stackoverflow.com/questions/16004668/… Я задал вопрос по этой проблеме: stackoverflow.com/questions/27457076/… – 42n4 13 December 2014 в 10:16
  • 2
    @ 42n4 Case 4 охватывает (для C ++ тоже), что. Для динамически распределенных массивов только строка внутри цикла будет меняться от b[i] = a[i]; до, скажем, b[i] = new int[10];. Можно также сделать b динамически выделенным int **b = int *[5];, и он все равно будет работать как есть. – legends2k 15 December 2014 в 07:42
  • 3
    Как адресация array[i][j] работает в функции в 4) ? Потому что он получил ptr для ptr и не знает значение последнего измерения, которое необходимо для выполнения сдвига для правильной адресации? – user1234567 16 December 2014 в 17:27
  • 4
    array[i][j] - просто арифметика указателя, то есть значение указателя array, оно добавило бы i и разыменовало бы результат как int*, к которому он добавил бы j, и разыменовал бы это местоположение, прочитав int. Итак, нет, для этого не нужно знать никаких измерений. Но вот в этом весь смысл! Компилятор берет слово программиста в вере, и если программист был некорректен, возникает неопределенное поведение. Именно по этой причине я упомянул, что случай 4 является наименее безопасным вариантом. – legends2k 17 December 2014 в 04:04

anArray[10][10] не является указателем на указатель, это непрерывный блок памяти, подходящий для хранения 100 значений типа double, который компилятор знает, как обращаться, поскольку вы указали размеры. Вам нужно передать его функции в виде массива. Вы можете опустить размер начального измерения следующим образом:

void f(double p[][10]) {
}

Однако это не позволит вам передавать массивы с последним измерением, отличным от десяти.

Лучшее решение в C ++ используется std::vector<std::vector<double> >: он почти такой же эффективный и значительно более удобный.

10
ответ дан dasblinkenlight 18 August 2018 в 20:51
поделиться
  • 1
    Я предпочитаю это решение, поскольку библиотека std очень эффективна - кстати, мне нравится dasblinkenlight; Я использовал dasblikenlicht – mozillanerd 18 January 2012 в 23:34
  • 2
    Почти так же эффективно? Да правильно. Головка указателя всегда дороже, чем чеканка без указателя. – Thomas Eding 24 September 2015 в 19:07

Вот вектор матричного примера векторов

#include <iostream>
#include <vector>
using namespace std;

typedef vector< vector<int> > Matrix;

void print(Matrix& m)
{
   int M=m.size();
   int N=m[0].size();
   for(int i=0; i<M; i++) {
      for(int j=0; j<N; j++)
         cout << m[i][j] << " ";
      cout << endl;
   }
   cout << endl;
}


int main()
{
    Matrix m = { {1,2,3,4},
                 {5,6,7,8},
                 {9,1,2,3} };
    print(m);

    //To initialize a 3 x 4 matrix with 0:
    Matrix n( 3,vector<int>(4,0));
    print(n);
    return 0;
}

:

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

0 0 0 0
0 0 0 0
0 0 0 0
1
ответ дан edW 18 August 2018 в 20:51
поделиться

Удивлен, что никто не упомянул об этом, но вы можете просто создать шаблон на любом 2D, поддерживающем [] [] семантику.

template <typename TwoD>
void myFunction(TwoD& myArray){
     myArray[x][y] = 5;
     etc...
}

// call with
double anArray[10][10];
myFunction(anArray);

Он работает с любой 2D-массивной структурой данных, такой как std::vector<std::vector<T>> или пользовательский тип, чтобы максимизировать повторное использование кода.

9
ответ дан LemonPi 18 August 2018 в 20:51
поделиться

Одномерный массив распадается на указатель указателя, указывающий на первый элемент в массиве. В то время как 2D-массив распадается на указатель, указывающий на первую строку. Итак, прототип функции должен быть -

void myFunction(double (*myArray) [10]);

Я предпочел бы std::vector по необработанным массивам.

7
ответ дан Mahesh 18 August 2018 в 20:51
поделиться

Мы можем использовать несколько способов передать 2D-массив функции:

  • Используя один указатель, мы должны прибегнуть к двумерному массиву.
    #include<bits/stdc++.h>
    using namespace std;
    
    
    void func(int *arr, int m, int n)
    {
        for (int i=0; i<m; i++)
        {
           for (int j=0; j<n; j++)
           {
              cout<<*((arr+i*n) + j)<<" ";
           }
           cout<<endl;
        }
    }
    
    int main()
    {
        int m = 3, n = 3;
        int arr[m][n] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
        func((int *)arr, m, n);
        return 0;
    }
    
  • Использование двойного указателя Таким образом, мы также выводим 2d-массив
    #include<bits/stdc++.h>
    using namespace std;

   void func(int **arr, int row, int col)
   {
      for (int i=0; i<row; i++)
      {
         for(int j=0 ; j<col; j++)
         {
           cout<<arr[i][j]<<" ";
         }
         printf("\n");
      }
   }

  int main()
  {
     int row, colum;
     cin>>row>>colum;
     int** arr = new int*[row];

     for(int i=0; i<row; i++)
     {
        arr[i] = new int[colum];
     }

     for(int i=0; i<row; i++)
     {
         for(int j=0; j<colum; j++)
         {
            cin>>arr[i][j];
         }
     }
     func(arr, row, colum);

     return 0;
   }
0
ответ дан rashedcs 18 August 2018 в 20:51
поделиться

Вы можете сделать что-то вроде этого ...

#include<iostream>

using namespace std;

//for changing values in 2D array
void myFunc(double *a,int rows,int cols){
    for(int i=0;i<rows;i++){
        for(int j=0;j<cols;j++){
            *(a+ i*rows + j)+=10.0;
        }
    }
}

//for printing 2D array,similar to myFunc
void printArray(double *a,int rows,int cols){
    cout<<"Printing your array...\n";
    for(int i=0;i<rows;i++){
        for(int j=0;j<cols;j++){
            cout<<*(a+ i*rows + j)<<"  ";
        }
    cout<<"\n";
    }
}

int main(){
    //declare and initialize your array
    double a[2][2]={{1.5 , 2.5},{3.5 , 4.5}};

    //the 1st argument is the address of the first row i.e
    //the first 1D array
    //the 2nd argument is the no of rows of your array
    //the 3rd argument is the no of columns of your array
    myFunc(a[0],2,2);

    //same way as myFunc
    printArray(a[0],2,2);

    return 0;
}

Ваш выход будет следующим:

11.5  12.5
13.5  14.5
7
ответ дан Sagar Shah 18 August 2018 в 20:51
поделиться
  • 1
    Единственная причина, по которой я могу прийти к выводу о том, почему в этом случае следует манипулировать массивом, заключается в том, что у человека не хватает знаний о том, как работают указатели массива. – Lundin 16 June 2015 в 08:09
  • 2
    переменная i должна быть умножена на столбцы, а не на строки, если столбцы и строки не равны, как в этом случае – Andrey Chernukha 5 January 2016 в 22:51

Одна важная вещь для передачи многомерных массивов:

  • First array dimension не требуется указывать.
  • Second(any any further)dimension необходимо указать.

1. Когда только второе измерение доступно глобально (либо как макрос, либо как глобальная константа)

`const int N = 3;

`void print(int arr[][N], int m)
{
int i, j;
for (i = 0; i < m; i++)
  for (j = 0; j < N; j++)
    printf("%d ", arr[i][j]);
}`

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

2. Использование одного указателя: в этом методе мы должны 2D-массив при переходе к функции.

`void print(int *arr, int m, int n)
{
int i, j;
for (i = 0; i < m; i++)
  for (j = 0; j < n; j++)
    printf("%d ", *((arr+i*n) + j));
 }

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

// We can also use "print(&arr[0][0], m, n);"
print((int *)arr, m, n);
return 0;
}`
1
ответ дан sonorous 18 August 2018 в 20:51
поделиться

Вы можете использовать объект шаблона в C ++ для этого. Я сделал что-то вроде этого:

template<typename T, size_t col>
T process(T a[][col], size_t row) {
...
}

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

int some_mat[3][3], another_mat[4,5];
process(some_mat, 3);
process(another_mat, 4);

дважды создает шаблон для создания двух определений функций (один из которых col = 3 и один, где col = 5).

0
ответ дан vantony 18 August 2018 в 20:51
поделиться

Модификация первого предложения Шенги позволяет использовать шаблоны, чтобы заставить функцию принять многомерную переменную массива (вместо хранения массива указателей, которые нужно управлять и удалять):

template <size_t size_x, size_t size_y>
void func(double (&arr)[size_x][size_y])
{
    printf("%p\n", &arr);
}

int main()
{
    double a1[10][10];
    double a2[5][5];

    printf("%p\n%p\n\n", &a1, &a2);
    func(a1);
    func(a2);

    return 0;
}

Операторы печати должны показать, что массивы передаются по ссылке (путем отображения адресов переменных)

33
ответ дан Zrax 18 August 2018 в 20:51
поделиться
  • 1
    Вы должны использовать %p для печати указателя, и даже тогда вы должны указать его на void *, иначе printf() вызывает неопределенное поведение. Кроме того, вы не должны использовать оператор addressof (&) при вызове функций, так как функции ожидают аргумент типа double (*)[size_y], тогда как вы в настоящее время передаете им double (*)[10][10] и double (*)[5][5]. – user 20 October 2013 в 09:26
  • 2
    Если вы используете шаблоны, делающие оба измерения аргументами шаблонов более подходящими и лучше, поскольку доступ к указателям низкого уровня может быть полностью устранен. – legends2k 24 March 2014 в 14:43
  • 3
    Это работает только в том случае, если размер массива известен во время компиляции. – Jeb_is_a_mess 27 December 2017 в 17:51
Другие вопросы по тегам:

Похожие вопросы: