指向指針數組的指針與存儲字符串的指針數組

今天看到一道題目,突然把自己對指針的理解整混亂了。

題目是這樣的:假設定義了一個指針數組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) 取得內嵌指針運算之後的地址對應地址的數據

 

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