c - C:指向结构的指针数组的指针(分配/解除分配问题)

标签 c pointers memory memory-management

我已经回到C方面了,但是我很难记住很多这种内存管理的工作方式。我想要一个指向结构指针数组的指针。

说我有:

struct Test {
   int data;
};


然后是数组:

struct Test **array1;


它是否正确?我的问题正在处理这件事。因此,数组中的每个指针都指向单独分配的对象。但我认为我首先需要这样做:

array1 = malloc(MAX * sizeof(struct Test *));


我无法理解上述内容。我需要这样做吗,为什么要这样做?特别是,如果我要为指针所指向的每件事分配内存,那么为指针分配内存是什么意思?

现在说我有一个指向结构指针数组的指针。现在,我希望它指向我之前创建的相同数组。

struct Test **array2;


我是否需要像上面一样为指针分配空间,还是可以这样做:

array2 = array1

最佳答案

分配数组

使用分配的数组,它很容易理解。

声明您的指针数组。该数组中的每个元素都指向一个struct Test

struct Test *array[50];


然后根据需要分配指针并将其分配给结构。使用循环很简单:

array[n] = malloc(sizeof(struct Test));


然后声明一个指向该数组的指针:

                               // an explicit pointer to an array 
struct Test *(*p)[] = &array;  // of pointers to structs


这使您可以使用(*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));

    // allocate 100 pointers, effectively an array
    struct Test **t_array = malloc(100 * sizeof(struct Test *));

    // allocate 100 structs and have the array point to them
    for (int i = 0; i < 100; i++) {
        t_array[i] = malloc(sizeof(struct Test));
    }

    // lets fill each Test.data with a random number!
    for (int i = 0; i < 100; i++) {
        t_array[i]->data = rand() % 100;
    }

    // now define a pointer to the array
    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指向),这就是应该释放它们的方式。

那么为什么有人要这样做呢?几个原因。

首先,它从根本上减少了内存分配调用的数量。而不是N+1(一个用于指针数组,N用于单个结构),您现在只有两个:一个用于数组块,一个用于指针数组。内存分配是程序可以请求的最昂贵的操作之一,并且在可能的情况下,希望将其最小化(注意:文件IO是另一个问题)。

另一个原因:同一数据基数组的多个表示形式。假设您想对数据进行升序和降序排序,并且同时具有两个排序的表示形式。您可以复制数据数组,但是这将需要大量复制并占用大量内存。相反,只需分配一个额外的指针数组,并用基本数组中的地址填充它,然后对该指针数组进行排序。当排序的数据很大(每个项目也许千字节,甚至更大)时,这具有特别显着的好处。原始项目保留在基本数组中的原始位置,但是现在您有了一种非常有效的机制可以对它们进行排序无需实际移动它们。您对指向项目的指针数组进行排序;这些物品根本不会动弹。

我意识到这很麻烦,但是指针的使用对于理解C语言可以完成的许多强大功能至关重要,因此请阅读本书并不断刷新您的内存。它将回来。

关于c - C:指向结构的指针数组的指针(分配/解除分配问题),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53468397/

相关文章:

c - 是否可以创建一个在编译时大小未知的结构?

java - 跟踪 tomcat webapp 中的内存使用情况

c++ - 为什么我们需要在 C++ 中显式声明指针类型?

c - 新 Linux 内核中的内存隔离,或者什么?

c - 分配一个 char **?

c++ - 如何使用 Lua 从表中的表中获取值?

C++,从同一个原始对象复制的多个对象中的成员指针的 'coupling'

c++ - *&aPtr 和 &*aPtr 有什么区别?

C - 仅使用 & 和 ~ 计算按位或

C# 调用以 char ** 指针作为参数的 C API