我正在尝试在结构中初始化一个二维数组,但我总是得到一个错误:
gcc -g -Wall -W -I/usr/include/SDL -c -o fractal.o fractal.c
In file included from fractal.c:2:0:
fractal.h:12:12: error: array type has incomplete element type ‘double[]’
double values[][];
以下是代码:
struct fractal {
char name[64];
int height;
int width;
double a;
double b;
double meanValue;
double values[][]; /*This line is causing the error*/
};
理想情况下,我想像这样初始化二维数组的高度和宽度:
struct fractal {
/*... Same code as above ...*/
double values[width][height];
};
但是在编译时我又遇到了两个错误:
gcc -g -Wall -W -I/usr/include/SDL -c -o fractal.o fractal.c
In file included from fractal.c:2:0:
fractal.h:12:19: error: ‘width’ undeclared here (not in a function)
double values[width][height];
^
fractal.h:12:26: error: ‘height’ undeclared here (not in a function)
double values[width][height];
^
我到处都看过,但我的代码应该可行,我无法弄清楚它为什么没有。
感谢您的帮助
答案 0 :(得分:2)
动态维度数组不是C最佳的点...简单可变长度数组仅在C99版本的语言中引入,并且在C11版本中是可选的。它们仍未被MSVC 2017接受......
但是在这里,你试图在一个结构中设置一个。这根本不受支持,因为结构必须具有恒定大小(*)(如何处理结构数组)。所以我很抱歉,但这段代码不应该,我也无法用C语言表达。
一种常见的方法是用指针替换2D动态数组,将指针分配给2D数组然后使用它,但即使这样也不是很简单。
你必须以不同的方式设计你的结构......
(*)结构的最后一个元素可能是不完整的类型,例如int tab[];
。这是一个危险的特性,因为程序员有责任为它提供空间。但无论如何你不能建立一个不完整类型的数组。
答案 1 :(得分:2)
作为免责声明,这是一个高级主题,所以如果你是初学者,你可能只想完全退出它,只需使用double*
数组,然后为每个指针调用malloc 。 (对于初学者来说很好,在专业代码中是不可接受的。)
这是一个高级主题,因为这个特殊情况是C中的一个弱点。您尝试使用的功能,在结构的末尾有一个空数组,称为灵活的数组成员 。这只适用于一个维度。如果在编译时两个维度都是未知的,则必须进行解决。
分配部分与任何灵活的数组成员一样:动态分配结构并为尾随数组设置大小。
fractal_t* f = malloc(sizeof *f + sizeof(double[height][width]) );
(在这种情况下利用方便的VLA语法,尽管灵活的数组成员不是VLA。)
从技术上讲,结构的最后一个成员现在应该是double[]
,或者说结构声明。但是,malloc返回的内存在您访问它之前没有实际的有效类型,之后该内存的有效类型将成为用于访问的类型。
我们可以使用此规则来访问该内存,就好像它是double[][]
一样,即使结构中的指针类型是不同的。给定分形f
,通过指针访问的代码变为如下:
double (*array_2D)[width] = (double(*)[width]) f->values;
其中array_2D
是数组指针。这里使用的最正确的类型是一个指向double double (*)[height][width]
数组的数组指针,但是那个带有强制丑陋的访问(*array_2D)[i][j]
。为了避免这种丑陋,一个常见的技巧是在数组指针声明中省略最左边的维度,然后我们可以将它作为array_2D[i][j]
访问,它看起来更漂亮。
示例代码:
#include <stdlib.h>
#include <stdio.h>
typedef struct
{
char name[64];
size_t height;
size_t width;
double a;
double b;
double meanValue;
double values[];
} fractal_t;
fractal_t* fractal_create (size_t height, size_t width)
{
// using calloc since it conveniently fills everything with zeroes
fractal_t* f = calloc(1, sizeof *f + sizeof(double[height][width]) );
f->height = height;
f->width = width;
// ...
return f;
}
void fractal_destroy (fractal_t* f)
{
free(f);
}
void fractal_fill (fractal_t* f)
{
double (*array_2D)[f->width] = (double(*)[f->width]) f->values;
for(size_t height=0; height < f->height; height++)
{
for(size_t width=0; width < f->width; width++)
{
array_2D[height][width] = (double)width; // whatever value that makes sense
}
}
}
void fractal_print (const fractal_t* f)
{
double (*array_2D)[f->width] = (double(*)[f->width]) f->values;
for(size_t height=0; height < f->height; height++)
{
for(size_t width=0; width < f->width; width++)
{
printf("%.5f ", array_2D[height][width]);
}
printf("\n");
}
}
int main (void)
{
int h = 3;
int w = 4;
fractal_t* fractal = fractal_create(h, w);
fractal_fill(fractal); // fill with some garbage value
fractal_print(fractal);
fractal_destroy(fractal);
}
答案 2 :(得分:1)
我在设计一个结构以同时保存ODE求解器中的域值(N x 1矢量)和解值(N x M矩阵)时遇到了此问题,从而简化了函数接口。 N和M与仿真有关,因此先验未知。我使用GNU Scientific Library的向量矩阵模块解决了这个问题。我发现它比将FAM(尽管分配为2D)投射到独立的整体数组指针上更加简化。
为结构分配内存后,我们需要做的就是调用gsl_matrix_alloc()
来为矩阵保留空间。完成后,调用gsl_matrix_free()
将销毁它。请注意,这些功能取决于文档中说明的数据类型。
文件名: struct_mat.c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_statistics.h>
typedef struct _fractal {
char name[64];
size_t height;
size_t width;
double a;
double b;
double meanValue;
gsl_matrix *values;
} fractal;
fractal * fractal_create(size_t height, size_t width) {
fractal * fractalObj = (fractal *) malloc(sizeof(fractal));
fractalObj -> values = gsl_matrix_alloc(height, width);
if (fractalObj == NULL || fractalObj -> values == NULL) {
fprintf(stderr, "NULL pointer returned while allocating fractal object.. Exiting program.\n");
exit(EXIT_FAILURE);
}
fractalObj -> height = height;
fractalObj -> width = width;
fractalObj -> meanValue = 0.0;
return fractalObj;
}
void fractal_populate(fractal * fractalObj) {
srand(time(NULL));
double current_value = 0.0;
for (size_t r = 0; r < fractalObj -> height; ++r) {
for (size_t c = 0; c < fractalObj -> width; ++c) {
current_value = (double) rand() / (double) RAND_MAX;
gsl_matrix_set(fractalObj -> values, r, c, current_value);
}
}
}
void fractal_calcMeanValue(fractal * fractalObj) {
gsl_vector_view colVec;
for (size_t col = 0; col < fractalObj -> values -> size2; ++col) {
colVec = gsl_matrix_column(fractalObj -> values, col);
fractalObj -> meanValue += gsl_stats_mean(colVec.vector.data, colVec.vector.stride, colVec.vector.size);
}
fractalObj -> meanValue /= fractalObj -> values -> size2;
printf("\nThe mean value of the entire matrix is %lf\n", fractalObj -> meanValue);
}
void fractal_display(fractal * fractalObj) {
printf("\n");
for (size_t r = 0; r < fractalObj -> height; ++r) {
for (size_t c = 0; c < fractalObj -> width; ++c) {
printf("%lf ", gsl_matrix_get(fractalObj -> values, r, c));
}
printf("\n");
}
}
void fractal_delete(fractal * fractalObj) {
gsl_matrix_free(fractalObj -> values);
free(fractalObj);
}
int main(int argc, char const *argv[]){
// Program takes number of rows and columns as command line parameters
switch(argc) {
case 3:
printf("Running program..\n"); // to avoid the declaration-succeeding-label error
size_t height = atoi(argv[1]);
size_t width = atoi(argv[2]);
fractal * myFractal = fractal_create(height, width);
fractal_populate(myFractal);
fractal_display(myFractal);
fractal_calcMeanValue(myFractal);
fractal_delete(myFractal);
return 0;
default:
fprintf(stderr, "USAGE: struct_mat <rows> <columns>\n");
return 1;
}
}
通过与GSL和GSL CBLAS库链接来进行编译:
gcc -std=c99 struct_mat.c -o struct_mat -lgsl -lgslcblas -lm
您可以通过发行版的GSL,Windows上的Cygwin或编译package manager来安装source。
根据我的有限经验,事实证明,使用标准数据结构要比使用FAM或指向1D数组的指针要容易得多。但是,需要注意的是,我们必须记住在分配结构本身之后为矩阵分配内存。