首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >VLA原型与多维数组参数

VLA原型与多维数组参数
EN

Stack Overflow用户
提问于 2018-12-15 16:33:10
回答 2查看 197关注 0票数 2

我创建了一个C99 VLA函数,如下所示:

代码语言:javascript
复制
void create_polygon(int n, int faces[][n]);

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

代码语言:javascript
复制
void parse_faces()
{
    int faces[3][6];

    create_polygon(6, faces);
}

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

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

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

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-12-15 17:51:36

faces[i][j]总是等同于*(*(faces + i) + j),不管是否VLA。

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

代码语言:javascript
复制
void create_polygon1(int faces[][6]);
void create_polygon2(int n, int faces[][n]);

如果传递给的数组最初是作为经典数组创建的,还是作为VLA创建的,那么第一个函数接受长度正好为6的数组,第二个函数可以接受任意长度的数组(假设这一点到目前为止是明确的.)。

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

代码语言:javascript
复制
*((int*)faces + (i * 6 + j)) // (1)
*((int*)faces + (i * n + j)) // (2)

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

代码语言:javascript
复制
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[]案例:由指向数组的指针数组表示。

代码语言:javascript
复制
*((int*)faces + (i * ??? + j))

不再工作了,因为在本例中,faces不是连续内存(当然,指针本身在连续内存中,但不是所有的faces[i][j])。我们被迫这样做:

代码语言:javascript
复制
*(*(faces + i) + j)

因为我们需要取消引用数组中的真正指针,然后才能应用下一个索引。汇编程序代码(为了比较,首先需要一个指向2D数组的指针的更完整的变体):

代码语言:javascript
复制
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
票数 2
EN

Stack Overflow用户

发布于 2018-12-16 00:02:43

我分解了这个代码:

代码语言:javascript
复制
void    create_polygon(int n, int faces[][6])
{
    int a = sizeof(faces[0]);
    (void)a;
}

对于VLA的论点:

代码语言:javascript
复制
movl    %edi, -4(%rbp)   # 6
movq    %rsi, -16(%rbp)  # faces
movl    %edi, %esi
shlq    $2, %rsi         # 6 << 2 = 24
movl    %esi, %edi

固定尺寸:

代码语言:javascript
复制
movl    %edi, -4(%rbp)
movq    %rsi, -16(%rbp)
movl    $24, %edi        # 24

正如Aconcagua所指出的,在使用VLA的第一个示例中,在运行时通过将int的大小乘以存储在rsi中的参数的第二个维度的大小来计算大小,然后移动到edi中。

在第二个示例中,在编译时直接计算大小并将其放置到edi中。其主要优点是,如果传递不同的大小,则可以检查不正确的指针类型参数,从而避免崩溃。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53795107

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档