简体   繁体   English

C中结构中的二维数组

[英]2-dimensional array in a struct in C

I'm trying to initialize a 2-dimensional array in a structure but I always get an error :我试图在结构中初始化一个二维数组,但我总是收到一个错误:

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[][];

Here's the code:这是代码:

struct fractal {

    char name[64];
    int height;
    int width;
    double a;
    double b;
    double meanValue;       
    double values[][];  /*This line is causing the error*/
 };

Ideally I'd like to initialize the height and width of the 2-dimensional array like this:理想情况下,我想像这样初始化二维数组的高度和宽度:

struct fractal {

    /*... Same code as above ...*/      

    double values[width][height];  
 };

But then I get two other errors when compiling:但是我在编译时遇到了另外两个错误:

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];
                          ^

I've looked about everywhere but my code should work and I can't figure out why it doesn't.我到处寻找,但我的代码应该可以工作,但我不知道为什么不可以。

Thanks for the help感谢您的帮助

Dynamic dimensions arrays is not the point where C is at its best... Simple Variable Length Arrays were only introduced in the language in the C99 version and were made optional in C11 version.动态维度数组并不是 C 最擅长的地方...简单可变长度数组仅在 C99 版本的语言中引入,并在 C11 版本中成为可选的。 They are still not accepted in MSVC 2017...它们仍然不被 MSVC 2017 接受......

But here, you are trying to set one in a struct.但是在这里,您试图在结构中设置一个。 That is not supported at all because a struct must have a constant size (*) (how could be handled arrays of structs).这根本不受支持,因为结构必须具有恒定大小(*) (如何处理结构数组)。 So I am sorry but this code should not work and I know no way to express that in C language.所以我很抱歉,但是这段代码不应该工作,我不知道用 C 语言表达它的方法。

A common way would be to replace the 2D dynamic array with a pointer, allocate the pointer to a 2D array and then use it, but even this is not really simple.一种常见的方法是用指针替换二维动态数组,将指针分配给二维数组然后使用它,但即使这样也不是很简单。

You have to design your struct differently...你必须以不同的方式设计你的结构......


(*) The last element of a struct may be of an incomplete type, for example int tab[]; (*)结构体的最后一个元素可能是不完整的类型,例如int tab[]; . . That is a dangerous feature because the programmer is responsable for providing room for it.这是一个危险的特性,因为程序员有责任为它提供空间。 But anyway you cannot build an array of incomplete types.但无论如何你不能构建一个不完整类型的数组。

As a disclaimer, this is something of an advanced topic, so if you are a beginner you might want to just back away from it entirely and just use a double* array followed by a call to malloc for each pointer.作为免责声明,这是一个高级主题,因此如果您是初学者,您可能希望完全远离它,只使用double*数组,然后为每个指针调用 malloc。 (Fine for beginners, unacceptable in professional code.) (适合初学者,在专业代码中不可接受。)

It is an advanced topic since this particular case is a weakness in C. The feature you are trying to use, with an empty array at the end of a struct, is known as flexible array member .这是一个高级主题,因为这种特殊情况是 C 中的一个弱点。您尝试使用的功能(结构末尾有一个空数组)被称为灵活数组成员 This only works for one dimension however.然而,这只适用于一维。 If both dimensions are unknown at compile time, you have to come up with a work-around.如果在编译时这两个维度都是未知的,您必须想出一个解决方法。

The allocation part is as for any flexible array member: allocate the struct dynamically and make size for the trailing array.分配部分与任何灵活的数组成员一样:动态分配结构并为尾随数组设置大小。

fractal_t* f = malloc(sizeof *f + sizeof(double[height][width]) );

(In this case taking advantage of the convenient VLA syntax, although a flexible array member is not a VLA.) (在这种情况下,利用方便的 VLA 语法,尽管灵活的数组成员不是 VLA。)

Technically, the last member of the struct is supposedly double[] now, or so says the struct declaration.从技术上讲,结构体的最后一个成员现在应该是double[] ,或者说结构体声明。 But memory returned by malloc has no actual effective type until you access it, after which the effective type of that memory becomes the type used for the access.但是 malloc 返回的内存在您访问它之前没有实际的有效类型,之后该内存的有效类型成为用于访问的类型。

We can use this rule to access that memory as if it was a double[][] , even though the pointer type in the struct is a different one.我们可以使用这个规则来访问那个内存,就像它是一个double[][] ,即使结构中的指针类型是不同的。 Given a fractal f , the code for accessing through a pointer becomes something like this:给定一个分形f ,通过指针访问的代码变成了这样:

double (*array_2D)[width] = (double(*)[width]) f->values;

Where array_2D is an array pointer.其中array_2D是一个数组指针。 The most correct type to use here would have been an array pointer to an array of double, double (*)[height][width] , but that one comes with mandatory ugly accessing (*array_2D)[i][j] .此处使用的最正确的类型是指向 double、 double (*)[height][width]数组的数组指针,但该类型带有强制丑陋访问(*array_2D)[i][j] To avoid such ugliness, a common trick is to leave out the left-most dimension in the array pointer declaration, then we can access it as array_2D[i][j] which looks far prettier.为了避免这种丑陋,一个常见的技巧是在数组指针声明中省略最左边的维度,然后我们可以将它作为array_2D[i][j] ,它看起来更漂亮。

Example code:示例代码:

#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);
}

I encountered this problem while designing a struct to hold both the domain values (N x 1 vector) and the solution values (N x M matrix) in my ODE solver, so as to simplify the function interfaces.我在设计一个结构以在我的 ODE 求解器中同时保存域值(N x 1 向量)和解值(N x M 矩阵)以简化函数接口时遇到了这个问题。 N and M are simulation-dependent and hence are unknown a priori. N 和 M 依赖于模拟,因此是先验未知的。 I solved it by using GNU Scientific Library 's vector-matrix module.我通过使用GNU Scientific Library的 vector-matrix 模块解决了它。 I found it more streamlined to work with than casting a FAM (albeit allocated as 2D) to a standalone whole-array-pointer.我发现它比将 FAM(尽管分配为 2D)转换为独立的全数组指针更易于使用。

After allocating memory for the struct, all we need to do is invoke gsl_matrix_alloc() to reserve space for the matrix.为结构分配内存后,我们需要做的就是调用gsl_matrix_alloc()为矩阵保留空间。 After we are done, calling gsl_matrix_free() will destroy it.完成后,调用gsl_matrix_free()将销毁它。 Please note that these functions are data-type dependent as explained in the documentation.请注意,如文档中所述,这些函数依赖于数据类型。

Filename: struct_mat.c文件名: 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;
    }

}


Compile by linking with the GSL and GSL CBLAS libraries:通过链接 GSL 和 GSL CBLAS 库进行编译:

gcc -std=c99 struct_mat.c -o struct_mat -lgsl -lgslcblas -lm  

You may install GSL via your distribution's package manager , Cygwin on Windows or by compiling the source .您可以通过发行版的包管理器、Windows 上的 Cygwin 或通过编译源代码来安装GSL

In my limited experience, using a standard data structure proves to be far easier than wrestling with either FAMs or array-of-pointers-to-1D-arrays.根据我有限的经验,使用标准数据结构证明比使用 FAM 或指向一维数组的指针数组要容易得多。 However, the caveat is that we have to remember allocating memory for the matrix after allocating the struct itself.但是,需要注意的是,我们必须记住在分配结构本身之后为矩阵分配内存。

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

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