【问题标题】:how to pass down multidimensional pointers/arrays to recursive function如何将多维指针/数组传递给递归函数
【发布时间】:2018-07-10 02:11:25
【问题描述】:

我想编写一个函数,它接受一个包含 'n' 个元素的 'm' 个维度的数组,以及一个指向 other-trivial-function 的指针。该函数将在每个维度上调用自身,遍历其元素并在每次提到的指针指向的函数时调用(微不足道的)。 问题是我找不到一种方法来定义一个函数足够通用 来接受任何维度的数组,或者一个指向任何维度数组的指针。基本上是因为每次函数调用自己时都会摆脱一维,这与函数原型和定义不兼容。 我编写了一个函数,它接受一个单维指针,并根据其他两个参数(即元素的总数和每个维度的大小)将其威胁为多维。

int main(int argc, char **argv)
{
    void goesThrough (int *, int *, size_t, void (*) (int));
    void print (int);

    int array[] = {1, 2, 3, 4, 5, 6};
    int (* ptrToArray) = array;

/*
 * Arguments are a pointer to an array, an array of sizes for each 
 * dimension, the amount of elements, and a pointer to whatever 
 * function.
 */
    goesThrough (ptrToArray, (int []) {2, 3}, 6, print);
    return 0;
}

void goesThrough (int * anArray, int * dimSize, size_t quantity,
                  void (* aFunction) (int))
{
    size_t index;

    for (index = 0; index < (* dimSize); ++index)
    {
        if (quantity / (* dimSize) > 1)
        {
            goesThrough (anArray + (index * (quantity /(*dimSize))),
                         dimSize + 1, quantity / (* dimSize), aFunction);
        }
        else
        {
            aFunction (anArray[index]);
        }
    }
}

void print (int aValue)
{
    printf ("%d ", aValue);
}

但是,我发现这种方法有两个缺点。感觉就像 如果出现问题,编译器将无济于事;并且要读取/写入这些 数组 中的单个成员,我也需要编写自己的函数。

问题1:如何使用多维数组来写这个?

问题2:在这种情况下,递归是否比循环更好?如果没有,我会很感激一个使用循环的例子,(无论如何我都会这样做);)。为了更好,我的意思是认真的程序员会选择它的任何原因。

编辑:我最初要求数组/指针Q1),但我的意思是 数组/指向数组的指针

注意:请注意,这里的问题是传递与函数期望的维数不同的维数数组。在这方面,非常欢迎对标题进行编辑或提出建议。

【问题讨论】:

  • void goesThrough(void *p, ...) 将接受p 的任何对象指针。
  • @chux - 是的,但这就像砌砖砌房子一样。我不想编写自己的多维数组实现,我宁愿使用已经存在的那个。正如我上面所说,访问这些数组的单个成员需要一个新函数。

标签: c pointers recursion multidimensional-array


【解决方案1】:

一个单维指针并威胁它为多维

这完全没问题,因为这基本上就是 c 所做的。 array_1 和array_2 在内存中的布局相同,只是解释问题:

int array_1[60] = {
    111, 112, 113, 114, 115,
    121, 122, 123, 124, 125,
    131, 132, 133, 134, 135,
    141, 142, 143, 144, 145,

    211, 212, 213, 214, 215,
    221, 222, 223, 224, 225,
    231, 232, 233, 234, 235,
    241, 242, 243, 244, 245,

    311, 312, 313, 314, 315,
    321, 322, 323, 324, 325,
    331, 332, 333, 334, 335,
    341, 342, 343, 344, 345
};

int array_2[3][4][5] = {
    {{111, 112, 113, 114, 115},
     {121, 122, 123, 124, 125},
     {131, 132, 133, 134, 135},
     {141, 142, 143, 144, 145}},

    {{211, 212, 213, 214, 215},
     {221, 222, 223, 224, 225},
     {231, 232, 233, 234, 235},
     {241, 242, 243, 244, 245}},

    {{311, 312, 313, 314, 315},
     {321, 322, 323, 324, 325},
     {331, 332, 333, 334, 335},
     {341, 342, 343, 344, 345}}
};

