1

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

3 回答 3

5

从问题修改代码

您的代码有:

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

几次。第二个循环应该递增 j,而不是 i。复制粘贴时要非常小心。

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

#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(简短、自包含、正确的示例),以在没有任何无关材料的情况下演示代码中的问题。

代码版本 2

这是一个更复杂的代码版本,它模拟 N 次分配后的内存分配失败(以及一个测试工具,它使用从 0 到 35 的每个 N 值运行它,其中实际上只有 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 进行了一些必要的修复,但没有足够的抑制) .

在我开发答案时触发了诊断打印(以“糟糕”开头);当时我的指针计算错误。

于 2013-09-02T20:06:10.123 回答
4

抱歉,直言不讳:这是一种处理 3D 数组的可怕方式:一个双嵌套循环,其中包含大量对 的调用malloc(),然后是三重间接以在运行时获取值。呸!:o)

执行此操作的常规方法(在 HPC 社区中)是使用一维数组并自己进行索引计算。假设 index在x方向上i迭代nx 平面,在y方向上迭代铅笔,并在z方向上迭代单元格。那么铅笔有元素,平面有元素,整个“砖”都有元素。因此,您可以使用以下方法迭代整个结构:jny knznznz*nynz*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从内存中获取该地址a[i],添加偏移量,j从内存中获取该地址a[i][j],添加偏移量k,和(最后) 获取数据,a[i][j][k]. 所有这些中间指针都在浪费缓存行和 TLB 条目。

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

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

ij = 0;
for(i=0; i<nx; i++) {
    ii=i*ny;
    for(j=0; j<ny; j++) {
        ij=(ii+j)*nz;
        for(k=0; k<nz; k++) {
            printf("a(%d,%d,%d) = %d\n", i, j, k, a[ij+k]);
        }
    }
}

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

于 2013-09-02T20:52:48.570 回答
0

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

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

于 2013-09-02T19:28:41.790 回答