【问题标题】:Allocate contiguous memory分配连续内存
【发布时间】:2012-10-27 23:21:11
【问题描述】:

我正在尝试在 C 中分配大量连续内存并将其打印给用户。我这样做的策略是创建两个指针(一个指向 double 的指针,一个指向 double 的指针),将其中一个指向整个大小(m * n),在这种情况下是指向 double 的指针。然后将第二个 malloc 到 m 的大小。最后一步是遍历 m 的大小并执行指针运算,以确保大数组中双精度数的地址将存储在连续的内存中。这是我的代码。但是当我打印出地址时,它似乎并不连续(或任何顺序)。如何正确打印出双打的内存地址(它们的值都是 0.0)?

/* correct solution, with correct formatting */


/*The total number of bytes allocated was:  4
0x7fd5e1c038c0 - 1
 0x7fd5e1c038c8 - 2
 0x7fd5e1c038d0 - 3
 0x7fd5e1c038d8 - 4*/

 double **dmatrix(size_t m, size_t n);

int main(int argc, char const *argv[])
{
    int m,n,i;
    double ** f;
    m = n = 2;  
    i = 0;

    f = dmatrix(sizeof(m), sizeof(n));

    printf("%s %d\n", "The total number of bytes allocated was: ", m * n);
    for (i=0;i<n*m;++i) {
        printf("%p - %d\n ", &f[i], i + 1);
    }
    return 0;
}

double **dmatrix(size_t m, size_t n) {

    double ** ptr1 = (double **)malloc(sizeof(double *) * m * n);
    double * ptr2 = (double *)malloc(sizeof(double) * m);

    int i;
    for (i = 0; i < n; i++){
        ptr1[i] = ptr2+m*i;
    }


    return ptr1;
}

