首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >作为函数参数的VLA语法

作为函数参数的VLA语法
EN

Stack Overflow用户
提问于 2015-08-11 03:30:03
回答 3查看 110关注 0票数 1

我写了奈维姆矩阵乘法码,以完成我对C99协议的理解。让我有点困惑的是,当我在函数定义的参数列表中声明指向VLA的指针时。

例如,在fill_matrix_randomly中,m声明为double (*m)[n_cols]的参数编译得很好。double (*m)[*]是编译错误,因为[*]只能出现在声明中。double (*m)[]也是一个错误,因为我不能访问不完整类型的数组。直到现在还没什么奇怪的,但是。double (*m)[n_rows]编译得很好,甚至运行得很好?double (*m)[1]double (*m)[2]也能工作,我在这里真的很困惑。帮我减少困惑。

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>

static void fill_matrix_randomly(int, int, double (*)[*]);
static void print_matrix(int, int, double (*)[*]);
static void multiply_matrices(int, int, int, double (*restrict)[*],
double (*restrict)[*], double (*restrict)[*]);

int main(void) {
    const int a = 1, b = 3, c = 5;
    double m[a][c], m2[a][b], m3[b][c];
    fill_matrix_randomly(a, b, m2);
    fill_matrix_randomly(b, c, m3);
    multiply_matrices(a, b, c, m, m2, m3);
    print_matrix(a, b, m2);
    print_matrix(b, c, m3);
    print_matrix(a, c, m);
}

static void fill_matrix_randomly
(int n_rows, int n_cols, double (*m)[n_cols]) {
    for (int i = 0; i < n_rows; ++i) {
        for (int j = 0; j < n_cols; ++j) {
            m[i][j] = (double)rand() / RAND_MAX + 1;
        }
    }
}

static void print_matrix(int n_rows, int n_cols, double (*m)[n_cols]) {
    for (int i = 0; i < n_rows; ++i) {
        printf("[ ");
        for (int j = 0; j < n_cols; ++j) {
            printf("%.3f", m[i][j]);
            if (j != n_cols - 1) {
                printf(", ");
            } else {
                printf(" ]\n");
            }
        }
    }
    putchar('\n');
}

static void multiply_matrices
(int n, int m, int p, double (*restrict r)[p],
double (*restrict a)[m], double (*restrict b)[p]) {
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < p; ++j) {
            double sum = 0;
            for (int k = 0; k < m; ++k) {
                sum += a[i][k] * b[k][j];
            }
            r[i][j] = sum;
        }
    }
}
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-08-11 03:46:44

double (*m)n_rows编译良好,甚至运行良好

如果您用double (*)[n_rows]类型声明了函数参数,但是传递了一个类型为double (*)[n_columns]的参数,并且n_rowsn_columns不同,则行为是未定义的。

double (*m)[1]double (*m)[2]变体也是如此。

参数传递要求参数类型与参数类型兼容。对于指向数组的指针,指针必须指向兼容的数组类型。在您的案例中,适用以下内容

6.7.5.2数组声明器 两个数组类型兼容的6,都应该具有兼容的元素类型,如果两个大小说明符都存在,并且是整数常量表达式,那么两个大小说明符都应该具有相同的常量值。如果两个数组类型在要求它们兼容的上下文中使用,则如果两个大小说明符计算为不等值,则为未定义的行为。

显然,没有人能够合理地预期这种违规行为会在编译时被捕获,因为编译器通常不能在编译时预测和执行运行时关系(VLA大小)。

在发生此冲突(这本身足以触发UB)之后,您将执行对fill_matrix_randomly中数组的越界访问,从而提交另一个UB。

至于运行它..。在我看来,double (*m)[n_rows]代码“运行良好”是从何而来的?一个快速的实验表明,如果你幸运的话,像这样对编译器撒谎会导致不正确的填充数组。

http://coliru.stacked-crooked.com/a/6032864f2baa2eae

如果你不是那么幸运的话

http://coliru.stacked-crooked.com/a/7ba1002e3150bd1c

票数 2
EN

Stack Overflow用户

发布于 2015-08-11 03:37:40

定义:

代码语言:javascript
复制
static void print_matrix(int n_rows, int n_cols, double (*m)[n_cols]) {
    …
}

表示m是指向数组的指针,其中数组的每个元素都有n_cols列。这是完美的圣洁。

行大小不变的选项(double (*m)[1]double (*m)[2])与可变行大小的选项一样有效。

您可能还需要注意,代码中的这个次要变量也会编译和运行,并且(因为没有随机数生成器的种子)产生了相同的答案:

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>

