【问题标题】:How do I write functions which accept two-dimensional arrays when the width is not known at compile time?当在编译时宽度未知时,如何编写接受二维数组的函数?
【发布时间】:2013-06-18 14:26:16
【问题描述】:

在编译时宽度未知的情况下,是否可以编写一个接受二维数组的函数?

详细的描述将不胜感激。

【问题讨论】:

  • 传递一个指向函数的指针?
  • 是的,但您必须手动完成所有数学运算。

标签: c arrays function pointers


【解决方案1】:

您不能传递原始二维数组,因为例程不知道如何索引特定元素。二维数组实际上是一个连续的内存段。

当您编写x[a][b] 时(当x 是二维数组时),编译器知道查看地址(x + a * width + b)。如果你不告诉它宽度,它就无法知道如何处理特定元素。

例如,检查http://www.dfstermole.net/OAC/harray2.html#offset(其中有一个表格显示如何找到 int[5][4] 中每个元素的线性索引)

有两种方法可以解决这个限制:

1) 让您的程序使用指针指向指针 (char *)。这与 char[][] 不同。 char * 实际上是一个内存段,每个值都是另一个内存段的内存地址。

2) 传递一个一维指针,然后自己进行引用。然后,您的函数必须采用“宽度”参数,并且您可以使用上述公式来引用特定点

举个代码例子:

#include <stdio.h>
int get2(int *x) { return x[2]; }
int main() {
    int y[2][2] = {{11,12},{21,22}};
    printf("%d\n", get2((int *)y));
}

这应该打印出 21,因为 y 在内存中布局为 { 11, 12, 21, 22 }。

【讨论】:

  • 听说能过但不知道怎么过?
  • @VarunChhangani 您将其作为一维指针(int* 或其他)传递
  • @VarunChhangani 我添加了一个例子
  • 但是就像凯文说的那样,你必须在接收数组的函数中手动进行数学运算。
  • @Medinoc 是正确的,但我回答后提交了凯文的评论
【解决方案2】:

C 支持变长数组。您必须根据运行时已知的值指定宽度,这可能是函数声明中的较早参数:

void foo(size_t width, int array[][width]);
【解决方案3】:

一种方法是使用古老的“指向数组指针数组的指针”技巧以及单个连续分配:

/* Another allocation function
   --------------------------- */
double ** AnotherAlloc2DTable(
 size_t size1, /*[in] Nb of lines */
 size_t size2  /*[in] Nb of values per line */
 )
{
    double ** ppValues;
    size_t const size1x2 = size1*size2;
    if(size1x2 / size2 != size1)
        return NULL; /*size overflow*/

    ppValues = malloc(sizeof(*ppValues)*size1);
    if(ppValues != NULL)
    {
        double * pValues = malloc(sizeof(*pValues)*size1x2);
        if(pValues != NULL)
        {
            size_t i;
            /* Assign all pointers */
            for(i=0 ; i<size1 ; ++i)
                ppValues[i] = pValues + (i*size2);
        }
        else
        {
            /* Second allocation failed, free the first one */
            free(ppValues), ppValues=NULL;
        }
    }/*if*/
    return ppValues;
}

/* Another destruction function
   ---------------------------- */
void AnotherFree2DTable(double **ppValues)
{
    if(ppValues != NULL)
    {
        free(ppValues[0]);
        free(ppValues);
    }
}

那么您所要做的就是将char ** 传递给您的函数。该矩阵是连续的,可用作mat[x][y]

【讨论】:

  • 你不必释放每个i (i = 0 to n) 而不是free(ppValeurs[0]);
  • 没有这个特殊的:每个维度只执行一次分配,这意味着分配而不是sizeX+1分配。这使得创建过程中的错误处理变得更加容易。
  • 就像 Ram Rajamony 说的那样,“好的。如果您要对帖子投反对票,请考虑添加评论以说明您这样做的原因。”
【解决方案4】:

可能的访问器函数:

int get_multi(int rows, int cols, int matrix[][cols], int i, int j)
{
    return matrix[i][j];
}

