如何将没有大小的二维数组传递给C

时间:2017-06-20 06:37:11

标签: c arrays function multidimensional-array

给我的问题具体说明函数采用整数2d数组和大小作为参数。那意味着, 1)我不允许在声明中定义数组的大小。 2)我必须将行数和列数传递给函数。

我以前为1D数组做过它并且它有效,但是在2D数组的情况下它会产生语法错误。我制作了测试代码来检查语法的逻辑。这是有效的一维数组逻辑。 此模块也要求我只使用C中的stido.h库。传递数组大小的原因是在不同的函数中填充数组,其中循环条件将使数组大小通过索引

#include<stdio.h>
void print(int table[],int r);
int main()
{   int r=7,table[r];
    print(table,r);
}

 void print(int table[],int r)
{  printf("table rows is%d\n",r);
} 

然而,当我尝试2D数组时,这个相同的逻辑不起作用。(给出语法错误)

#include<stdio.h>
void print(int table[][],int c,int r);
int main()
{int c=7,r=7,table[c][r];
    print(table,c,r);
}

void print(int table[][],int c,int r)
{printf("table rows is\ntable cols is\n",r,c)
}

有人可以解释为什么会这样,并建议解决这个问题。 ? 提前致谢

4 个答案:

答案 0 :(得分:4)

您无法获得具有未指定尺寸的2D数组。但是,您似乎有一个C99 +编译器,因此可以使用可变长度数组。但是,您还需要重新排序参数。

请注意,我在这里使用size_t而不是int作为索引/维度 - 因为可能有一个表(尽管不太可能),其中包含的元素数多于{{1 }}

int

它在C99中编译,(以及那些支持可选VLA扩展的C11编译器);并且在运行时打印

#include <stdio.h>
#include <stdlib.h>

void print(size_t, size_t, int [*][*]);

int main(void) {
    size_t c = 7, r = 9;
    int table[c][r];
    print(c, r, table);
}

void print(size_t c, size_t r, int table[c][r]) {
    printf("table rows is %zu\ntable cols is %zu\n", r, c);
}

答案 1 :(得分:3)

首先,你想知道为什么这是不可能的。语言标准不允许这样做,技术原因是当第二个维度未知时,编译器无法计算数组的偏移量。

2D数组就像一维数组一样,一个接一个地存储在内存中。与其他一些语言一样,数组。使用像int a[5][3]这样的2D数组,您连续5次int次,全部在15 int次。为了访问元素,编译器将为您计算偏移量,因此如果您编写例如a[2][2],这是第2*3 + 2个元素(第三行的开头有两行3列要跳过)。

如果编译器不知道第二个维度,则此计算的offets 不可能

Antti Haapala's answer已经展示了如何使用C99功能可变长度数组来解决这个问题。这是一个很好的方法,但不幸的是,对于C11,此功能是可选,因此可能有编译器不支持它。如果您确定只使用支持 VLA 的编译器(这是所有现代C编译器的绝大多数),请使用此答案中的代码。

当将数组传递给函数而不是数组时,会传递指向其第一个元素的指针(数组衰减作为指针)。所以以下声明是相同的:

void print(size_t n, int arr[]);
void print(size_t n, int *arr);

有了这些知识,你可能会想要用不同的方式写它并自己做偏移计算:

void print(size_t r, size_t c, void *data)
{
    int *arr = data;
    // access arr[2][2]:
    int v = arr[2*c + 2];
}

事实上,这很可能会奏效,但请注意,这是未定义的int[]int[][]不兼容。有关此想法的详细信息,请参阅this question,其中包含两个非常有趣的答案。底线:这种代码的概率非常高。不过,可以肯定的是,不要这样做......

所以,如果你没有可变长度数组,如果你不想依赖于没有完美定义的东西,唯一的另一种方法是实际上通过一个指针数组构建一个数组

int row1[3] = {0, 1, 2};
int row2[3] = {3, 4, 5};
int *rows[2] = { row1, row2 };

然后你可以这样传递:

void print(size_t r, size_t c, int **arr)
{
    int a = arr[0][2]; // 2
}

