簡體   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