是否有充分的理由说明结构中的指针不允许使用VLA?
这是一种定义Matrix类型的方法
typedef struct { int nr, nc; double *elem; } Matrix;
我想定义一下
typedef struct { int nr, nc; double elem[nr][nc]; } Matrix;
这会很好,因为我不必担心索引。 这就是VLA首先有用的原因,因为它们只能透明地完成索引算法的简单操作。
当然,如果只是因为结构的大小不能很好地定义,上述情况是不可能的。 然后,我仍然会满意:
typedef struct { int nr, nc; double (*elem)[nc]; } Matrix;
现在,矩阵数据被存储为指针,就像在非VLA情况下一样。 但算法仍然可以由编译器完成。 该定义仅告诉它是某种指向double
数据的指针,其中double
精度数以nc
为单位排列。
它似乎不允许标准,我想知道为什么,因为通过transtyping很容易做同样的事情。 例如,使用第一个定义(带有double *
),我可以做到
double get(Matrix *a, int i, int j) { int nc = a->nc; double (*p)[nc] = (double (*)[nc])a->elem; return p[i][j]; }
当然,这里不是很有趣,因为只有一个访问elem,但可能有很多。
所以,我的问题,希望它是关于主题:禁止第三个定义的原因是什么?
我可以想象它是危险的,因为它不能保证nc
处理正确的值,但无论如何使用指针这是危险的,所以它看起来不是一个好理由。
这符合您的要求吗? 它在结构中存储void *
,并且访问函数将其转换为指向2D VLA的指针并使用它。 Mac OS X 10.10.5上的GCC 5.2.0可以干净地编译它,而valgrind
(2014年11月左右的3.11.0-SVN或其附近)给它一个干净的健康状况。
#include #include 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]
,那是)。
示例运行
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
我相信在定义局部变量nc
的函数中,您可以使用typedef创建局部类型double (*arr)[nc]
,然后将*double
转换为该类型。 我相信这样的强制转换对于任何*double
都是合法的,它可以识别足够长的double
值序列,而不考虑它是否是使用与函数中定义的相同类型创建的[如果多个函数各自定义它们自己的数组类型,编译器不会将这些类型识别为等效,但它应该无关紧要]。 我不是100%肯定不存在严格的别名问题,但我认为不应该存在。
否则,一个基本的困难是涉及VLA的typedef使用在特定时刻存在的值创建一个类型,并且只能在被评估为可执行语句的typedef中发生,而这反过来只能在typedef被嵌入时发生在function内。 此外,数组维度中使用的任何标识符将在封闭函数的上下文中进行计算,而不是在部分定义的类型的上下文中进行评估。