在没有VLA的情况下传递可变大小的多维数组

时间:2016-01-01 20:59:55

标签: c arrays multidimensional-array

(这是一个跟进to this question。)

我试图了解"最佳做法" (或者实际上是任何实践)用于将多维数组传递给C中的函数。当然这取决于应用程序,因此我们考虑编写一个函数来打印可变大小的二维数组。特别是,我对如何在以下代码中编写函数printArry(_____)感兴趣。

上面引用的问题中的答案使用C98标准中C These are not present的可变长度数组(VLA)功能,并且在C11标准中是可选的(它们似乎也不会在C中编译) ++ 11要么)。

如果不使用VLA功能,如何回答这个问题?传递一个二维数组似乎是一个相当基本的任务,所以我无法想象没有合理的方法。

void printArry(_____){
    /* what goes here? */
}

int main(void){

    int a1=5;
    int b1=6;
    int a2=7;
    int b2=8;

    int arry1[a1][b1];
    int arry2[a2][b2];

    /* set values in arrays */

    printArry(arry1, a1, b1);
    printArry(arry2, a2, b2);
    return 0;
}

3 个答案:

答案 0 :(得分:2)

您不需要VLA来传递VLA作为参数。实际上你必须知道数组和函数既不能传递给函数也不能从函数返回(除非你使用结构hack,但在我们的例子中这不起作用)。

但是,您可以使用VM类型将指针传递给您的阵列。像这样:

void printArry(size_t a1, size_t b1, int (*parry)[a1][b1]){
/* accessing array 'array' by '*parry' */
}

在声明定义数组维度的参数之后,必须声明指向VLA数组参数的指针,原因显而易见('a1'和'b1'必须在使用时定义)。

使用此功能的代码如下所示:

int main(int argc, char** argv){

size_t a1=5;
size_t b1=6;
size_t a2=7;
size_t b2=8;

int arry1[a1][b1];
int arry2[a2][b2];

/* set values in arrays */

printArry(a1, b1, &arry1);
printArry(a2, b2, &arry2);

}

我们不能首先使用结构hack,因为结构不能有VLA成员,其次因为即使它们可以在传递参数时也无法指定它们的长度(成员类型必须在声明的时间,我们不能在函数声明中声明结构。)

确认 - 即使普通(非VM)阵列也必须使用此版本的'printArry'(假设它们的尺寸大小已正确传递给该函数)。至少这是C11标准在它的例子中说到它。

所有指向尺寸相同的数组的指针都可以相互分配(无论是否为VM),而无需调用UB。

以下是最新C11标准的示例:

  

示例3以下声明演示了兼容性   可变修改类型的规则。

extern int n;

extern int m;

void fcompat(void)
{
    int a[n][6][m];

    int (*p)[4][n+1];

    int c[n][n][6][m];

    int (*r)[n][n][n+1];

    p = a;   // invalid: not compatible because 4 != 6

    r = c;   // compatible, but defined behavior only if
             // n == 6 and m == n+1

}

6.7.6.2美元。

这意味着您可以调用此函数,例如:

int main(int argc, char **argv)
{
    int arry[10][23];

    printArry(sizeof(arry) / sizeof(arry[0]), 
            sizeof(arry[0]) / sizeof(arry[0][0]), &arry);
}

另一方面,您也可以使用指向它的第一个元素的简单指针访问您的数组:

void printArry(size_t a1, size_t b1, int *parryFirstElement){
    for(size_t i = 0; i < a1; ++i)
        for(size_t y = 0; y < b1; ++y)
            parryFirstElement[i * b1 + y]; //your array [i][y] element
}

调用此函数可以像以前一样完成(因为“C”中的IIRC指针不需要进行转换)。

我在更好地使用'size_t'的时候替换'int'。虽然如果你的目标是一些旧标准,你可以将它们反转回'int'。

我可能错了。我现在困了。如果我错了 - 那就糟糕了。

