指向指针数组的指针与存储字符串的指针数组

今天看到一道题目,突然把自己对指针的理解整混乱了。

题目是这样的:假设定义了一个指针数组tBooks如下,请定义一个指向它的数组指针p,让程序可以顺利执行,并按要求打印出数据。

代码:

    char *tBooks[] = {  
        "《数据结构》", 
        "《计算机组成原理》", 
        "《C语言程序设计》",
    	"《计算机网络》",
        "《哆啦A梦》" 
};

    __A__ = __B__;


    printf("请打印出《哆啦A梦》:\n%s\n",__C__);

如果是直接定义一个每个指针元素指向指针的指针数组,那么题目就简单了。

只需要定义一个长度为4的元素指向指针的指针数组并将tBooks的二级指针的地址(就是指向字符串指针的指针的地址)赋予给新定义的指针即可,再对数组内地址进行字符串打印。

倘若利用指向指针的指针数组

代码: 

    char **p[4] = &tBooks[0];

    
    printf("请打印出《哆啦A梦》:\n%s\n",*p[3]);

 (因为指针数组tBooks存放的地址指向的是字符串指针,所以指针数组里的指针元素为二级指针&tBooks[0],整个数组的指针为三级指针&tBooks)

可题目要求使用数组指针完成,没办法,一时突然凝滞,于是经过长时间的推敲,最后勉强得到了原理:

可能有错误,等以后真正明白了回头看可能会改正

为了使用数组指针 来达到 指向指针的数组指针 的效果,我们需要先建立模型。

使新定义的指针的模型与原有数据的指针的模型相同的意义似乎就是告诉编译器我新定义指针的运算规则和我要操作数据的模型存在的指针的运算规则是相同的,不要出现我想加+1的目的是想+一整个数组步数的运算,结果编译器你给我加了数组内一个元素的步数。

我们先对原数据的模型数组tBooks的指针进行分析,它为一个只能存放地址的指针数组,并且这个数组的元素存放的是字符串的地址,我们知道,字符串本身存在内嵌指针,此字符串指针为一级指针,本身为指针的数组元素为二级指针,数组同样存在内嵌指针,此内嵌指针为为三级指针。所以,此指针数组的内嵌指针实际上是三级指针。(如果存储的地址是数据元素的地址,那数组内嵌指针就是二级指针了。)

经过对原数据模型的指针进行分析过后,我们可以利用数组指针进行以下设想:指针指向数组,然后让数组内的元素都为指针元素,只能存放地址,这里就是存放字符串的地址,进行字符串输出时,只需要拿到数组内对应指针元素所保存的字符串地址就可以使用%s完成连续的字符打印。这样子我们仅仅需要定义指针所指数组的长度为4。

如何实现这个模型?我们这样定义:

    char *(*p)[4];

根据右左法则,从新定义的变量p看起,向右边遇到括号,转向,遇到 * 结合成为指着p,遇到括号,出括号,到括号右边,遇到无名数组[4] 结合,成为指向数组正义的数组指针,最后读取括号左边,表示数组内所有元素均为指针类型,只可以保存地址。

我们定义的 char *(*p)[4] 是一个指针类型,指向一个长度4位置名字的数组,该数组里放的是char类型的*(指针)。

定义指针完毕后,接下来就是如何给新定义的指针赋值了。

我们对这个指针进行分析,发现唯一有名字的指针p实际上是四级指针。

一级指针是指针数组的指针元素存取的地址所对应的字符指针 ,二级指针是没有名字的指针数组的指针元素,三级指针是没有名字的指针数组的内嵌指针 , 四级指针是指向这个指针数组内嵌指针的指针,只有四级指针有名字 它叫做p,而四级指针p需要的是三级指针的地址。所以我们只需要取得已定义数组指针tBooks的三级指针的地址就可以了。

     char *(*p)[4] = &tBooks;

完成赋值。

那我们指针的赋值也完成了,该如何通过新定义的指针p取得想要的字符串《哆啦A梦》呢?

目标是什么我们知道,只需要取得新定义的指针p指向的指针数组tBooks取得第四个指针元素内的地址即可。

利用tBooks取值好取,问题是我们只允许使用p;

对p解引用,即(*p),取得p存储的地址对应的元素的数据,p是四级指针,刚刚我们让它存储了三级指针tBooks的地址,我们拿到了指针p存储的地址对应的指针tBooks存取的数据(此数据也是地址),数值上为三级指针的tBooks的地址。

有个这个三级指针tBooks存取的地址,我们就可以对这个三级指针tBooks进行指针运算。当我tBooks+1就代表地址加一个单位,就是tBooks数组第二个指针元素的地址,而后使 tBooks+1 再解引用,即*(tBooks+1) 写法同 tBooks[1],就可以拿到tBooks内第二个指针元素的存储的数据(存储的也是地址),这个地址呢就是字符串的首地址,就是一级指针的地址。我们拿到这里就够了。我们就需要这个字符串的地址,至于里面的字符,%s会帮我们自动打印。

