C++中的 * 和 &

C++中一直出现的 * 和 & 符号,大部分情况下我们可能比较了解他们对应的指针和取地址的概念,但是对于后面的解引用和引用的概念可能就不是很清楚了。下面结合具体的代码一起来分析一下。之后补上C++函数的几种传参方式和左值引用右值引用的概念。

指针和取地址的概念

1
2
3
4
5
6
7
8
9
10
#include<iostream>
using namespace std;

int main()
{
int num;
int *p=&num;//初始化指针,int* p=&num;是一个意思
cout<<p<<endl;
return 0;
}

上面int *p定义了一个指向int类型指针p(我们使用*符号把p声明为指针),并初始化p使其指向int类型的变量num,这里&num中的&是取地址操作符,当&作用于一个对象上时,它返回了该对象的地址。
所以p指针指向的是num所对应的地址。如图

引用和解引用的概念

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include<iostream>
using namespace std;

int main()
{
int i=100;
int &r=i;
//&紧随类型名出现,因此是声明的一部分,r是一个引用
cout<<"r:\t"<<r<<endl;

int *p;
//*紧随类型名出现,因此是声明的一部分,p是一个指针

p=&i;
//&出现在表达式中,是一个取地址符
cout<<"*p:\t"<<*p<<endl;

*p=200;
//*出现在表达式中,是一个解引用符
cout<<"*p:\t"<<*p<<endl;

int &r2=*p;
//&是引用,*是一个解引用符
cout<<"r2:\t"<<r2<<endl;
return 0;
}

最后输出的结果如下:

下面对上面代码做一些额外解释:

  • *p=200;

注意这里*操作符为解引用操作符,它返回指针p所指的对象(左值)。

我们可以对*p赋值(对左值赋值),从而改变p所指的地址上所保存的值,从而改变此地址所存储的变量num的值。(上面num的值变为200)

  • int &r2=*p;

引用是C++语法做的优化,引用的本质还是靠指针来实现的。引用相当于变量的别名。

引用的基本规则:
声明引用的时候必须初始化,且一旦绑定,不可把引用绑定到其他对象;即引用必须初始化,不能对引用重定义;
引用可以改变指针的指向,还可以改变指针所指向的值;
引用的概念可以理解为一种绑定,或者是一个对象的别名,对引用的一切操作,就相当于对原对象的操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<cstdio>  

int main()
{
int val = 7, val2 = 100;
int &refval = val, &refval2 = val2; ///引用必须要初始化,使其绑定到一个变量上
///修改引用的值将改变其所绑定的变量的值
refval = -12;
printf("%d %d\n", val, refval);//-12,refval的值和val一样

///将引用b赋值给引用a将改变引用a所绑定的变量的值,
///引用一但初始化(绑定),将始终绑定到同一个特定对象上,无法绑定到另一个对象上
refval = refval2;
printf("%d %d\n", val, refval);//变为输出100
return 0;
}

c++函数参数的三种传递方式

  1. 值传递
  2. 引用传递
  3. 指针传递

那么三种传递方式有什么区别,下面用一个网上流行的例子举例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include<stdio.h>
void myswap(int x, int y)
{
int t;
t=x;
x=y;
y=t;
}
int main()
{
int a, b;
printf("请输入待交换的两个整数:");
scanf("%d %d", &a, &b);
myswap(a,b); //作为对比,直接交换两个整数,显然不行
printf("调用交换函数后的结果是:%d 和 %d\n", a, b);
return 0;
}

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include<stdio.h>
void myswap(int *p1, int *p2)
{
int t;
t=*p1;
*p1=*p2;
*p2=t;
}
int main()
{
int a, b;
printf("请输入待交换的两个整数:");
scanf("%d %d", &a, &b);
myswap(&a,&b); //交换两个整数的地址
printf("调用交换函数后的结果是:%d 和 %d\n", a, b);
return 0;
}

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<stdio.h>
void myswap(int &x, int &y) //这里的形参为引用类型,引用与实参进行绑定,作为实参的别名
{ //所以,使用引用类型,传入实参后,函数对引用的操作,
int t; //就是对实参的操作,所以实参会发生变化
t=x;
x=y;
y=t;
}

int main()
{
int a, b;
printf("请输入待交换的两个整数:");
scanf("%d %d", &a, &b);
myswap(a,b); //直接以变量a和b作为实参交换
printf("调用交换函数后的结果是:%d 和 %d\n", a, b);
return 0;
}

运行结果:

在第一个程序中,传值不成功的原因是指在形参上改变了数值,没有在实参上改变数值。
在第二个程序中,传地址成功的原因利用指针,改变了对应地址的值。
在第三个程序中,这里的形参为引用类型,引用与实参进行绑定,作为实参的别名。传入实参后,函数对引用的操作,就是对实参的操作,所以实参会发生变化。

c++的左值引用和右值引用

左值和右值

左值是可以放在赋值号左边可以被赋值的值;左值必须要在内存中有实体;
右值当在赋值号右边取出值赋给其他变量的值;右值可以在内存也可以在CPU寄存器。
一个对象被用作右值时,使用的是它的内容(值),被当作左值时,使用的是它的地址。

左值引用
左值引用的基本语法:type &引用名 = 左值表达式;

右值引用
右值引用的基本语法:type &&引用名 = 右值表达式;
右值引用在企业开发人员在代码优化方面会经常用到。

关于左值引用和右值引用的一篇文章解释