void fun(int (*arr)[3]);
или точно такой же, но, возможно, более читабельный:
void fun(int arr[][3]);
arr
- указатель на двумерный массив с 3 строками и 3 столбцами. arr
распадающийся на указатель имеет тип указателя на массив из 3 элементов. Вам нужно передать указатель на массив из 3 элементов. Вы можете получить доступ к данным в обычном режиме, используя arr[a][b]
.
#define size_1D 3
#define size_2D 3
void fun(int arr[][3])
{
for(int i = 0; i < size_1D ; i++) {
for(int j = 0; j < size_2D ; j++) {
int value = arr[i][j];
}
}
}
int main()
{
int arr[size_1D][size_2D] = {{1,2,7},{8,4,9}};
fun(arr);
}
Вы можете указать размеры в качестве аргументов и использовать объявление массива переменной длины в списке параметров функции. Компилятор сделает некоторую работу за вас.
#include <stdlib.h>
void fun(size_t xmax, size_t ymax, int arr[xmax][ymax]);
// is equivalent to
void fun(size_t xmax, size_t ymax, int arr[][ymax]);
// is equivalent to
void fun(size_t xmax, size_t ymax, int (*arr)[ymax]);
void fun(size_t xmax, size_t ymax, int arr[xmax][ymax])
{
for(int i = 0; i < xmax ; i++) {
for(int j = 0; j < ymax ; j++) {
int value = arr[i][j];
}
}
}
int main()
{
int arr[3][4] = {{1,2,7},{8,4,9}};
fun(3, 4, arr);
}
@ редактировать
Мы знаем, что результат оператора индекса массива точно совпадает с оператором разыменования указателя суммы:
a[b] <=> *(a + b)
Из арифметики указателей мы знаем, что:
type *pnt;
int a;
pnt + a = (typeof(pnt))(void*)((uintptr_t)(void*)pnt + a * sizeof(*pnt))
pnt + a = (int*)(void*)((uintptr_t)(void*)pnt + a * sizeof(type))
И что массив равен значению указателя на первый элемент массива:
type pnt[A];
assert((uintptr_t)pnt == (uintptr_t)&pnt[0]);
assert((uintptr_t)pnt == (uintptr_t)&*(pnt + 0));
assert((uintptr_t)pnt == (uintptr_t)&*pnt);
Итак:
int arr[A][B];
, то:
arr[x][y]
эквивалентно (игнорировать предупреждения, вид псевдокода):
*(*(arr + x) + y)
*( *(int[A][B])( (uintptr_t)arr + x * sizeof(int[B]) ) + y )
// ---- x * sizeof(int[B]) = x * B * sizeof(int)
*( *(int[A][B])( (uintptr_t)arr + x * B * sizeof(int) ) + y )
// ---- C11 6.5.2.1p3
*( (int[B])( (uintptr_t)arr + x * B * sizeof(int) ) + y )
*(int[B])( (uintptr_t)( (uintptr_t)arr + x * B * sizeof(int) ) + y * sizeof(int) )
// ---- *(int[B])( ... ) = (int)dereference( ... ) = *(int*)( ... )
// ---- loose braces - conversion from size_t to uintptr_t should be safe
*(int*)( (uintptr_t)arr + x * B * sizeof(int) + y * sizeof(int) )
*(int*)( (uintptr_t)arr + ( x * B + y ) * sizeof(int) )
*(int*)( (uintptr_t)( &*arr ) + ( x * B + y ) * sizeof(int) )
// ---- (uintptr_t)arr = (uintptr_t)&arr[0][0]
*(int*)( (uintptr_t)( &*(*(arr + 0) + 0) ) + ( x * B + y ) * sizeof(int) )
*(int*)( (uintptr_t)( &arr[0][0] ) + ( x * B + y ) * sizeof(int) )
*(int*)( (uintptr_t)&arr[0][0] + ( x * B + y ) * sizeof(int) )
// ---- decayed typeof(&arr[0][0]) = int*
*( &arr[0][0] + ( x * B + y ) )
(&arr[0][0])[x * B + y]
Итак:
arr[x][y] == (&arr[0][0])[x * B + y]
arr[x][y] == (&arr[0][0])[x * sizeof(*arr)/sizeof(**arr) + y]
В нормальной архитектуре, где sizeof(uintptr_t)
== sizeof(size_t)
== sizeof(int*)
== sizeof(int**)
и т. Д., И нет разницы в доступе к данным за указателем int*
и к доступу к данным за int(*)[B]
указатель и т. д. Вы должны быть в безопасности при доступе к одномерному массиву при использовании указателя на первый член массива, поскольку операции должны быть эквивалентными («безопасными», за исключением доступа за пределами границ, это никогда не безопасно)
Обратите внимание, что это правильно неопределенное поведение в соответствии со стандартом C и не будет работать на всех архитектурах. Пример: может существовать архитектура, в которой данные типа int[A]
хранятся в другом банке памяти, а не int[A][B]
данных (аппаратно, по замыслу). Таким образом, тип указателя сообщает компилятору, какой банк данных выбрать, поэтому доступ к тем же данным с тем же указателем значения, но с другим типом указателя приводит к UB, так как компилятор выбирает другой банк данных для доступа к данным.