在单独的函数中创建指向数组的结构指针

Create struct pointer to array in separate function

我在创建和访问作为数组的结构指针时遇到问题。这是一道作业题,不过是我对操作的误解

我的头文件(我无法更改并且必须以这种方式实现)包含:

typedef struct gw_struct GW;

extern GW *gw_build(int nrows, int ncols, int pop, int rnd);

在实现文件中我实现了两个声明

#include <stdio.h>
#include "gw.h"
#include <stdlib.h>

struct gw_struct {
    int Alive;
    int row;
    int column;
    int id;
};

GW *gw_build(int nrows, int ncols, int pop, int rnd){

    GW* list = NULL;
    int rows = nrows;
    int cols = ncols;

    for(nrows = 0; nrows < rows; nrows++){
        for(ncols = 0; ncols < cols; ncols++){
            GW *nn = malloc(sizeof *nn);

            if (nn == NULL){
                break;
            }

            list = nn;
        }

    }

    return list;
}

最后我的测试文件包含:

#include <stdio.h>
#include "gw.h"
#include <stdlib.h>

int main (){

    GW* World = gw_build(50, 50, 34, 1);

    World[0][0] -> Alive = 2;         //Can't get this to work
}

最后,我用

编译我的程序
gcc -c gw.c
gcc gw.o main.c

似乎创建了结构指针,但我无法像访问数组一样访问它。

我尝试了多种方法来解决这个问题。我最初将变量世界初始化为

GW* World[50][50] = gw_build(50,50,34,1);

我在 gw_build 函数中用 "list" 尝试了同样的事情,但它 returned 初始化错误。

不用循环就可以做

GW* list[nrows][ncols];

也是不允许的,因为您不能将变量作为大小字段。

我可以创建、maloc 和访问

GW* World[50][50];

在 main 函数本身中一切正常,但是一旦我尝试通过 gw_build 函数中的 returning 设置它,它根本不起作用。因此,我尝试更改 return 类型,但再次导致无效 return 错误。

感谢任何指导,谢谢!

extern GW *gw_build(int nrows, int ncols, int pop, int rnd);

这意味着 gw_build return 是指向一维数组的指针。

您要实现的是二维数组:

World[0][0] -> Alive = 2

无论如何,初始化二维数组的方法是:

gw *world[r];
for (i=0; i<r; i++)
world[i] = (int *)malloc(rowsize * sizeof(gw));

现在,调用 gw_build 列次数,因为 gw_build 只能 return 一行。

 for(i=0;i<row;i++)
  world[i] = gw_build();

在幕后,二维数组只是一个一维数组。通过在引用元素时做一些数学运算,您可以将一维数组视为二维数组。

查看此 post 了解一些有用的详细信息

Performance of 2-dimensional array vs 1-dimensional array

正如 basav in his/her 所指出的,creator 函数的定义只允许 return 指向一维数组的指针。

但是,您可以 return 使用讨厌的铸锤制作二维数组:

#include <stdio.h>
#include <stdlib.h>

struct gw_struct
{
  int Alive;
  int row;
  int column;
  int id;
};

typedef struct gw_struct GW;

#define ROWS 50
#define COLS 50

GW * gw_build(int nrows, int ncols, int pop, int rnd)
{
  GW * pgw = malloc(nrows * ncols * sizeof *pgw);

  /* To access the array members do: */
  {
    GW (*world)[ncols] = (GW (*)[ncols]) pgw; /* "Dirty" casting here. */
    world[0][0].row = 0;
    world[0][0].column = 0;
    ...
  }

  return pgw;
}

int main(void)
{
  GW (*world)[COLS] = (GW (*)[COLS]) gw_build(ROWS, COLS, 34, 1); /* "Dirty" casting here. */

  world[0][0].Alive = 2;
  world[0][1].Alive = 2;         

  world[1][0].Alive = 2;         
  world[1][1].Alive = 2;         

  world[2][0].Alive = 2;         
  world[2][1].Alive = 2;         

  ...

  free(world);

  return 0;
}

basav解决方案的不同之处在于,您最终得到一个线性数组,即所有元素都放在一个内存块。

注意:要摆脱 "dirty" 转换,您可以使用另一个技巧,即引入临时 void 指针。

为此替换此

  GW (*world)[COLS] = (GW (*)[COLS]) gw_build(ROWS, COLS, 34, 1);

来自

  GW (*world)[COLS] = NULL;

  {
    void * p = gw_build(ROWS, COLS, 34, 1);
    world = p;
  }

最后说明:我上面展示的技巧并不好。不要在绿地上这样做。我刚刚展示了如何绕过 broken 规范。

您遇到了挑战,您必须从一个简单的 1d 数组 模拟 一个 2d 数组。如果您查看给定的函数声明,您会发现 GW *gw_build(...) 告诉您 gw_build 将 return 和 arraystruct GW。现在理想情况下,您可以通过创建 array of pointers to struct GW 让事情变得更容易,但显然作业的一部分是让您使用一个简单的结构数组。

