我已经在这里和其他网站上看到很多人说如果宣布这样的话:
double a[5][2];
它将像内存一样分配在内存中,如:
a[0][0] | a[0][1] | a[1][0] | a[1][1] | ....etc
但这总是一个规则吗? 我想创建一个函数来乘以可变大小的矩阵,但在纯C中我不能通过参数传递矩阵而不知道至少一个维度。所以我做了这个:
void MatMult(double* m1, double* m2, double* res, int h, int w, int l)
{
int i, j, k;
for (i = 0; i < h; i++)
{
for (j = 0; j < w; j++)
{
double p_res = 0;
for (k = 0; k < l; k++)
{
p_res += (*(m1+i*l+k))*(*(m2+k*w+j));
}
*(res+i*w+j)=p_res;
}
}
}
致电:
double m1[2][3], m2[3][1], m3[2][1];
...
MatMult(&(m1[0][0]),&(m2[0][0]),&(m3[0][0]),2,1,3);
它有效。但这会一直有效还是我应该注意的例外情况,如内存对齐或类似的内容?
答案 0 :(得分:3)
要将2D数组传递给函数,您必须更改界面
void MatMult(size_t h, size_t w, size_t l. double m1[h][w], double m2[w][l], double res[h][l]);
或类似:
另外,我在这里使用了size_t
,因为这是所有索引计算的正确类型。
这适用于所有实现C99的编译器。 (基本上所有人都可以做到。)
答案 1 :(得分:1)
是的,它总能奏效。声明像你做的ara保证是“连续的”,这意味着他们的物品将被紧紧包装起来。因此,如果您声明double[55]
,您知道[50]
- 元素始终位于[49]
- 元素之后,没有可感知的间隙。
我认为,但我不完全确定,对于一些非常罕见的数据类型(如包括不平衡的位域等),“对齐”仍然可以启动并抵消某些东西。我不确定。但即使 (参见Jens注释)如果编译器添加了一些对齐偏移,它将在单个数据元素内部或在元素之间的boudary中执行此操作,并且在两种情况下都是如此编译器会知道它。因此,它应该在每个[], ->, .
操作中应用所有必需的更正,只要它仍然具有所有必需的类型信息(double-double)。如果删除类型信息并开始通过“无类型”(或错误类型)指针访问数组,例如:
double array[50];
char* p = (char*)array;
int size = sizeof(double);
for(i=0;i<50;++i)
.. *(double*)(p+size) ..
然后编译器当然没有类型信息,也无法应用正确的对齐偏移。但是,如果你按照上述方法行事,你可能已经知道风险了。
接下来的事情是,没有二维数组这样的东西。无论是在C还是在C ++中。
定义为double[5][2]
的数组是double的数组(2)的数组(5)。 CMIIW,我可以交换它们。无论如何,关键是'double'是一种数据类型,是更高级别1D阵列的元素。然后,double[2]
是数据类型和更高级别1D数组的元素,依此类推。
现在,请记住数组的“顺序+连续”布局:
double -> DD
double[2] -> [DD | DD]
double[5][2] -> [ {DD:DD} | {DD:DD} | {DD:DD} | {DD:DD} | {DD:DD} ]
由于数组必须是连续的和连续的,所以double [2]必须将其元素布局如上 - 显而易见。
然而,由于double [5] [2]是一个数组,并且它的数组是[5],并且由于它的元素是double [2] - 它必须以相同的方式布局它的元素。首先是整个元素,然后是第二个整数元素,依此类推。
就像double [2]不能将其双精度“拆分”成分散的1字节块一样,double [5] [2]不能拆分它的数组[2]。
答案 2 :(得分:-4)
通过使用double a[5][2]
,您将在堆栈中创建二维数组,该数组始终是一个连续的内存块。另一方面,如果您尝试在堆上创建二维数组(即使用malloc),则无法保证您将获得连续的内存块,因此您可能无法以线性方式遍历已分配的内存。 / p>