引用&和取地址&的区别

  • 一句话总结:和类型在一起的是引用,和变量在一起的是取址
1
2
3
4
5
6
7
int a=3;
int &b=a; //引用,这里声明一个a的别名为b
int *p=&a; //取地址
void function(int &i); //传引用
void function(int *i); //传指针
vector<int> &vec2 = vec1; //vec1的引用是vec2
vector<int> *vec3 = &vec2; //vec3是指向vec2的指针

指针和引用的区别

  • 指针是一个变量,保存的值是变量的地址,(*p)才是这个变量,指针的值可变
  • 引用是一个变量的别名,就相当于这个变量,引用一旦定义就必须初始化且不可变,不能为空

传指针即传值

  • 传指针本质上也是传值,实际都是拷贝了一个副本到函数里面
  • 在swap函数这个例子中,传指针可以修改外面的变量,是因为传进去的是变量的地址,传值的话是传变量的拷贝
  • 传指针进函数后,修改的是指针的副本,原来的指针还保存原来的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int m = 1;
void func(int *p){
p = &m;
}
int main(int argc, char *argv[]){
int n = 2;
int *p = &n;
cout << *p << endl;
func(p);
cout << *p <<endl;
return 0;
}
/*
2
2
*/

传引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int m = 1;
void func(int &num){
num = m;
}
int main(int argc, char *argv[]){
int n = 2;
cout << n << endl;
func(n); //这里实际完成了int &num = n;
cout << n <<endl;
return 0;
}
/*
2
1
*/

使用指针的指针

  • int *p = &a; //定义一个指针p,保存int变量的地址!!!
  • int **q = &p; //定义一个指向指针变量的指针q,保存指针变量的地址!!!
  • *p //解引用时,表示a这个变量
  • *q //解引用一次,表示p这个指针变量
  • **q //解引用两次,表示p所指的a这个变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int m = 1;
void func(int **p){
*p = &m; //解引用一次,表示外面那个指针变量,然后把m的地址赋给外面那个指针变量
**p = 5; //解引用两次,表示外面那个指针变量所指的变量,即m,然后重新赋值为5
}

int main(int argc, char *argv[]){
int n = 2;
int *p = &n;
cout << *p << endl;
func(&p); //传进去的是指针变量的地址
cout << *p <<endl;
cout << m <<endl;
cout << n <<endl;
return 0;
}
/*
2
5
5 //m已被改成5
2
*/

使用指针的引用

  • int a; //a这个变量
  • int *p = &a; //定义指针变量p,保存a的地址
  • int *&q = p; //定义p的别名为q
  • 此时传进函数的就是p这个变量,而不是副本了!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int m = 1;

void func(int *&p){
p = &m;
*p = 5;
}

int main(int argc, char *argv[]){
int n = 2;
int *p = &n;
cout << *p << endl;
func(p);
cout << *p <<endl;
return 0;
}
/*
2
5
*/

什么时候用指针的指针或者指针的引用(重点)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
struct Node{
int data;
Node *next;
};
void create(Node **L){
(*L) = (Node *)malloc(sizeof(Node));
(*L)->data = 0;
(*L)->next = NULL;
}
void print(Node *L){
printf("%d\n",L->data);
}
int main(){
Node *L = NULL;
create(&L);
print(L);
}
  • 因为一开始定义了一个指向Node的空指针,我们要在函数里面申请一个头节点,让该指针指向这个头结点,故该指针的指向要发生变化
  • 不能直接传递指针进去,因为传进去的是拷贝,原指针还是NULL
  • 因此必须传进L的地址,即&L,传进去后,*L是二级指针, L才是指向头结点的指针

  • 对于插入,删除操作,因为链表指针一直都指向头结点,没有发生变化,可以不使用,可以使用,如果不带头结点的单链表,那么在头部插入删除的话,链表指针的指向就会发生变化,就必须使用

  • 所以关键是看该指针的指向是否发生变化,或者传进去的指针在外面还有没有用
  • 下面给出书和考试的版本!!
    • 不伦不类的混合代码,只要涉及了构造,插入,删除均用了引用!
    • 自己写代码的时候还是要看情况
1
2
3
4
5
6
7
8
9
10
11
12
13
14
typedef struct ThreadNode{
int data;
struct ThreadNode *lchild,*rchild;
}ThreadNode, *ThreadTree;

void InThread(ThreadTree &p, ThreadTree &pre);
void CreateInThread(ThreadTree T){
ThreadTree pre = NULL;
if(T!=NULL){
InThread(T, pre);
pre->lchild = NULL; //传进去的指针在外面还有用!
pre->rtag = 1;
}
}