将二维数组作为单个指针传递给函数

时间:2019-01-24 06:34:50

标签: c arrays

我需要将二维数组作为单个指针传递给函数。这里有不同类型的方法,但是由于某些限制(CodeGeneration),我只想传递单个指针。我有包含每个维度的大小的宏。我实现了以下方法,但我不确定它是否也适用于N个维度

#define size_1D 3
#define size_2D 3

void fun(int *arr)
{
   int i,total_size = size_1D* size_2D;
   for(i = 0; i < total_size ; i++)
   {
      int value = arr[i];
   }
}
int main()
{
    int arr[size_1D][size_2D] = {{1,2,7},{8,4,9}};
    fun(&arr[0][0]);
}

如果我遵循上述方法,会有漏洞吗?

1 个答案:

答案 0 :(得分:4)

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);
}

@edit

我们知道数组下标运算符的结果与总和的指针解引用运算符完全相同:

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**)等,访问{{ 1}}指针访问int*指针等后面的数据。使用指向第一个数组成员的指针时,访问一个一维数组应该是安全的,因为操作应等效(“安全”,但对于out-边界访问,这永远都不安全)

请注意,根据C标准,这是正确未定义的行为,并且不适用于所有体系结构。例如:可能有一个体系结构,其中int(*)[B]类型的数据与int[A]数据存储在不同的存储体中(通过硬件,通过设计)。因此,指针的类型告诉编译器要选择哪个数据库,因此,使用相同的值指针访问相同的数据,但使用不同的指针类型会导致UB,因为编译器选择了不同的数据库来访问数据。