-2

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

将不胜感激详细的描述。

4

6 回答 6

3

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

当您编写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 }。

于 2013-06-18T14:31:05.967 回答
1

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

void foo(size_t width, int array[][width]);
于 2013-06-18T14:58:00.647 回答
0

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

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

然后你所要做的就是将 a 传递char **给你的函数。矩阵是连续的,可用作mat[x][y].

于 2013-06-18T14:34:47.593 回答
0

可能的访问器功能:

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);
于 2013-06-18T15:55:30.783 回答
-1

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

使用 2D 数组时,编译器需要知道数组中的列数才能计算索引。例如,如果您希望将指向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] = ...
    }
}
于 2013-06-18T14:40:11.527 回答
-1

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

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

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

于 2013-06-18T14:58:17.037 回答