Как передать многомерный массив функции в C и С++
#include<stdio.h>
void print(int *arr[], int s1, int s2) {
int i, j;
for(i = 0; i<s1; i++)
for(j = 0; j<s2; j++)
printf("%d, ", *((arr+i)+j));
}
int main() {
int a[4][4] = {{0}};
print(a,4,4);
}
Это работает в C, но не в С++.
Ошибка:
cannot convert `int (*)[4]' to `int**' for argument `1' to
`void print(int**, int, int)'
Почему это не работает на С++? Какие изменения необходимо сделать?
Ответы
Ответ 1
Этот код не будет работать ни на C, ни на С++. Массив типа int[4][4]
не конвертируется в указатель типа int **
(что означает int *arr[]
в объявлении параметра). Если вам удалось скомпилировать его на C, это просто потому, что вы, вероятно, проигнорировали предупреждение компилятора C в основном в том же формате, что и сообщение об ошибке, полученное от компилятора С++. (Иногда компиляторы C выдают предупреждения для того, что по существу является ошибкой.)
Итак, опять же, не делайте утверждений, которые неверны. Этот код не работает в C. Чтобы преобразовать встроенный 2D-массив в указатель int **
, вы можете использовать технику, подобную этой
Преобразование многомерных массивов в указатели в С++
(См. принятый ответ. Проблема точно такая же.)
РЕДАКТИРОВАТЬ: Код работает в C, потому что другая ошибка в коде печати маскирует эффекты ошибки при передаче массива. Чтобы правильно получить доступ к элементу псевдо-массива int **
, вы должны использовать выражение *(*(arr + i) + j)
или лучше простое arr[i][j]
(что то же самое). Вы пропустили дополнительный *
, который заставил его распечатать то, что абсолютно не имеет никакого отношения к содержимому вашего массива. Опять же, инициализируйте свой массив в main
еще что-нибудь, чтобы увидеть, что результаты, которые вы печатаете на C, абсолютно не связаны с вашим предполагаемым контентом массива.
Если вы измените оператор printf
, как показано выше, ваш код, скорее всего, сбой из-за ошибки, передаваемой массивом, которую я описал изначально.
Еще один раз: вы не можете передать массив int[4][4]
как псевдо-массив int **
. Это то, что говорит С++ в сообщении об ошибке. И, я уверен, это то, что вам сказал ваш компилятор C, но вы, вероятно, проигнорировали его, так как это было "просто предупреждение".
Ответ 2
Проблема заключается в том, что
int a[4][4];
будет фактически сохранен в физически непрерывной памяти. Таким образом, для доступа к произвольной части вашего массива 4x4 функция "print" должна знать размеры массива. Например, следующая небольшая часть кода будет обращаться к одной и той же части памяти двумя разными способами.
#include <iostream>
void print(int a[][4]){
for (int i = 0; i <4; i++){
for (int j = 0; j < 4; j++){
//accessing as 4x4 array
std::cout << a[i][j] <<std::endl;
//accessing corresponding to the physical layout in memory
std::cout << *(*(a)+ i*4 + j) << std::endl;
}
}
}
int main(){
int a[4][4];
//populating the array with the corresponding indices from 0 to 15
int m = 0;
for (int i = 0; i<4; i++){
for (int j= 0; j < 4; j++){
a[i][j] = m;
m++;
}
}
print(a);
}
Таким образом, макет памяти не изменяется, но способ доступа имеет значение. Он может быть визуализирован как шахматная доска.
0 1 2 3
----------
0| 1 2 3 4
1| 5 6 7 8
2| 9 10 11 12
3|13 14 15 16
Но реальная физическая память выглядит так.
0*4+0 0*4+1 0*4+2 0*4+3 1*4+0 1*4+1 1*4+2 1*4+3 2*4+1 etc.
-----------------------------------------------------
1 2 3 4 5 6 7 8 9 etc.
В С++ данные массива сохраняются по строкам, и длина строки (в этом случае 4) всегда необходима, чтобы перейти к соответствующему смещению памяти для следующей строки. Поэтому первый индекс указывает только объем памяти, который необходим при объявлении массива, но больше не требуется для вычисления смещения.
Ответ 3
#include<stdio.h>
void print(int arr[][4], int s1, int s2) {
int i, j;
printf("\n");
for(i = 0; i<s1; i++) {
for(j = 0; j<s2; j++) {
printf("%d, ", *((arr+i)+j));
}
}
printf("\n");
}
int main() {
int a[4][4] = {{0}};
print(a,4,4);
}
Это будет работать, когда по работе я имею в виду компиляцию. @AndreyT объяснил, почему ваша версия не работает уже.
Вот как вам следует передать массив 2d.
Для ясности вы также можете указать оба размера в объявлении функции:
#include<stdio.h>
void print(int arr[4][4], int s1, int s2) {
int i, j;
printf("\n");
for(i = 0; i<s1; i++) {
for(j = 0; j<s2; j++) {
printf("%d, ", *((arr+i)+j));
}
}
printf("\n");
}
int main() {
int a[4][4] = {{0}};
print(a,4,4);
}
Оба будут работать.
Вы также должны изменить *((arr+i)+j)
на a[i][j]
(предпочтительно) или *(*(arr+i)+j)
, если вы хотите получить доступ к j
-му элементу строки i
.
Ответ 4
Здесь версия, которая работает, но теоретически недействительна (см. ниже) C90 и С++ 98:
#include <stdio.h>
static void print(int *arr, size_t s1, size_t s2)
{
size_t i, j;
printf("\n");
for(i = 0; i < s1; i++) {
for(j = 0; j < s2; j++) {
printf("%d, ", arr[i * s2 + j]);
}
}
printf("\n");
}
int main(void) {
int a[4][4] = {{0}};
print(a[0], 4, 4);
return 0;
}
Версия С++ с использованием шаблонов (адаптирована из Ответ на вопросник) может выглядеть так:
#include <iostream>
#include <cstring>
using namespace std;
template <size_t N, size_t M>
struct IntMatrix
{
int data[N][M];
IntMatrix() { memset(data, 0, sizeof data); }
};
template <size_t N, size_t M>
ostream& operator<<(ostream& out, const IntMatrix<N,M>& m)
{
out << "\n";
for(size_t i = 0; i < N; i++) {
for(size_t j = 0; j < M; j++) {
out << m.data[i][j] << ", ";
}
}
out << "\n";
return out;
}
int main()
{
IntMatrix<4,4> a;
cout << a;
return 0;
}
В качестве альтернативы вы можете использовать вложенные контейнеры STL - т.е. vector< vector<int> >
- вместо простого массива.
С C99 вы можете сделать
static void print(size_t s1, size_t s2, int arr[s1][s2]) {
printf("\n");
for(size_t i = 0; i < s1; i++) {
for(size_t j = 0; j < s2; j++) {
printf("%d, ", arr[i][j]);
}
}
printf("\n");
}
и назовите его
print(4, 4, a);
Как заметил Роберт в комментариях, первый фрагмент фактически включает поведение undefined. Однако, предполагая, что арифметика указателя всегда будет приводить к указателю, даже если поведение undefined участвует (и не взрывает ваш компьютер), существует только один возможный результат из-за других ограничений в стандарте, т.е. Это экземпляр где стандарт оставляет что-то излишне undefined.
Насколько я могу судить, подставляя
print(a[0], 4, 4);
с
union m2f { int multi[4][4]; int flat[16]; } *foo = (union m2f *)&a;
print(foo->flat, 4, 4);
сделает его законным.
Ответ 5
Вместо этого вы можете использовать int**
. Его гораздо более гибкая:
#include <stdio.h>
#include <stdlib.h>
void print(int **a, int numRows, int numCols )
{
int row, col ;
for( int row = 0; row < numRows; row++ )
{
for( int col = 0; col < numCols ; col++ )
{
printf("%5d, ", a[row][col]);
}
puts("");
}
}
int main()
{
int numRows = 16 ;
int numCols = 5 ;
int **a ;
// a will be a 2d array with numRows rows and numCols cols
// allocate an "array of arrays" of int
a = (int**)malloc( numRows* sizeof(int*) ) ;
// each entry in the array of arrays of int
// isn't allocated yet, so allocate it
for( int row = 0 ; row < numRows ; row++ )
{
// Allocate an array of int's, at each
// entry in the "array of arrays"
a[row] = (int*)malloc( numCols*sizeof(int) ) ;
}
int count = 1 ;
for( int row = 0 ; row < numRows ; row++ )
{
for( int col = 0 ; col < numCols ; col++ )
{
a[row][col] = count++ ;
}
}
print( a, numRows, numCols );
}
Еще одна вещь, которая может вас заинтересовать, - это структура, например D3DMATRIX:
typedef struct _D3DMATRIX {
union {
struct {
float _11, _12, _13, _14;
float _21, _22, _23, _24;
float _31, _32, _33, _34;
float _41, _42, _43, _44;
};
float m[4][4];
};
} D3DMATRIX;
D3DMATRIX myMatrix ;
Сладкое в этом маленьком лаконичном фрагменте вы можете использовать как myMatrix.m[0][0]
(для доступа к первому элементу), либо вы можете использовать myMatrix._11
для доступа к этому же элементу. union является секретом.
Ответ 6
#include<cstdio>
template <size_t N, size_t M>
struct DataHolder
{
int data[N][M];
DataHolder()
{
for(int i=0; i<N; ++i)
for(int j=0; j<M; ++j)
data[i][j] = 0;
}
};
template <size_t N, size_t M>
void print(const DataHolder<N,M>& dataHolder) {
printf("\n");
for(int i = 0; i<N; i++) {
for(int j = 0; j<M; j++) {
printf("%d, ", dataHolder.data[i][j]);
}
}
printf("\n");
}
int main() {
DataHolder<4,4> a;
print(a);
}
Ответ 7
Помимо использования массивов переменной длины в C99, вы не можете переносимо переносить функцию для приема многомерного массива, если размеры массивов не известны во время компиляции, см. Вопрос 6.19 C-FAQ. Лучший способ справиться с этим - имитировать многомерные массивы с использованием динамически распределенной памяти. Вопрос 6.16 очень хорошо объясняет детали этого.
Ответ 8
Короткий ответ, вы можете изменить программу следующим образом
void print(int arr[], int s1, int s2) {
...
printf("%d,", *(a+i + s2*j));
...
print((int*)a,4,4);
Это потребует лучшего ответа, объясняющего различия между указателем и арифметикой указателей и массивами в C и С++. Я не стану этим заниматься. Может быть, кто-то еще?
Я, очевидно, не шокирован тем же моментом, что и другие плакаты в вашем коде. Что беспокоит меня больше всего в заголовке функции печати, так это то, что вы используете двойную косвенность для массива, где вы не собираетесь менять исходный указатель назад (на самом деле это невозможно, поскольку оно является константой). @| V | lad answer исправить это, установив одно или два измерения в фиксированную константу, но затем передача s1 и s2 станет бесполезной.
Все зависит от того, что вы действительно хотите сделать. Печатает ли функцию печати массива общего назначения или специализированную для некоторых типов массивов?
Ответ 9
Первое, что нужно сделать, это получить правильные типы. Если правила С++ совпадают с C по отношению к типам массивов (я уверен, что они есть), то с учетом объявления
int a[4][4];
выражение a
имеет тип int [4][4]
, который неявно преобразован ( "распадается" ) в тип указателя int (*)[4]
(указатель на 4-элементный массив int) при передаче на print
, поэтому вы необходимо изменить print
на
void print(int (*arr)[4], int s1, int s2)
{
int i, j;
for(i = 0; i<s1; i++)
for(j = 0; j<s2; j++)
printf("%d, ", arr[i][j]);
}
Выражение arr[i]
неявно разыменовывается arr
, поэтому вам не нужно связываться с явным разыменованием.
Недостатком является то, что print
может обрабатывать только массивы Nx4 int; если вы хотите обрабатывать другие размеры массива, вам нужно будет использовать другой подход.
Одна вещь, которую вы можете сделать, - это не передавать массив, передавать адрес первого элемента и иметь print
вручную вычислять смещения так:
int main() {
int a[4][4] = {{0}};
print(&a[0][0],4,4); // note how a is being passed
}
void print(int *arr, int s1, int s2) // note that arr is a simple int *
{
int i, j;
for (i = 0; i < s1; i++)
for (j = 0; j < s2; j++)
printf("%d, ", arr[i * s2 + j]);
}
Ответ 10
Многомерные массивы являются непрерывными блоками памяти. Таким образом, вы можете сделать это следующим образом:
#include <stdio.h>
void pa(const int *a, int y, int x)
{
int i, j;
for (i=0;i<y;i++)
{
for (j=0;j<x;j++)
printf("%i", *(a+j+i*x));
printf("\n");
}
}
int main()
{
int a[4][3] = { {1,2,3},
{4,5,6},
{4,5,6},
{7,8,9} };
pa(a[0], 4, 3);
return 0;
}
Он также работает в С++;
Ответ 11
Я просто хочу показать версию ответа bobobobo на С++.
int numRows = 16 ;
int numCols = 5 ;
int **a ;
a = new int*[ numRows* sizeof(int*) ];
for( int row = 0 ; row < numRows ; row++ )
{
a[row] = new int[ numCols*sizeof(int) ];
}
Остальная часть кода такая же, как у bobobobo.
Ответ 12
#include<stdio.h>
void print(int (*arr)[4], int s1, int s2) {
int i, j;
for(i = 0; i<s1; i++)
for(j = 0; j<s2; j++)
printf("%d, ", arr[i][j]);
}
int main() {
int a[4][4] = {{6}};
print(a,4,4);
}
это скомпилирует редактирование: кто-то уже разместил это решение, мое плохое