答案 1 :(得分:2)

这个问题目前存在问题:

  

上面提到的问题中的答案使用C的可变长度数组(VLA)特征。这些特征在C98标准中不存在,并且在C11标准中是可选的。

C99(ISO / IEC 9899:1990)之前的C标准是C89(ANSI)或C90(ISO) - 功能相同的标准。有C ++ 98(ISO / IEC 14882:1998),但这是一种完全不同的语言。

int main(void)
{
    int a1=5;
    int b1=6;
    int a2=7;
    int b2=8;

    int arry1[a1][b1];
    int arry2[a2][b2];

此示例代码正在使用VLA;它不会在严格的C90规则下编译。

如果您受限于1999年之前的C标准,那么您将被限制使用与此类似的代码。显然,您可以通过读取文件中的数据或其他任何您想要的数据来初始化数组;使用直接初始化器是可能的,因为阵列不再是VLA。

#include <stdio.h>

void printArry(int d1, int d2, int *data);

int main(void)
{
    enum { a1 = 5, b1 = 6, a2 = 7, b2 = 8 };
    int arry1[a1][b1] =
    {
        {  9,  8,  7,  6,  5,  4 },
        {  2,  3,  4,  3,  2,  1 },
        { -9, -8, -7, -6, -5, -4 },
        { 39, 38, 37, 36, 35, 34 },
        { 99, 98, 97, 96, 95, 94 },
    };
    int arry2[a2][b2] =
    {
        { 198, 158, 165, 136, 198, 127, 119, 103, },
        { 146, 123, 123, 108, 168, 142, 119, 115, },
        { 160, 141, 168, 193, 152, 152, 147, 137, },
        { 144, 132, 187, 156, 188, 191, 196, 144, },
        { 197, 164, 108, 119, 196, 171, 185, 133, },
        { 107, 133, 184, 191, 166, 105, 145, 175, },
        { 199, 115, 197, 160, 114, 173, 176, 184, },
    };

    printArry(a1, b1, &arry1[0][0]);
    printArry(a2, b2, &arry2[0][0]);

    return 0;
}

void printArry(int d1, int d2, int *data)
{
    int i, j;
    for (i = 0; i < d1; i++)
    {
        for (j = 0; j < d2; j++)
            printf(" %4d", data[i * d2 + j]);
        putchar('\n');
    }
}

printArry()函数本质上是做编译器为你做的索引(下标)计算 - 但它不能,因为它无法处理C99 VLA。

第一个数据数据是手工制作的,可以发现问题(例如在下标计算中使用d1而不是d2。第二组数据只是56和100之间的56个随机值。

示例输出:

    9    8    7    6    5    4
    2    3    4    3    2    1
   -9   -8   -7   -6   -5   -4
   39   38   37   36   35   34
   99   98   97   96   95   94
  198  158  165  136  198  127  119  103
  146  123  123  108  168  142  119  115
  160  141  168  193  152  152  147  137
  144  132  187  156  188  191  196  144
  197  164  108  119  196  171  185  133
  107  133  184  191  166  105  145  175
  199  115  197  160  114  173  176  184

答案 2 :(得分:0)

似乎存在对标准和VLA的误解:C99和以下所有 C 标准都包含VLA,编译器(例如gcc)支持它({{1} }},-std=c99等标志。)

什么行不通,是任何标准的 C ++ 。 VLA根本不是该语言的一个特征,C在这方面比C ++更强大。

因此,如果您想使用VLA,只需确保使用真正的C编译器(-std=C11)进行编译,而不是使用C ++编译器(gcc)进行编译。如果你这样做,他们应该像魅力一样工作。

Afaik,有一个关于C ++ 17的提议,它表示它希望在语言中添加VLA。但是,该提案中的VLA功能受到严重限制。直到我眼中完全无法使用的程度。在这方面,C99仍然比C ++ 17强大得多。