【问题标题】:Stack memory use continuous dynamically allocated array vs statically [closed]堆栈内存使用连续动态分配的数组与静态[关闭]
【发布时间】:2016-03-21 21:58:16
【问题描述】:

我正在使用可变大小的 3D 和 4D 数组,它们必须是连续的(有时更容易调用 *(&x[0][0][0] + k) 而不是 'x[][][]' 方式)。因为数组的大小可变,我需要动态分配。我在另一个答案 (Dynamic memory allocation for 3D array) 中找到了这段代码,它工作正常,但我不知道堆栈使用了多少内存。

double ***arr3dAlloc(const int ind1, const int ind2, const int ind3) {
    int i;
    int j;
    double ***array = (double***)malloc((ind1 * sizeof(double*)) + 
                                        (ind1 * ind2 * sizeof(double**)) + 
                                        (ind1 * ind2 * ind3 * sizeof(double)));
    for (i = 0; i < ind1; ++i) {
        array[i] = (double**)(array + ind1) + i * ind2;
        for (j = 0; j < ind2; ++j) {
            array[i][j] = (double*)(array + ind1 + ind1 * ind2) + i * ind2 * ind3 + j * ind3;
        }
    }
    return array;
}

问题是:

  • double ***arr1 = arr3dAlloc(N1,N2,N3);double arr2[N1][N2][N3];有什么区别

  • 鉴于arr2 使用堆栈的N1*N2*N3*sizeof(double) 内存,arr1 使用多少内存?只有sizeof(double***)

  • 一般来说,有没有一种方法可以测量任何变量的堆栈内存使用情况?

【问题讨论】:

  • 没有 3D 或 4D 数组,也没有任何东西可以作为一个数组使用。在 C 语言中,作为 3 星级程序员也绝对不是恭维。大多数情况下,这是设计不佳的信号。
  • 代码是针对科学海豚的,所以数组***类似于population[J][K][N],其中 J 和 K 索引代表空间网格的单元格,N 代表单元格的人口数我,j。代码的粗略部分使用指向第一个元素的指针并进行一些计算,但是一些要计算的函数必须易于编辑和阅读,所以我想使用 population[i][j][n] 表示法。我需要使用相同的方案但不同的“特殊功能”运行多个模拟,所以一直停下来思考“我在哪个内存位置”不是最佳的
  • double *** 不是数组甚至是类似的数据结构!指针不是数组。并且 tp 指针不构成 2D 数组, 3 不是 3D 数组。如果需要,为什么不使用真正的数组?狂野的演员应该让你怀疑。你甚至没有分配正确的内存。

标签: c multidimensional-array stack


【解决方案1】:

指针的大小与各自机器架构的指针大小相同 (现在经常longlong long)。该大小与被引用的内存类型无关。 (“**”只是指另一个指针。)

数组 type[N] 总是分配 sizeof(type)*N 字节的内存。 sizeof() 包括填充到正确对齐边界的类型。 因此,使用malloc(sizof(type)*N) 将为类型N 个元素的数组分配足够的内存,并提供指向已分配内存的指针。您也可以使用calloc() 将内存初始化为零。

由于分配的内存不依赖于多维数组的组织结构,因此这种方法适用于 >=1 的任意维数。

解决您的问题:

  • double ***arr1 = … 只是将指针存储到某个内存区域, 而arr2[N1][N2][N3] 正在为N1*N2*N3 双打分配填充空间。
  • arr1 使用 sizeof(void *) 字节, 而arr2 正在使用N1*N2*n3*sizeof(double) 字节。
  • sizeof(var) 总是给出变量的大小, 这是在 data/bss 段中还是在堆栈中。

