int **a = malloc2d(M, N) // dynamically allocates a 2D array
拥有 int ** a 副int * a的目的是什么?我知道动态分配需要指针,但为什么有一个指向指针的指针?
对于三维数组,它将是:
int ***a
答案 0 :(得分:8)
由于多种原因,您需要一个指向指针的指针。
在您给出的示例中,双指针用于存储2D数组。
C中的2D数组被视为1D数组,其元素为1D 数组(行)。
例如,T的4x3数组(其中“T”是某种数据类型)可以 声明:“T mat [4] [3]”,并由以下描述 计划:
+-----+-----+-----+
mat == mat[0] ---> | a00 | a01 | a02 |
+-----+-----+-----+
+-----+-----+-----+
mat[1] ---> | a10 | a11 | a12 |
+-----+-----+-----+
+-----+-----+-----+
mat[2] ---> | a20 | a21 | a22 |
+-----+-----+-----+
+-----+-----+-----+
mat[3] ---> | a30 | a31 | a32 |
+-----+-----+-----+
另一种情况是,当你传递一个指向函数的指针时,你希望该函数分配该指针。为此,您必须指向变量的地址,因此指向指针
void make_foofoo(int** change) {
*change = malloc(4);
}
答案 1 :(得分:7)
在C中,指向类型T
的指针指向存储类型T
的某些数据的位置。为了使事情具体化,我将在下面讨论T = int
。
指针的最简单用法可能是指向一个值:
int a = 42;
int *pa = &a;
现在,*pa
和a
都是相同的,等于42
。此外,*pa
和pa[0]
都是等效的:例如,您可以这样做:
*pa += 1; /* a is 43 */
pa[0] += 1; /* a is 44 */
a += 1; /* a is 45 */
实际上,C编译器会自动将pa[0]
转换为*(pa+0)
。
指针可以指向数据序列中的位置:
int arr[] = { 1, 2, 3 }; /* 3 ints */
int *parr = arr; /* points to 1 */
现在,我们的记忆如下:
+---+---+---+
arr: | 1 | 2 | 3 |
+---+---+---+
+------+ |
| parr | ----+
+------+
parr
是一个指针,指向上图中arr
的第一个元素。顺便说一下,parr
周围也有一个框,因为我们需要将对象parr
存储在内存中的某个位置。 parr
的值是arr
的第一个元素的地址。
现在,我们可以使用parr
来访问arr
:
arr[0] == parr[0]; /* true */
parr[1]++; /* make arr[1] equal to 3 */
因此,指针可用于表示“我指向某些对象的连续存储中的n个元素中的第一个”。当然,人们必须知道这个方案有多少对象可以工作:但是一旦我们记得这样做,这是一种非常方便的方式来访问C中的内存。
指针也可以指向动态分配的内存:
#include <stdlib.h>
size_t n;
/* now, obtain a value in n at runtime */
int *p = malloc(n * sizeof *p);
如果malloc()
调用成功,p
现在指向分配给10 int
的连续区域中的第一个。我们现在可以在我们的计划中使用p[0]
到p[n-1]
。
你可能知道上面的大部分或全部内容:-),但是,上述内容有助于理解我接下来要说的内容。
还记得我们说指针可以指向相同类型的连续对象序列吗? “相同类型”也可以是另一种指针类型。
#include <stdlib.h>
int **pp;
pp = malloc(3 * sizeof *pp);
现在,pp
指向int *
。回到我们之前的图片:
+------+------+------+
| | | |
+------+------+------+
+------+ |
| pp | ----+
+------+
并且3个框中的每一个都是int *
,它可以指向int
s的连续序列的第一个元素:
for (i=0; i < 3; ++i)
pp[i] = malloc((i + 1) * sizeof *ppi[i]);
在这里,我们为pp[0]
中的一个int,pp[1]
中的2个和pp[3]
中的3个分配了空间:
+------+ +---+
pp -------->| |-------->| |
+------+ +---+---+
| |-------->| | |
+------+ +---+---+---+
| |-------->| | | |
+------+ +---+---+---+
因此,pp[0]
是指向一个int
的指针,int
是动态分配的int
块中唯一的int
。换句话说,pp[0][0]
是int
,并指向上方最顶部的“width-3”框。同样,pp[1][0]
和pp[1][1]
都有效,是pp[0][0]
框下方的两个框。
指向指针的最常见用法是在运行时创建一个二维“数组”:
int **data;
size_t i;
data = malloc(n * sizeof *data);
for (i=0; i < n; ++i)
data[i] = malloc(m * sizeof *data[i]);
现在,假设所有malloc()
成功,data[0]
... data[n-1]
是有效int *
值,每个值都指向一个单独的m
个连续长度int
个对象。
但是,正如我在上面所示,指向指针的指针不需要在每个“行”中具有相同的“元素数”。最明显的例子是argv
in main()
。
到目前为止,你可以猜到,一个“3级深度”指针,例如int ***p;
是可以的,可以在C中使用。
答案 2 :(得分:2)
为了好玩,这是一个*****
示例:
***
*
)*
)因此我们有tex [2] [3] [10] [2] [10] ....这是:
要传递具有修改权限的数据,您需要传递int******
...有趣!的xD
(这就是我喜欢struct和类的原因....)
答案 3 :(得分:1)
帮助绘制图片。想象一个一维数组,其中每个元素都包含一个指针。这些指针中的每一个都指向另一个一维数组。 malloc2d不是标准的库函数,但我猜它返回了一个以这种方式构造的二维数组。
答案 4 :(得分:1)
您可以使用它来创建2维数组, 有点像数学矩阵或棋盘。 因此,您几乎可以存储数据表。
答案 5 :(得分:1)
普通C数组是指向内存块的指针。
2D数组是指向数组每一行指针列表的指针。
所以,指向指针的指针。
答案 6 :(得分:0)
你只传递指针的大小(通常是32位或64位),而不是整个对象。