一. 指针
const int n=10;
int y=2,z,k,t;
int a[n]={3,4},b[n][n];
指针的基本使用
指针的定义:
<类型名> *变量名1,*类型名2;
指针的使用必须赋初始值
int *x=NULL; //0同NULL),即空指针
x=&y; //把x指向y所在的地址
*x=3; //*x即访问y,即y赋值为3
cout<<"x="<<x<<" "<<"*x="<<*x<<" "<<"y="<<y<<endl;
//----------指针指向一维数组
x=a; //等价于x=&a[0],都是指向一维数组的第一个 元素
cout<<"x="<<x<<" "<<"*x="<<*x<<" "<<"a[0]="<<a[0]<<endl;
x+=1;
cout<<"x="<<x<<" "<<"*x="<<*x<<" "<<"a[1]="<<a[1]<<endl;
//---------指针指向二维数组
方法1:通过单个指针直接访问数组首个元素
x=&b[0][0]; //b数组定义为b[n][n]
cout<<"x="<<x<<" "<<"*x="<<*x<<" "<<"b[0][0]="<<b[0][0]<<endl;
x+=1; //访问a[i][j+1]
cout<<"x="<<x<<" "<<"*x="<<*x<<" "<<"b[0][1]="<<b[0][1]<<endl;
x+=n; //访问a[i+1][j]
cout<<"x="<<x<<" "<<"*x="<<*x<<" "<<"b[1][1]="<<b[1][1]<<endl;
方法2:通过指针数组访问二维数组
为了更好的理解指针和二维数组的关系,我们先来定义一个指向 a 的指针变量 p:
int (*p)[4] = a;
括号中的*表明 p 是一个指针,它指向一个数组,数组的类型为int [4],这正是 a 所包含的每个一维数组的类型。
[ ]的优先级高于*,( )是必须要加的,如果赤裸裸地写作int *p[4],那么应该理解为int *(p[4]),p 就成了一个指针数组,而不是二维数组指针。
对指针进行加法(减法)运算时,它前进(后退)的步长与它指向的数据类型有关,p 指向的数据类型是int [4],那么p+1就前进 4×4 = 16 个字节,p-1就后退 16 个字节,这正好是数组 a 所包含的每个一维数组的长度。也就是说,p+1会使得指针指向二维数组的下一行,p-1会使得指针指向数组的上一行。
数组名 a 在表达式中也会被转换为和 p 等价的指针!
int (*x1)[n]; //指针数组
b[0][0]=1;
b[0][1]=2;
b[1][1]=3;
x1=b; //一维数组指针
cout<<"x1="<<x1<<" "<<"*x1="<<*x1<<" "<<"**x1="<<**x1
<<" "<<"b[0][0]="<<b[0][0]<<endl;
b[4][5]=10;
cout<<"(*(x1+4)+5)="
<<(*(x1+4)+5) //(*(x1+i)+j)即访问第i行第j列的元素地址
<<"*(*(x1+4)+5)="
<<*(*(x1+4)+5) //*(*(x1+i)+j)即访问第i行第j列的元素的值
<<" "<<"b[4][5]="<<b[4][5]<<endl;
二. 动态内存
堆:存放动态数据的区域,动态内存也称为堆内存
动态内存时程序执行时才可以申请,使用和释放的内存,即存放动态数据的内存区域
——使用堆内存情况:
1:需要储存大量数据,一般申请使用堆内存
2:需要储存一组数,数据类型相同,但数据个数再编程时无法确定,运行时才能确定,这种时候无法定义数组,只能使用堆内存。
动态内存的申请与释放
1:malloc(size)申请————————free()释放
size表示要申请size个字节大小的内存空间
例:
int *p=(int *)malloc(sizeof(int));
free(p)
2:new申请————————delelte释放
int *p=new int;
delete p;
当new申请的是数组空间时,释放语句变为:
delete []<指针名>;
int *p=new int[10];
delete []p;
三.引用
引用是变量或者其他编程实体(如对象)的实名
·引用不可以单独定义
·且必须在声明的同时完成初始化
·引用定义之后不可更改
定义格式:
<类型名>&引用名=变量名
例:
int x_n;
int &y_n=x_n;
此时y_n和x_n具有相同的地址
y_n代表x_n的值
&y_n代表x_n的地址
即 sizeof(y_n) 可以得到x_n(所指向的变量)的大小
指针与引用在函数中的应用
1:指针作为函数形参
采用的是地址调用:
函数实参是内存的地址(数组名,变量的地址,用变量地址初始化的指针)
指针(函数形参)指向内存的地址(函数实参)
特点:形参指针通过间接引用直接访问实参,可以改变实参的值,即函数调用后保留对实参变量的操作结果
————————————相当于实现返回多个结果
例:
void swap_(int *a,int *b)
{
int *t;
*t=*a;
*a=*b;
*b=*t;
}
int main()
{
int x=1;
int y=2;
cout<<"x="<<x<<" "<<"y="<<y<<endl;
swap_(&x,&y);
cout<<"x="<<x<<" "<<"y="<<y<<endl;
}
2:引用作为函数形参
特点
1:实参是相同类型的变量
2:参数传递属于地址传递
3:形参的引用和实参的变量是同一个实体,实际上没有参数
所以对引用的操作就是对实参的操作,函数调用可以改变实参的值。
void swap_(int &a,int &b)
{
int t;
t=a;
a=b;
b=t;
}
int main()
{
int x=1;
int y=2;
cout<<"x="<<x<<" "<<"y="<<y<<endl;
swap_(x,y);
cout<<"x="<<x<<" "<<"y="<<y<<endl;
}
3:常指针和指针常量
常指针:
指针指向的内容不可以通过指针的间接引用来改变
定义格式:
const <类型名>*<指针名>;
例:
int x=2;
const int *p=&x;
此时x的值可以自行修改,但无法通过*p进行修改
常引用:
如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。
声明方式:
const <类型名> &<引用名>=变量名
int x=2;
const int &y_x=x;
指针常量:
指针本身成为常量,无法修改(即无法更改地址)
声明方式:
<类型名>*const<指针名>=<变量地址(&变量名)>;
例:
int x=2,y=1;
int * const p=&x;
此时可以直接或者修改x的值,但无法更改p指向的地址
若添加语句 p=&y,会报错
const出现在 * 的左边,则说明指针所指向的内容是常量(即可以指向常量)
--------不可以通过指针去修改指向地址的值(已经是常量),但可以通过变量修改
同时可以改变指针的指向
const出现在 * 的右边,则说明指针本身是常量(不可以指向常量)
--------------不可以改变地址的指向,因为本身是常量
如果*左右两边都出现const,那么说明指针本身是常量,它所指向的内容也是常量。