请记住,记忆只是记忆。听起来很陈词滥调,但很多人似乎认为 C 中的内存分配和内存管理是一些魔法巫术。它不是。在一天结束时,您分配所需的任何内存,并在完成后释放它。
所以从最基本的问题开始:如果你需要“n”个double
值,你会如何分配它们?
double *d1d = calloc(n, sizeof(double));
// ... use d1d like an array (d1d[0] = 100.00, etc. ...
free(d1d);
很简单。下一个问题,分两部分,第一部分与内存分配无关(尚未):
double
一个大小的二维数组中有多少个值m*n
?
- 我们如何分配足够的内存来容纳它们。
答案:
- am*n 2D-matrix of doubles 中有
m*n
双打
- 分配足够的内存来保存 (m*n) 双打。
看起来很简单:
size_t m=10;
size_t n=20;
double *d2d = calloc(m*n, sizeof(double));
但是我们如何访问实际元素呢?有点数学是为了。知道m
and n
,你可以简单地做到这一点
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);
您需要盯着其中的数学一分钟,才能真正了解它如何计算double
大块 ram 中的值实际在哪里。使用上述维度和所需索引,“原始”索引为:
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 等图形渲染库等),你可以使用上述技术相对轻松地做到这一点。
最后,您可能想知道,如果您可以这样做,为什么有人首先要做整个指针数组到指针数组到指针数组到值的事情?很多原因。假设您要替换行。交换指针很容易;复制整行?昂贵的。假设您要替换(m*n)
3D array 中的整个表格维度(l*n*m)
,甚至更重要的是,交换指针:easy; 复制整个m*n
表?贵。而不是那么明显的答案。如果行宽需要独立于行(即 row0 可以是 5 个元素,row1 可以是 6 个元素),该怎么办?固定l*m*n
分配根本不起作用。
祝你好运。