那么综上所述,这一系列的取地址实际上就是先对四级指针p进行解引用(*p)就得到三级指针所存放的数据,是一个地址。(即数组tBooks的首地址,数组内嵌指针地址为首元素地址,存储的数据也是首元素地址)对此地址进行运算+3 即(*p)+3 ,得到的是数组tBooks第四个元素的地址,再次解引用,即  *((*p)+3),得到运算后的地址就是tBooks数组第四个元素存放的数据(因为tBooks是指针数组所以这里的数据还是地址),即字符串的地址,搞定。

最终用指针p表示呢就是 : *((*p)+3)    可以换一种写法,(*p)[3]  。我们知道 a[3] == *(a+3) ,这个同理,设a = *p即可。

其实这样推敲出来并不难,只是对于初学者的我突然遇到了就会懵掉,花费大量时间推敲不如尽快熟悉它,找到它的规律。

有一点是,为什么开始分析的时候花费大量时间来推敲模型呢?最后好像也没有用到。

实际上,对于这个问题我也推理了一遍,只是太繁琐抽象,下面是我的过程,以后有兴趣就看吧,可能不太对。

还是别看了全都不对= =(留作纪念)

char *(*p)[4] = &tBooks;  
//这里定义了指向长度为4的指针数组的指针  我们要一定要用p来表示指针!!
//先拿出一小块内存作指针p,再定义一个长度为4个内存空间的数组,并将其首地址赋值给指针p保存。
//最左边的*表示告诉计算机,我的数组只能保存地址,数组的元素都是指针!  这么定义的结构实际占用4+4 = 8个内存空间。 
	 						   
//一级指针是数组的指针元素  二级指针是数组的内嵌指针   三级指针是指向这个数组的指针
//只有三级指针有名字 它叫做p
//三级指针p需要的是二级指针的地址
								
//对于已定义的存储了字符串地址的指针数组来说,二级指针是最高等级指针,就是指针数组内嵌的指针。
//指针p那边保存的还是我们新定义的数组的地址,
//对p运算呢,p指针的步长是4,对p解引用呢,p得到的是新定义的数组的指针存储的地址,那他存储什么了?目前为止什么都还没存储(或者说是随即地址)?
//不过对它进行的地址运算时步长+1是我们想要看到的。
								
//为了得到指针数组tBooks的内嵌指针的地址,我们对内嵌指针tBooks进行取址。 
//=相连 赋值成功。现在三级指针p存储的地址改变,现在存储的地址更改为指针数组内嵌指针tBooks的地址
//我们对三级指针P直接运算,依据我们定义时的规则(它是一个数组指针),编译器对它运算使用的步长会是定义时的数组的长度还是4,这是我们不希望的
								 
//希望唯一有名字的三级指针p能够符合二级指针tBooks的运算模式,能够通过p准确操控地址,准确拿到一级字符指针的地址,
//我们定义的三级指针p的结构模型就派上了用场
//刚刚我们说过,三级指针p里存放的就是二级指针tBooks的地址,
//计算机对指针的运算始终是对内存中的地址进行操作,实在这一块固定的内存中使用这一段内存的指针进行运算!!不论p存储的地址是什么 对其解引用后,数据都放在这里的内存里,运算步长这个数组指针的步长,这里就是都是+1  关键就在这里, 
//可能以为指针的指向已变,这个指针已经成为别人家的(tBooks指针)上级指针了,那既然别人存在指针,我还定义这个结构干嘛?实际不对,它虽然所存储的内容发生了改变,但是它数据(这里的数据是地址)始终在这段内存中没变,所他的运算规则是不变的,这是我们在刚开始定义p时形成的负责这块内存的指针时就被编译器永远记住的关于p的运算规则
//也就是说,此时我们对p运算步长还是为4,对p解引用后获取的二级指针存取地址(因为p是三级指针)存储在这块长度为4的指针数组的指针元素里,运算规则最初定义数组时的指针运算规则一样
								
								 
//计算机对指针的运算始终是对内存中的地址进行操作 ,新定义的指针指向的指针数组内存是用来存储指针的地址的,而不是说我解引用之后,计算机读取的内存位置就跑了,跑到tBooks指针了 
								
//而是在我们定义三级指针p后新开辟的一连串内存的地址,
//所以,当我们对三级指针p解引用之后呢,系统找到二级指针tBooks,并且获取其存取的地址,系统会将这个地址数据赋值给没有名字的,为二级指针的数组内嵌指针作为数据存储,
//当二级指针拿到地址数据之后,对其在自己的地盘上用自己的指针规则对地址进行运算时,得到的结果是和tBooks指针作同样的运算的结果是相同的,
//由此,我们便可以使用P来进行取值等一系列的操作 
								
//对于普通数组指针p,p存储了数组内嵌指针的地址  *p按p保存的地址寻数据得到内嵌指针存储的地址,这个地址给与数组内嵌指针做数据保存,以便进一步运算 
//(*p+1)对保存在内嵌指针内的地址进行运算  *(*p+1) 取得内嵌指针运算之后的地址对应地址的数据

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章