一.引用(&)
概念:引用是为对象起了另外一个名字,通过将声明符写成&d的形式来定义引用类型,其中d是声明的变量名,如:int i=5;int &d=i;
。初始化变量时,若定义了引用,那么程序会把引用和他的初始值绑定在一起,而不是将初始值拷贝给引用。一旦初始化完成,引用将和他的初始对象一直绑定在一起,不能改变。所以定义了引用必须进行初始化。
1.&:&符号既能作为表达式里的运算符,也能作为声明的一部分出现,符号的上下文决定了他的意义。
例如:int val=0;int &d=val;
这里的&就是引用了,他紧跟在类型名之后。如果他出现在表达式中,例如:int a=0;int b=1;int c=a&b
。这里的&符号就是按位与,再例如:int i=1;int *p=&i;
。这里的&就是取地址符。
2.对引用的操作
定义了一个引用后,在其上面进行的所有操作实际上是在与之绑定的对象上进行的。
3.对引用的赋值
我们看下面这段代码:
int i;
int &ri = i;//:ri指向i;二者绑定在一起 ri相当于i的另外一个名字
i = 5;
ri = 10;//:相当于把10赋给了i
cout << i << " " << ri << endl;
执行这段代码后,i和ri的值分别是多少?很显然ri=10。那么i呢?是5嘛?显然不是的。当我们在编译器你中执行这段代码时,会发现i和ri都是10;为什么呢?在第二行代码中,ri是i的引用,它相当于i的另外一个名字。所以在第四行代码中给ri赋值10,相当于给i赋值10.所以i和ri的值都为10;
4.引用的定义
4.1运行一条语句中定义多个引用,其中每个引用标识符都必须以&符号开头。我们看如下代码:
int i=1024,i1=2048;//:i和i1都是int
int &r=i,r2=i2;//:r是i的引用,r2是int
int i3=10,&ri=i3;//:i3是int,ri是i3的引用
int &i4=i3,&r4=i2;//:i4与r4都是引用
4.2引用的类型必须要与之绑定的对象一样,而且引用只能绑定在对象上,而不能绑定在某个字符或数字或某个表达式的计算结果上
int &ri=10;//:错误
double val=3.11;
int &ri=val;//:错误,类型不同
二.指针基础
概念:指针是一个对象,利用地址,它的值直接指向存在电脑存储器中另一个地方的值。与引用类似,指针也实现了对其他对象的间接访问。
1.获取对象的地址。
指针存放某个对象的地址,要想获取该地址,需要使用取地址符(操作符&)
int val=1;
int *p=&val;//:在这里p存放val的地址,p也就是指向val的指针。
这里p是一个指向int的指针,随后初始化p让其指向名为val的int对象。
2.指针值
指针的值应该属下列四种状态之一:
2.1 指向一个对象
2.2指向紧邻对象所占空间的下一个位置
2.3空指针,意味着指针没有指向任何对象
2.4无效指针,也就是上述情况之外的其他值
3.利用指针访问对象.
如果指针指向了一个对象,则允许使用解引用符(操作符*)来访问对象;对指针解引用会得出所指的对象,因此如果给解引用的结果赋值,实际上也就是给指针所指的对象赋值。我们来看下面这段代码
int val=1;
int *p=&val;
*p=0;
cout<<*p<<" "<<val<<endl;
这时的val的值是什么呢?没错是0;
4.空指针
空指针不指向任何对象,在试图使用一个指针之前可以首先检查他是否为空。如何生成空指针?
int *p1=nullptr;
int *p2=0;
int *p3=NULL;
5.指针初始化
如果使用未初始化的指针,那么将引发运行时错误。访问未初始化的指针也将造成程序奔溃,而且一旦奔溃,要想定位到出错位置将是特别棘手的问题。因此建议初始化所有的指针,并且在可能的情况下,尽量等定义了对象之后再定义指向它的指针。
6.赋值和指针
给指针赋值就是让它存放一个新的地址,从而指向一个新的对象。
7.void* 指针
void*类型的指针可以指向任何类型的对象
8.二级指针
我们知道指针是存放变量的地址,那么二级指针是什么?二级指针是存放指针的地址
9.指针数组
指针数组是指针还是数组?是数组,数组中的元素存放的是指针
三.指针运算
1.指针加减整数.我们来看下面这段代码
int val = 5;
short val1 =5;
int *p = &val;
int *p1 = p+1;
short *p2 = &val1;
short *p3 = p2+1;
cout << p << " " << p1 <<" "<< p2<<" "<<p3<<endl;
在vs下输出看看什么效果
我们可以看出,如果是int类型的指针+1的话,它的地址值就加了4,而如果是short型的指针+1的话,他的地址值就加了2.因此我们可以看出指针的加减与它的类型有关。
四.数组指针
首先我们需要知道数组指针是指针,它是能够指向数组的指针。如:int (*p)[10];
在这里p先与*结合,说明p是一个指针变量,然后指向一个大小为10个int的数组。
1.数组名与&数组名
int arr[5] = { 0 };
cout << arr <<endl;
cout << &arr << endl;
这里的输出是什么呢?
arr与&arr输出是一样的。那么二者是否一样呢?我们再看看下面这段代码的输出
int arr[5] = { 0 ,1,2,3,4};
cout << arr <<endl;
cout << &arr << endl;
cout << arr + 1 << endl;
cout << &arr + 1 << endl;
输出:
很明显二者再进行加1后地址值就不一样了。因为arr是数组首元素的地址,它+1是到了下一个元素的地址,而&arr是整个数组的地址,它+1就跳过了这个数组,故二者意义不同。
2.数组指针的使用
void print(int(*arr)[5], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
printf("%d", arr[i][j]);
}
}
}
这里的int(arr)[5]相当于一个二维数组
五.函数指针与函数指针数组
1.函数指针:如果我们想存放一个函数的地址,那么我们就需要函数指针了。void (*pfun1)();
在这里,pfun1先与*
结合,所以pfun1是指针,它指向的是一个函数,返回值类型为void。
2.函数指针数组:存放函数地址的数组就是函数指针数组。int (*arr[10])();
arr先与[]结合,所以arr是数组,数组的类型是 int ()()类型的函数指针。
六.用指针实现冒泡排序
#include<iostream>
using namespace std;
void order(int *p, int n)
{
int a;
for (int i = 0; i < n-1; i++)
{
for (int j = 0; j < n-1-i; j++)
{
if (*(p +j)>*(p +j + 1))
{
a = *(p + j);
*(p + j) = *(p + j + 1);
*(p + j + 1) = a;
}
}
}
for (int i = 0; i < n; i++)
{
cout << *(p + i) << endl;
}
}
int main()
{
int arr[5] = { 2, 1, 0, 5, 3 };
int n = 5;
order(arr, 5);
}
七.指针和引用的异同。
异:
1.指针是一个实体,是一个对象,而引用知识一个对象的别名。
2.指针可以有多级,而引用只能有一级
3.指针可以为NULL,而引用不能为空,且在定义引用时必须进行初始化。
4.指针初始化后可以改变,即可以指向其他的存储单元,而引用初始化后就和那个对象绑定在一起了,不能随意改变。
5.sizeof引用得到的是引用所指对象的大小,而sizeof指针得到的是指针的大小。
同:二者都可以间接访问其他对象。