一、多级指针原理
指针的本质就是一个普通变量,它的值表示的是一个内存地址,这个地址中可能存放了其它变量。那么二级指针其实也是一个普通的变量,这个变量中同样也存放了一个内存地址,而这个内存地址是一个指针变量的地址。例如:
int a = 0;
int b = 1;
int *p = &a;
int **p2 = &p;
a是一个普通变量,而p是一个指针变量,它存放了a的地址,而p2是一个二级指针变量,它存放了p的地址:它们在内存中的关系如下图:
如果我们需要修改a的值除了直接对a赋值之外,还可以通过*p来实现,即:
*p = 2;
经过这样的操作之后,a的值就被修改为了2。
如果我们要修改p的值,想要使其指向b,也是有两种方法,可以直接修改p的值,也可以使用它的二级指针*p2来修改p的值,即:
p = &b;
*p2 = &b;
这两种方法的结果是一样的。同理,对于多级针指来说,*(pointer)就是修改其指向地址的变量内容。关于三级指针、四级指针或更高级指针来讲原理都是一样的,这里不在赘述。
二、多级指针应用
下面内容涉及到了关于C语言结构体、多级指针和数据结构的相关知识,对这部分内容不了解的读者也不用过于担心,我们会在下一章中学习关于结构体与联合体的相关知识。
通常,在使用指针机制实现的数据结构中对结构内容的修改和变更常常要修改指针的内容。例如:我们已经实现了一个有序链表(升序,由小到大):
//节点
typedef struct s_node
{
//关键字
int key;
//下一个节点指针
struct s_node *next;
} s_node;
//链表
typedef struct s_list
{
//头节点
struct s_node *header;
} s_list;
现需要在现有链表中插入一个新的节点:
在这种情况下,常规的做法是从头遍历链表,并找到第一个关键字不小于待插入关键字的节点,然后插入:
int list_insert(s_list *list, int key)
{
if (list == NULL)
{
return -1;
}
//创建新节点
s_node *n = malloc(sizeof(s_node));
n->key = key;
n->next = NULL;
//如果链表头为空,表头即为新节点
if (list->header == NULL)
{
list->header = n;
return 0;
}
//如果关键字key小于链表头的key
if (key < list->header->key)
{
//替换新节点为链表头,原表头变为新节点的next
n->next = list->header;
list->header = n;
return 0;
}
//遍历链表,保持待比较节点的前一个节点
s_node *p = list->header;
//找到第一个大于新节点key的节点(注意,此时p其实是这个节点的上一个节点)
while (p->next != NULL && p->next->key < key)
{
p = p->next;
}
//插入新节点
n->next = p->next;
p->next = n;
return 0;
}
如果直接使用s_node的一级指针来插入新节点,需要判断的情况很多,比如需要判断头节点是否为空,需要判断新节点的key是否小于头节点等等,这样的代码功能上是没有问题的,但代码冗长,不好解理。
下面,我们使用s_node的二级指针来完成新节点的插入功能:
int list_insert(s_list *list, int key)
{
if (list == NULL)
{
return -1;
}
//创建新节点
s_node *n = malloc(sizeof(s_node));
n->key = key;
n->next = NULL;
//二级指针
s_node **p = &list->header;
//找到第一个大于key的节点
while ((*p) != NULL && (*p)->key < key)
{
p = &(*p)->next;
}
//插入新节点
n->next = *p;
*p = n;
return 0;
}
可以明显看到,使用二级指针可以大大的减少代码冗余,简单便捷的实现指针值的修改,真正的逻辑功能代码只有5行:
s_node **p = &list->header;
while ((*p) != NULL && (*p)->key < key)
p = &(*p)->next;
n->next = *p;
*p = n;
所以说,多级指针的作用非常之大,我们在使用C语言编程的过程中,如果遇到需要修改指针的值,使它指向另外一个节点,或变量,与其使用大量代码直接修改它的值,不如使用它的二级指针,直接修改其地址值,会大大减少代码冗余,提高代码质量。下面再来看一下删除链表节点的代码,同样是使用二级指针来完成的,代码清晰、简洁:
int list_remove(s_list *list, int key)
{
if (list == NULL)
{
return -1;
}
//二级指针
s_node **p = &list->header;
while ((*p) != NULL && (*p)->key != key)
{
p = &(*p)->next;
}
//等待释放节点
s_node *del = *p;
if (del == NULL)
{
return -1;
}
//释放内存
free(del);
*p = (*p)->next;
return 0;
}
Copyright © 2015-2023 问渠网 辽ICP备15013245号