static void fill_matrix_randomly(int, int, double[*][*]);
static void print_matrix(int, int, double[*][*]);
static void multiply_matrices(int, int, int, double[*][*],
                              double[*][*], double[*][*]);

int main(void)
{
    const int a = 1, b = 3, c = 5;
    double m[a][c], m2[a][b], m3[b][c];
    fill_matrix_randomly(a, b, m2);
    fill_matrix_randomly(b, c, m3);
    multiply_matrices(a, b, c, m, m2, m3);
    print_matrix(a, b, m2);
    print_matrix(b, c, m3);
    print_matrix(a, c, m);
}

static void fill_matrix_randomly(int n_rows, int n_cols, double m[n_rows][n_cols])
{
    for (int i = 0; i < n_rows; ++i)
    {
        for (int j = 0; j < n_cols; ++j)
            m[i][j] = (double)rand() / RAND_MAX + 1;
    }
}

static void print_matrix(int n_rows, int n_cols, double m[n_rows][n_cols])
{
    for (int i = 0; i < n_rows; ++i)
    {
        printf("[ ");
        for (int j = 0; j < n_cols; ++j)
        {
            printf("%.3f", m[i][j]);
            if (j != n_cols - 1)
                printf(", ");
            else
                printf(" ]\n");
        }
    }
    putchar('\n');
}

static void multiply_matrices(int n, int m, int p, double r[n][p],
                              double a[n][m], double b[m][p])
{
    for (int i = 0; i < n; ++i)
    {
        for (int j = 0; j < p; ++j)
        {
            double sum = 0;
            for (int k = 0; k < m; ++k)
                sum += a[i][k] * b[k][j];
            r[i][j] = sum;
        }
    }
}
票数 1
EN

Stack Overflow用户

发布于 2015-08-11 03:53:47

您需要指定除第一个维度之外的所有维度的大小。这是因为多维数组存储在相邻的内存位置中,因此需要使用维度来计算起始地址的实际偏移量,以找到第一维度中的每个元素。

你的二维数组有行和列(在高层意义上)。在记忆中,它可能是这样的:

代码语言:javascript
复制
Row   0            | 1            | 2 ...
    +----+----+----|----+----+----|----+----+
    |    |    |    |    |    |    |    |    | ...
    +----+----+----|----+----+----|----+----+
Col   0    1    2  | 0    1    2  | 0    1

array[i][j]形式进行的数组访问相当于指针array*(*(array + i) + j)的加法和取消引用。

array + i用于抵消行号i (并取消引用以给出保持在该行中的数组),j用于使您进一步了解该行中的jth元素。显然,要获得正确的行偏移量,编译器必须知道每一行的大小。

例如,在上面的示例中,编译器需要知道跳过3个内存位置/数据单元以从一行前进到另一行。

更完整的公式如下(注意编译器自动完成此扩展):

代码语言:javascript
复制
*((array + (i * #cols/row * sizeof(array elem))
         + (j * sizeof(array elem)))

很明显,需要提前知道的是sizeof(array elem)#cols/row

sizeof(array elem)是已知的,因为您指定了存储在多维数组中的基本类型(在您的例子中是double)。但是,为了使编译器能够正确计算偏移量,#cols/row必须由程序员指定。

在所有的工作代码示例中,您都给出了每个行中包含的列的具体值。但是,在所有这些方法中,您都会得到意想不到的行为,因为特定大小的数组将被视为不同大小的数组。通过指定不正确或不匹配的大小,您将调用未定义的行为,因为在参数和提供的参数中基本上存在类型不匹配。您用x列声明了2D数组,但将其作为带有y列的数组传递。

声明第二个维度为3的数组并将其传递给接受具有二维2的数组的函数如下所示,其中以两种不同的方式解释相同的内存:

代码语言:javascript
复制
Row   0            | 1            | 2 ...
    +----+----+----|----+----+----|----+----+
    |    |    |    |    |    |    |    |    | ...
    +----+----+----|----+----+----|----+----+
Col   0    1    2  | 0    1    2  | 0    1

Row   0       | 1       | 2       | 3 ...
    +----+----|----+----|----+----|----+----+
    |    |    |    |    |    |    |    |    | ...
    +----+----|----+----|----+----|----+----+
Col   0    1  | 0    1  | 0    1  | 0    1

您可以看到,访问array[1][0]会给出两个完全不同的结果,第一个结果是预期的结果,第二个结果是由于类型不匹配而呈现的结果。

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

https://stackoverflow.com/questions/31932730

复制
相关文章

相似问题

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