Правильный способ передачи 2-мерного массива в функцию
У меня есть двумерный массив, и я передаю его в функцию для выполнения определенных операций. Я хотел бы знать правильный способ сделать это...
#define numRows 3
#define numCols 7
#define TotalNum (numRows*numCols)
int arr[numRows][numCols] = {{0,1,2,3,4,5,6}, {7,8,9,10,11,12,13},{14,15,16,17,18,19,20}};
void display(int **p)
{
printf("\n");
for (int i = 0; i< numRows;i++)
{
for ( int j = 0;j< numCols;j++)
{
printf("%i\t",p[i][j]);
}
printf("\n");
}
}
int main() {
display(arr);
}
Появляется сообщение об ошибке:
'display': cannot convert parameter1 from 'int' to 'int*'
Является ли это правильным способом передачи двумерного массива в функцию? Если нет, то каков правильный путь?
Ответы
Ответ 1
Вы должны объявить свою функцию следующим образом:
void display(int p[][numCols])
Этот C FAQ полностью объясняет, почему. Суть его в том, что массивы распадаются на указатели один раз, это не происходит рекурсивно. Массив массивов распадается на указатель на массив, а не на указатель на указатель.
Ответ 2
Если (как в вашем случае), вы знаете размеры массива во время компиляции, вы можете написать только void display(int p[][numCols])
.
Некоторое объяснение: вы, вероятно, знаете, что когда вы передаете массив функции, вы фактически передаете указатель на первый член. На языке C 2D-массив представляет собой массив массивов. Из-за этого вы должны передать функцию указателю на первый под-массив в 2D-массиве. Таким образом, естественным путем является int (*p)[numCols]
(это означает, что p является указателем на массив из numCols
ints). В объявлении функции у вас есть "shortcut" p[]
, что означает точно такую же вещь, как (*p)
(Но говорит читателю, что вы передаете указатель на начало массива, а не только на одну переменную)
Ответ 3
Вы делаете неправильно. Вы можете передать 2-й массив с помощью указателя на массив или просто передать массив или через одиночный указатель.
#define numRows 3
#define numCols 7
void display(int (*p)[numcols],int numRows,int numCols)//First method//
void display(int *p,int numRows,int numCols) //Second Method//
void display(int numRows,int numCols,int p[][numCols]) //Third Method
{
printf("\n");
for (int i = 0; i < numRows;i++)
{
for ( int j = 0; j < numCols;j++)
{
printf("%i\t",p[i][j]);
}
printf("\n");
}
}
int main() {
display(arr,numRows,numCols);
}
Ответ 4
Есть несколько, иногда эквивалентных способов сделать это. Объявляя массив (cf. method_c()
), используя указатель (cf. method_b()
) или используя указатель на массив массива (cf. method_a()
). method_b()
, используя один указатель, немного сложнее получить право, поскольку нелегко использовать стандартную индексацию массива и, следовательно, мы используем арифметику указателя. method_a()
и method_c()
в основном эквивалентны, поскольку массивы не рекурсивно распадаются на указатели во время компиляции. Вот небольшая программа, иллюстрирующая все три метода. Сначала мы инициализируем 2x4
-array arr
в простом цикле и печатаем его. Он будет выглядеть следующим образом:
arr:
0 1 2 3
0 1 2 3
Затем мы вызываем все три метода. method_a()
добавляет 1, method_b()
добавляет 2 и method_c()
добавляет 3 ко всем элементам. После каждого вызова мы снова выводим массив arr
. Если функция работает правильно, вы легко увидите ее на выходе. Размер произвольный и может регулироваться с помощью двух макросов ROW
и COL
. В последнем примечании method_c()
используется массив переменной длины, присутствующий с C99
.
#include <stdio.h>
#include <stdlib.h>
#define ROW 2
#define COL 4
void method_a(int m, int n, int (*ptr_arr)[n]);
void method_b(int m, int n, int *ptr_arr);
void method_c(int m, int n, int arr[][n]);
int main(int argc, char *argv[]) {
int arr[ROW][COL];
int i;
int j;
for(i = 0; i < ROW; i++) {
for(j = 0; j < COL; j++) {
arr[i][j] = j;
}
}
printf("Original array:\n");
for (i = 0; i < ROW; i++) {
for(j = 0; j < COL; j++) {
printf("%d\t", arr[i][j]);
}
printf("\n");
}
printf("\n\n");
method_a(ROW, COL, arr);
printf("method_a() array:\n");
for (i = 0; i < ROW; i++) {
for(j = 0; j < COL; j++) {
printf("%d\t", arr[i][j]);
}
printf("\n");
}
printf("\n\n");
printf("method_b() array:\n");
method_b(ROW, COL, (int *)arr);
for (i = 0; i < ROW; i++) {
for(j = 0; j < COL; j++) {
printf("%d\t", arr[i][j]);
}
printf("\n");
}
printf("\n\n");
method_c(ROW, COL, arr);
printf("method_c() array:\n");
for (i = 0; i < ROW; i++) {
for(j = 0; j < COL; j++) {
printf("%d\t", arr[i][j]);
}
printf("\n");
}
printf("\n\n");
return EXIT_SUCCESS;
}
void method_a(int m, int n, int (*ptr_arr)[n])
{
int i, j;
for (i = 0; i < m; i++)
{
for (j = 0; j < n; j++)
{
ptr_arr[i][j] = j + 1;
}
}
}
void method_b(int m, int n, int *ptr_arr)
{
int i, j;
for (i = 0; i < m; i++)
{
for (j = 0; j < n; j++)
{
/* We need to use pointer arithmetic when indexing. */
*((ptr_arr + i * n) + j) = j + 2;
}
}
/* The whole function could have also been defined a bit different by taking
* the i index out of the pointer arithmetic. n alone will then provide our
* correct offset to the right. This may be a bit easier to understand. Our
* for-loop would then look like this:
* for (i = 0; i < m; i++)
* {
* for (j = 0; j < n; j++)
* {
* *((ptr_arr + n) + j) = j + 2;
* }
* ptr_arr++;
* }*/
}
void method_c(int m, int n, int arr[][n])
{
int i, j;
for (i = 0; i < m; i++)
{
for (j = 0; j < n; j++)
{
arr[i][j] = j + 3;
}
}
}
Ответ 5
Объявите это просто
void display(int (*p)[numCols][numRows]);
Таким образом, ваш указатель p
передает всю необходимую информацию, и вы можете извлечь из него все измерения без повторения numCols
и numRows
снова и снова.
void display(int (*p)[numCols][numRows])
{
size_t i, j;
printf("sizeof array=%zu\n", sizeof *p);
printf("sizeof array[]=%zu\n", sizeof **p);
printf("sizeof array[][]=%zu\n", sizeof ***p);
size_t dim_y = sizeof *p / sizeof **p;
printf("dim_y = %zu\n", dim_y);
size_t dim_x = sizeof **p / sizeof ***p;
printf("dim_x = %zu\n", dim_x);
for(i=0; i<dim_y; i++) {
puts("");
for(j=0; j<dim_x; j++)
printf(" %6d", (*p)[i][j]);
}
}
Это особенно интересно, если вы используете typedefs (который мне не нравится btw)
typedef int matrix[5][6];
В этом случае размеры не видны в сигнатуре функции, но функция все равно будет иметь правильные значения для размеров.
Ответ 6
Вы можете изменить подпись метода отображения следующим образом:
void display(int (*p)[numCols])
Здесь p
является указателем на строку двумерного массива. Указателю нужно знать только количество столбцов в массиве.
На самом деле указатель должен знать размер каждой строки. Это очень важно для арифметики указателя. Чтобы при указании указателя указатель указывал на следующую строку.
Обратите внимание, что p
не является нормальным целым указателем. Это целочисленный указатель на размер памяти, равный integer_size x columns
.
В основном вам ничего не нужно менять. display(arr)
просто отлично.
Ответ 7
Для большинства ответов выше вам нужно знать, по крайней мере, нет. столбцов в 2-мерном массиве. И даже если вы пройдете "нет". столбцов в самой функции или объявить no. столбцов в глобальном масштабе, некоторые компиляторы могут показать некоторые ошибки, и это может не сработать.
Таким образом, вы можете вызвать функцию, например
func((int **)a,r,c);
где a - это 2-мерный массив, а r и c - нет. строк и столбцов соответственно.
Вы можете поймать эти данные в функции как
void func(int **a,int r,int c);
И вы можете пересечь его с помощью указателей, где *((a+r*i)+j)
даст вам значение a[i][j]
.