C中结构中的二维数组
2-dimensional array in a struct in C
我正在尝试初始化结构中的二维数组,但总是出现错误:
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];
^
我到处都看了,但我的代码应该可以工作,但我不明白为什么不行。
感谢帮助
动态维度数组不是 C 的最佳之处...简单可变长度数组仅在 C99 版本的语言中引入,并在 C11 版本中成为可选。它们在 MSVC 2017 中仍然不被接受...
但是在这里,您正试图在结构中设置一个。这根本不受支持,因为结构必须具有常量大小(*)(如何处理结构数组)。所以我很抱歉,但是这段代码 不应该工作 而且我不知道用 C 语言表达它的方法。
一种常见的方法是将二维动态数组替换为指针,将指针分配给二维数组然后使用它,但即使这样也不是很简单。
你必须以不同的方式设计你的结构...
(*) 结构的最后一个元素可能是不完整的类型,例如 int tab[];
。这是一个危险的特性,因为程序员有责任为其提供空间。但是无论如何,您不能构建不完整类型的数组。
作为免责声明,这是一个高级主题,所以如果您是初学者,您可能想完全放弃它,只需使用 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 (*)[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);
}
我在设计一个结构来保存我的 ODE 求解器中的域值(N x 1 向量)和解值(N x M 矩阵)以简化函数接口时遇到了这个问题。 N 和 M 依赖于模拟,因此先验未知。我使用 GNU Scientific Library 的矢量矩阵模块解决了这个问题。我发现它比将 FAM(尽管分配为 2D)转换为独立的整个数组指针更简化。
为结构分配内存后,我们需要做的就是调用gsl_matrix_alloc()
为矩阵保留space。完成后,调用 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 via your distribution's package manager, Cygwin on Windows or by compiling the source.
以我有限的经验,事实证明使用标准数据结构比使用 FAM 或指针数组到一维数组要容易得多。但是,需要注意的是,我们必须记住在分配结构本身之后为矩阵分配内存。
我正在尝试初始化结构中的二维数组,但总是出现错误:
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];
^
我到处都看了,但我的代码应该可以工作,但我不明白为什么不行。
感谢帮助
动态维度数组不是 C 的最佳之处...简单可变长度数组仅在 C99 版本的语言中引入,并在 C11 版本中成为可选。它们在 MSVC 2017 中仍然不被接受...
但是在这里,您正试图在结构中设置一个。这根本不受支持,因为结构必须具有常量大小(*)(如何处理结构数组)。所以我很抱歉,但是这段代码 不应该工作 而且我不知道用 C 语言表达它的方法。
一种常见的方法是将二维动态数组替换为指针,将指针分配给二维数组然后使用它,但即使这样也不是很简单。
你必须以不同的方式设计你的结构...
(*) 结构的最后一个元素可能是不完整的类型,例如 int tab[];
。这是一个危险的特性,因为程序员有责任为其提供空间。但是无论如何,您不能构建不完整类型的数组。
作为免责声明,这是一个高级主题,所以如果您是初学者,您可能想完全放弃它,只需使用 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 (*)[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);
}
我在设计一个结构来保存我的 ODE 求解器中的域值(N x 1 向量)和解值(N x M 矩阵)以简化函数接口时遇到了这个问题。 N 和 M 依赖于模拟,因此先验未知。我使用 GNU Scientific Library 的矢量矩阵模块解决了这个问题。我发现它比将 FAM(尽管分配为 2D)转换为独立的整个数组指针更简化。
为结构分配内存后,我们需要做的就是调用gsl_matrix_alloc()
为矩阵保留space。完成后,调用 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 via your distribution's package manager, Cygwin on Windows or by compiling the source.
以我有限的经验,事实证明使用标准数据结构比使用 FAM 或指针数组到一维数组要容易得多。但是,需要注意的是,我们必须记住在分配结构本身之后为矩阵分配内存。