c - 分配连续内存

标签 c arrays unix memory

我正在尝试在C中分配较大的连续内存空间,并将其输出给用户。我这样做的策略是创建两个指针(一个指向double的指针,一个指向double的指针),将其中一个分配给整个大小(m * n),在这种情况下,指向double的指针。然后将第二个分配给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语言中的内存分配和内存管理视为魔咒。不是。在一天结束时,您可以分配所需的任何内存,并在完成后释放它。

因此,从最基本的问题开始:如果您需要'n'double值,您将如何分配它们?

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

很简单。下一个问题分为两部分,其中第一部分与无关(但仍与无关):
  • 2D数组中大小为doublem*n值是多少?
  • 我们如何分配足够的内存来容纳它们。

  • 答案:
  • 在双精度的m * n 2D矩阵中有m*n双精度
  • 分配足够的内存以容纳(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吗?当然,数学看起来有些奇怪,但它仍然只是将偏移量计算成一个大的“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数组(m*n)中的整个表维度(l*n*m),甚至更多,替换一个指针:easy;复制整个m*n表?昂贵。还有不太明显的答案。如果行的宽度需要与行之间是独立的(即row0可以是5个元素,row1可以是6个元素)怎么办?固定的l*m*n分配然后根本不起作用。

    祝你好运。

    关于c - 分配连续内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13105056/

    相关文章:

    c - 理解下面一行

    c - 在 SDL2 C 中使用 for 循环显示矩形

    linux - 并行运行 shell 脚本

    linux - 获取下周星期二和星期三,使用 Linux date 命令

    c - 使用关键字生成排列

    c - 如果我们按位移动一个大于其大小的整数会发生什么

    c++ - 让 GDB 保存断点列表

    c - C 中高级和不寻常的函数声明

    arrays - 逐行填充多维数组

    c - 在 Unix 中编程 : Sharing libraries with libraries