2

我有一个结构“单元格”定义为

typedef struct{
int id;
terrainType terrain;
} cell;

然后我制作一个二维单元格数组

cell** makeCellGrid(int sizeX, int sizeY)
{  
    cell** theArray;  
    int i;

    theArray = (cell**) malloc(sizeX*sizeof(cell*));  

    for ( i = 0; i < sizeX; i++)
    {
        theArray[i] = (cell*) malloc(sizeY*sizeof(cell));  
    }

   return theArray;  
}  

起初我认为这工作正常,但后来出现了一些段错误,我发现使用某些值(例如 makeCellGrid(32, 87) )它会中断。我对 C 指针和内存垃圾相当新鲜,希望有人能在这里为我指明正确的方向。

使用较低的数字界限,我可以毫无问题地访问它

map[i][j].id = x;

等等

编辑:忘记添加,从测试中,段错误源自

theArray[i] = (cell*) malloc(sizeY*sizeof(cell)); 
4

1 回答 1

3

malloc()该代码缺少对系统调用的错误检查。

因此,如果第一次调用malloc()失败,第二次调用(在循环中)尝试分配内存,NULL这确实会导致您正在目睹的分段违规。

您可能会考虑像这样修改您的代码:

#include <stdlib.h>

typedef struct {
  int id;
  TerrainType terrain;
} CellType;

void freeCellGrid(CellType ** ppCells, size_t sizeX)
{
  size_t i = 0;
  for (; i < sizeX; ++i)
  {
    free(ppCells[i]);
  }

  free(ppCells);
}

CellType ** makeCellGrid(size_t sizeX, size_t sizeY)
{  
    CellType ** ppCells = malloc(sizeX * sizeof(*ppCells));  

    if (ppCells)
    {
      size_t i = 0;

      for (; i < sizeX; ++i)
      {
          ppCells[i] = malloc(sizeY * sizeof(**ppCells));  
          if (NULL == ppCells[i])
          {
            freeCellGrid(ppCells, i);
            ppCells = NULL;

            break;
          }
      }
   }

   return ppCells;  
} 

关于我的修改的注意事项:

  • 始终检查系统调用是否有错误(在返回malloc()on 错误的情况下NULL
  • 更好地使用unsigned类型来访问内存/数组索引;size_t是为了这个
  • 在 C 中,不需要转换void *函数返回的值,例如malloc()
  • 始终尝试尽快初始化变量;未初始化的变量很容易导致应用程序的“非理性”行为
  • 如果使用指针,将间接级别“编码”到它们的名称中可能会有所帮助(我在这里使用前缀pp表示它是一个 2 级间接)
  • 类型与变量不同:区分这一点的一种方法是使用大写字母 ( CellType) 开头类型名称和使用小写字母 ( ppCells) 开头的变量。
  • 如果将内存分配给指针并且分配内存的大小适合指针的类型很重要,那么使用(取消引用的)指针本身作为sizeof运算符的参数总是更安全,然后是某种类型。由于分配内存的指针的声明可能会在开发过程中发生更改,并且参数的调整malloc()将被忘记。简而言之:像我一样做是不容易出错的。
  • 如果封装结构(包括数组)的动态创建,那么还可以实现一个解除分配它的方法(这里:)freeCellGrid()。最好先编写此释放器,然后在编写分配器的错误处理时手动编写(如第二次调用所示malloc())。
于 2013-02-12T07:20:17.673 回答