我正在尝试在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);
很简单。下一个问题分为两部分,其中第一部分与无关(但仍与无关):
double
的m*n
值是多少? 答案:
m*n
双精度似乎很简单:
size_t m=10;
size_t n=20;
double *d2d = calloc(m*n, sizeof(double));
但是,我们如何访问实际元素?进行一点数学运算。知道
m
和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吗?当然,数学看起来有些奇怪,但它仍然只是将偏移量计算成一个大的“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/