内容
- 指针和指针变量
- 指针作为函数参数
- 指向数组的指针
- 指针数组
- 指向指针的指针
- 函数指针
一 指针
1.定义
内存中的一个字节(Byte)为一个存储单元。
存储单元的编号称为地址。
变量的地址是指该变量所在存储区域的第一个字节(单元)的地址。
![c4aa0421f37c477ed12da6a94832a2db.png](https://img-blog.csdnimg.cn/img_convert/c4aa0421f37c477ed12da6a94832a2db.png)
指针变量用于存放地址值。
指针变量的定义格式:类型说明符 *指针变量名;
例:int *pi ; 定义 pi 为指向 int 类型变量的指针变量
注:C++语言规定有效数据的指针不指向0单元(为非0值),如果指针变量值为0,即NULL(在ios.h中已定义),表示空指针,即不指向任何变量。
2.运算符
& 取址运算符
功能:返回变量的内存地址(升一级)
* 取值运算符
功能:访问指针指向的变量(降一级)
例:
int *p,m;
m = 200;
p = &m;
//则p等价于&m,*p等价于m
![0dd7fe2e8d447a4226a5ec039640524e.png](https://img-blog.csdnimg.cn/img_convert/0dd7fe2e8d447a4226a5ec039640524e.png)
3.指针变量的初始化
初始化格式:类型说明符 *指针变量名=初始地址值 ;
例:
char c, *p1 ;
p1=&c ; /* 赋值语句,定义后进行 */
例:
char c ;
char *pc=&c ; /* 指针初始化,定义时进行 */
注:
(1)指针变量p必须初始化或赋值获得值后才能使用*p访问。
即
int x,*p ;
x=0 ;
*p=x ;
//是错误的
(2)若有 int m ; int *p = &m ;等价于:int m ; int *p ; p = &m ;
4.直接访问与间接访问
直接存取方式(直接访问):在程序中体现为直接使用变量名来存取变量值
间接存取方式(间接访问):在程序中体现为通过 p 来存取变量 m 的值。
![0bf0dd9eb1d89cb4cd13531daea7dd4b.png](https://img-blog.csdnimg.cn/img_convert/0bf0dd9eb1d89cb4cd13531daea7dd4b.png)
5.指针做函数参数
(1)基本类型量做函数参数
#include <iostream>
using namespace std;
void swap(int x,int y)
{ int t;
t=x; x=y; y=t;
}
void main( )
{ int x=3, y=9;
swap(x,y);
cout<< x<< ','<< y<<endl;
}
输出:3,9
(2)使用引用
#include <iostream>
using namespace std;
void swap(int &x,int &y)
{ int t;
t=x; x=y; y=t;
}
void main( )
{ int x=3, y=9;
swap(x,y);
cout<< x<< ','<< y<<endl;
}
输出:9,3
(3)指针做函数参数
#include <iostream>
using namespace std;
void swap(int *px,int *py)
{ int t;
t=*px;*px=*py;*py=t;
}
void main( )
{ int x=3,y=9,*p1,*p2;
p1=&x; p2=&y;
swap(p1,p2);
cout<< x<< ','<< y<<endl;
}
等价于
#include <iostream>
using namespace std;
void swap(int *px,int *py)
{ int t;
t=*px;*px=*py;*py=t;
}
void main( )
{ int x=3,y=9;
swap(&x,&y);
cout<< x<< ','<< y<<endl;
}
输出:9,3
二 一维数组与指针
1.一维数组与指针
数组元素在内存中连续存放,数组名是该存储区的起始地址,是地址常量。
例:
int a[10]; float b[10];
//a 是 a[0] 元素的起始地址。
//b 是 b[0] 元素的起始地址。
例:
//假定: b = = 1000,且两个数组连续存放,则:a = = 1040
int a[10],*p;
p=a;
(1) 第i个元素值的表示(p的初值为a) :
- *(a+i) 数组名法
- *(p+i) 指针法
- a[i] 下标法
- p[i] 下标法
(2)第i个元素地址的表示(p的初值为a) :
- a+i 数组名法
- p+i 指针法
- &a[i] 下标法
- &p[i] 下标法
注:
(1)int a[]等价于int *p。
(2)p=a;用指针变量名代替数组名,p指向a[0]。但p不能赋值a,因为a是常量,即a=p是错误的。
(3)p++跳4个字节(int)。
(4)若 p = &a[6]; 则 *p 访问a[6]。a右加[]降一级变int,左&升一级变int*。
(5)通过指针变量和数组名访问元素的重要区别:
指针变量是地址变量,其指向由所赋值确定;数组名是地址常量(指针常量),恒定指向数组的第1个元素。
2.指针运算
(1)指针±数值:p±n等价于p±n*c
(2)指针-指针:结果为两指针所指向地址间数据的个数。
例:
int *px, *py, n, a[5];
px=&a[1]; py=&a[4]; n=py-px;
![de00d9438ea68631b0d62a6ccafccb00.png](https://img-blog.csdnimg.cn/img_convert/de00d9438ea68631b0d62a6ccafccb00.png)
(3)指针 比较 指针:产生的结果为 0(假)和 1(真)。
3.一维数组名做函数参数
void f( int b[ ], int n ) //形参可以写成 int *b或int b[10]或 int b[ ]
{ …
b[i] //或 *( b+i )
…
}
void main(void)
{ int a[10], *p;
p=a;
f( a , 10 ); //实参可以写 a 或 p 或 &a[0]
……
}
三 指针和字符串
1.指针和字符串
(1)定义一个字符数组存放字符串
char str[10]="Hello!";
//str是一维字符数组名,即起始地址。
(2)定义一个字符指针并令其指向一个字符串
char *strp="Hello!" ;
//将字符串“Hello!”的首地址赋给指针变量strp。
(3)字符数组名(str)与字符指针(strp)的区别:
char str[10], *strp;
① str --- 指针常量,不论是否对字符数组赋值,数组空间已分配,str的指向明确。
strp --- 指针变量,若不赋初值,则其指向不确定。
② 赋值: char str[10], *strp;
strcpy(str, “ABC”);
str的指向不变,改变的是存储单元的内容。
strp=“Hello!”;
将 strp 指向字符串常量“Hello!”。
strp=str;
将 strp 指向字符串数组str。
(4)使用字符串指针易犯的错误:
- char str[10]; str=“Hello!”;
× str是指针常量,不能给常量赋值。 - char str[10]; str[ ]=“Hello!”;
× 不能用赋值语句给数组整体赋值。
2.字符串指针作函数参数
例:
int my_strlen(char *s) // 自定义函数
{ int n ;
for(n=0 ;*s!='0' ;s++) n++ ;
return(n);
}
void main( )
{ char str[ ]="Hello!" ;
cout << *str << 't';
cout << my_strlen(str) << 't';
cout << *str << endl;
}
输出:H 6 H
注:形参s数值的变化不会影响主调函数中的实参, 函数调用后再次输出str数组首字符, 没有变化。
字符串拷贝函数,比较以下几个函数:
1)
void copy_string(char to[ ], char from[ ])
{ int i=0 ;
while(from[i]!='0';
{ to[i]=from[i];i++ ; }
to[i]='0' ; }
2)
void copy_string(char to[ ], char from[ ])
{ int i=0 ;
while((to[i]=from[i])!='0') i++ ; }
3)
void copy_string(char *to, char *from)
{ while((*to=*from)!='0')
{ to++ ; from++ ; } }
4)
void copy_string(char *to, char *from)
{ while((*to++ = *from++)!='0') ; }
5)
void copy_string(char *to, char *from)
{ while(*to++ = *from++) ; }
四 二维数组与指针
1. 二维数组与指针
(1)行指针和元素指针
例:
![4ea7b1fa7455a96e0a3c46216cdede2e.png](https://img-blog.csdnimg.cn/img_convert/4ea7b1fa7455a96e0a3c46216cdede2e.png)
二维数组名 a 指向 a[0],a 是行指针,a+1指向下一行, 即指向a[1]。
一维数组名a[0]指向a[0][0],a[0]是元素指针,a[0]+1指向下一元素, 即指向a[0][1]。
从元素指针的角度来看:若欲访问a[i][j],可以写成 *( a[i]+j )
从行指针的角度来看:*(a+i) ==> a[i]
所以访问元素a[i][j],也可写成 *(*(a+i)+j )
(2)二维数组中元素a[i][j]地址的表示:
- &a[i][j]
- a[i]+j
- *(a+i)+j
(3)二维数组中元素值的引用方法:
- a[i][j]
- *(a[i]+j)
- *(*(a+i)+j)
- (*(a+i))[j]
2. 指向一维数组的指针变量(行指针)
例:
int (*p)[4]; int a[3][4];
//定义一个行指针p,指向一维数组,该一维数组包含4个整型数值(相当于二维数组的一行元素)。
p=a ; // p和a的类型一样,类型为 int(*)[4]
注:
(1)不可缺少() ;
(2)数组名 a 和 p 同为行指针,但 a 是指针常量,而 p 是指针变量,其他特性完全一样。
通过行指针 p 引用数组元素的方式:
- p[i][j]
- *(p[i]+j)
- *(*(p+i)+j)
- (*(p+i))[j]
对比:
- a[i][j]
- *(a[i]+j)
- *(*(a+i)+j)
- (*(a+i))[j]
比较:
①与一维数组名等价的指针变量(指向数组元素的指针)
int a[10], *p; // 类型是 int *
p=a;
②与二维数组名等价的指针变量(指向一维数组的指针)
int a[3][4];
int (*p)[4];
p=a; // 类型是 int (*)[4]
3.二维数组名作函数参数
例:
int total(int(*p)[4], int n) //等价int p[3][4]或int p[ ][4]
{ int i,j, sum=0;
for( i=0 ;i<n;i++)
for( j=0 ;j<4;j++)
sum += *(*(p+i)+j) ;
return sum;
}
void main(void)
{ int a[3][4],sum,i,j ;
for(i=0;i<3;i++)
for(j=0; j<4; j++) cin>> *(a[i]+j);
sum = total( a, 3 );
cout << sum << endl;
}
4.将二维数组看成一维数组访问
法一:通用二维数组输出函数
void print(int *p, int row, int col)
{ int i;
for(i=0; i<row*col; i++, p++)
{ if(i%col==0) cout<<'n';
cout<<setw(4)<< *p;
}
cout<<'n';
}
void main(void)
{ int a[3][4]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
int b[2][3]={1, 2, 3, 4, 5, 6};
print(a[0], 3, 4);
print(b[0], 2, 3);
}
法二:
void main(void)
{ int a[3][4]={...};
int *p,i,j;
p=a[0];
cin >> i >> j;
cout<< "a[" << i << "][" << j<< "]="<< *(p+i*4+j) << endl;
}
注:二维数组a[N][M]。二维数组按行存放,a[i][j]的前面有i*M+j 个元素;若序号从0开始,则a[i][j]的序号是:i*M+j。
五 获得函数处理结果的几种方法
1、利用return语句返回值(只能返回一个值)
2、利用全局变量得到函数调用结果(安全性欠佳)
3、利用指针变量作为函数参数来取得函数调用的结果(较好,可安全地得到多个结果值,涉及参数空间的分配)
(1)普通变量指针作参数
void fun(int a, int b, int *pmax, int *pmin)
{ *pmax=(a>b)?a:b ; *pmin=(a<b)?a:b ; }
void main( )
{
int a, b, max, min;
cin>>a>>b;
fun(a, b, &max, &min);
cout<<"max="<< max <<endl;
cout<<"min="<< min <<endl;
}
(2)数组名做参数
void reverse( int b[ ], int n ) //等价于 int *b, int n
{
int t;
int *p2 = b+n-1;
while(b < p2)
{ t = *b; *b = *p2; *p2=t; b++; p2-- ; }
}
void main(void)
{ int a[10]={1,2,3,4,5,6,7,8,9,10}, i;
reverse(a, 10);
for(i=0; i<10; i++) cout << a[i] << 't' ;
cout << endl ;
}
4、利用引用作为函数参数来取得函数调用的结果(较好,可安全地得到多个结果值,不涉及参数空间的分配)
void fun(int a, int b, int &ma, int &mi)
{ ma=(a>b)?a:b ; mi=(a<b)?a:b ; }
void main( )
{
int a, b, max, min;
cin>> a>> b;
fun(a, b, max, min);
cout<<"max="<< max <<endl;
cout<<"min="<< min <<endl;
}
六 指针数组
1.定义
数组元素全为指针变量的数组称为指针数组
int *p[10] ;
//数组p 的10个元素 p[0], p[1] ……p[9] 都是指向 int 型数据的指针。数组的10个元素都是 int * 类型的。
注:int (*p)[4]中,定义了一个指向数组的指针,p是指针变量;int *p[10]中p是一个以10个指针为元素的数组的数组名,是指针常量。
2.使用指针数组处理二维数组
int a[3][3],*p[3],i ;
for(i=0;i<3;i++) p[i]=a[i];
通过指针 p 引用数组元素的方式:
- p[i][j]
- *(p[i]+j)
- *(*(p+i)+j)
- (*(p+i))[j]
3.利用字符指针数组处理字符串
例:
char *name[]={"George", "Mary", "Susan", "Tom", "Davis"};
![fac157040c076235698ebccb1c3bd392.png](https://img-blog.csdnimg.cn/img_convert/fac157040c076235698ebccb1c3bd392.png)
将上述多个字符串排序,输出:
#include <iostream>
#include <string>
void main( )
{
char *name[ ]={ "George", "Mary", "Susan", "Tom", "Davis" };
char *ptr;
int i, j, k, n=5;
for(i=0; i<n-1; i++)
{
k=i;
for(j=i+1; j<n; j++)
if( strcmp(name[k], name[j])>0 ) //字符串比较
k=j; //记住最小字符串指针的下标
if(k!=i)
{
ptr=name[i]; name[i]=name[k]; name[k]=ptr;
//通过交换地址交换指针数组元素值
}
}
for(i=0; i<n; i++) //输出结果
cout << name[i] <<endl;
}
4. 指针数组作 main 函数的形参
main( )函数的参数格式:
void main(int argc, char *argv[ ])
{
….
}
//argc和argv就是main函数的参数,他们是程序的“命令行参数”。
//形参argc:命令行中参数的个数。这个值一开始不确定,是看自己输入了多少个参数而确定的。
//形参argv:指针数组的各元素分别指向命令行中各字符串的首地址。
注:
(1)main函数是操作系统调用的,实参只能由操作系统给出。在操作命令状态下,实参是和执行文件的命令一起给出的。例如在DOS,UNIX,Linux等系统的操作命令状态下,在命令行中包括了命令名和需要传给main函数的参数。
(2)利用指针数组作为main函数的参数,可以向程序传送命令行参数(这些参数是字符串),这些字符串的长度事先并不知道,而且各参数字符串的长度一般并不相同,命令行参数的数目是可以任意的。
七 指向指针的指针(二级间接访问)
指向指针的变量称为指针的指针。
其定义的一般格式:类型说明符 **指针变量名
![df3fac9c603fc0360f3d988f4157e1fd.png](https://img-blog.csdnimg.cn/img_convert/df3fac9c603fc0360f3d988f4157e1fd.png)
八 指针和函数
1.函数指针的定义和说明
类型说明符 (*函数指针名)(参数类型表);
(1)一个函数被编译后生成一段二进制代码,该段代码的首地址称为函数的入口地址,即函数指针。
(2)C++将函数名的值处理成函数的入口地址,函数名即函数指针。
(3)可以定义一个指针变量,用于存放函数的入口地址,该指针称为函数指针变量。
例:
int (*fp)(int,int); //定义函数指针 fp 指向具有两个整型参数,并且返回值为整型数据的函数。
int max(int,int); //说明函数 max。
fp=max ; //将函数 max 的入口地址赋给指针变量fp,fp 和 max 都指向函数的入口,即fp 和 max 的值都是函数的入口地址。
![b6ece66c3ec1d569ce0b4eb4fb83b736.png](https://img-blog.csdnimg.cn/img_convert/b6ece66c3ec1d569ce0b4eb4fb83b736.png)
2. 函数指针的使用
函数可以通过函数名调用,也可通过函数指针调用。
如果 fp=max,则对函数 max 的调用方式是:
max(实参表); // 通过函数名调用
fp(实参表); // 通过函数指针调用
(*fp)(实参表);// 通过函数指针调用
例:
#include <iostream>
using namespace std;
void main( )
{ int max(int,int),min(int,int),sum(int,int);
int process(int, int, int (*)(int,int));
int a,b ;
cout << "Enter a and b:";
cin >> a >> b ;
cout << "max=" << process(a,b,max) <<endl;
cout << "min=" << process(a,b,min) <<endl;
cout << "sum=" << process(a,b,sum) <<endl; //三次调用,传递了三个不同的函数入口地址
}
int process(int x,int y, int (*fun)(int,int))
{
return fun(x,y);
}
int max(int x, int y)
{ return( x>y ? x :y ); }
int min(int x, int y)
{ return( x<y ? x :y ); }
int sum(int x, int y)
{ return( x + y ); }
3. 返回指针值的函数(指针函数)
类型说明符 *函数名(参数表){ 函数体 }
例 :
int *f1( ); 返回 int 型指针
float *f2( ); 返回 float 型指针
char *f3( ); 返回 char 型指针
例:
#include <iostream.h>
char *find(char *str, char ch)
{ while(*str!='0')
if(*str==ch) return(str);
else str++;
return(NULL); //若找不到,返回空指针
}
void main( )
{ char s[ ]="warrior"; char *p;
p=find(s, 'r'); if(p) cout << p<< endl;// 输出 rrior
p=find(s, 'i'); if(p) cout << p<< endl;// 输出 ior
p=find(s, 'b'); if(p) cout << p<< endl;// 不输出
}
九 指针小结
(1)int *p;
一般指针,可与一维数组名等价。
如有:int a[10]; p=a; 可用p[i]访问数组元素。
(2)int (*p)[M];
指向含有 M 个元素的一维数组, 可与二维数组名等价。p 是一个行指针。
如有:int a[4][M]; p=a; 可用p[i][j]访问数组元素。
![2e6c4704ef5e3ba99e93f6c8fbb36459.png](https://img-blog.csdnimg.cn/img_convert/2e6c4704ef5e3ba99e93f6c8fbb36459.png)
(3)int *p[M];
指针数组,有M个元素,每个元素都是指针,即有M个指针。
(4)int **p;
指向指针的指针。
(5)int (*p)(int, int);
函数指针,指向参数是两个整型值,并且返回整型值的函数。
(6)int *p( ){...}
返回整型指针值的函数。