分配连续的记忆

The*_*net 2 c unix memory arrays

我正在尝试在C中分配一大块连续内存并将其打印给用户.我这样做的策略是创建两个指针(一个指向double的指针,一个指向指向double的指针),malloc中的一个指向整个大小(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;
}
Run Code Online (Sandbox Code Playgroud)

Who*_*aig 13

请记住,记忆只是记忆.听起来很陈词滥调,但很多人似乎都认为C中的内存分配和内存管理是一种神奇的巫术.事实并非如此.在一天结束时,您可以分配所需的内存,并在完成后释放它.

所以从最基本的问题开始:如果你需要'n' double值,你会如何分配它们?

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

很简单.下一个问题,分为两部分,第一部分与内存分配无关(尚未):

  1. double2D阵列中有多少个值m*n
  2. 我们如何分配足够的内存来保存它们.

回答:

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

看起来很简单:

size_t m=10;
size_t n=20;
double *d2d = calloc(m*n, sizeof(double));
Run Code Online (Sandbox Code Playgroud)

但是我们如何访问实际元素?一点点数学是有序的.了解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;
Run Code Online (Sandbox Code Playgroud)

有更简单的方法吗?在标准C中, ; 在C++中没有.标准C支持非常方便的功能,可生成适当的代码来声明动态大小的可索引数组:

size_t m=10;
size_t n=20;
double (*d2d)[n] = calloc(m, sizeof(*d2d));
Run Code Online (Sandbox Code Playgroud)

不能强调这一点:标准C支持这一点,C++不支持.如果你正在使用C++,你可能想要编写一个对象类来为你完成这一切,所以除此之外不会提到它.

那么上面的实际做了什么呢?首先,显而易见的是,我们仍在分配之前分配的相同数量的内存.也就是说m*n,每个元素都sizeof(double)很大.但你可能会问自己,"变量声明是什么?" 这需要一点解释.

这有明显的区别:

double *ptrs[n];  // declares an array of `n` pointers to doubles.
Run Code Online (Sandbox Code Playgroud)

还有这个:

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

编译器现在知道每行的宽度(每行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);
Run Code Online (Sandbox Code Playgroud)

我们可以将其扩展到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);
Run Code Online (Sandbox Code Playgroud)

你需要盯着数学计算一分钟才真正凝聚它如何计算double实际上大块公羊的价值所在.使用上述维度和所需索引,"原始"索引是:

i*m*n = 3*20*30 = 1800
j*m   = 4*20    =   80
k     = 5       =    5
======================
i*m*n+j*m+k     = 1885
Run Code Online (Sandbox Code Playgroud)

所以我们在那个大线性区块中击中了第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
Run Code Online (Sandbox Code Playgroud)

即线性阵列中的第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);
Run Code Online (Sandbox Code Playgroud)

同样,所有这一切确实是我们之前手工做的相同的数学,但让编译器为我们做.

我意识到可能有点太多了,但这很重要.如果它是最重要的,你有连续的内存矩阵(比如将矩阵输入到图形渲染库,如OpenGL等),你可以使用上述技术相对轻松地完成它.

最后,你可能想知道为什么有人会把指针数组的指针数组到指针数组的第一个位置,如果你能这样做呢?原因很多.假设您正在替换行.交换指针很容易; 复制整行?昂贵.假设你正在替换(m*n)3D数组中的整个表维度(l*n*m),甚至更多 - 交换指针:简单; 复制整个m*n表?很贵.而那个不那么明显的答案.如果行宽度需要在行与行之间独立(即row0可以是5个元素,row1可以是6个元素),该怎么办?固定l*m*n分配根本不起作用.

祝你好运.