这简化了分配,但使索引复杂化。意思是,你的 gw_build 声明可以简单地是:

GW *gw_build(int nrows, int ncols, int pop, int rnd){

    GW *list = calloc (nrows * ncols, sizeof *list);

    if (!list) {
        fprintf (stderr, "gw_build() error: virtual memory exhausted.\n");
        return NULL;
    }

    return list;
}

但是挑战在于填充和引用每个,特别是如果您想要伪二维表示。该作业实际上是一种理解指针和索引操作的练习。谈论这个的最好方法是通过例子。 (注意:下面的,我在gw.h中包含了gw_struct的声明,但是如果你做不到,就把声明放在每个源中文件)

#ifndef _gw_header_
#define _gw_header_ 1

typedef struct gw_struct GW;

struct gw_struct {
    int Alive;
    int row;
    int column;
    int id;
};

extern GW *gw_build(int nrows, int ncols, int pop, int rnd);

#endif

#ifdef _gw_header_只是一个控制头文件包含的例子。它确保 gw.h 只包含一次。加不到头文件可以报废

源文件gw.c只需要分配nrows * ncols个结构,所以可以这么简单:

#include <stdio.h>
#include <stdlib.h>

#include "gw.h"

GW *gw_build(int nrows, int ncols, int pop, int rnd){

    GW *list = calloc (nrows * ncols, sizeof *list);

    if (!list) {
        fprintf (stderr, "gw_build() error: virtual memory exhausted.\n");
        exit (EXIT_FAILURE);
    }

    return list;
}

既标准又不多讨论。真正的工作来自包含 main() 的源文件,您将在其中填充和使用由 gw_build 编辑的数组 return。

#include <stdio.h>

#include "gw.h"

#define ROWS 10
#define COLS 10

int main (void) {

    GW *world = gw_build (ROWS, COLS, 34, 1);
    int i, j;

    for (i = 0; i < ROWS; i++)
        for (j = 0; j < COLS; j++)
            (world + i * ROWS + j)->Alive = (i*ROWS + j) % 3;

    for (i = 0; i < ROWS; i++)
        for (j = 0; j < COLS; j++)
            printf (" world[%2d][%2d] : %d\n", i, j, (world + i*ROWS + j)->Alive);


    return 0;
}

(出于示例目的,我刚刚根据索引值在 0-2 之间填充了 Alive

当您从 gw_build 收到分配的数组时,您必须从数组的开头管理 offset 的填充和使用。您可以简单地使用 (0 < index < (nrows*ncols)),但是使用 伪二维索引 会失败。诀窍是简单地找到一种使用二维数组语法计算和引用每个偏移量的方法。如果您在上面注意到,每个结构的偏移量由 i * ROWS + j 访问。这允许 array[i][j] 的伪二维引用,其中 i 表示 i * nrowsj 只是该地址的额外偏移量。

您还可以选择使用 -> 运算符来引用如上所示的单元格,或者您可以编写使用 . 运算符引用结构成员的等效形式。备选方案如下所示:

#include <stdio.h>

#include "gw.h"

#define ROWS 10
#define COLS 10

int main (void) {

    GW *world = gw_build (ROWS, COLS, 34, 1);
    int i, j;

    for (i = 0; i < ROWS; i++)
        for (j = 0; j < COLS; j++)
            world[i * ROWS + j].Alive = (i * ROWS + j) % 3;

    for (i = 0; i < ROWS; i++)
        for (j = 0; j < COLS; j++)
            printf (" world[%2d][%2d] : %d\n", i, j, world[i * ROWS + j].Alive);


    return 0;
}

查看两者,如果您有任何问题,请告诉我。我使用了两个 #define 语句来修复示例中的 nrowsncols。编译并 运行 代码,您将看到输出:

$ ./bin/gwtest
 world[ 0][ 0] : 0
 world[ 0][ 1] : 1
 world[ 0][ 2] : 2
 world[ 0][ 3] : 0
 world[ 0][ 4] : 1
 world[ 0][ 5] : 2
 world[ 0][ 6] : 0
 world[ 0][ 7] : 1
 world[ 0][ 8] : 2
 world[ 0][ 9] : 0
 world[ 1][ 0] : 1
 world[ 1][ 1] : 2
 world[ 1][ 2] : 0
 world[ 1][ 3] : 1
 world[ 1][ 4] : 2
 world[ 1][ 5] : 0
 world[ 1][ 6] : 1
 world[ 1][ 7] : 2
 world[ 1][ 8] : 0
 world[ 1][ 9] : 1
 world[ 2][ 0] : 2
...
 world[ 8][ 8] : 1
 world[ 8][ 9] : 2
 world[ 9][ 0] : 0
 world[ 9][ 1] : 1
 world[ 9][ 2] : 2
 world[ 9][ 3] : 0
 world[ 9][ 4] : 1
 world[ 9][ 5] : 2
 world[ 9][ 6] : 0
 world[ 9][ 7] : 1
 world[ 9][ 8] : 2
 world[ 9][ 9] : 0