【问题讨论】:

    标签: c arrays unix memory


    【解决方案1】:

    记住,记忆只是记忆。听起来很陈词滥调,但很多人似乎认为 C 中的内存分配和内存管理是一些魔法巫术。它不是。在一天结束时,您可以分配所需的任何内存,并在完成后释放它。

    所以从最基本的问题开始:如果您需要“n”个double 值,您将如何分配它们?

    double *d1d = calloc(n, sizeof(double));
    // ... use d1d like an array (d1d[0] = 100.00, etc. ...
    free(d1d);
    

    足够简单。下一个问题,分两部分,其中第一部分与内存分配无关(还):

    1. 大小为m*n 的二维数组中有多少个double 值?
    2. 我们如何分配足够的内存来容纳它们。

    答案:

    1. 在 m*n 二维双打矩阵中有 m*n 双打
    2. 分配足够的内存来保存 (m*n) 个双精度数。

    看起来很简单:

    size_t m=10;
    size_t n=20;
    double *d2d = calloc(m*n, sizeof(double));
    

    但是我们如何访问实际元素呢?有点数学是为了。知道mn,你就可以这么简单了

    size_t i = 3; // value you want in the major index (0..(m-1)).
    size_t j = 4; // value you want in the minor index (0..(n-1)).
    d2d[i*n+j] = 100.0;
    

    有没有更简单的方法来做到这一点?在标准 C 中,;在 C++ 中。标准 C 支持一种非常方便的功能,可以生成正确的代码来声明动态大小的可索引数组:

    size_t m=10;
    size_t n=20;
    double (*d2d)[n] = calloc(m, sizeof(*d2d));
    

    怎么强调都不为过:标准 C 支持这一点,C++ 不支持。如果您使用 C++,您可能想编写一个对象类来为您完成这一切,所以除此之外不再提及。

    那么上面的实际做了什么?首先,很明显我们仍在分配与之前相同的内存量。即m*n 元素,每个sizeof(double) 大。但你可能会问自己,“那个变量声明是怎么回事?”这需要一点解释。

    这之间有明显的区别:

    double *ptrs[n];  // declares an array of `n` pointers to doubles.
    

    还有这个:

    double (*ptr)[n]; // declares a pointer to an array of `n` doubles.
    

    编译器现在知道每行的宽度是多少(n 每行加倍),因此我们现在可以使用 两个 索引来引用数组中的元素:

    size_t m=10;
    size_t n=20;
    double (*d2d)[n] = calloc(m, sizeof(*d2d));
    d2d[2][5] = 100.0; // does the 2*n+5 math for you.
    free(d2d);
    

    我们可以将其扩展到 3D 吗?当然,数学开始看起来有点奇怪,但它仍然只是将计算偏移到一个 big'ol'block'o'ram 中。首先是“自己做数学”的方式,使用 [i,j,k] 进行索引:

    size_t l=10;
    size_t m=20;
    size_t n=30;
    double *d3d = calloc(l*m*n, sizeof(double));
    
    size_t i=3;
    size_t j=4;
    size_t k=5;
    d3d[i*m*n + j*m + k] = 100.0;
    free(d3d);
    

    您需要盯着其中的数学一分钟,才能真正了解它如何计算大块 ram 中 double 值的实际位置。使用上述维度和所需索引,“原始”索引为:

    i*m*n = 3*20*30 = 1800
    j*m   = 4*20    =   80
    k     = 5       =    5
    ======================
    i*m*n+j*m+k     = 1885
    

    所以我们在那个大线性块中击中了第 1885 个元素。让我们再做一个。 [0,1,2] 呢?

    i*m*n = 0*20*30 =    0
    j*m   = 1*20    =   20
    k     = 2       =    2
    ======================
    i*m*n+j*m+k     =   22
    

    即线性数组中的第 22 个元素。

    现在应该很明显,只要您保持在数组的自定义范围内,i:[0..(l-1)], j:[0..(m-1)], and k:[0..(n-1)] 任何有效的索引三重奏都会在线性数组中找到一个唯一值,而其他 有效的三重奏也会定位。

    最后,我们使用与之前对 2D 数组相同的数组指针声明,但将其扩展到 3D:

    size_t l=10;
    size_t m=20;
    size_t n=30;
    double (*d3d)[m][n] = calloc(l, sizeof(*d3d));
    d3d[3][4][5] = 100.0;
    free(d3d);
    

    再一次,这一切真的和我们之前手工做的数学一样,但让编译器为我们做这件事。

    我意识到这可能有点绕你的头,但这很重要。如果你有连续的内存矩阵是最重要的(比如将矩阵提供给 OpenGL 等图形渲染库等),你可以使用上述技术相对轻松地做到这一点。

    最后,您可能想知道,如果您可以这样做,为什么有人首先要做整个指针数组到指针数组到指针数组到值的事情?很多原因。假设您要替换行。交换指针很容易;复制整行?昂贵的。假设您要替换 3D 数组 (l*n*m) 中的整个表格维度 (m*n),更是如此,交换指针:简单;复制整个m*n 表? 昂贵。而不是那么明显的答案。如果行宽需要独立于行(即 row0 可以是 5 个元素,row1 可以是 6 个元素),该怎么办?固定的l*m*n 分配根本不起作用。

    祝你好运。

    【讨论】:

      【解决方案2】:

      没关系,我想通了。

        /* The total number of bytes allocated was:  8
          0x7fb35ac038c0 - 1
           0x7fb35ac038c8 - 2
           0x7fb35ac038d0 - 3
           0x7fb35ac038d8 - 4
           0x7fb35ac038e0 - 5
           0x7fb35ac038e8 - 6
           0x7fb35ac038f0 - 7
           0x7fb35ac038f8 - 8 */
      
      double ***d3darr(size_t l, size_t m, size_t n);
      
      int main(int argc, char const *argv[])
      {
          int m,n,l,i;
          double *** f;
          m = n = l = 10; i = 0;
      
          f = d3darr(sizeof(l), sizeof(m), sizeof(n));
          printf("%s %d\n", "The total number of bytes allocated was: ", m * n * l);
          for (i=0;i<n*m*l;++i) {
              printf("%p - %d\n ", &f[i], i + 1);
          }
      
          return 0;
      }
      
      double ***d3darr(size_t l, size_t m, size_t n){
      
          double *** ptr1 = (double ***)malloc(sizeof(double **) * m * n * l);
          double ** ptr2 = (double **)malloc(sizeof(double *) * m * n);
          double * ptr3 = (double *)malloc(sizeof(double) * m);
      
          int i, j;
          for (i = 0; i < l; ++i) {
              ptr1[i] = ptr2+m*n*i;
              for (j = 0; j < l; ++j){
                  ptr2[i] = ptr3+j*n;
              }
          }
      
          return ptr1;
      }
      

      【讨论】:

      • 所以您不希望将 end- 存储在连续的内存中,只需要指向它们的指针吗? mmk。
      • 什么意思?我错过了什么?
      • 我认为您没有遗漏任何东西。我可能误解了这个问题。当你说你希望你的矩阵分配在连续的内存中时,我认为它是指 whole 矩阵的 one 内存块(这是可行的,而不是所有那些 malloc你的自我回答)。如果您有兴趣,我可以在另一个答案中发布代码示例来演示这一点,尽管您可能已经知道如何操作。
      • 那太好了。我认为我正在尝试做的是并排分配内存。所以对于整个矩阵,中间没有空格。
      • 好的。我会看看我是否可以为您提供一些东西,以演示如何在 C 中使用单个分配进行 2D 和 3D 工作。
      猜你喜欢
      • 1970-01-01
      • 2012-07-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-14
      • 2023-04-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多