当然,这不再是 2D阵列,它是一种替代构造,可以以类似的方式使用。

答案 2 :(得分:1)

您无法为最终尺寸传递没有定义常量的2D数组。 (没有C99 VLA补充)。但是,您可以在print中更改参数的顺序,以便在将r声明为指向int [r] 数组的指针之前定义table,并传递table如下,例如

#include<stdio.h>

void print (int c, int r, int (*table)[r]);

int main (void) {

    int c = 7, r = 7, table[c][r];

    for (int i = 0; i < r; i++)
        for (int j = 0; j < c; j++)
            table[i][j] = i * j + i + j;

    print (c, r, table);

    return 0;
}

void print (int c, int r, int (*table)[r])
{
    for (int i = 0; i < r; i++) {
        for (int j = 0; j < c; j++)
            printf (" %3d", table[i][j]);
        putchar ('\n');
    }
}

此方法适用于使用带有C99 VLA扩展的VLA。

示例使用/输出

$ ./bin/fpass2d
   0   1   2   3   4   5   6
   1   3   5   7   9  11  13
   2   5   8  11  14  17  20
   3   7  11  15  19  23  27
   4   9  14  19  24  29  34
   5  11  17  23  29  35  41
   6  13  20  27  34  41  48

如果VLA扩展不可用,您可以通过分配然后手动将2D阵列索引为1D阵列来解决此问题。这是因为2D数组是按顺序存储在内存中的数组数组(例如a[2][3]在内存中是a00,a01,a02,a10,a11,a12。)为了防止任何未定义的行为,您可以为table分配存储空间(例如int *p = malloc (sizeof table);复制table,然后将每个调用print (p, c, r)的元素编入索引,如下所示,例如

void print (int *table, int c, int r)
{
    for (int i = 0; i < r; i++) {
        for (int j = 0; j < c; j++)
            printf (" %3d", table[i * r + j]);
        putchar ('\n');
    }
}

tabletable[i * r + j]编入索引以访问2D数组的每个原始元素。虽然这种方法是一种解决方法,但没有VLA扩展,您可能会为最终数组维度传递一个已定义的常量,从而使得变通方法或参数顺序的更改变得不必要。

仔细看看,如果您有疑问,请告诉我。

答案 3 :(得分:0)

首先,函数参数的类型衰减为指向数组第一个元素的指针。也就是说,以下声明对完全等效:

void f(int a[]);
void f(int *a);

void g(int a[][7]);
void g(int (*a)[7]);

void h(int a[][7][6]);
void h(int (*a)[7][6]);

现在,当您索引数组时,该操作实际上是根据指针算法定义的。因此,表达式a[3]实际上等同于*(a + 3)

在这个表达式中,a产生一个指向数组第一个元素的指针,并且相加会添加偏移量以跳过数组的三个元素。也就是说,机器将执行此计算:*(elementType*)((char*)a + 3*sizeof(elementType))

这方面的关键部分是sizeof(elementType):如果a的类型为int[][],则其元素的类型为int[],您无法接受由于显而易见的原因,未知长度的数组的大小。因此,必须知道除最外层维之外的所有数组维的类型,以便能够索引到多维数组。

对于正在发生的事情的解释非常重要。你的后果是,你必须提供内部尺寸的大小。正如其他答案所示,这可以通过首先简单地排序大小参数来完成,因此它们可用于数组参数的声明:

void print(int c, int r, int table[c][r]);

或者,等效地,因为c的值实际上在上面的声明中被忽略了:

void print(int rowCount, int columnCount, int (*table)[columnCount]) {
    printf("table has %d columns and %d rows\n", columnCount, rowCount);

    for(int curRow = 0; curRow < rowCount; curRow++) {
        for(int curColumn = 0; curColumn < columnCount; curColumn++) {
            printf("%d ", table[curRow][curColumn]);
        }
        printf("\n");
    }
}

(在最后一个例子中,我冒昧地使用了正确的格式和变量名。请不要只使用单个字符作为变量名称,这与数组维度大小一样重要。)