是否有充分的理由为什么在结构体的指针中不允许使用VLA? [英] Is there a good reason why VLA are not permitted in pointers in structs?

查看:99
本文介绍了是否有充分的理由为什么在结构体的指针中不允许使用VLA?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是定义矩阵类型的一种方法

Here is a way to define a Matrix type

typedef struct {
    int nr, nc;
    double *elem;
} Matrix;

我想定义这个

typedef struct {
    int nr, nc;
    double elem[nr][nc];
} Matrix;

这很好,因为我不必担心索引.这就是VLA首先有用的原因,因为它们只能透明地执行索引算法容易做到的事情.

It would be nice, because I would not have to worry about indexes. That's the reason VLA are useful in the first place, since they only do transparently what would be easy with index arithmetic.

当然,仅由于结构的大小无法很好地定义,就不可能实现上述目的.然后,我仍然会很满意:

Of course, the above is not possible, if only because the size of the struct would not be well defined. Then, I would still be happy with:

typedef struct {
    int nr, nc;
    double (*elem)[nc];
} Matrix;

现在,像在非VLA情况下一样,将矩阵数据存储为指针.但是算术仍然可以由编译器完成.该定义仅表明它是指向double数据的某种指针,双精度数组排列在宽度为nc的数组中.

Now, the matrix data is stored as a pointer, like in the non-VLA case. But the arithmetic could still be done by the compiler. The definition only tells it's some kind of pointer to double data, with the doubles arranged in an array of width nc.

该标准似乎也不允许这样做,我想知道为什么,因为通过类型转换很容易做到这一点.例如,使用第一个定义(使用double *),我可以做到

It seems that it's not permitted either by the standard, and I wonder why, since it's easy to do the same by transtyping. For example, using the first definition (with double *), I could do

double get(Matrix *a, int i, int j) {
    int nc = a->nc;
    double (*p)[nc] = (double (*)[nc])a->elem;
    return p[i][j];
}

当然,这里不是很有趣,因为只有一种访问elem的方法,但是如果有很多的话,也可能是这样.

Of course, it's not very interesting here, since there is only one access to elem, but it could be if there are many.

所以,我的问题是希望它成为主题:禁止第三个定义的真正原因是什么?

So, my question, with the hope that it's on topic: what's the very reason of prohibiting the third definition?

我可以想象这很危险,因为不能保证nc处理正确的值,但是无论如何对于指针来说这都是危险的,因此这似乎不是一个很好的理由.

I could imagine that it's dangerous since it's not guaranteed that nc handles the correct value, but this is dangerous anyway with pointers, so it does not look like a good reason.

推荐答案

这符合您的要求吗?它在结构中存储void *,访问函数将其强制转换为指向2D VLA的指针并使用它. Mac OS X 10.10.5上的GCC 5.2.0可以对其进行干净地编译,而valgrind(2014年11月左右为3.11.0-SVN)可以提供清晰的运行状况.

Does this meet your requirements? It stores a void * in the structure, and the access functions cast that to a pointer to a 2D VLA and use that. GCC 5.2.0 on Mac OS X 10.10.5 compiles it cleanly, and valgrind (3.11.0-SVN from November 2014 or thereabouts) gives it a clean bill of health.

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

typedef struct
{
    int nr, nc;
    void *data;     // Actually double a[nr][nc]
} Matrix;

static double get(Matrix *a, int i, int j)
{
    double (*array)[a->nr][a->nc] = a->data;
    return (*array)[i][j];
}

static void set(Matrix *a, int i, int j, double v)
{
    double (*array)[a->nr][a->nc] = a->data;
    (*array)[i][j] = v;
}

static Matrix *mat_alloc(int nr, int nc)
{
    Matrix *m = malloc(sizeof(*m));
    if (m != 0)
    {
        m->nr = nr;
        m->nc = nc;
        m->data = malloc(nr * nc * sizeof(double));
        if (m->data == 0)
        {
            free(m);
            m = 0;
        }
    }
    return m;
}

static void mat_free(Matrix *m)
{
    free(m->data);
    free(m);
}

int main(void)
{
    int nr = 3;
    int nc = 5;

    Matrix *m = mat_alloc(nr, nc);
    if (m == 0)
    {
        fprintf(stderr, "Matrix allocation for %dx%d matrix failed\n", nr, nc);
        exit(1);
    }

    for (int i = 0; i < nr; i++)
    {
        for (int j = 0; j < nc; j++)
        {
            double v = (i * (nc + 1)) + j + 1;
            set(m, i, j, v);
            printf("Set: [%d,%d] = %4.1f\n", i, j, v);
        }
    }

    for (int j = 0; j < nc; j++)
    {
        for (int i = 0; i < nr; i++)
            printf("Get: [%d,%d] = %4.1f\n", i, j, get(m, i, j));
    }

    mat_free(m);
    return 0;
}

我不确定是否有一种巧妙的方法可以丢失访问功能中符号的(*array)部分.如果有一个(而不是使用array[0][i][j]),我会更喜欢.

I'm not sure whether there's a neat way to lose the (*array) part of the notation in the access functions. I'd prefer it if there was one (other than using array[0][i][j], that is).

Set: [0,0] =  1.0
Set: [0,1] =  2.0
Set: [0,2] =  3.0
Set: [0,3] =  4.0
Set: [0,4] =  5.0
Set: [1,0] =  7.0
Set: [1,1] =  8.0
Set: [1,2] =  9.0
Set: [1,3] = 10.0
Set: [1,4] = 11.0
Set: [2,0] = 13.0
Set: [2,1] = 14.0
Set: [2,2] = 15.0
Set: [2,3] = 16.0
Set: [2,4] = 17.0
Get: [0,0] =  1.0
Get: [1,0] =  7.0
Get: [2,0] = 13.0
Get: [0,1] =  2.0
Get: [1,1] =  8.0
Get: [2,1] = 14.0
Get: [0,2] =  3.0
Get: [1,2] =  9.0
Get: [2,2] = 15.0
Get: [0,3] =  4.0
Get: [1,3] = 10.0
Get: [2,3] = 16.0
Get: [0,4] =  5.0
Get: [1,4] = 11.0
Get: [2,4] = 17.0

这篇关于是否有充分的理由为什么在结构体的指针中不允许使用VLA?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