三维数组内存分配中的分段错误错误

我在C中有一个指针变量int ***a 。我将它作为&a ie引用传递给函数。 在函数中我得到一个int ****a类型的指针变量。 我正在分配这样的内存。

 *a=(int***)malloc(no1*sizeof(int**)); some loop from 0 to no1 (*a)[++l]=(int**)malloc((no1+1)*sizeof(int*)); some loop from 0 to no1 (*a)[l][h]=(int*)malloc(2*sizeof(int)); 

这只是我分配内存的时间。 没有给出实际的程序; 这里没有错误。 但是当我要这样做时:

 (*a)[l][h][0]=no1; 

它给了我一个“分段错误”错误,我无法理解为什么。

更新:我写了一个示例程序,它只分配内存。 这也给出了“分段错误”错误。

 #include #include #include void allocate(int ****a) { int i,j,k; if(((*a)=(int***)malloc(5*sizeof(int**)))==NULL) { printf("\nError in allocation of double pointer array\n"); exit(0); } for(i=0;i<5;i++)if(((*a)[i]=(int**)malloc(4*sizeof(int*)))==NULL) { printf("\nError in allocation of single pointer array on index [%d]\n",i); exit(0); } for(i=0;i<5;i++) for(j=0;j<4;i++) if(((*a)[i][j]=(int*)malloc(3*sizeof(int)))==NULL) { printf("\nError in allocation of array on index [%d][%d]\n",i,j); exit(0); } for(i=0;i<5;i++) for(j=0;j<4;i++) for(k=0;k<3;k++) (*a)[i][j][k]=k; } main() { int ***a; int i,j,k; allocate(&a); for(i=0;i<5;i++) for(j=0;j<4;i++) for(k=0;k<3;k++) printf("\na[%d][%d][%d] = %d ",i,j,k,a[i][j][k]); } 

来自问题的修改代码

你的代码有:

 for(i=0;i<5;i++) for(j=0;j<4;i++) 

几次。 第二个循环应该是递增j,而不是i。 copy'n'paste要非常小心。

此代码不会崩溃(但会泄漏)。

 #include  #include  void allocate(int ****a); void allocate(int ****a) { int i,j,k; printf("allocate: 1B\n"); if(((*a)=(int***)malloc(5*sizeof(int**)))==NULL) { printf("\nError in allocation of double pointer array\n"); exit(0); } printf("allocate: 1A\n"); printf("allocate: 2B\n"); for(i=0;i<5;i++) if(((*a)[i]=(int**)malloc(4*sizeof(int*)))==NULL) { printf("\nError in allocation of single pointer array on index [%d]\n",i); exit(0); } printf("allocate: 2A\n"); printf("allocate: 3B\n"); for(i=0;i<5;i++) for(j=0;j<4;j++) if(((*a)[i][j]=(int*)malloc(3*sizeof(int)))==NULL) { printf("\nError in allocation of array on index [%d][%d]\n",i,j); exit(0); } printf("allocate: 3A\n"); printf("allocate: 4B\n"); for(i=0;i<5;i++) for(j=0;j<4;j++) for(k=0;k<3;k++) (*a)[i][j][k]=k; printf("allocate: 4A\n"); } int main(void) { int ***a; int i,j,k; allocate(&a); for(i=0;i<5;i++) for(j=0;j<4;j++) for(k=0;k<3;k++) printf("a[%d][%d][%d] = %d\n",i,j,k,a[i][j][k]); } 

以前的答案

由于您没有向我们展示大部分代码,因此很难预测您是如何处理错误的,但同样地,由于您正在获得核心转储,因此您必须处理不当内容。

这是一些工作代码 - 没有用valgrind检查,因为它不适用于Mac OS X 10.8 - 这似乎有效。 分配失败的错误恢复未完成,并且还缺少销毁完全分配的数组的function。

 #include  #include  #include  static int ***allocate_3d_array(int no1, int ****a) { *a = (int***)malloc(no1 * sizeof(int**)); if (*a == 0) return 0; for (int l = 0; l < no1; l++) { if (((*a)[l]=(int**)malloc((no1+1)*sizeof(int*))) == 0) { while (l > 0) free((*a)[--l]); return 0; } } for (int l = 0; l < no1; l++) { for (int h = 0; h < no1; h++) { if (((*a)[l][h]=(int*)malloc(2*sizeof(int))) == 0) { /* Leak! */ return 0; } } } for (int l = 0; l < no1; l++) for (int h = 0; h < no1; h++) for (int k = 0; k < 2; k++) (*a)[l][h][k] = 10000 * l + 100 * h + k; return *a; } int main(void) { int no1 = 5; int ***a = 0; int ***b = allocate_3d_array(no1, &a); const char *pad[] = { " ", "\n" }; assert(b == a); if (a != 0) { for (int l = 0; l < no1; l++) for (int h = 0; h < no1; h++) for (int k = 0; k < 2; k++) printf("a[%d][%d][%d] = %.6d%s", l, h, k, a[l][h][k], pad[k]); // free memory - added by harpun; reformatted by Jonathan Leffler // Would be a function normally — see version 2 code. for (int l = 0; l < no1; l++) { for (int h = 0; h < no1; h++) free(a[l][h]); free(a[l]); } free(a); } return 0; } 

样本输出:

 a[0][0][0] = 000000 a[0][0][1] = 000001 a[0][1][0] = 000100 a[0][1][1] = 000101 a[0][2][0] = 000200 a[0][2][1] = 000201 a[0][3][0] = 000300 a[0][3][1] = 000301 a[0][4][0] = 000400 a[0][4][1] = 000401 a[1][0][0] = 010000 a[1][0][1] = 010001 a[1][1][0] = 010100 a[1][1][1] = 010101 a[1][2][0] = 010200 a[1][2][1] = 010201 a[1][3][0] = 010300 a[1][3][1] = 010301 a[1][4][0] = 010400 a[1][4][1] = 010401 a[2][0][0] = 020000 a[2][0][1] = 020001 a[2][1][0] = 020100 a[2][1][1] = 020101 a[2][2][0] = 020200 a[2][2][1] = 020201 a[2][3][0] = 020300 a[2][3][1] = 020301 a[2][4][0] = 020400 a[2][4][1] = 020401 a[3][0][0] = 030000 a[3][0][1] = 030001 a[3][1][0] = 030100 a[3][1][1] = 030101 a[3][2][0] = 030200 a[3][2][1] = 030201 a[3][3][0] = 030300 a[3][3][1] = 030301 a[3][4][0] = 030400 a[3][4][1] = 030401 a[4][0][0] = 040000 a[4][0][1] = 040001 a[4][1][0] = 040100 a[4][1][1] = 040101 a[4][2][0] = 040200 a[4][2][1] = 040201 a[4][3][0] = 040300 a[4][3][1] = 040301 a[4][4][0] = 040400 a[4][4][1] = 040401 

将此与您所拥有的相比较。 您可以添加更多诊断打印消息。 如果这没有足够的帮助,请创建一个类似于此的SSCCE( 简短,自包含,正确的示例 ),它可以在没有任何无关材料的情况下演示代码中的问题。

代码的第2版

这是一个稍微复杂的代码版本,模拟N分配后的内存分配失败(以及运行它的测试工具,其中N的每个值从0到35,实际上只有30个数组的分配。它也是包括释放数组的代码(类似于但不同于harpun编辑到我的答案中的代码 。最后与包含PID的行的交互意味着我可以在另一个终端窗口中检查ps内存使用情况。 (否则,我不喜欢那些做那种事情的程序 - 我想我应该通过system()从我的程序运行ps ,但我感觉很懒。)

 #include  #include  #include  #include  static int fail_after = 0; static int num_allocs = 0; static void *xmalloc(size_t size) { if (fail_after > 0 && num_allocs++ >= fail_after) { fputs("Out of memory\n", stdout); return 0; } return malloc(size); } static int ***allocate_3d_array(int no1, int ****a) { *a = (int***)xmalloc(no1 * sizeof(int**)); if (*a == 0) return 0; for (int l = 0; l < no1; l++) { if (((*a)[l]=(int**)xmalloc((no1+1)*sizeof(int*))) == 0) { for (int l1 = 0; l1 < l; l1++) free((*a)[l1]); free(*a); *a = 0; return 0; } } for (int l = 0; l < no1; l++) { for (int h = 0; h < no1; h++) { if (((*a)[l][h]=(int*)xmalloc(2*sizeof(int))) == 0) { /* Release prior items in current row */ for (int h1 = 0; h1 < h; h1++) free((*a)[l][h1]); free((*a)[l]); /* Release items in prior rows */ for (int l1 = 0; l1 < l; l1++) { for (int h1 = 0; h1 < no1; h1++) free((*a)[l1][h1]); free((*a)[l1]); } free(*a); *a = 0; return 0; } } } for (int l = 0; l < no1; l++) for (int h = 0; h < no1; h++) for (int k = 0; k < 2; k++) (*a)[l][h][k] = 10000 * l + 100 * h + k; return *a; } static void destroy_3d_array(int no1, int ***a) { if (a != 0) { for (int l = 0; l < no1; l++) { for (int h = 0; h < no1; h++) free(a[l][h]); free(a[l]); } free(a); } } static void test_allocation(int no1) { int ***a = 0; int ***b = allocate_3d_array(no1, &a); const char *pad[] = { " ", "\n" }; assert(b == a); if (a != 0) { for (int l = 0; l < no1; l++) { for (int h = 0; h < no1; h++) { for (int k = 0; k < 2; k++) { if (a[l][h][k] != l * 10000 + h * 100 + k) printf("a[%d][%d][%d] = %.6d%s", l, h, k, a[l][h][k], pad[k]); } } } } destroy_3d_array(no1, a); } int main(void) { int no1 = 5; for (fail_after = 0; fail_after < 33; fail_after++) { printf("Fail after: %d\n", fail_after); num_allocs = 0; test_allocation(no1); } printf("PID %d - waiting for some data to exit:", (int)getpid()); fflush(0); getchar(); return 0; } 

注意内存恢复是多么痛苦。 和以前一样,没有用valgrind进行测试,但是我从harpun对先前版本的测试中得到了保证。

第3版 - 清洁valgrind的健康状况

此代码与版本2中的测试非常相似。它在叶级分配中的内存分配失败时修复了清理中的内存泄漏。 该程序不再提示输入(更可取); 它需要一个可选的单个参数,即之后失败的分配数。 使用valgrind进行测试表明,如果参数为0-6,则没有泄漏,但是参数7存在泄漏。 没过多久就发现问题并修复它。 (当运行valgrind的机器可用时,它会更容易 - 在整个现场电源升级的长周末,它会被关闭。)

 #include  #include  #include  static int fail_after = 0; static int num_allocs = 0; static void *xmalloc(size_t size) { if (fail_after > 0 && num_allocs++ >= fail_after) { fputs("Out of memory\n", stdout); return 0; } return malloc(size); } static int ***allocate_3d_array(int no1, int ****a) { *a = (int***)xmalloc(no1 * sizeof(int**)); if (*a == 0) return 0; for (int l = 0; l < no1; l++) { if (((*a)[l]=(int**)xmalloc((no1+1)*sizeof(int*))) == 0) { for (int l1 = 0; l1 < l; l1++) free((*a)[l1]); free(*a); *a = 0; return 0; } } for (int l = 0; l < no1; l++) { for (int h = 0; h < no1; h++) { if (((*a)[l][h]=(int*)xmalloc(2*sizeof(int))) == 0) { /* Release prior items in current (partial) row */ for (int h1 = 0; h1 < h; h1++) free((*a)[l][h1]); /* Release items in prior (complete) rows */ for (int l1 = 0; l1 < l; l1++) { for (int h1 = 0; h1 < no1; h1++) free((*a)[l1][h1]); } /* Release entries in first (complete) level of array */ for (int l1 = 0; l1 < no1; l1++) free((*a)[l1]); free(*a); *a = 0; return 0; } } } for (int l = 0; l < no1; l++) for (int h = 0; h < no1; h++) for (int k = 0; k < 2; k++) (*a)[l][h][k] = 10000 * l + 100 * h + k; return *a; } static void destroy_3d_array(int no1, int ***a) { if (a != 0) { for (int l = 0; l < no1; l++) { for (int h = 0; h < no1; h++) free(a[l][h]); free(a[l]); } free(a); } } static void test_allocation(int no1) { int ***a = 0; int ***b = allocate_3d_array(no1, &a); const char *pad[] = { " ", "\n" }; assert(b == a); if (a != 0) { for (int l = 0; l < no1; l++) { for (int h = 0; h < no1; h++) { for (int k = 0; k < 2; k++) { if (a[l][h][k] != l * 10000 + h * 100 + k) printf("a[%d][%d][%d] = %.6d%s", l, h, k, a[l][h][k], pad[k]); } } } } destroy_3d_array(no1, a); } int main(int argc, char **argv) { int no1 = 5; int fail_limit = 33; if (argc == 2) fail_limit = atoi(argv[1]); for (fail_after = 0; fail_after < fail_limit; fail_after++) { printf("Fail after: %d\n", fail_after); num_allocs = 0; test_allocation(no1); } return 0; } 

版本4 - 更少的内存分配

更新2014-12-20

上面的代码会产生大量的内存分配,这会使发布和错误恢复变得复杂。 这是一个替代版本,只进行3次分配,一次用于指针指针的向量,一种用于指针数组,一种用于整数数组。 然后它将指针设置为指向内存中的正确位置。

 #include  #include  #include  static int fail_after = 0; static int num_allocs = 0; static void *xmalloc(size_t size) { if (fail_after > 0 && num_allocs++ >= fail_after) { fputs("Out of memory\n", stdout); return 0; } return malloc(size); } static int ***allocate_3d_array(int no1, int ****a) { int ***d0 = (int***)xmalloc(no1 * sizeof(int**)); int **d1 = (int **)xmalloc(no1 * no1 * sizeof(int *)); int *d2 = (int *)xmalloc(no1 * no1 * 2 * sizeof(int)); if (d0 == 0 || d1 == 0 || d2 == 0) { free(d0); free(d1); free(d2); *a = 0; return 0; } for (int l = 0; l < no1; l++) { d0[l] = &d1[l * no1]; for (int h = 0; h < no1; h++) { d0[l][h] = &d2[(l * no1 + h) * 2]; for (int k = 0; k < 2; k++) d0[l][h][k] = l * 10000 + h * 100 + k; } } *a = d0; return *a; } static void destroy_3d_array(int ***a) { if (a != 0) { free(a[0][0]); free(a[0]); free(a); } } static void test_allocation(int no1) { int ***a = 0; int ***b = allocate_3d_array(no1, &a); const char *pad[] = { " ", "\n" }; assert(b == a); if (a != 0) { for (int l = 0; l < no1; l++) { for (int h = 0; h < no1; h++) { for (int k = 0; k < 2; k++) { if (a[l][h][k] != l * 10000 + h * 100 + k) printf("Oops: a[%d][%d][%d] = %.6d%s", l, h, k, a[l][h][k], pad[k]); } } } } destroy_3d_array(a); } int main(int argc, char **argv) { int no1 = 5; int fail_limit = 4; if (argc == 2) fail_limit = atoi(argv[1]); for (fail_after = 0; fail_after < fail_limit; fail_after++) { printf("Fail after: %d\n", fail_after); num_allocs = 0; test_allocation(no1); } return 0; } 

这与Mac OS X 10.10.1上的GCC 4.9.1有一个干净的健康状况,使用valgrind版本valgrind-3.11.0.SVN(从SVN树构建,带有一些针对Mac OS X的必要修复,但没有足够的抑制) )。

当我得出答案时,触发了诊断打印(以'哎呀'开头); 当时我的指针计算错了。

对不起,但是,直言不讳:这是一种处理3D数组的可怕方式:一个双嵌套循环,带有一堆对malloc()的调用,然后是三重间接,以便在运行时获取一个值。 Yeuch! :O)

执行此操作的常规方法(在HPC社区中)是使用一维数组并自己进行索引计算。 假设索引ix方向上迭代nx 平面jy方向上迭代ny 铅笔 ,并且kz方向上迭代nz单元。 然后铅笔有nz元素,一个平面有nz*ny元素,整个“砖”有nz*ny*nx元素。 因此,您可以使用以下代码遍历整个结构:

 for(i=0; i 

这种结构的优点是你可以通过一次调用malloc()来分配它,而不是一堆嵌套的调用:

 int *a; a = malloc(nx*ny*nz*sizeof(int)); 

构造x=a[i][j][k]有三个间接级别:你必须从内存中获取一个地址, a ,添加一个偏移量, i ,从内存中获取该地址, a[i] ,添加一个offset, j ,从存储器中获取该地址, a[i][j] ,添加偏移量, k ,并且(最终)获取数据a[i][j][k] 。 所有这些中间指针都在浪费缓存行和TLB条目。

构造x=a[(i*ny+j)*nz+k]具有一个间接级别,代价是两个额外的整数乘法:从内存计算偏移量,提取地址,'a',计算并添加offset,(i * ny + j)* nz + k,获取数据。

此外,基本上没有办法根据数据访问模式改进三重间接方法的性能。 如果我们实际访问每个单元格,我们可以做这样的事情以避免一些索引计算的开销。

 ij = 0; for(i=0; i 

根据您正在做的事情,这可能也不是很好,并且根据您的访问模式,所有替代布局和索引方法(例如Morton或Ahnenteufel索引)可能更合适。 我并不是想对3D笛卡尔网格表示或索引进行完整的论述,只是说明“三星级”解决方案由于多种原因而非常糟糕。

通过使用(*a)[l][h][0]您试图取消引用普通的int而不是指针。

直接使用a[l][h][0]为其分配任何值。