简体   繁体   English

ANSI C中内存中的二维数组

[英]2-Dimensional array in memory in ANSI C

I've read many people here and in other websites saying that if a declare something like this: 我在这里和其他网站上看过很多人说如果宣布这样的话:

double a[5][2];

it will be allocated in memory like a contiguous block like: 它将在内存中分配,如:连续块,如:

a[0][0] | a[0][1] | a[1][0] | a[1][1] | ....etc

But is this always a rule? 但这总是一个规律吗? I would like to create a function to multiply matrices of variable sizes but in pure CI won't be able to pass matrices by parameters without knowing at least one dimension. 我想创建一个函数来乘以可变大小的矩阵,但在纯CI中,如果不知道至少一个维度,就不能通过参数传递矩阵。 So I've made this: 所以我做了这个:

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;
    }
  }
}

with call: 随叫随到:

 double m1[2][3], m2[3][1], m3[2][1];
 ...
 MatMult(&(m1[0][0]),&(m2[0][0]),&(m3[0][0]),2,1,3);

And it worked. 它奏效了。 But will this always work or there are exceptions that I should be aware of like memory aligment or something like this? 但这会一直有效还是我应该注意的例外情况,如内存对齐或类似的东西?

To pass 2D arrays to functions you'd have to change your interface 要将2D数组传递给函数,您必须更改接口

void MatMult(size_t h, size_t w, size_t l. double m1[h][w], double m2[w][l], double res[h][l]);

or similar: 或类似的:

  • have the sizes first 首先是尺寸
  • then use them to declare the dimensions 然后用它们来声明尺寸

Also, I have used size_t , here, since this is the correct type for all index calculations. 此外,我在这里使用了size_t ,因为这是所有索引计算的正确类型。

This should work for all compilers that implement C99. 这适用于所有实现C99的编译器。 (Basically all do but Microsoft.) (基本上所有人都可以做到。)

Yes it will always work. 是的,它总能奏效。 Arrays declared like you did ara guaranteed to be "contiguous" which means that their items will be tightly packed up. 声明像你做的ara保证是“连续的”,这意味着他们的物品将被紧紧包装起来。 So, if you declare double[55] you know that always the [50] -th element will come right after [49] -th element with no perceivable gaps. 所以,如果你声明double[55]你就会知道[50] -th元素总是会在[49]元素之后,没有可感知的间隙。

I think, but I'm not perfectly sure, that for some very uncommon data types (like including unbalanced bitfields, etc), the "alignment" can still kick in and offset something. 我想,但我不完全确定,对于一些非常罕见的数据类型(如包括不平衡的位域等),“对齐”仍然可以启动并抵消某些东西。 I'm not sure. 我不确定。 But even if (see Jens comment) If the compiler adds some alignment offsets, it will do so either inside a single data element, or at the boudary between elements, and in both cases the compiler will know about it. 即使 (参见Jens评论)如果编译器添加了一些对齐偏移,它将在单个数据元素内部或在元素之间的boudary中这样做,并且在两种情况下编译器都会知道它。 So, it should apply all required corrections at every [], ->, . 因此,它应该在每个[], ->, .应用所有必需的更正[], ->, . operation, as long as it still has all the required type information (array-of-doubles). 操作,只要它仍然具有所有必需的类型信息(double-number)。 If you erase the type information and start accessing the array by "untyped" (or wrong-typed) pointers, for example: 如果删除类型信息并开始通过“无类型”(或错误类型)指针访问数组,例如:

double array[50];
char* p = (char*)array;
int size = sizeof(double);
for(i=0;i<50;++i)
    .. *(double*)(p+size) ..

then of course the compiler will not have the type information and will be unable to apply proper alignment offsets. 那么编译器当然没有类型信息,也无法应用正确的对齐偏移。 But if you do things as above, you probably know the risks already. 但是如果你按照上面所做的那样做,你可能已经知道了风险。

Next thing is, that there is no such thing as two-dimensional array. 接下来的是,没有二维数组这样的东西。 Neither in C nor in C++. 既不是C语言,也不是C ++语言。

Array defined as double[5][2] is an array(5) of array(2) of double. 定义为double[5][2]的数组是double的数组(2)的数组(5)。 CMIIW, I could swap them. CMIIW,我可以交换它们。 Anyways, the point is that 'double' is a datatype and is an element of an higher-level 1D-array. 无论如何,关键是'double'是一种数据类型,是更高级别1D阵列的元素。 Then, double[2] is a datatype and an element of an higher-level 1D-array, and so on. 然后, double[2]是数据类型和更高级别1D数组的元素,依此类推。

Now, remember the 'sequential+contiguous' layout of arrays: 现在,请记住数组的“顺序+连续”布局:

double            ->  DD
double[2]         -> [DD | DD]
double[5][2]      -> [ {DD:DD} | {DD:DD} | {DD:DD} | {DD:DD} | {DD:DD} ]

Since array has to be sequential and contiguous, the double[2] must layout its element as above - obvious. 由于数组必须是连续的和连续的,所以double [2]必须将其元素布局如上 - 显而易见。

However, since double[5][2] is an array, and its an array of [5] and since its elements are double[2] - it must layout its elements just in the same way. 但是,由于double [5] [2]是一个数组,它的数组是[5],并且由于它的元素是double [2] - 它必须以相同的方式布局它的元素。 First whole element first, then second whole element, and so on. 首先是整个元素,然后是第二个整数元素,依此类推。

Just like double[2] can't "split" its doubles into scattered 1-byte chunks, the double[5][2] can't split its array[2]. 就像double [2]不能将其双精度“拆分”成分散的1字节块一样,double [5] [2]不能拆分它的数组[2]。

By using double a[5][2] , you are creating two dimensional array in stack which will always be a contiguous block of memory. 通过使用double a[5][2] ,您将在堆栈中创建二维数组,它始终是一个连续的内存块。 On the other hand, if you try to create 2-D array on heap (ie using malloc), it is not guaranteed that you will get contiguous block of memory hence you might not be able traverse your allocated memory in a linear way. 另一方面,如果您尝试在堆上创建二维数组(即使用malloc),则无法保证您将获得连续的内存块,因此您可能无法以线性方式遍历已分配的内存。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM