三维阵列内存分配中的分段错误错误

时间:2013-09-02 19:21:07

标签: c

我在C中有一个指针变量int ***a。我将它传递给&a的函数,即参考。在函数中,我得到一个类型为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<stdio.h>
#include<malloc.h>
#include<stdlib.h>

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]);
}

3 个答案:

答案 0 :(得分:5)

来自问题的修订代码

您的代码有:

for(i=0;i<5;i++)
    for(j=0;j<4;i++)
几次。第二个循环应该是递增j,而不是i。 copy'n'paste要非常小心。

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

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

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 - 这似乎有效。分配失败的错误恢复未完成,并且还缺少销毁完全分配的数组的功能。

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

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(Short, Self-Contained, Correct Example),以便在没有任何无关材料的情况下演示代码中的问题。

代码的第2版

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

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

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 <assert.h>
#include <stdio.h>
#include <stdlib.h>

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 <assert.h>
#include <stdio.h>
#include <stdlib.h>

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,但没有足够的抑制)。

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

答案 1 :(得分:3)

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

这样做的常规方法(在HPC社区中)是使用一维数组并自己进行索引计算。假设索引i x 方向上迭代nx 个飞机j遍历ny 铅笔 y 方向的k遍历 z 方向的nz个单元格。然后一支铅笔有nz个元素,一个平面有nz*ny个元素,整个“砖”有nz*ny*nx个元素。因此,您可以使用:

遍历整个结构
for(i=0; i<nx; i++) {
    for(j=0; j<ny; j++) {
        for(k=0; k<nz; k++) {
            printf("a(%d,%d,%d) = %d\n", i, j, k, a[(i*ny+j)*nz+k]);
        }
    }
}

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

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

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

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

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

x=a[(i*ny+j)*nz+k]

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

答案 2 :(得分:0)

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

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