繁体   English   中英

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、 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的 vector-matrix 模块解决了它。 我发现它比将 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  

您可以通过发行版的包管理器、Windows 上的 Cygwin 或通过编译源代码来安装GSL

根据我有限的经验,使用标准数据结构证明比使用 FAM 或指向一维数组的指针数组要容易得多。 但是,需要注意的是,我们必须记住在分配结构本身之后为矩阵分配内存。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM