什么事双重指针?
在C语言中,二重指针是指指向指针的指针。它的语法形式是type **ptr
,其中type
是指针指向的数据类型。二重指针通常用于函数参数传递,特别是在需要修改指针指向的内存地址时非常有用。
为什么一定要用双重指针?
在链表中使用双重指针的原因是因为malloc,要将malloc创建的内存的头指针传递下去。
1.正常情况下使用malloc函数
int main()
{
int *p = (int *)malloc(sizeof(int));
*p = 100;
free(p);
p = NULL;
return 0;
}
使用malloc来创建地址,将创建的内存的首地址给指针变量*p;
最后free(p);来释放。
2.如果在局部函数中使用malloc函数,并不使用free
在C语言中,如果你在函数内部分配了内存并将其地址存储在一个指针中,那么当函数执行完毕并返回时,函数内部的局部变量(包括指针)将会被销毁,但指针指向的内存块不会被自动释放,它们仍然存在于堆内存中。
然而,如果你只是简单地在函数内部分配了内存给一个指针,并且该指针是一个单重指针,那么在函数返回后,你将无法访问到这块内存,因为指针已经失效了。
但是,如果你在函数内部使用了双重指针,并且通过它来操作内存,即使函数返回后,通过双重指针间接访问到的内存块仍然存在,因为指针所指向的内容在堆上分配,而不是在函数的栈帧中。因此,使用双重指针能够在函数外部保持对分配的内存块的引用,以便后续的操作或释放。
下面是一个简单的示例,演示如何声明、分配内存和使用二重指针:
在这个示例中,allocate_2D_array
函数接受一个二重指针作为参数,通过分配内存为二维数组并将其地址存储在传递的指针中。然后,main
函数调用allocate_2D_array
函数来分配内存给myArray
,并在之后释放了这些内存。
#include <stdio.h>
#include <stdlib.h>
// 函数原型,接受一个int型的二重指针作为参数
void allocate_2D_array(int rows,int **array) {
// 分配内存给一维数组
*array = (int *)malloc(rows * sizeof(int));
for (int i = 0; i < rows; i++) {
(*array)[i] = i;
}
}
int main() {
int *myArray; // 声明一个二重指针
int rows = 3;
// 调用函数,将分配的一维数组的地址存储在myArray中
allocate_2D_array(rows, &myArray);
// 打印一维数组
printf("一维数组:\n");
for (int i = 0; i < rows; i++) {
printf("%d\n", myArray[i]);
}
free(myArray);
return 0;
}
这是一个用malloc创建一个指针的例子。
在函数
// 函数原型,接受一个int型的二重指针作为参数
void allocate_2D_array(int rows,int **array) {
// 分配内存给一维数组
*array = (int *)malloc(rows * sizeof(int));
for (int i = 0; i < rows; i++) {
(*array)[i] = i;
}
}
中;
*arrar指向malloc创建的内存的首地址。但是当allocate_2D_array调用接受时,因为没有free,所以那么当函数执行完毕并返回时,函数内部的局部变量(包括指针)将会被销毁,但指针指向的内存块不会被自动释放,它们仍然存在于堆内存中。
然而,如果你只是简单地在函数内部分配了内存给一个指针,并且该指针是一个单重指针,那么在函数返回后,你将无法访问到这块内存,因为指针已经失效了。
但是,如果你在函数内部使用了双重指针,并且通过它来操作内存,即使函数返回后,通过双重指针间接访问到的内存块仍然存在,因为指针所指向的内容在堆上分配,而不是在函数的栈帧中。因此,使用双重指针能够在函数外部保持对分配的内存块的引用,以便后续的操作或释放。
再看一看链表,你应该就明白了
//建立带表头结点的单链线性表L(尾插法)
void CreateList(Node **L)
{
Node *p,*r;
datatype ch;
ch=getchar();
*L = (Node *)malloc(sizeof(Node));
r=*L; /* r为指向尾部的结点 */
while(ch!='@')
{
p = (Node *)malloc(sizeof(Node)); /* 生成新结点 */
p->data = ch;
r->next=p; /* 将表尾终端结点的指针指向新结点 */
r = p; /* 将当前的新结点定义为表尾终端结点 */
ch=getchar();
}
r->next = NULL; /* 表示当前链表结束 */
}
就是想让CreateList(Node **L)调用完成时,将malloc的首地址保存下来,所以使用Node **L。通过双重指针间接访问到的内存块仍然存在,因为指针所指向的内容在堆上分配,而不是在函数的栈帧中。