将1d缓冲区重新组织为2d阵列时崩溃

时间:2014-06-30 07:14:50

标签: c arrays pointers

我有一个1d缓冲区,我必须重新组织才能作为二维数组进行访问。我在下面粘贴了我的代码:

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

void alloc(int ** buf, int r, int c)
{
    int **temp=buf;
    for(int i=0; i<r; i++)
        buf[i]=(int *)temp+i*c;
}
void main()
{
    int *buffer=(int *)malloc(sizeof(int)*100);
    int **p = (int**) buffer;
    alloc(p, 4, 4);
//for(int i=0;i<r;i++)
    //for(int j=0;j<c;j++)
      //  printf("\n %p",&p[i][j]);


    p[0][3]=10;
    p[2][3]=10;
    p[3][2]=10; //fails here
    printf("\n %d", p[2][3]);
}

我进行作业时代码崩溃了。 我已经为不同的测试用例运行了代码。我观察到当p [0] [x]赋值,然后赋值给p [x] [任何]时代码崩溃,代码在第二次赋值时崩溃。只有当第一个赋值的第一个索引为0并且没有其他索引在第二个赋值时发生崩溃且第一个索引的第一个索引等于第一个赋值的第二个索引时,才会看到此崩溃。

例如,在上面的代码崩溃发生在执行p [0] [3]之后的p [3] [2]。如果我将第一个赋值更改为p [0] [2],则崩溃将发生在p [2] [3](或p [2] [任何]此事件)。

我已经检查了p指向的内存,通过取消注释double for循环,它似乎没问题。我怀疑在非法记忆位置写作,但上述观察已经排除了这一点。

4 个答案:

答案 0 :(得分:2)

我认为你的根本问题是对C中的2D数组的误解(你的代码是C,而不是C ++)。 2D数组是连续的存储空间,并且必须事先知道内部数组的大小。因此,除非在编译时已知内部数组的大小,否则基本上无法将1D数组转换为2D数组。如果已知,您可以执行类似

的操作
int *buffer=(int *)malloc(sizeof(int)*100);
typedef int FourInts[4];
FourInts *p = (FourInts *)buffer; 

并且您不需要alloc功能,数据已经正确对齐。 如果您事先不知道内部数组的大小,则可以定义并分配指向1D缓冲区的数组数组。代码:

int ** alloc(int * buf, int r, int c)
{
   int **array2d = (int **) malloc(r*sizeof(int *));
   for(int i=0; i<r; i++)
       array2d[i] = buf+i*c;   
   return array2d;
}

void _tmain()
{
    int *buffer=(int *)malloc(sizeof(int)*100);
    int **p = alloc(buffer,4,4);

   p[0][3]=10;
   p[2][3]=10;
   p[3][2]=10; //fails here
   printf("\n %d", p[2][3]);
   free(buffer);
   free(p);

}

但是在不使用缓冲区的情况下简单地构建数组数组会更容易。如果你可以使用C ++而不是C,那么一切都会更容易。

答案 1 :(得分:2)

问题是你的2D数组实际上是一个指向数组的指针数组。这意味着你需要为指针留出空间。目前,您的指针位于数组中的位置0-3,但p[0]也指向位置0.当您写入&#39; p [0,3]&#39;你正在覆盖p[3]

一种(诱人的)修复方法是在数组的开头允许指针空间。因此,您可以更改alloc方法,以便在前面留出一些空间。类似的东西:

buf[i] = (int *)(temp+r) + i*c;

请注意+r添加temp。它需要在投放之前添加到temp,因为您可以假设intint *属于同一类型。

我不建议使用此方法,因为您仍需记住在原始malloc中分配额外空间以考虑指针数组。这也意味着你不只是将一维数组转换为二维数组。

另一个选择是将数组分配为指向单独分配的数组的指针数组。这是分配2D数组的常规方法。但是,这不会像您在1D阵列中那样产生连续的数据数组。

在这两个选项之间,你可以分配一个额外的指针数组来保存你需要的指针,然后将它们指向数据。将您的分配更改为:

int **alloc(int * buf, int r, int c)
{
    int **temp = (int **)malloc(sizeof (int *)* r);
    for (int i = 0; i<r; i++)
        temp[i] = buf + i*c;
    return temp;
}

然后你称之为:

int **p = alloc(buffer, 4, 4);

你还需要释放额外的缓冲区。

这样您的数据和访问它的指针就会保持独立,您可以保持原始的一维数据连续。

请注意,您不需要在c中投射malloc的结果,事实上有人说您不应该这样做。

另请注意,此方法删除了对转换指针的所有要求,任何不需要转换的东西都是好事。

答案 2 :(得分:1)

如果您已经拥有1D数据块,那么将其作为2D数组访问的方法是创建一个指针数组 - 每行一个。您将第一个指向块的开头,下一个按列数偏移,等等。

int **b;
b = malloc(numrows*sizeof(int*));
b[0]=temp; // assuming temp is 1D block
for(int ii=1; ii<numrows;ii++)
    b[ii]=b[0]+ii*numcols;

现在您可以访问b[i][j],它将指向您的原始数据。只要在运行时知道行数和列数,就可以传递可变长度的2D数组。请记住,完成后必须释放指针向量以及主数据块,否则会导致内存泄漏。

如果你谷歌nrutil.c,你会发现这个例子 - 这是从C使用的数字食谱技巧中得到的。

答案 3 :(得分:0)

这个函数原型应该是:

void alloc(int *buf[][], int r, int c) //buf[][] <=> **buf, but clearer in this case
{
   //*(buf[i]) = 
   ...
}

如果你想处理同一个数组,你必须将指针传递给这个2D数组(* [] [])。

你现在这样做的方法就是处理副本,所以当你返回时它没有被修改。

您还应该正确初始化数组:

p = malloc(sizeof(int *[]) * nb of row);
for each row
  p[row] = malloc(sizeof(int []) * nb of col);