У меня есть функция, которую я хочу взять в качестве параметра двумерный массив переменного размера.
Пока у меня есть это:
void myFunction(double** myArray){
myArray[x][y] = 5;
etc...
}
И я объявил массив в другом месте в моем коде:
double anArray[10][10];
Тем не менее, вызов myFunction(anArray)
дает мне ошибку.
Я не хочу копировать массив, когда я передаю его. Любые изменения myFunction
должны изменить состояние anArray
. Если я правильно понимаю, я хочу передать в качестве аргумента указатель на 2D-массив. Функция также должна принимать массивы разных размеров. Так например [10][10]
и [5][5]
. Как я могу это сделать?
c++
arrays
pointers
multidimensional-array
RogerDarwin
источник
источник
func(int* mat, int r, int c){ for(int i=0; i<r; i++) for(int j=0; j<c; j++) printf("%d ", *(mat+i*c+j)); }
. Назовите это какint mat[3][5]; func(mat[0], 3, 5);
Ответы:
Есть три способа передать 2D-массив в функцию:
Параметр представляет собой двумерный массив
Параметр представляет собой массив, содержащий указатели
Параметр является указателем на указатель
источник
array
сarray[i][j]
:)int (*a)[10]
.int **
.int (*a) [10]
.Исправленный размер
1. Передать по ссылке
В C ++ передача массива по ссылке без потери информации об измерениях, вероятно, является наиболее безопасной, поскольку не нужно беспокоиться о том, что вызывающая сторона передаст неправильное измерение (флаги компилятора при несовпадении). Однако это невозможно с динамическими (freestore) массивами; он работает только для автоматических ( обычно стековых ) массивов, т.е. размерность должна быть известна во время компиляции.
2. Пройдите по указателю
Эквивалент предыдущего метода на C передает массив по указателю. Это не следует путать с передачей типа потерянного указателя массива (3) , который является распространенным, популярным методом, хотя и менее безопасным, чем этот, но более гибким. Подобно (1) , используйте этот метод, когда все размеры массива фиксированы и известны во время компиляции. Обратите внимание, что при вызове функции должен передаваться адрес массива,
process_2d_array_pointer(&a)
а не адрес первого элемента при распадеprocess_2d_array_pointer(a)
.Размер переменной
Они унаследованы от C, но менее безопасны, у компилятора нет способа проверить, гарантируя, что вызывающая сторона передает требуемые измерения. Функция полагается только на то, что передает вызывающая сторона в качестве измерения. Они более гибкие, чем описанные выше, поскольку массивы различной длины могут быть переданы им неизменно.
Следует помнить, что нет такой вещи, как передача массива непосредственно в функцию в C [в то время как в C ++ они могут передаваться как ссылка (1) ]; (2) передает указатель на массив, а не сам массив. Всегда передача массива как есть становится операцией копирования указателя, чему способствует природа распада массива в указатель .
3. Передать (значение) указатель на распавшийся тип
Хотя
int array[][10]
это разрешено, я бы не рекомендовал использовать этот синтаксис выше, поскольку приведенный выше синтаксис проясняет, что идентификаторarray
представляет собой один указатель на массив из 10 целых чисел, хотя этот синтаксис выглядит как двумерный массив, но тот же указатель на массив из 10 целых чисел. Здесь мы знаем количество элементов в одной строке (то есть размер столбца, здесь 10), но количество строк неизвестно и, следовательно, должно быть передано в качестве аргумента. В этом случае есть некоторая безопасность, так как компилятор может пометить, когда передан указатель на массив со вторым измерением, не равным 10. Первое измерение является переменной частью и может быть опущено. См. Здесь обоснование того, почему только первое измерение может быть опущено.4. Передать указатель на указатель
Опять же есть альтернативный синтаксис,
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);
поскольку он может потенциально привести к неопределенному поведению из-за выравнивания массива. Правильный способ передачи массива в этом методе приводит нас к неудобной части, то есть нам нужен дополнительный (суррогатный) массив указателей, каждый элемент которого указывает на соответствующую строку фактического, подлежащего передаче массива; этот суррогат затем передается функции (см. ниже); все это для выполнения той же работы, что и вышеописанные методы, которые более безопасны, чище и, возможно, быстрее.Вот программа-драйвер для проверки вышеуказанных функций:
источник
b[i] = a[i];
, скажем,b[i] = new int[10];
. Можно также сделатьb
динамически распределенный,int **b = int *[5];
и он все еще будет работать как есть.array[i][j]
работает в функции в 4) ? Поскольку он получил ptr в ptr и не знает значения последнего измерения, что необходимо для перехода к правильной адресации?array[i][j]
это просто арифметика указателя, т.е. к значению указателяarray
, он добавляетi
и разыменовывает результат какint*
, к которому он добавляетj
и разыменовывает это местоположение, читаяint
. Так что нет, для этого не нужно знать какого-либо измерения. Но в этом все дело! Компилятор принимает слово программиста с верой, и если программист ошибался, возникает неопределенное поведение. По этой причине я упомянул, что вариант 4 является наименее безопасным вариантом.В качестве модификации первого предложения shengy вы можете использовать шаблоны, чтобы заставить функцию принимать переменную многомерного массива (вместо хранения массива указателей, которые должны управляться и удаляться):
Операторы печати предназначены для того, чтобы показать, что массивы передаются по ссылке (путем отображения адресов переменных).
источник
%p
для печати указатель, и даже тогда вы должны привести его к типуvoid *
, иначеprintf()
вызовет неопределенное поведение. Кроме того, вы не должны использовать&
оператор addressof ( ) при вызове функций, так как функции ожидают аргумент типаdouble (*)[size_y]
, тогда как вы в настоящее время передаете ихdouble (*)[10][10]
иdouble (*)[5][5]
.Удивило, что никто еще не упомянул об этом, но вы можете просто создать шаблон для чего-нибудь 2D, поддерживающего семантику [] [].
Он работает с любой двумерной «массивоподобной» структурой данных, например
std::vector<std::vector<T>>
, или пользовательским типом, чтобы максимизировать повторное использование кода.источник
Вы можете создать шаблон функции следующим образом:
Тогда у вас есть оба размера измерений с помощью R и C. Для каждого размера массива будет создана отдельная функция, поэтому, если ваша функция велика и вы вызываете ее с различными размерами массива, это может быть дорогостоящим. Вы можете использовать его как обертку над такой функцией:
Он рассматривает массив как одномерный и использует арифметику для определения смещений индексов. В этом случае вы должны определить шаблон следующим образом:
источник
size_t
это лучший тип для индексов массива, чемint
.anArray[10][10]
это не указатель на указатель, это непрерывный кусок памяти, подходящий для хранения 100 значений типа double, который компилятор знает, как обращаться, потому что вы указали измерения. Вам нужно передать его в функцию в виде массива. Вы можете опустить размер исходного измерения следующим образом:Однако это не позволит вам передавать массивы с последним измерением, кроме десяти.
Лучшее решение в C ++ - использовать
std::vector<std::vector<double> >
: оно почти столь же эффективно и значительно более удобно.источник
Одномерный массив распадается на указатель указателя, указывающий на первый элемент в массиве. В то время как 2D-массив распадается на указатель, указывающий на первую строку. Итак, прототип функции должен быть -
Я бы предпочел
std::vector
сырые массивы.источник
Вы можете сделать что-то вроде этого ...
Ваш вывод будет следующим ...
источник
Вот пример вектора матрицы векторов
вывод:
источник
Мы можем использовать несколько способов передачи 2D-массива в функцию:
Используя один указатель, мы должны типизировать 2D-массив.
Использование двойного указателя Таким образом, мы также вводим 2d массив
источник
Одна важная вещь для передачи многомерных массивов:
First array dimension
не нужно указывать.Second(any any further)dimension
должен быть указан.1. Когда только второе измерение доступно глобально (или как макрос, или как глобальная константа)
2. Использование одного указателя : в этом методе мы должны типизировать 2D-массив при переходе к функции.
источник
Вы можете использовать шаблон в C ++, чтобы сделать это. Я сделал что-то вроде этого:
проблема этого подхода состоит в том, что для каждого значения col, которое вы предоставляете, создается новое определение функции с использованием шаблона. так,
создает экземпляр шаблона дважды, чтобы получить 2 определения функции (одно, где col = 3 и одно, где col = 5).
источник
Если вы хотите перейти
int a[2][3]
кvoid func(int** pp)
вам, необходимо вспомогательные действия, как указано ниже.Поскольку первое
[2]
может быть неявно указано, оно может быть дополнительно упрощено как.источник
В случае, если вы хотите передать двумерный массив динамического размера в функцию, использование некоторых указателей может работать для вас.
источник
Вам разрешено опускать крайнее левое измерение, поэтому у вас есть два варианта:
Это то же самое с указателями:
Распад N-мерного массива на указатель на N-1-мерный массив разрешен стандартом C ++ , так как вы можете потерять крайнее левое измерение и все еще иметь возможность правильно обращаться к элементам массива с N-1-мерной информацией.
Подробности здесь
Хотя массивы и указатели не одно и то же : массив может распасться на указатель, но указатель не несет состояния о размере / конфигурации данных, на которые он указывает.
A
char **
- указатель на блок памяти, содержащий символьные указатели , которые сами указывают на блоки памяти символов. Achar [][]
- это отдельный блок памяти, который содержит символы. Это влияет на то, как компилятор переводит код и как будет выглядеть конечная производительность.Источник
источник