int get_flat(int rows, int cols, int matrix[], int i, int j)
{
    return matrix[i * cols + j];
}

int get_ptr(int rows, int cols, int *matrix[], int i, int j)
{
    return matrix[i][j];
}

一个真实的多维数组和一个假的:

int m_multi[5][7];
int m_flat[5 * 7];

使用访问器函数的定义明确的方法:

get_multi(5, 7, m_multi, 4, 2);

get_flat(5, 7, m_flat, 4, 2);

{
    int *m_ptr[5];

    for(int i = 0; i < 5; ++i)
        m_ptr[i] = m_multi[i];

    get_ptr(5, 7, m_ptr, 4, 2);
}

{
    int *m_ptr[5];

    for(int i = 0; i < 5; ++i)
        m_ptr[i] = &m_flat[i * 7];

    get_ptr(5, 7, m_ptr, 4, 2);
}

在实践中有效的技术上未定义的用法:

get(5, 7, (int *)m_multi, 4, 2);

【讨论】:

    【解决方案5】:

    [警告 - 这个答案解决了列数 - WIDTH - 已知的情况]

    使用二维数组时,编译器需要知道数组中的列数才能计算索引。例如,如果您希望将指向内存范围的指针p 视为二维值集,则编译器无法进行必要的索引运算,除非它知道每行占用多少空间数组。

    通过一个具体的例子,事情变得更清楚了,比如下面的例子。这里,指针p 作为指向一维内存范围的指针传入。您(程序员)知道将其视为 2D 数组是有意义的,并且您还知道(必须知道)该数组中有多少列。有了这些知识,您就可以编写代码来创建 q,编译器会将其视为具有未知行数的二维数组,其中每一行正好有 NB 列。

    当我希望编译器完成所有索引运算时,我通常会使用它(编译器可以做到的时候为什么要手动呢?)。过去,我发现这种构造对于执行从一种形状到另一种形状的 2D 转置很有用 - 请注意,尽管将 MxN 数组转置为 NxM 数组的广义 2D 转置相当糟糕。

    void
    WorkAs2D (double *p)
    {
        double (*q)[NB] = (double (*)[NB]) p;
    
        for (uint32_t i = 0; i < NB; i++)
        {
            for (uint32_t j = 0; j < ZZZ; j++) /* For as many rows as you have in your 2D array */
                q[j][i] = ...
        }
    }
    

    【讨论】:

    • 好的。如果您要对帖子投反对票,请考虑添加评论以说明您这样做的原因。
    • 好的。如果您对帖子投反对票,请考虑添加评论以说明您这样做的原因。这个答案恰好解决了@varun-chhangani 想要的。
    • (不是downvoter)不,它没有,它提供了一个未知高度的数组,而不是一个未知宽度的数组。复杂的是后者。
    • 啊。好的@Medinoc。 [正如许多人指出的那样] 您不能使用任何编译器帮助来处理列数未知的情况。我正在考虑留下这个答案,以防它帮助那些不知道如何为已知列数(宽度)的情况声明指针的人。
    【解决方案6】:

    我相信一个不错的解决方案是使用结构。 所以我有一个一维数组的例子:
    结构体定义:

    struct ArrayNumber {
        unsigned char *array;
        int size;
    };
    

    函数定义:

    struct ArrayNumber calcMultiply(struct ArrayNumber nra, struct ArrayNumber nrb);
    

    初始化结构:

    struct ArrayNumber rs;
    rs.array = malloc(1);
    rs.array[0] = 0;
    rs.size = 1;
    //and adding some size:
    rs.size++;
    rs.array = realloc(rs.array, rs.size);
    

    希望这可以成为您的解决方案。只需更改为二维数组即可。

    【讨论】:

      猜你喜欢
      • 2012-01-07
      • 1970-01-01
      • 1970-01-01
      • 2014-12-13
      • 2015-11-15
      • 1970-01-01
      • 1970-01-01
      • 2016-03-18
      • 2015-08-13
      相关资源
      最近更新 更多