首页 > 解决方案 > VLA 原型和多维数组参数

问题描述

我这样创建了一个 C99 VLA 函数:

void create_polygon(int n, int faces[][n]);

我想在另一个函数中调用这个函数,我将在其中分配我的二维数组:

void parse_faces()
{
    int faces[3][6];

    create_polygon(6, faces);
}

当我将二维数组作为参数传递时,它会传递一个指向 6 整数数组的指针,引用调用函数中的堆栈内存。

这里的 VLA 参数仅充当类型声明(不分配任何实际内存),告诉编译器以行优先顺序访问数据,((int*)faces)[i * 6 + j]而不是faces[i][j].

使用 VLA 参数或固定大小声明函数有什么区别?

标签: carraysmultidimensional-arrayvariable-length-array

解决方案


faces[i][j] always等价于*(*(faces + i) + j),无论是否 VLA。

现在让我们比较两个变体(不考虑您实际上还需要外部维度来防止迭代时超出数组边界):

void create_polygon1(int faces[][6]);
void create_polygon2(int n, int faces[][n]);

传递给最初的数组是创建为经典数组还是 VLA 都没有关系,第一个函数接受长度正好为 6 的数组,第二个函数可以接受任意长度的数组(假设到目前为止这很清楚......)。

faces[i][j]现在将被翻译成:

*((int*)faces + (i * 6 + j)) // (1)
*((int*)faces + (i * n + j)) // (2)

差异看起来微不足道,但在汇编程序级别可能会变得更加明显(假设所有变量都存储在堆栈中;假设sizeof(int) == 4):

LD     R1, i;
LD     R2, j;
MUL    R1, R1, 24; // using a constant! 24: 6 * sizeof(int)!
MUL    R2, R2, 4;  // sizeof(int)
ADD    R1, R2, R2; // index stored in R1 register

LD     R1, i;
LD     R2, j;
LD     R3, m;      // need to load from stack
MUL    R3, R3, 4;  // need to multiply with sizeof(int) yet     
MUL    R1, R1, R3; // can now use m from register R3
MUL    R2, R2, 4;  // ...
ADD    R1, R2, R2; // ...

当然,真正的汇编代码可能会有所不同,特别是如果您使用允许在寄存器中传递一些参数的调用约定(然后可能不需要将 n 加载到 R3 中)。


为了完整性(由于评论而添加,与原始问题无关):
还有这种int* array[]情况:由指向数组的指针数组表示。

*((int*)faces + (i * ??? + j))

不再起作用,因为faces在这种情况下没有连续的内存(当然,指针本身在连续的内存中,但不是全部faces[i][j])。我们被迫这样做:

*(*(faces + i) + j)

因为我们需要在应用下一个索引之前取消引用数组中的真实指针。汇编代码(为了比较,首先需要一个更完整的指向 2D 数组 case 的指针的变体):

LD     R1, faces;
LD     R2, i;
LD     R3, j;
LD     R4, m;      // or skip, if no VLA
MUL    R4, R4, 4;  // or skip, if no VLA
MUL    R2, R2, R3; // constant instead of R3, if no VLA
MUL    R3, R3, 4;
ADD    R2, R2, R3; // index stored in R1 register
ADD    R1, R1, R2; // offset from base pointer
LD     R1, [R1];   // loading value of faces[i][j] into register

LD     R1, faces;
LD     R2, i;
LD     R3, j;
MUL    R2, R2, 8;  // sizeof(void*) (any pointer)
MUL    R3, R3, 4;  // sizeof(int)
ADD    R1, R1, R2; // address of faces[i]
LD     R1, [R1];   // now need to load address - i. e. de-referencing faces[i]
ADD    R1, R1, R3; // offset within array
LD     R1, [R1];   // loading value of faces[i][j] into register

推荐阅读