指针到底是什么?或许初学者会有这样的疑惑,有些书上可能会说指针就是地址,那么指针真的就就是地址吗?小博的答案是:指针是一种映射关系,是可以映射一段或大或小的地址区域的特殊变量。
1.基本数据类型指针
因为比较简单,小博就直接上代码了
int i = 8;
int * p = &i; //p就是指向了一个四字节大小的指针
char c = 's';
char * pc =&c;
printf("%c",*pc); //这里的pc就是指向字符的指针
short si = 8;
short * ps = &si; //ps就是指向了短整型数据的指针,它映射了两个字节
其余的基本数据类型与之类似,小博就不一一列举了。那么既然指针是一种映射关系,既可以映射小块区域,是不是也可以映射大一点的区域呢? 下面我们就来看一下结构体指针。
2. 结构体指针
说到结构体指针,那就必须要说一下什么是结构体了。结构体(struct)是由一系列具有相同类型或不同类型的数据构成的数据集合,也叫结构。上段代码:
struct {
char sex;
int id;
double mon;
}stu;
好了,这就是一个集合了字符型、整型、双精度浮点型的数据集合,这样来看,数组其实也是特殊的结构体,不过是每个元素的数据类型相同而已。扯远了,我们来看结构体指针
typedef struct node{
int date ;
char c ;
}ElamSN, * ElamList;
ElamSN h ;
h.c='s'; h.date=8;
ElamList p = &h; //让一个结构体指针指向了一个struct node类型的元素,此时映射了8个字节的地址空间
//为什么是8个字节小博就不详述了,有兴趣的朋友可以查阅相关资料
printf("%5d",p->data);
以上就是结构体指针,以及对它的使用。
3.数组指针
有了结构体指针,数组指针就不难理解了吧,数组指针,也叫行指针 因为它指向一维数组 ,下面是它的简单使用
int (*pp)[6]; //数组指针的定义
int a[2][6]={5,6,7,8,9,10,11,12,13,14,15,16};
pp=a;
pp++; //数组指针pp即指向了一维数组,那么它的++操作就指向了二维数组的第二行
for(int i = 0 ; i < 6; i++)
printf("%d ",pp[0][i]);
数组指针的定义还是蛮特殊的,int (*pp)[6] 可以这样理解,他是一个指针pp指向了int * [ 6 ]类型的指向关系,^-^但编译器上不能这样写哦。
3.2指针数组
说到数组指针,在这里小博也顺便说一下指针数组,顾名思义,指针数组是数组,而不是指针,但数组元素确实指针,下面来让我们看一个例子。
int saw[4][5]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,22,33,44};
int * ps[4]; //指针数组
for(int i=0;i<4;i++)
ps[i]=saw[i];
for(int i=0;i<5;i++)
printf("%5d",ps[3][i]);
在此代码中,对于指针数组而言,指针数组的元素个数必须等于二维数组的行数,让一个指针指向一行数组。
同样的有数组指针,指针可以映射一块区域,那么如果指针映射一个链表,那么这个数组链表就是哈希表的一种存储方式------拉链法,数组中存放链表的首地址,例如下图,有兴趣的朋友们可以实现一下。
4.函数指针
既然指针可以指向数组,结构体,那么指向函数好像也不是很大不了。先来一段简单的代码吧。
void func()
{
puts("func函数被调用!!!");
}
int main (void)
{
void (*x)(); //函数指针的定义
x=&func; //给指针指向
(*x)(); //通过函数指针调用函数
}
这里的指针x就是指向了一个无参无返回值类型的函数func,而(*x)()就是对函数的的调用。那么在稍微复杂一下
(*(void(*) ())0)();
哎?这是个什么东西? 此代码出自《C Traps and Pitfalls》
让我们来分析一下 1 :
void(*) () 这是一个函数指针,这个指针指向的函数无参,无返回值
2 :(
void
(*) ())
0
将
0
强制转换为函数指针类型,
0
是一个地址,也就是说一个函数存在首地址为
0
的一段区域内。
3 :(*(
void
(*) ())
0
) 取
0
地址开始的一段内存里面的内容,其内容就是保存在首地址为
0
的一段区域内的函数。
4 :(*(
void
(*) ())
0
)() 函数调用。
4.2函数指针数组
既然函数指针也是指针,那么也可以存于数组,结构体当中
int proa(int c)
{
c*=c;
printf("%5d",c);
return c;
}
int prob(int c)
{
c*=c;
printf("%5d",c);
return c;
}
int proc(int c)
{
c*=c;
printf("%5d",c);
return c;
}
int main (void)
{
int (*pa[3])(int c);
pa[0]=&proa;
pa[1]=&prob;
pa[2]=&proc;
pa[0](3);
pa[1](2);
pa[2](7);
}
既然有函数指针数组,那就可以有函数指针结构体、函数指针数组的指针、函数指针数组的指针数组等等,小博就不一一列举了,有兴趣的朋友们可以尝试一下。
5.类指针
或许有一些没有接触c++的朋友就纳闷了,类我咋没听过?当然对于c++小博也学习的很浅,在这里就浅尝辄止的说说类指针。其实最开始类的概念就是从C语言结构体结构体扩展来的,相当于结构体的plus版本,除了数据外还添加方法的实现,不说闲话了,上代码
class Adaptor{
public :
int x = 0;
void adeptpro()
{
cout<<"Adapetor "<<endl;
}
};
int main (void)
{
Adaptor * sda=new Adaptor(); //类指针sda
sda->adeptpro();
cout<<sda->x;
delete sda;
}
可以通过类指针访问类的方法或数据。
是不是很简单,其实在面向对象语言中很多概念都是互通的,比如在java中没有了指针,但其实还存在着引用,和指针也是“猫”和“咪” 的关系,就不在这里赘述了。那么为什么不用对象而要用指针,因为在传参时如果传对象,那就太浪费空间了,但如果传的是指针,在32位系统上都只有4个字节大小。来一段伪代码,即: sizeof(不管什么类型指针)=4 byte
6. 二级指针
二级指针,即指针的指针。先来一段非常简单的代码
int a = 4;
int * p = & a;
int * * pp = &p;
同样的,pp是指向了整型地址类型(int * )的指针,可以说是最简单二级指针。那么和本文前面的结合起来,就可以实现一个指向数组元素的指针parray,而每个数组元素的类型可以是结构体指针类型,即每个元素可以指向一个结构体,这个指针parray就是一个二级指针。具体代码有兴趣的朋友可以去尝试,以此类推二级指针的种类更加繁多。那么正确的理解指针就可以以不变应万变。
而二级指针的好处在于改变指针的值时使用二级指针比较方便。
综上所述,指针就是一个映射关系,它可以映射不同大小的内存空间,如果映射4字节就是整型指针,映射一个函数就是函数指针,映射一个数组就是数组指针,映射一块地址就可能是一个二级指针,等等。同样的发散一下思维,如果以后还出现了新的数据结构,它的指针相信读者们也能快速理解。
PS:这是博主小子的第一篇博客,还请各位大佬“键”下留情,有不对的请指出,希望各位多给小子信心。