【讨论】:

    【解决方案2】:

    您的代码分配了一个 3D 数组和一个指向 double 的指针数组的指针数组,以通过双重间接访问其元素。这种方法存在一些问题:

    • 您的代码未将double 数组与double 边界对齐。如果指针大小为 4 字节且数组大小为 3x4x5,则double 数组将未对齐,这可能会在某些系统上导致未定义的行为。

    • 在您的尺寸计算中使用的类型是近似的。

    • 在计算超大矩阵的大小时,int 类型可能会溢出。

    这是一个更正的版本(假设所有指针类型都具有相同的大小):

    double ***arr3dAlloc(int ind1, int ind2, int ind3) {
        size_t level1_size = sizeof(double**) * ind1;
        size_t level2_size = sizeof(double*) * ind1 * ind2;
        size_t padding = (sizeof(double) - (level1_size + level2_size) % sizeof(double))
                          % sizeof(double);
        size_t level3_size = sizeof(double) * ind1 * ind2 * ind3;
        int i, j;
        double ***array = calloc(1, level1_size + level2_size + padding + level3_size);
        double *array3d = (double*)((unsigned char*)array + level1_size + level2_size + padding);
        for (i = 0; i < ind1; ++i) {
            array[i] = (double**)(array + ind1) + i * ind2;
            for (j = 0; j < ind2; ++j) {
                array[i][j] = array3d;
                array3d += ind3;
            }
        }
        return array;
    }
    

    请注意,使用 C99,您可以分配可以直接在主计算代码中使用的真实 3D 数组:

    /* array3d is a pointer to an allocated 3D array: */
    double (*array3d)[ind2][ind3] = calloc(sizeof(*array3d), ind1);
    
    /* array3d can be used like a statically defined array: */
    for (int i = 0; i < ind1; i++) {
        for (int j = 0; j < ind2; j++) {
            for (int k = 0; k < ind3; k++) {
                array3d[i][j][k] = i + j + k;
            }
        }
    }
    

    【讨论】:

    • “您的尺寸计算中使用的类型是近似的”是什么意思?计算似乎是在指针和整数方面,它们不是近似类型。我需要考虑使用的值是否只是大致正确。
    • @JonathanLeffler: 例如(ind1 * sizeof(double*)) + (ind1 * ind2 * sizeof(double**)) 应该是(ind1 * sizeof(double**)) + (ind1 * ind2 * sizeof(double*))... 这并不重要,因为我还假设所有指针都具有相同的大小。
    • 啊,我明白了——是的,从技术上讲,sizeof 运算符中使用的指针类型之间存在不匹配,但正如您所说,sizeof(double *) == sizeof(double **) 在所有实际系统上都是如此。 (标准要求void *char * 大小相同;它要求所有结构指针大小相同;它要求所有联合指针大小相同;它对指向其他指针的指针没有任何要求对象,包括指向指针的指针。这是 §6.2.5 类型,C11 标准中的 ¶28。)
    • 在几分钟内了解大量信息!代码不是我的,是另一个问题的答案,但我认为我开始明白你在说什么...... size_t insted 的使用直接在 calloc() 中使用 indX 是针对“int 问题“大矩阵?为什么*array3d 中有填充? &amp;array[i][j][0]&amp;array[i][j+1][0] 之间有空格吗?谈论 C99 方法:我可以从主外部调用array3d,其中 ind1、ind2、ind3 未知?抱歉,如果这是新手问题...
    • 如何测量填充大小?
    【解决方案3】:

    这里有几个问题需要解决。

    首先是全局变量、堆和栈的区别。这些都是 RAM 的一部分。 malloc() 分配堆的一部分并返回一个指针。这在 Internet 上的许多其他地方都有讨论。

    另一个问题是编译时分配与运行时分配。对于编译时分配,必须事先知道数组的大小。您的示例使用运行时分配 (malloc()),因为数组的大小作为变量传递给函数。在编译时不知道数组大小的情况下,不可能在堆栈或全局变量中分配数组。

    【讨论】:

      猜你喜欢
      • 2011-04-25
      • 1970-01-01
      • 2021-04-01
      • 2019-05-17
      • 1970-01-01
      • 2023-04-02
      • 2010-12-11
      • 2016-02-01
      • 2020-09-15
      相关资源
      最近更新 更多