已分配数组
对于已分配的数组,遵循以下步骤即可:
声明指针数组。该数组中的每个元素都指向一个struct Test
结构体。
struct Test *array[50];
然后按照您的喜好来分配并指定指向结构体的指针。使用循环会更简单:
array[n] = malloc(sizeof(struct Test));
然后声明一个指向这个数组的指针:
// an explicit pointer to an array
struct Test *(*p)[] = &array
这使您可以使用(*p)[n]->data
来引用第n个成员。
如果这些内容让您感到困惑,请不要担心。这可能是C语言中最困难的方面。
动态线性数组
如果您只想分配一个结构块(实质上是结构体数组,而不是结构体指针),并且有一个指向该块的指针,您可以更轻松地完成它:
struct Test *p = malloc(100 * sizeof(struct Test)); // allocates 100 linear
// structs
然后您可以指向此指针:
struct Test **pp = &p
现在你不再有一个指向结构体的指针数组,但它显著简化了整个事情。
动态分配的结构体数组
最灵活的方法,但很少需要。它与第一个示例非常相似,但需要额外的分配。我编写了一个完整的程序来演示这一点,应该可以正常编译。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
struct Test {
int data;
};
int main(int argc, char **argv)
{
srand(time(NULL));
struct Test **t_array = malloc(100 * sizeof(struct Test *));
for (int i = 0; i < 100; i++) {
t_array[i] = malloc(sizeof(struct Test));
}
for (int i = 0; i < 100; i++) {
t_array[i]->data = rand() % 100;
}
struct Test ***p = &t_array;
printf("p points to an array of pointers.\n"
"The third element of the array points to a structure,\n"
"and the data member of that structure is: %d\n", (*p)[2]->data);
return 0;
}
输出:
> p points to an array of pointers.
> The third element of the array points to a structure,
> and the data member of that structure is: 49
或者整个集合:
for (int i = 0; i < 100; i++) {
if (i % 10 == 0)
printf("\n");
printf("%3d ", (*p)[i]->data);
}
35 66 40 24 32 27 39 64 65 26
32 30 72 84 85 95 14 25 11 40
30 16 47 21 80 57 25 34 47 19
56 82 38 96 6 22 76 97 87 93
75 19 24 47 55 9 43 69 86 6
61 17 23 8 38 55 65 16 90 12
87 46 46 25 42 4 48 70 53 35
64 29 6 40 76 13 1 71 82 88
78 44 57 53 4 47 8 70 63 98
34 51 44 33 28 39 37 76 9 91
动态指针数组的单一动态分配结构体
这个例子比较特殊。它是一个指针动态数组,就像我们之前看到的例子那样,但不同的是,所有元素都在单个分配中分配。这有其用途,最显著的是可以对数据进行不同配置的排序,同时保留原始分配。
我们首先像在最基本的单块分配中一样分配一组元素:
struct Test *arr = malloc(N*sizeof(*arr));
现在我们分配一个独立的指针块:
struct Test **ptrs = malloc(N*sizeof(*ptrs));
然后我们使用原始数组中的一个地址填充指针列表中的每个槽位。由于指针算术运算可以使我们从元素到元素地址移动,因此这是直接的:
for (int i=0;i<N;++i)
ptrs[i] = arr+i;
此时以下两者都指向同一个元素字段
arr[1].data = 1
ptrs[1]->data = 1
经过以上的审核,我希望内容已经清晰明了了 为什么。
当我们处理完指针数组和原始块数组后,它们会被释放掉:
free(ptrs);
free(arr);
注意: 我们不会逐个释放ptrs[]
数组中的每个项目。那不是它们分配的方式。它们被分配为单个块(由arr
指向),因此应该如何释放。
那么为什么有人想这样做呢?原因有几个。
首先,它极大地减少了内存分配调用的数量。现在只有两个:一个是数组块,另一个是指针数组。内存分配是程序可以请求的最昂贵的操作之一,尽可能地减少它们是可取的(请注意:文件IO是另一个需要注意的地方)。
另一个原因是:相同基础数据的多种表示。假设您想要按升序和降序排序数据,并且同时拥有两个排序好的表示。您可以复制数据数组,但这将需要大量复制并消耗大量内存。相反,只需分配一个额外的指针数组并将其填充为来自基本数组的地址,然后对该指针数组进行排序。当要排序的数据很大(可能是每个项目的千字节甚至更大)时,这尤其具有显着的好处。原始项保留在基本数组中的原始位置,但现在您有了一种非常有效的机制,可以对它们进行排序,而无需实际上将它们移动。您对项目指针数组进行排序;项目根本没有被移动。
我知道这很难理解,但指针用法是理解C语言许多强大功能的关键,因此请好好学习并不断刷新您的记忆。它会回来的。