本章主要介绍c++常用的指针,包括基本类型指针,数组指针,函数指针,数组指针等。
常用的数组,包括基本类型数组,函数数组,指针数组等。
1.基本类型指针
如:int* s;//该指针表示只向一个int型数据的指针,内存里存放的是某个int型数据的地址,(任何指针类型在32位系统中占4个字节)
int a; //a在栈里存放
s=&a;//s里存放的是变量a的地址
基本类型数组
如:int b[7];//b表示一个装有7个int数据元素的数组,定义数组时,明确数组的长度
数组名表示的是数组首元素地址,也就是上面,b,&b,&b[0]的值是一样的。虽然值是一样的,但是表达的意思还是不同,b(数组名)和&b[0]表示的意思一样,都表示数组首元素的地址,而&b表示的是该数组的地址。
int* t = b;
int *t1 = &b[0];
//这两种写法都正确,因为数组首元素是int类型,用指向int型的指针是正确的。
int* er = &b;//错误的,因为&b表示的是数组的地址,本例中应该用指向装有7个int型数据元素的数组指针才对。
int (* tr)[7];//定义一个指向装有7个int型元素数组的指针。(下面详细讲解)
tr = &b;//正确。
2.数组指针
如上例的:int (*tr)[7]; //这里定义的一个数组指针,指向的装有7个Int型元素数组的指针
理解方式:都知道int p[7]; p表示一个装有7个元素的数组,这里int (*tr)[7],用*tr替换p,*tr表示一个装有7个元素的数组,则tr表示的就是一个装有7个int型元素数组的指针。
指针数组(即存放指针的数组)
如: int* s[10];//这是一个简单的指针数组,s表示一个存放10个int型指针的数组。
理解方式:[](下标运算符)的优先级高于*运算符,s[10]表示存放10个元素的数组,*号表示存放的数据类型是否是指针。
注意:指针移动如,int* p;p+1;指针加1,并不是意味指针移动一个字节,指针移动是按照指针所指向的数据类型所占字节为单位进行移动,这里的p指针指向的是int类型数据,p+1表示移动1个int类型个字节,因此是4个字节。
3.函数指针
如: int (*p)(int a,int b) //定义一个函数指针p,用于指向一个带两个int型参数,且返回值是int型的函数
int add(int a,int b)
{
return a+b;
}
p = add; //正确赋值,函数名实际上就是函数地址
p(3,4); //正确调用
一般我们会用typedef int (*p)(int a,int b) 声明一个函数指针类型
p s; //s表示的就是一个指向带两个Int型参数且返回值是Int型数据的函数
函数指针数组
如: int (*p[2])(int a,int b); //表示函数指针数组,p表示存放2个函数指针的的数组,且函数指针是指向带两个Int型参数且返回值是Int型数据的函数;
理解方式:都知道int (*)(int a,int b)声明带两个int型参数返回值是Int型数据的函数指针类型,而[]运算符的优先级高于*,int (*p[2])(int a,int b);p表示存有两个函数指针的数组.
指向函数指针数组的指针
如:int (* (*p)[2])(int a,int b) ; //p表示指向存有2个函数指针的数组。
理解方式:()优先级高,*p看成一个整体t,则int (* t[2])(int a,int b);t表示的是函数指针数组,所以p表示指向函数指针的数组
注意: 指针是针对内存的,指针的加减实际上是以指针所指类型为单位进行加减的
下面着重说明函数指针与指向类成员函数指针
我们经常接触的函数指针定义或声明函数指针类型时时,只注意:
1.函数返回值
2.函数参数(形参)个数以及参数类型
实际上函数指针的定义和声明要注意的应该有三点:
1.函数返回值
2.函数参数(形参)个数和参数类型
3.函数所属类类型。
一、指向不属于某个类里的函数指针
普通函数指针的定义 int (*fun)(int,int);
一般我们说:函数指针fun指向一个返回值类型为int,且带两个形参为int型的函数。
其实这么说是不准确的,准确来说,函数指针fun指向一个不属于某个类里、返回值类型为int型,带两个形参
分别为int,int型的函数。
赋值如:
int add(int a,int b)
{
return a+b;
}
fun = add;//正确,普通函数名表示函数的地址。
fun = &add;//正确
使用函数指针:fun(5,6);//正确
(*fun)(5,6);//正确
二、指向类的成员函数的指针
指向类成员函数的函数指针有两种情况:
1.指向类里非静态成员函数
2.指向类里静态成员函数
注意:这里说的成员函数均是用public修饰的,只有用public修饰的成员才能在类外使用。
1)指向类里非静态成员函数的函数指针的声明和定义,如:
void ttt()
{}
class A
{
public:
int fun(char,int);
static void (*p)();
static int sfun(char,int);
};
void (*A::p)()=ttt;//正确,void (*A::p)()表示给class A里的static 成员p赋值。注意void(A::*p)()这个表示的是一个指向类A里的无参数无返回值得成员函数。
int A::fun(char,int)
{
printf("class::\n");
return 0;
}
int A::sfun(char,int)
{
printf("static\n");
return 0;
}
typedef int(A::*q)(char,int); //声明一个指向A类里,返回值为int型,带两个形参
,类型分别是char,int型的函数指针类型。可以通过自定义的类型,定义变量,如:
q h=&A::fun; //正确
int (A::*P)(char,int);//定义一个函数指针,指向A类里,返回值为int型,带两个形参
,类型分别是char,int型的函数。
2)指向类里非静态成员函数的赋值,如(使用上面的p指针):
p = &A::fun; //这里和指向不属于类里的函数指针有区别,记住,要获取类里的非静
态成员函数的地址必须通过类才能获得,同样,要想使用类里的非静态成员函数,必须通过类实例化的
对象才可以。
注意:这里说的成员函数指的是非静态的成员函数。
3)使用指向类里非静态成员函数的指针,如(使用商密看的p指针):
因为p是指向A(类)里的fun方法的,所以需要用类实例化的对象才能调用p;
A a;
(a.*p)('c',5); //正确,注意这里(a.*p)的括号不能少。*p才是a的成员函数。
或者A * a=new A();
(a->*p)('c',6); //正确。
4)指向类里静态成员函数的函数指针的声明和定义,如:
因为静态成员函数是属于类的,不属于某个具体对象。他和全局函数一样放在静态存储区
里,所以声明时,不能用类名限定(反而用类名限定还编译出错)
typedef int(*k)(char,int); //正确;可以看出,指向静态成员函数的函数指针声
明与指向不属于某个类里的函数声明一样。
int (*j)(char,int); //定义一个函数指针j,该指针既能指向不属于类里的函数,也
能指向属于类里的静态成员函数,但是函数的返回值是int型,带两个参数,类型分别为
char、int。
5)指向类里静态成员的函数指针的赋值,如:
j=A::sfun; //正确;
j=&A::sfun; //正确;这里也可以看出,指向类里静态成员函数的函数指针赋值和指
向不属于类里的函数指针赋值一样,都有两种方式,不过指向类里的需要用类名::限定。
6)指向类里静态成员函数的使用,如(使用上面的j指针):
j('c',9); // 正确;调用方式与指向不属于类里的函数指针一样,直接使用。
(*j)('c',9);//也正确;
A::j('c',9);//错误,类A里没有j成员函数。
这里着重说一下const指针和非const指针赋值问题:
在c++里,在指针或指针引用赋值时,只能缩小权限,不能扩大权限。
如:
int i;
int* t1 = &i;
const int* t2 = &i;//可以将非const指针类型赋值给const指针类型,缩小权限。
t1 = t2;//error ?将const指针类型赋值给非const指针类型错误,扩大权限,编译出错。
const int* & t3 = &i;
int* &t4=&i;
t4=t3;//错误,同上
在c++里,有两种方式可以去掉变量的const:
一、使用强转
t1=(int*)t2;//可以
二、使用const_cast操作符
t1 = const_cast<int*>(t2);//正确
char s[]="abcd";//这里的s实际上时char* const 类型,始终指向数组首元素的地址。不能s++。但是可以*s='d';