指针的原则:
- 首先不论是什么类型的指针都有单独的存储单元,大小为操作系统的位数(现在大部分是32位,其余是64位)。
- 不论指针的类型是type*、type**、type(*)[],其最终指向的是一个占一个字节的存储单元地址。
- 指针本身不知道所指向的存储单元是否是指定的类型,但仍然以本指针指定的类型处理其数据。(例:int *p,其中指定的类型是int,不论此指针指向的存储单元是 struct 或者是 double 又或者是 char,都按int类型处理数据)。
指针的使用及其意义:
例子:
- 其中 p 表示指针本身所占存储单元(其中保存的是一个地址)和 其值(表示所指向的存储单元地址)。
- 其中 *p 表示指针指向的存储单元及其值,此例子中是a,其值在这里是10。
讨论指针的四个问题:
- 指针的类型:定义中去掉变量本身所构成的东西,上例中是 int*
- 指针所指向的类型:定义中去掉变量及其前面的*构成的东西,上例中是 int
- 指针的值:指针所指向的内存单元的值,此例中是 &a
- 指针本身所占内存单元:此例中此指针的保存地址是 &p
以后见到指针就先将其的上述四个问题解决,那么其他的便不是问题。
下面讲述指针的一些具体用法:
1、指向指针的指针
解读:
ptr 保存的是 p 的地址,p 保存的是 a 的地址。
*ptr == p == &a。
**ptr == *p == a。
2、指针*p去掉*后,表示一个存储单元地址,*p表示此单元地址的值。
3、数组与指针的关系
type a[20];
其中a表示两个意思:
其一,它代表整个数组,它的类型是type[20]。
其二,它是一个指针,具有单独的存储地址,类型是type*,指向的是a[0]。
4、结构体与指针
调用结构体数据的方法:
pex->a;
pex->b;
pex->c;
或者
*pl; //调用a
*(pl+1); //调用b
*(pl+2); //调用c
如上两种方案都可以调用结构体的数据,前提是结构体的数据时连续存放,且没有填充数据(填充数据的原因是在某种编译环境下有字对齐,双字对齐,或者4字对齐)。如果有填充数据,那么第二种方案就可能调用填充的字节。
5、指针和引用
- 引用实际上是使用指针实现的,编译器隐藏了其实现的细节,是特殊化的指针。
- 引用在创建时必须初始化,即引用到一个有效的对象,而指针可以是NULL,不与其他的存单元相关联。
- 引用从产生到销毁都只能指向一个存储单元,但指针可以改变指向存储单元。
- 引用的创建和销毁,与指针类似,不使用类的拷贝构造函数。
- 引用的效率与指针相同,使用也很直观。
例:
//使用指针交换数据
指针做为函数的形参,只用当NULL有意义的时候才选择,除此之外,都使用引用作为形参。
例:
//如下是得到结构体大小的函数
分析:第一个函数使用的是传值调用,使用此函数时要创建一个Example对象ex,调用结束时要销毁这个对象。
第二个函数就只是引用一下对象ex,不创建新的对象,使用const也不改变对象。
当传递struct作为形参时,使用const和引用。
6、指针数组和数组指针
- 指针数组:形如type *p[N],因为 [] 的优先级大于 * 所以 p 先是数组然后变成指针数组。使用在如下的情况:有关系的多组数据存储在不连续的存储单元中,需要一个索引将他们关联起来。于是指针数组就形成了。
- 数组指针:形如type (*p)[N],因为 () 和 [] 的优先级相同,从左到右执行,所以 p 是一个指针然后指向一个N个单元的数组。使用在如下情况:一个固定大小为N个单元的数据连续存储,需要一个索引,只要求其指向第一个单元就可以引用其他的单元,就有了指向数组的指针。
7、指针与函数
例:
关于指针我就介绍到这里了,算是抛砖引玉吧。
现在有个问题等待解答:
- 请问,指针是一个和操作系统位数大小一样的存储单元,保存的是一个32位或64位的地址,那么请问它是怎么确定自己的类型的?