【问题标题】:Passing 2D arrays in C在 C 中传递二维数组
【发布时间】:2011-04-20 08:39:09
【问题描述】:

我从 C 中中断了一段时间,现在又回到了它。

如果我想创建一个二维数组,我可以通过两种方式完成:

double** m_array = (double**) malloc(2*sizeof(double*));
double*  m_array = (double*)  malloc(2*sizeof(double));

double array[2][2];

但是,当我希望传递 malloc'd 数组而不是传递另一个时,似乎有两种约定:

//allowed for passing in malloc'd array, but not for other array
func_m(m_array) //allowed
func_m(array) //disallowed
func_m(double** m_array)

//allowed for passing in either array; required for passing in non-malloc'd array
func(m_array) //allowed
func(array) //allowed
func(double array[][2])

首先,除了它是指向指针数组的指针之外,我不需要任何信息。但它只能是一个 malloc 的数组。

其次,我需要传递 double* 数组指向的每个数组的长度。这似乎很愚蠢。

我错过了什么吗?提前致谢。

【问题讨论】:

  • 请让您的问题更具体。

标签: c pointers multidimensional-array argument-passing


【解决方案1】:

第一个根本不创建二维数组。它创建了一个指针数组,显然没有指向任何地方。如果您确实将每个指针初始化为一个数组,那仍然是一个数组数组,而不是二维数组。

为什么不直接创建一个二维数组?

double* dynamicArray = malloc(rows * columns * sizeof (double));

double autoArray[rows][columns];

然后你可以使用任何一个与这个功能:

void func(double* array, size_t rows, size_t columns);

【讨论】:

  • 我取消了我的复选标记,因为您的代码无法编译。 double* 数组变量确实需要一些东西来告诉它列有多长,以便编译器可以正确计算 array[row][col](对于 BOTH malloc 和 static 情况)。请参阅下面我尝试的答案。
  • @dougvk:这就是为什么我的示例显示同时传递行数和列数,这些都是为数组中的任何(i, j) 坐标计算正确偏移量所需的所有信息。
  • 我明白,但编译器不明白。另外,我在问为什么需要这样做(因为我希望在我的代码中跳过该要求)。
  • array of arrays 方法允许在不知道边界的情况下计算任何元素的地址,但我不明白这是一个优势。如果函数不知道边界,它可能会访问数组外的内存。您必须单独通过界限的原因是,在 C 语言中,您无需为不使用的东西付费。如果调用者和被调用函数有其他方式来约定大小,那么将大小存储在数组中将是一种浪费。当然,如果您希望它们成为一个单元,您可以创建一个结构来保存指针和边界并将它们作为一个单元传递。
  • 另外,array-of-arrays 会更慢。内存解引用比偏移计算慢,而且数组数组的内存局部性也差很多。
【解决方案2】:

为澄清起见,假设您声明:

int my_static_array[row][col];

pmg 的答案中 my_static_array 与 malloc 数组(我们称之为 my_malloc 数组)的类型是什么? my_static_array 是内存中连续的 row*col*sizeof(int) 字节数,带有指向其开头的 int* 指针。在计算 my_static_array[x][y] 时,编译器知道该怎么做(因为它们存储在 row-major order 中):

*(addr(my_static_array) + [sizeof(int)*col*x] + [sizeof(int)*y])

这就是为什么我需要传入 C 的 col 值:

(1) func(int my_static_array[][col])
(2) func(int my_static_array[][])

在 (1) 中,编译器知道如何正确计算静态数组的地址。在(2)中,编译器没有足够的信息。这就是为什么 (1) 可以编译,而 (2) 不会。

如果是 malloc 数组,则可以通过:

func(int** my_malloc_array)

因为它是一个指向一维数组的连续数组的指针,编译器不需要帮助。 my_malloc_array[x][y] 很简单:

*(*(addr(my_malloc_array)+x)+y)

编译器不需要关于其维度的其他信息来计算。

那么,教训是什么?二维数组与指向一维数组的指针数组相同。前者的类型为 int (*my_static_array)[row*col] -- 指向 row*col 元素数组的指针,后者的类型为 int** -- 指向 数组的指针行指针。

【讨论】:

    【解决方案3】:

    您对二维数组的分配是错误的。通常的2D方式是

    double **m_array = malloc(NROWS * sizeof *m_array);
    for (i = 0; i < NROWS; i++) {
      m_array[i] = malloc(NCOLS * sizeof *m_array[i]);
    }
    

    将数组或指针(任何维度)传递给函数时,您必须让函数知道维度:它们不会在数组或指针本身中编码。

    一方面,您有指向某些类型的指针(指向指针(指向指针 (...))),另一方面,您有某种类型的数组(数组(数组 (...)))。

    当您将指针传递给函数时,这正是发生的情况。当您“传递一个数组”时,会发生什么是数组衰减为指向其第一个元素的指针。所以

    foo(array); // is the same as foo(&(array[0]));
    

    array 是一个多维数组时,衰减指针的类型是pointer to array[size] of SOMETHING(但你不能使用那个大小!)。

    简单的方法是用一个简单的指针编写函数并传递所有维度的乘法

    double array[3][4][5];
    foo(&(array[0][0][0]), 3*4*5);
    

    【讨论】:

    • 是的,这仍然无法避免不必通过维度。为什么 C 需要这样做?
    • 这仍然不是一个二维数组,它是一个(可能是锯齿状的)数组。
    【解决方案4】:

    假设计算机内存是一个装满按顺序编号的盒子的仓库。当你这样做时

    int matrix[10][3];
    

    您保留 30 个框用于保存矩阵值(假设框 131 到 160 包括在内)。

    现在,假设您想在一个函数中对矩阵中的所有值求和:

    int matrix_sum(int matrix[10][3]) { /* ... */ }
    

    但所有函数接收(由标准强制)是指向矩阵第一个元素的指针:指向 3 ints 数组的指针。所以该函数知道框 131、132 和 133 属于矩阵,但它不知道矩阵的实际长度。

    【讨论】:

      【解决方案5】:

      使用简单的 typedef:

      typedef double Twodouble_t[2];
      
      void func(Twodouble_t *ptr)
      {
        size_t i, numelements = sizeof*ptr/sizeof**ptr;
        puts("first array");
        for( i=0; i<numelements; ++i )
          printf("\n%f",ptr[0][i]);
      }
      ...
      Twodouble_t array[]={ {1., 1.},{2., 2.},{3.,3.} };
      func(array);
      ...
      

      【讨论】:

        猜你喜欢
        • 2013-01-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-15
        • 2015-09-19
        • 2017-03-27
        相关资源
        最近更新 更多