提问人:User1291 提问时间:4/22/2016 最后编辑:Peter MortensenUser1291 更新时间:4/27/2016 访问量:7702
分配二维数组的怪异方式?
Freaky way of allocating two-dimensional array?
问:
在一个项目中,有人推了这样一句话:
double (*e)[n+1] = malloc((n+1) * sizeof(*e));
据推测,它创建了一个 (n+1)*(n+1) 双精度的二维数组。
我说,据说是因为到目前为止,我问过的人都无法告诉我它到底是做什么的,也不知道它来自哪里或为什么它应该起作用(据称,它确实如此,但我还没有买它)。
也许我遗漏了一些明显的东西,但如果有人能向我解释上面的行,我将不胜感激。因为就我个人而言,如果我们使用我们真正理解的东西,我会感觉好得多。
答:
这是动态分配 2D 阵列的典型方法。
e
是指向 类型的数组的数组指针。double [n+1]
sizeof(*e)
因此,给出指向类型的类型,即一个数组的大小。double [n+1]
- 为此类数组分配空间。
n+1
- 将数组指针设置为指向此数组数组中的第一个数组。
e
- 这允许您使用 as 访问 2D 数组中的单个项目。
e
e[i][j]
我个人认为这种风格更容易阅读:
double (*e)[n+1] = malloc( sizeof(double[n+1][n+1]) );
评论
ptr = malloc(sizeof *ptr * count)
malloc(row*col*sizeof(double))
row*col*sizeof()
sizeof()*row*col
int
)
sizeof *e * (n+1)
double
long double
e
sizeof
malloc
sizeof *e
该变量是指向 类型的元素数组的指针。e
n + 1
double
使用 dereference 运算符 on 为您提供其基本类型为“类型的元素数组”。e
e
n + 1
double
调用只是获取 的基本类型(如上所述)并获取其大小,将其乘以 ,然后将该大小传递给函数。实质上是分配 的元素数组的数组。malloc
e
n + 1
malloc
n + 1
n + 1
double
评论
sizeof(*e)
sizeof(double [n + 1])
n + 1
n+1
double (*e)[cols] = malloc(rows * sizeof(*e));
int rows = n+1
int cols = n+1
这个成语自然不属于一维数组分配。让我们从分配某个任意类型的一维数组开始:T
T *p = malloc( sizeof *p * N );
很简单,对吧?表达式的类型为 ,因此给出的结果与 相同,因此我们为 的 -element 数组分配了足够的空间。这适用于任何 T
型。*p
T
sizeof *p
sizeof (T)
N
T
现在,让我们用数组类型代替,如 .然后我们的分配变为T
R [10]
R (*p)[10] = malloc( sizeof *p * N);
这里的语义与一维分配方法完全相同;所有更改的只是 的类型。而不是 ,而是现在 .表达式的 type 是 类型 ,所以等价于 which 等价于 。因此,我们为 的 by element 数组分配了足够的空间。p
T *
R (*)[10]
*p
T
R [10]
sizeof *p
sizeof (T)
sizeof (R [10])
N
10
R
如果我们愿意,我们可以更进一步;假设本身就是一个数组类型。代之以它,我们得到R
int [5]
R
int (*p)[10][5] = malloc( sizeof *p * N);
同样的交易 - 与 相同,我们最终分配了一个连续的内存块,该内存块足够大,可以按数组保存 by by 的数组。sizeof *p
sizeof (int [10][5])
N
10
5
int
这就是分配方面;接入端呢?
请记住,下标操作是根据指针算术定义的:定义为 1。因此,下标运算符隐式取消引用指针。如果是指向 的指针,则可以通过使用一元运算符显式取消引用来访问指向值:[]
a[i]
*(a + i)
[]
p
T
*
T x = *p;
或使用下标运算符:[]
T x = p[0]; // identical to *p
因此,如果指向数组的第一个元素,则可以使用指针上的下标来访问该数组的任何元素:p
p
T arr[N];
T *p = arr; // expression arr "decays" from type T [N] to T *
...
T x = p[i]; // access the i'th element of arr through pointer p
现在,让我们再次执行替换操作,并替换为数组类型:T
R [10]
R arr[N][10];
R (*p)[10] = arr; // expression arr "decays" from type R [N][10] to R (*)[10]
...
R x = (*p)[i];
一个立即明显的差异;在应用下标运算符之前,我们会显式取消引用。我们不想下标到 ,我们想下标到指向什么(在本例中为数组)。由于一元的优先级低于下标运算符,因此我们必须使用括号来显式地使用 .但请记住,从上面看,这与 相同,因此我们可以将其替换为p
p
p
arr[0]
*
[]
p
*
*p
p[0]
R x = (p[0])[i];
或者只是
R x = p[0][i];
因此,如果指向一个 2D 数组,我们可以通过以下方式索引到该数组中:p
p
R x = p[i][j]; // access the i'th element of arr through pointer p;
// each arr[i] is a 10-element array of R
得出与上述相同的结论,并用以下方法代之:R
int [5]
int arr[N][10][5];
int (*p)[10][5]; // expression arr "decays" from type int [N][5][10] to int (*)[10][5]
...
int x = p[i][j][k];
如果指向常规数组,或者如果它指向通过 分配的内存,则其工作方式相同。p
malloc
这个成语有以下好处:
- 这很简单 - 只需一行代码,而不是零碎的分配方法
T **arr = malloc( sizeof *arr * N ); if ( arr ) { for ( size_t i = 0; i < N; i++ ) { arr[i] = malloc( sizeof *arr[i] * M ); } }
- 分配数组的所有行都是*连续的*,这与上面的分段分配方法不同;
- 只需调用 一次即可轻松解除分配数组。同样,零碎分配方法并非如此,在该方法中,您必须先取消分配每个分配,然后才能解除分配。
free
arr[i]
arr
有时,零碎分配方法更可取,例如,当堆严重碎片化并且无法将内存分配为连续块时,或者想要分配一个“锯齿状”数组,其中每行可以具有不同的长度。但总的来说,这是更好的方法。
1. 请记住,数组不是指针 - 相反,数组表达式会根据需要转换为指针表达式。
评论
int (*p)[10][5] = malloc( sizeof *p * N);
p
int (*foo(int N))[10][5]
foo
int
N
int
int (*foo(int N))[10][5]
评论