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

标签 c arrays

我需要将二维数组作为单个指针传递给函数。有不同类型的方法,但由于某些限制(代码生成),我只想传递一个指针。我有包含每个维度大小的宏。我实现了以下方式,但我不确定它是否也适用于 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]);
}

如果按照上面的做法,会不会有漏洞?

最佳答案

void fun(int (*arr)[3]);

或完全等效,但可能更具可读性:

void fun(int arr[][3]);

arr 是指向 3 行 3 列的二维数组的指针。 arr decayed to a pointer 具有指向 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,因为编译器选择不同的数据库来访问数据。

关于c - 将二维数组作为单个指针传递给函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54340643/

相关文章:

c++ - 当我在函数参数中使用指针时遇到错误

c - 以 ASCII 整理顺序打印字符串

c - 使用 C 中的函数和指针将 n 个数组合并为一个

c - 如何使用 fgets 获取第二个整数?

转换函数返回 void

c - 将 void* 指针转换为 char* 指针并对其进行指针算术运算是否有效?

javascript - 为什么这个 React 组件返回字符串 '0'

java - For 循环在线程内不能正常工作

c - 如何在 C 中使用 nanosleep()?什么是 `tim.tv_sec` 和 `tim.tv_nsec` ?

c - opengl 白色窗口 - Visual Studio 2010