【问题标题】:Create struct pointer to array in separate function在单独的函数中创建指向数组的结构指针
【发布时间】:2015-12-31 23:54:47
【问题描述】:

我无法将结构指针作为数组创建和访问。这是一道作业题,但这只是我对操作的误解。

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

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

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

我已尝试通过多种方式解决此问题。我最初将变量 world 初始化为

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

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

没有循环并做

GW* list[nrows][ncols];

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

我可以创建、maloc 和访问一个

GW* World[50][50];

在主函数本身中,一切正常,但是一旦我尝试通过从 gw_build 函数返回来设置它,它就根本不起作用。因此,我尝试更改返回类型,但又一次导致了无效的返回错误。

感谢任何指导,谢谢!

【问题讨论】:

    标签: c function multidimensional-array data-structures parameter-passing


    【解决方案1】:

    正如basav 在他/她的answer 中指出的那样,创建函数的定义只允许返回指向一维数组的指针。

    但是,您可以使用讨厌的铸造锤返回 2D 数组:

    #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 解决方案的不同之处在于你最终得到一个线性 数组,即所有元素都放在一个 内存中块。

    注意:要摆脱“脏”转换,您可以使用另一个技巧,即引入临时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;
      }
    

    最后说明:我上面展示的技巧并不好。不要在绿色场地上这样做。我只是展示了如何绕过破碎的规范。

    【讨论】:

      【解决方案2】:

      您偶然发现了必须模拟从简单的1d 数组中的2d 数组的挑战。如果您查看给出的函数声明,您会看到GW *gw_build(...),它告诉您gw_build 将返回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 返回的数组。

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

      (例如,我刚刚在Alive 之间填充了0-2 之间的值,具体取决于索引值)

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

      您还可以选择使用-&gt; 运算符来引用如上所示的单元格,或者您可以编写一个等效的表单,使用. 运算符来引用结构成员。替代方案如下所示:

      #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
      

      【讨论】:

      • 有时以正确和好的方式做事是困难的...... ;-)
      • 阿们...,但以艰难的方式去做可能正是这位狡猾的教授所计划的——只是为了巩固一些基础知识...
      • 绝对了不起的答案。非常感谢!
      • 很高兴我能帮上忙。一旦你克服了真正学习如何管理指针、分配以及在函数之间传递它们的微妙之处,你就可以真正开始明白为什么没有一种语言可以与 C 的灵活性、功能和控制相媲美。当你结合起来时由于能够处理指向函数的指针数组,因此在 C 中可以完成的工作几乎没有限制。学习曲线可能有点陡峭,但没有什么真正值得学习的东西是容易的..
      【解决方案3】:
      extern GW *gw_build(int nrows, int ncols, int pop, int rnd);
      

      这意味着 gw_build 返回一个指向一维数组的指针。

      你想要实现的是一个二维数组:

      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 只能返回一行。

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

      【讨论】:

      • 所以我认为我的解决方案是更改我的结构,使其只包含一件事:将结构 ** 转换为结构(它将充当我的 2d 结构数组)?
      • 您想将其用作二维数组还是一维数组??
      • 我需要将它用作 [rows][columns] 的二维数组
      【解决方案4】:

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

      查看这篇文章了解一些好的细节

      Performance of 2-dimensional array vs 1-dimensional array

      【讨论】:

      • 我担心我不得不这样做。它只是让事情更难访问。但是谢谢你,我现在会尝试实现它!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-03
      相关资源
      最近更新 更多