但在后一种情况下,您总是需要在编译时知道大小和尺寸。

第一季度:

因此,您的代码工作正常,尽管我建议声明 int subQuantity = quantity / *dimSize; 之类的内容并使用 subQuantity 以提高可读性。

第二季度:

知道你只是将函数应用于每个元素,这可以更容易完成:

void for_each_how_you_would_probably_do_it(int *arr, size_t elementCnt, void (*fn)(int)){
    int i;
    for(i = 0; i < elementCnt; ++i){
        fn(arr[i]);
    }
}

void for_each_beacause_its_c(int *arr, size_t elementCnt, void (*fn)(int)){
    while(elementCnt--){
        fn(*arr++);
    }
}

在我看来,您应该尝试编写迭代算法,递归算法很难理解。另外,不知道编译器是否可以进行尾递归消除。


函数每次调用自己都会去掉一维,这与函数原型和定义不兼容

这就是递归的意义所在,减少问题直到它变得微不足道。

减少

在这种情况下意味着将自身应用于较小的维度。

琐碎

表示,只剩下一个维度。


要读取/写入这些数组中的单个成员,我也需要编写自己的函数

可以通过以下代码获取对应的指针:

/* 
 * arr is the array
 * idx is an array of indice for each dimension (0-based like everything in c)
 * dimSisez are the sizes of each dimension
 * dim is the dimension
 */
int *get_p(int *arr, int *idx, int *dimSizes, int dim){
    int pos = idx[0];
    int i;
    for(i = 1; i < dim; ++i){
        pos *= dimSizes[i];
        pos += idx[i];
    }
    return arr + pos;   /* &arr[pos] */
}

这与使用&amp;arr[idx[0]][idx[1]]...[idx[dim - 1]] 相同,但同样,在c 中使用多维语法时,您需要在编译时知道大小。 顺便说一下,从不使用 dimSizes[0],这就是在将多维数组作为函数的参数时可以省略第一个大小的原因。


最后一件事: 您在块的开头声明了所有变量,就像在 ansi 标准中一样,但是在 ansi 中不允许将 (int []) {2, 3} 作为参数值。

【讨论】:

  • 我没有在变量或索引上使用 ++/-- 的原因(如在您的 Q2 中)是因为它在执行相同操作时不起作用维度不止一次。例如在ar[2][3] 中,您将使用++ 操作调用f 一次,然后读取ar[0] 并返回给调用者,然后迭代读取ar[1] 并再次调用,但到那时您的索引/变量是由于前一次调用,已经递增/递减(除非函数退出时相反递减/递增)。这就是我没有使用它们的原因。
  • 至于 ANSI:我不太了解标准,但我可以向您保证,我不会对使用 gcc -std=c11 -pedantic -Wall -o "%e" "%f" 的文字 (int []) {2, 3} 提出任何抱怨。顺便说一句,我只用它们来测试功能。
  • 现在Q1:假设我不想编写自己的递归兼容多维数组实现(就像我已经做过的那样), 有没有办法通过使用标准中定义的多维数组来定义传递任意维数数组的递归函数?
  • 现在Q2:我知道与循环相比,通常甚至不考虑递归,但这就是我的观点。 Q2 中的要求以及您已经回答的其他问题是一种使用循环实现此目的的方法,无论它是否存在。我还得看看你的* get_p。感谢您的回答!
  • 一些小问题-- 1) 递归算法应该易于理解,这就是重点;但是,C 确实保证尾调用优化(尽管一些编译器可能会这样做)。 2)“在c中使用多维语法时,您需要在编译时知道大小”:C自C99以来就有VLA(在C11中是可选的),其数组大小在运行时确定......
【解决方案2】:

在 C99 及更高版本中,您可以使用可变长度数组来传递多维数组:

void foo(int n, int m, int a[n][m]);

或使用指向数组的指针:

void bar(int n, int m, int (*a)[n][m]);

因为指针可能被取消引用到第一行/列,你甚至可以像这样传递它:

void bar2(int n, int m, int (*a)[m]);

仍然像a[i][j]一样访问它。

请注意,nm 在函数声明中声明在数组本身之前。

此外,您需要在每次递归调用时创建一个新数组并将数据复制到其中:

void bar(int n, int m, int a[n][m]) {
    if ((n > 1) && (m > 1)) {
        int b[n-1][m-1];
        for (int i = 0; i < n-1; i++) {
            for (int j = 0; j < m-1; j++) {
                b[i][j] = a[i][j];
            }
        }
    }
}

这是因为更改m 需要更改内存中每一行的大小。 数组自动分配的幼稚方法也将其推入堆栈。所以你可能想做动态分配:

int (*b)[m] = malloc(sizeof(int) * (n) * (m));

使用或不使用递归是一个棘手的问题。使用递归时,请确保递归的高度是有限的。如果是尾递归编译器可能会序列化它(但这不能保证)。如果您有多个递归调用(将数组的四分之一传递给新的递归调用,如四叉树),则可能需要实现一些自定义堆栈/队列。

【讨论】:

  • 恐怕这不是我问的原因。如果我理解(我可能不会,不是开玩笑),你可以创建一个 2D 循环来遍历一个 2D 数组。我需要的是递归函数,因为如果我有一个 25D 数组,我不想写 25 次相同的 for,并且它是否也足够通用以获取任何维度的数组,而不是固定的他们的数量。每个维度的大小都不是问题,因为我会很好地使用循环。谢谢!
【解决方案3】:

@Gabriel,这看起来是一个有趣的问题,但我只提出了部分答案并制作了这个 wiki。 *** 可能还需要工作。祝你好运。

回想一下,维度可能是 1,因此 quantity / (* dimSize) &gt; 1 不足以作为测试。

这使用level: 索引到dimSize 而不是quantity

void goesThrough(void * anArray, size_t * dimSize, size_t level, void (*aFunction)(int)) {
  if (level > 1) {
    for (size_t index = 0; index < *dimSize; index++) {
      void *(*a)[*dimSize];  // ***
      a = anArray;
      goesThrough(a[index], dimSize + 1, level - 1, aFunction);
    }
  } else if (level > 0) {
    int *a = anArray;
    for (size_t index = 0; index < *dimSize;index++) {
      aFunction(a[index]);
    }
  }
}

【讨论】:

  • 在我的函数中,* dimSize 始终是数量的一个因素。这意味着在对goesThroughquantity / (*dimSize) == 1 的最后一次调用中,实际上标识了这两者,数组在此阶段的维数和因子index 应该与每次迭代相乘。基本上,只要quantity / (*dimSize) &gt; 1falsequantity / (*dimSize) == 1 就是true。我还在看你的代码,谢谢你的回答!
  • 很抱歉,我对您的代码有点困惑。 void *(*a)[*dimSize] 是指向数组的指针吗?你需要它做什么? (是否应该在“a”之后声明?)。在 if 的“真实”部分,当调用 goesThrough 时,a[index] 是索引位置中数组的值。你是这个意思吗。。再次感谢!
  • @Gabriel void *(*a)[*dimSize]pointer to array (*dimSize) of pointer to void。我使用a,分配了anArray,来简化goesThrough(a[index]...的偏移量计算,但这是我没有信心的部分。等我有空的时候再考虑一下。
猜你喜欢
  • 1970-01-01
  • 2017-04-18
  • 2012-02-22
  • 1970-01-01
  • 1970-01-01
  • 2012-09-22
  • 2016-09-02
  • 1970-01-01
  • 2010-10-02
相关资源
最近更新 更多