在数据结构中我们经常能够看到 *& 在函数的形参中,但却有些难以明白它的含义。因为那些代码都是伪代码,不是某一类编程语言的代码,通常是 C 和 C++ 的混用。
而 *& 是 C++ 中才能使用的,被称之为”引用“。如果使用的是纯 C 编译器,那么很可能无法通过编译。
由于我并不会 C++ 语言,所以下面只是说一下对它使用的理解,并且在数据结构的解题中尽量避免去使用到它。
注:下面代码经过 Dev-C++ 5.11 版本验证。
举例说明,我最开始想创建一个链表,代码如下:
#include <stdio.h>
#include <malloc.h>
typedef struct LNode {
int data;
struct LNode *next;
} LNode;
/**
* 使用头插法创建单链表
* @param list 单链表
* @param nums 待插入的数据数组
* @param n 数组长度
*/
void createByHead(LNode *list, int nums[], int n) {
// 创建链表的头节点
list = (LNode *) malloc(sizeof(LNode));
list->next = NULL;
// 循环数组 nums 中所有数据
for (int i = 0; i < n; i++) {
// 创建新节点并指定数据域和指针域
LNode *newNode = (LNode *) malloc(sizeof(LNode));
newNode->data = nums[i];
newNode->next = NULL;
// 将新节点插入到链表的头部,但是在头结点的后面
LNode *temp = list->next;
newNode->next = temp;
list->next = newNode;
}
}
/**
* 打印单链表中的所有节点
* @param list 单链表
*/
void print(LNode list) {
printf("[");
// 链表的第一个节点
LNode *node = list.next;
// 循环单链表所有节点,打印值
while (node != NULL) {
printf("%d", node->data);
if (node->next != NULL) {
printf(", ");
}
node = node->next;
}
printf("]\n");
}
int main() {
LNode *list;
int nums[] = {111, 222, 333, 444, 555};
int n = 5;
createByHead(list, nums, n);
print(*list);
}
但运行不会打印任何结果,尽管我传递了一个指针变量。事实上我对指针的理解还是不够深刻。
上面的代码可能有些多了,看看简化后的情况,即我想要在函数 fun 内修改传入的形参 a 的值,并且在函数外也能访问到:
#include <stdio.h>
#include <malloc.h>
void fun(int *a) {
a = (int *) malloc(sizeof(int));
int b = 3;
a = &b;
printf("fun->a: %d\n", *a);
}
int main() {
int *a;
fun(a);
printf("main->a: %d\n", *a);
}
代码指向结果如下:
fun->a: 3
--------------------------------
Process exited after 3.191 seconds with return value 3221225477
请按任意键继续. . .
即在函数 fun 外并不能访问到在函数 fun 内被修改的 a,即使它是一个指针变量。
事实上再次证明了我对 C 语言指针的认知浅薄。
如果我们把形参 int *a 变成 int *&a 呢?那么代码如下:
#include <stdio.h>
#include <malloc.h>
void fun(int *&a) {
a = (int *) malloc(sizeof(int));
int b = 3;
a = &b;
printf("fun->a: %d\n", *a);
}
int main() {
int *a;
fun(a);
printf("main->a: %d\n", *a);
}
代码执行结果如下:
fun->a: 3
main->a: 3
--------------------------------
Process exited after 1.911 seconds with return value 0
请按任意键继续. . .
我们仅仅在为形参的指针变量添加一个 & 符号就发生了改变。
其实在函数内形参int* a和int a区别不大,不过是整型指针类型的变量int*和普通整型类型的变量int的区别,都传递的是值。在函数内对它们的值做修改,都无法影响到函数外。
而 int *&a 传递的是整型指针类型变量 a 的地址值,在函数内直接对地址所表示的变量进行修改,那么无论是函数内还是函数外都会被影响到。
如果仅仅是使用 C 语言,那么也可以在形参和实参中传递指针变量的地址,但修改有点多:
#include <stdio.h>
#include <malloc.h>
void fun(int **a) {
*a = (int *) malloc(sizeof(int));
int b = 3;
*a = &b;
printf("fun->a: %d\n", **a);
}
int main() {
int *a;
fun(&a);
printf("main->a: %d\n", *a);
}
即在函数 fun 内 **a 接收的是就是 main 函数内实参指针变量 *a 的地址,通过 & 操作符取得指针变量的地址;而在函数 fun 内 *a 就表示一个指针变量,对这个指针变量的修改无论是函数内还是函数外都会被影响到。
将函数的实参改为普通变量,而形参继续是指针变量也可以实现同样的效果,但注意在函数内指针变量修改值的方式(这样 a = &b; 赋值是行不通的)。
#include <stdio.h>
void fun(int *a) {
// 这里就不需要再为它动态分配内存空间了
// a = (int *) malloc(sizeof(int));
int b = 3;
*a = b;// 修改指针变量的值
printf("fun->a: %d\n", *a);
}
int main() {
// 声明一个普通变量,而非指针变量
int a;
fun(&a);// 因为函数的形参还是指针变量,所以这里要传递普通变量的地址,使用&取址符
printf("main->a: %d\n", a);
}
那么同样的道理,要想在函数内修改单链表成功,并且在其他函数也可以访问,就可以使用 *&。仅仅只需要改动形参,而不需要改动其他任何地方的代码。但注意这是 C++ 的语法,一般的 C 编译器是不支持的,但在解数据结构题目时为了省事可以考虑使用。
#include <stdio.h>
#include <malloc.h>
typedef struct LNode {
int data;
struct LNode *next;
} LNode;
/**
* 使用头插法创建单链表
* @param list 单链表
* @param nums 待插入的数据数组
* @param n 数组长度
*/
void createByHead(LNode *&list, int nums[], int n) {
// 创建链表的头节点
list = (LNode *) malloc(sizeof(LNode));
list->next = NULL;
// 循环数组 nums 中所有数据
for (int i = 0; i < n; i++) {
// 创建新节点并指定数据域和指针域
LNode *newNode = (LNode *) malloc(sizeof(LNode));
newNode->data = nums[i];
newNode->next = NULL;
// 将新节点插入到链表的头部,但是在头结点的后面
LNode *temp = list->next;
newNode->next = temp;
list->next = newNode;
}
}
/**
* 打印单链表中的所有节点
* @param list 单链表
*/
void print(LNode list) {
printf("[");
// 链表的第一个节点
LNode *node = list.next;
// 循环单链表所有节点,打印值
while (node != NULL) {
printf("%d", node->data);
if (node->next != NULL) {
printf(", ");
}
node = node->next;
}
printf("]\n");
}
int main() {
LNode *list;
int nums[] = {111, 222, 333, 444, 555};
int n = 5;
createByHead(list, nums, n);
print(*list);
}
还能使用指向指针的指针,但修改涉及的代码就比较多了。实参、形参及在函数中使用了形参的代码都需要改变:
#include <stdio.h>
#include <malloc.h>
typedef struct LNode {
int data;
struct LNode *next;
} LNode;
/**
* 使用头插法创建单链表
* @param list 单链表
* @param nums 待插入的数据数组
* @param n 数组长度
*/
void createByHead(LNode **list, int nums[], int n) {
// 创建链表的头节点
*list = (LNode *) malloc(sizeof(LNode));
(*list)->next = NULL;
// 循环数组 nums 中所有数据
for (int i = 0; i < n; i++) {
// 创建新节点并指定数据域和指针域
LNode *newNode = (LNode *) malloc(sizeof(LNode));
newNode->data = nums[i];
newNode->next = NULL;
// 将新节点插入到链表的头部,但是在头结点的后面
LNode *temp = (*list)->next;
newNode->next = temp;
(*list)->next = newNode;
}
}
/**
* 打印单链表中的所有节点
* @param list 单链表
*/
void print(LNode list) {
printf("[");
// 链表的第一个节点
LNode *node = list.next;
// 循环单链表所有节点,打印值
while (node != NULL) {
printf("%d", node->data);
if (node->next != NULL) {
printf(", ");
}
node = node->next;
}
printf("]\n");
}
int main() {
LNode *list;
int nums[] = {111, 222, 333, 444, 555};
int n = 5;
createByHead(&list, nums, n);
print(*list);
}
如果只是返回一个单链表,我们可以将创建成功的链表作为函数返回值返回,然后在主函数接收就可以,避免了它作为实参和形参的变化:
#include <stdio.h>
#include <malloc.h>
typedef struct LNode {
int data;
struct LNode *next;
} LNode;
/**
* 使用头插法创建单链表
* @param list 单链表
* @param nums 待插入的数据数组
* @param n 数组长度
*/
LNode *createByHead(int nums[], int n) {
// 创建链表的头节点
LNode *list = (LNode *) malloc(sizeof(LNode));
list->next = NULL;
// 循环数组 nums 中所有数据
for (int i = 0; i < n; i++) {
// 创建新节点并指定数据域和指针域
LNode *newNode = (LNode *) malloc(sizeof(LNode));
newNode->data = nums[i];
newNode->next = NULL;
// 将新节点插入到链表的头部,但是在头结点的后面
LNode *temp = list->next;
newNode->next = temp;
list->next = newNode;
}
// 将链表返回
return list;
}
/**
* 打印单链表中的所有节点
* @param list 单链表
*/
void print(LNode list) {
printf("[");
// 链表的第一个节点
LNode *node = list.next;
// 循环单链表所有节点,打印值
while (node != NULL) {
printf("%d", node->data);
if (node->next != NULL) {
printf(", ");
}
node = node->next;
}
printf("]\n");
}
int main() {
LNode *list;
int nums[] = {111, 222, 333, 444, 555};
int n = 5;
list = createByHead(nums, n);
print(*list);
}
其实还可以考虑实参用普通变量,形参用指针变量,也能达到同样的效果:
#include <stdio.h>
#include <malloc.h>
typedef struct LNode {
int data;
struct LNode *next;
} LNode;
/**
* 使用头插法创建单链表
* @param list 单链表
* @param nums 待插入的数据数组
* @param n 数组长度
*/
void createByHead(LNode *list, int nums[], int n) {
// list就是链表的头节点,不需要再分配内存空间了,但需要把next指针指向NULL
list->next = NULL;
// 循环数组 nums 中所有数据
for (int i = 0; i < n; i++) {
// 创建新节点并指定数据域和指针域
LNode *newNode = (LNode *) malloc(sizeof(LNode));
newNode->data = nums[i];
newNode->next = NULL;
// 将新节点插入到链表的头部,但是在头结点的后面
LNode *temp = list->next;
newNode->next = temp;
list->next = newNode;
}
}
/**
* 打印单链表中的所有节点
* @param list 单链表
*/
void print(LNode list) {
printf("[");
// 链表的第一个节点
LNode *node = list.next;
// 循环单链表所有节点,打印值
while (node != NULL) {
printf("%d", node->data);
if (node->next != NULL) {
printf(", ");
}
node = node->next;
}
printf("]\n");
}
int main() {
LNode list;
int nums[] = {111, 222, 333, 444, 555};
int n = 5;
createByHead(&list, nums, n);
print(list);
}
总结:
- 所谓
*&就是传递一个引用进去,让你可以在函数内做的修改影响到函数外。 - 通常
*&出现在一些关于数据结构中的书籍中,作为伪代码展示。 - 通常
*&用来修改链表、栈、树等数据结构。 - 除了
*&之外,还可以考虑使用其他的方式也能实现同样的效果,不必过分纠结。 - 变量在使用之前,普通变量可以不初始化。但指针变量必须初始化并且普通类型的指针变量可以通过
&取址符进行赋值,而结构体指针变量则需要通过malloc函数动态分配空间然后再赋值。
本文解析了C++中使用*&在函数参数中的作用,重点讲解了如何通过引用修改链表元素影响全局,以及不同指针传递方式的对比。学习了引用在数据结构操作中的实际运用和注意事项。
626

被折叠的 条评论
为什么被折叠?



