是否有充分的理由说明结构中的指针不允许使用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内。 此外,数组维度中使用的任何标识符将在封闭函数的上下文中进行计算,而不是在部分定义的类型的上下文中进行评估。