简明C++指针基础
文章目录
1. 什么是指针?
指针是一种数据类型,指针与内存单元的地址密切相关。
内存单元的直接和间接访问
在C/C++
语言中,每个变量都分配有确定的内存空间,使用变量名可直接访问内存中的数据;通过变量的地址也可以间接访问内存中的数据。
地址与指针
定义一个变量,系统按变量类型为变量分配不同数目的内存单元,将其第一个内存单元的地址作为变量的地址。
如:int a;
a
变量占用4
个内存单元,则变量a的地址为第一个内存单元的地址。使用变量名a可直接存取内存单元中的值。
在C/C++
中,允许定义一种特殊的变量,用于存放某变量的地址。
现假设变量pta
中存放着整型变量a
的地址。pta=&a;
我们说pta
中存放的是指向变量a
的指针。即pta
是一个变量指针。
我们将存放“地址”的变量称为指针变量,这里的“地址”就是指针。因此,变量的地址就是变量的指针。
指针的主要用途
-
参数传递:指针作参数可以实现参数按引用传递的功能
-
动态分配:利用动态分配可构建动态数组,动态数组需要借助指针实现
-
数据结构:创建可伸缩的数据结构,如链表、栈和队列、树和图等。
-
多态处理:面向对象编程中的“运行多态性”的处理是利用指针与引用实现的。
2. 变量与地址
使用指针变量的三个基本步骤:
- 定义指针变量:即给指针变量分配内存空间
- 对指针变量赋值:即使指针变量指向某对象,该对象可以是变量、数组、函数或动态分配的一块内存空间等
- 通过指针变量间接访问所指向的对象
定义指针变量
数据类型 *变量名
其中,*
是指针型变量的标志符号,变量名
为指针变量名(构成同标识符),数据类型
为指针变量所指向变量的数据类型。
数据类型 *
表示指针类型。
定义一个指针变量,系统将为该指针变量分配一定大小的内存。
指针变量的初始化:
int a=5,*pta=&a;
等价于
int a,*pta;
a=5;
pta=&a;
*pta
表示pta
指向的变量a
定义多个指针变量:
double *p1,*p2;
定义2个双精度型的指针变量p1
和p2
,它们只能指向double
型变量。
变量p1和p2的类型为:double *
。
通过指针变量间接访问所指向的变量
两个特殊的单目运算符:
&
取地址运算符
&变量名
获取变量的内存单元地址
*
指针运算符
*指针变量名
或 *指针常量
表示该指针所指向的变量
如果指针变量
pta
中存放着变量a
的指针,则*pta
表示pta
指向的变量即变量a
int a=5,*p=&a;
cout<<&a<<endl;
cout<<a<<endl;
cout<<&p<<endl;
cout<<p<<endl;
cout<<*p<<endl;
注意点:
- 不要访问没有被初始化的指针变量:如
int *p;
cin>>p;
由于p变量未初始化,p中可能存在一个不确定的单元地址,这时的输入将会改变原存储单元的值,造成结果混乱。
- 指针变量可以有空值,即该指针变量不指向任何变量。
常用符号常量NULL
表示空指针值(新编译器支持nullptr
),NULL
代表的值是0。编译系统中约定0号内存单元不存放有效数据。
3. 函数与指针
一个函数在编译时被分配一个入口地址,这个地址就被称为函数的指针。在C++中,函数名代表函数的入口地址。
- 指针作为函数的参数:实现地址传递
- 返回指针的函数
- 利用指向函数的指针调用函数
指针作为函数的参数:
实现地址传递
void swap(int *xp,int *yp)
{
int t;
t = *xp;
*xp = *yp;
*yp = t;
}
swap(&x,&y);
利用指针参数带回函数中的多个值(return只能带回1个值)
例如,要计算一维数组元素的平均值,并能带回数组中的最大值和最小值。
函数原型设计如下
double faver(int s[],int n,int *max,int *min);
代码如下:
double faver(int s[],int n,int *max,int *min){
double aver=s[0];
*max = *min = s[0];
for(int i =1;i < n; i++){
aver+=s[i];
if(s[i]>*max)*max=s[i];
if(s[i]<*min)*min=s[i];
}
return aver/n;
}
int main(){
int a[5]={12,421,12,4,2},max,min;
double aver;
aver = faver(a,5,&max,&min);
...
}
返回指针的函数
类型 *函数名 (形参表)
{
语句序列
}
例如,编写函数,返回字符串首次出现的非空格开始的字符串。
返回字符串就是返回字符串的首地址。
char *noblank(char *str){
while(*str==' ')str++;
return str;
}
int main(){
char *s1=" ss";
s2 = noblank(s1);
...
}
指向函数的指针变量
函数返回值类型(*指针变量名)(形参类型列表);
例如:定义指向double型函数的指针变量,该函数有一个double型参数
double (*pf)(double);
pf = sqrt;
4. 数组与指针
一维数组的指针
数组占内存中一块连续的存储空间,每个数组元素都有确定的内存地址;可通过定义指向数组元素类型的指针变量间接访问数组中的各个元素。
C++语言规定,数组名代表数组的首地址,它是一个常量指针。
例如,
int a[10], *p=a;
说明a
是一个整型数组,p
是一个整型的指针变量,且p
指向数组a
,其中a
为数组名,代表数组的首地址,即&a[0]
。
指针的算术和关系运算
指针的算术运算
- 指针和整数做加减法
指针 +或- 整数 = 指针
例如:
int a[10]={10,20,30},*p=a,i;
p+i
:表示p
所指元素后第i
个元素的指针
在c++
中,指针的算术运算和指针指向的变量类型有关,如p
指向int
型,因int
型变量长度为4字节,所以,p+1
相当于p+4
。
- 两个同类型指针做减法运算
指针2 - 指针1 = 整数
常用于计算两个指针之间包含的元素个数。
计算方法:
指针
2
−
指针
1
元素的字节长度
\frac{指针2-指针1}{元素的字节长度}
元素的字节长度指针2−指针1
指针的关系运算
即两个指针可以比较大小
double x[5]={1.2,3.2,212},*p;
for (p=x;p<x+5;p++){
cout<<*p<<"\t";
}
数组元素的不同表现形式
假设:
int a[10],*ptr=a,i;
那么a[i]
的地址可以用a+i
表示,对地址a+i
进行间接访问运算,即*(a+i)
;而*(a+i)
又解释为指针a+i
所指向的对象,即a[i]
。所以,a[i]
与*(a+i)
的表示是等价的。
其中,a[i]
被称为数组元素的下标法表示,*(a+i)
称为数组元素的指针法表示。
假设:
int a[10]={1,2,3,4,5,6},*p=a,i;
此时,a[i]
与p[i]
等价(下标法);*(a+i)
与*(p+i)
等价(指针法)。
二维数组的指针
设二维数组:
int a[3][4],i,j;
- 二维数组
a
中第i
行j
列元素的地址:&a[i][j]
二维数组在内存中映射为一个一维数组,因此可以通过指向元素的指针快速访问二维数组中的每个元素:
int *p, max = a[0][0];
for(p=&a[0][0],p<&a[0][0]+12;p++)
if(*p>max)
max = *p;
- 二维数组的行地址
int a[3][4]
由3行元素组成,即a
对应a[0],a[1],a[2]
。
而每行又由4个类型相同的元素组成,分别对应一个一维数组。
a[0]
对应a[0][1],a[0][1],a[0][2],a[0][3]
。
其中,a
为行元素数组的名字,即a
代表&a[0]
,即0行的地址。
*(a[i]+j)
等价于a[i][j]
a[i]
等价于*(a+i)
因此,
*(a[i]+j)
也等价于*(*(a+i)+j)
即
*(*(a+i)+j)
与a[i][j]
等价
我们将*(*(a+i)+j)
称为二维数组a[i][j]
的指针法表示。
其中,a
为首行地址,a+i
为第i
行的地址;*(a+i)
为a的第i
行0列的地址;而*(a+i)+j
为a的第i
行第j
列元素的地址。
指向具有M个元素的一维数组指针
类型名(*变量名)[M];
通常利用该指针变量,指向二维数组的行地址。其中,M表示二维数组的列数
int a[3][4]={{1,3,4,5},{1,2,3,4},{1,3,4,3}},(*p)[4];
for(p=a;p<a+3;p++){
for(int j=0;j<4;j++)
cout<<*(*p+j)<<"\t";
cout<<endl;
}
等价于
int a[3][4]={{1,3,4,5},{1,2,3,4},{1,3,4,3}},(*p)[4];
for(p=a;p<a+3;p++){
for(int *q=*p;q<*p+4;q++)//q是指向列元素的指针
cout<<*q<<"\t";
cout<<endl;
}
字符串指针
字符串指针是字符串的首地址,即第一个字符的地址。使用char
型指针变量存放其首地址。
char *str;
str = "hello"
等价于
char *str ="hello"
引用字符串中的字符可以使用*(指针变量+下标)
或 指针变量[下标]
。
编写在字符串中查找某字符的函数:
char* strchr(char* str,char c){
while(*str != '\0'){
if(*str == c) return str;
str ++;
}
return NULL;
}
int main(){
char* str = "abcde";
char* p;
p = strchr(str,e);
if(p==NULL) cout<<"NOT FOUND"<<endl;
else cout<<"POSITION:"<<p-str<<endl; //返回索引号
return 0;
}
注意常量字符串指针和变量字符串指针的区别:
char str[81]="abcde",*pstr="abcde"; 正确
char str[81],*pstr;
str = "abcde"; 错误,数组名是常量指针!不能被赋值
pstr = "abcde"; 正确
char str[81],*pstr;
cin>>str; 正确
cin>>pstr; 错误,pstr未被初始化,没有指向对象
指针数组
类型 *数组名[元素个数]
char *str[]={"Basic","C++","Java"}
5. 指针与结构体
结构体变量的指针:&结构体变量名
- 定义指向结构体变量的指针
结构体类型 *指针变量名;
- 使用结构指针访问结构变量中的成员
格式1:(*指针变量).成员名
格式2:指针变量->成员名
struct Data{
int year;
int month;
int day;
}
int main(){
Data d={2015,4,8},*p=&d;
cout<<(*p).year<<"-"<<(*p).month<<endl;
cout<<p->year<<"-"<<p->month<<endl;
return 0;
}
6. 动态数组
new
运算符
- 用于动态申请所需的内存空间
指针变量 = new 类型
double *p;
p = new double;
*p = 100.0;
等价于
double *p = new double(100.0);
- 动态申请数组
指针变量 = new 类型[元素个数]
char *str;
str = new char[80];
int n, *p;
cin>>n;
p = new int[n];
for (int i = 0; i<n;i++) cin>>p[i];
delete
运算符:释放动态申请到的内存空间