C++指针解读
一、概念:指针与引用
程序中的所有变量与函数都存放到内存中,C++提供了指针对它们在内存中的地址进行访问。指针的功能强大,操作灵活,还能提高程序的运行效率。相对地,对内存操作是一把双刃剑,如果处理不当,将会造成程序的崩溃。引用则是变量的别名,它们使用同一块地址。
1.指针的声明
声明指针的一般形式如下:
数据类型标识符 *指针变量名
比如:
int *p_iPoint; //声明一个整型指针
float *a, *b; //声明两个浮点指针
2.指针的赋值
指针可以在声明时赋值,也可以后期赋值
(1)在初始化时赋值,例如:
int i=100;
int *p_iPoint=&i;
(2)在后期赋值,例如:
int *p_iPoint;
int i=100;
p_iPoint=&i;
3.关于指针使用的说明
(1)指针变量名是p,而不是*p。
p=&i的意思是取变量i的地址赋给指针变量p。
可以通过函数直接将地址值输出,但由于变量是由系统分配空间,所以变量的地址不是固定不变的。
在定义一个指针之后,一般要使指针有明确的指向。与 常规的变量未赋值相同,没有明确指向的指针不会引起编译器出错, 但是对于指针则可能导致无法预料的或者隐藏的灾难性后果,所以指针一定要赋值。
(2)指针变量不可以直接赋值。例如:
int a=100;
int *p;
p=100;
(3)不能将*p当变量使用。例如:
int a=100;
int *p;
*p=100; //指针没有获得地址
printf("%d",p); //输出地址,出错语句
printf("%d",*p); //输出指针指向的值,出错语句
实例分析:
#include<iostream>
using namespace std;
void main()
{
int *p1,*p2;
int *p; //临时指针
int a,b;
cout<<"input a: "<<endl;
cin>>a;
cout<<"input b:"<<endl;
cin>>b;
p1=&a;p2=&b;
if(a<b)
{
p=p1;
p1=p2;
p2=p;
}
cout<<"a="<<a;
cout<<" ";
cout<<"b="<<b;
cout<<endl;
cout<<"max="<<*p1<<",min="<<*p2<<end
二、指针运算符和取地址运算符
1. *和&是两个运算符,*是指针运算符,&是取地址运算符。
取地址运算符如上图,变量i的值为100,存储在内存地址为4009的地方,取地址运算符&使指针变量p得到地址4009.
指针运算如上图所示,指针变量存储的地址编号是4009,指针通过指针运算符可以得到地址4009所对应的数值。
2.指针运算符和取地址运算符的说明
声明并初始化指针变量时同时用到了*和&这两个运算符。例如:
int *p=&a; 等同于 int *(p=&a);
3.&*p和*&a的区别
&和*的运算符优先级别相同,按自右而左的方向结合,因此&*p是先进行*运算,*p相当于变量a,再进行&运算,&*p就相当于去变量a的地址。*&a是先计算&运算符,&a就是取变量a的地址,然后进行*运算,*&a就相当于取变量a所在地址的值,实际就是变量a。
4.指针运算
指针变量存储的是地址值,对指针做运算就等于对地址做运算。
定义指针变量时必须指定一个数据类型。指针变量的数据类型用来指定该指针变量所指向数据的类型。
三、指针与数组
1、数组的存储
数组,作为同名、同类型元素的有序集合,被顺序存放在一块连续的内存中,而且每个元素存储空间的大小相同。数组第一个元素的存储地址就是整个数组的存储首地址,该地址放在数组名中。
对于一维数组而言,其结构是线性的,所以数组元素按下标值由小到大的顺序依次存放在一块连续的内存中。在内存中存储一维数组如下图。
2、二维数组
a代表二维数组的地址,通过指针运算符可以获取数组中的元素。
(1)a+n表示第n行的首地址;
(2)&a[0][0]既可以看作数组0行0列的首地址,同样还可以看作二维数组的首地址。&a[m][n]就是第m行n列元素的地址;
(3)&a[0]是第0行的首地址,当然&a[n]就是第n行的首地址;
(4)a[0]+n,表示第0行第n个元素地址;
(5)*(*(a+n)+m)表示第n行第m列元素;
(6)*(a[n]+m)表示第n行第m列元素。
3. 指针与字符数组
字符数组是一个一维数组,使用指针同样也可以引用字符数组。引用字符数组的指针为字符指针,字符指针就是指向字符型内存空间的指针变量,其一般的定义语句如下:
char *p;
字符数组就是一个字符串,通过字符指针可以指向一个字符串。
语句: char *string=“www.mingri.book”; 等价于下面两语句:
char *string;
string="www.mingri.book";
#include<iostream>
using namespace std;
void main()
{
char str1[50],str2[30],*p1,*p2;
p1=str1;
p2=str2;
cout<<"please input string 1: "<<endl;
gets(str1);
cout<<"please input string2: "<<endl;
gets(str2);
while(*p1!='\0')
p1++;
while(*p2!='\0')
*p1++=*p2++;
*p1='\0';
cout<<"the new string is: "<<endl;
puts(str1);
}
四、指针与函数
指针变量也可以指向一个函数。一个函数在编译时被分配给一个入口地址,这个函数入口地址就称为函数的指针。可以用一个指针变量指向函数,然后通过该指针变量调用此函数。
一个函数可以带回一个整型值、字符值、实型值等,也可以带回指针型的数据,即地址。其概念与以前类似,只是带回的值的类型是指针类型而已。返回指针值得函数简称为指针函数。
定义指针函数的一般形式为:
类型名 *函数名(参数表列);
例如定义一个具有两个参数和一个返回值的函数的指针;
int *a(int x, int y);
#include<iostream>
#include<iomanip>
using namespace std;
int avg(int a,int b);
void main()
{
int iWidth,iLenght,iResult;
iWidth=10;
iLength=30;
int(*pFun)(int,int); //定义函数指针
pFun=avg;
iResult=(*pFun)(iWidth,iLenght);
cout<<iResult<<endl;
}
int avg(int a, int b)
{
return (a+b)/2;
}
指针pFun是指向avg函数的函数指针,调用pFun函数指针,就和调用avg一样。
五、内存与安全
1、堆与栈
在程序中,我们定义一个变量,它的值会被放入内存中。如果我们没有申请动态分配的方式,它的值将放到栈中。在栈中的变量所属的内存大小是无法被改变的,它们的产生与消亡也与变量定义的位置和存储方式有关。与栈相对应的,堆是一种动态分配方式的内存。当我们申请使用动态分配方式去存储某个变量,那么这个变量会被放入堆中。根据需要,这个变量的内存大小可以发生改变,内存的申请和销毁的时机则由编程者来操作。
2、关键字new与delete
new 用来动态申请内存,delete用来销毁不使用的内存;
内存销毁实质上是系统判定该内存不是编程人员正常使用的空间,系统也会将它分配给别的任务。若擅自使用被销毁内存的指针更改该内存的数据,很可能会造成意想不到的结果。
六、引用
1、左值与右值
C++中的每个语句、表达式的结果分为左值与右值两类。左值指的是内存当中持续存储的数据,而右值是临时存储的结果。
2、引用
在C++11标准中提出了左值引用的概念,如果不加特殊声明,一般认为引用指的是左值引用。引用实际上是一种隐式指针,它为对象建立一个别名,通过操作符&来实现。引用的形式如下:
int a=10;
int & ia=i;
ia=2;
七、函数与引用
1、在C++语言中,函数参数的传递方式主要由两种,分别为值传递和引用传递。所谓值传递,是指在函数调用时,将实际参数的值赋值一份传递到调用函数中,这样如果在调用函数中修改了参数的值,其改变不会影响到实际参数的值。
2、而引用传递则恰恰相反,如果函数按引用方式传递,在调用函数中修改了参数的值,其改变会影响到实际参数。
八、实际操作
①指针实现冒泡排序
#include<iostream>
using namespace std;
void order(int *p,int n)
{
int i,t,j;
for(i=0;i<n-1;i++)
for(j=0;j<n-1-i;j++)
if(*(p+j)>*(p+j+1))
{
t=*(p+j);
*(p+j)=*(p+j+1);
*(p+j+1)=t;
}
printf("排序后的数组: ");
for(i=0;i<n;i++)
{
if(i%5==0)
printf("\n");
printf("%5d",*(p+i));
}
printf("\n");
}
void main()
{
int a[20],i,n;
printf("请输入数组元素的个数:\n");
scanf("%d",&n);
printf("请输入各个元素:\n");
for(i=0;i<n;i++)
scanf("%d",a+i);
order(a,n);
}
②//使用指针插入元素
#include<iostream>
using namespace std;
void insert(int *a,int n,int x)
{
int *p,*q=NULL; //声明指针变量
for(p=a;p<a+n;p++) //遍历数组元素
{
if(*p>x) //找到要插入的位置
{
q=p; //记录要插入的位置
break; //跳出循环
}
}
for(p=a+n;p>=q;p--) //将插入位置之后的数据下移
*p=*(p-1);
*q=x; //插入
}
void main()
{
int i,a[10+1],an;
int *p;
printf("Input 10 seriate integer:\n");
for(i=0;i<10;i++)
scanf("%d",&a[i]);
printf("input inserting data: ");
scanf("%d",&an);
insert(a,10,an);
for(p=a;p<a+10+1;p++)
{
printf("%3d",*p);
}
}
③//使用指针的指针输出字符串
#include<iostream>
using namespace std;
void main()
{
char *string[]={"赵 XX",
"钱 XX",
"孙 XX",
"李 XX",
"周 XX"}; //使用指针数组创建字符串数组
char **p,i; //声明变量
p=strings; //指针指向字符串数组首地址
for(i=0;i<5;i++)
{
printf("%s\n",*(p+i));
}
}
九、后记
指针是C++学习过程中的重点、难点,需要自身多加练习、操作。