指標是c語言的一大特色,它就像一把雙刃劍:使用得當能夠給程式帶來極大的靈活性和高效性;反之,程式就會變得難以除錯,漏洞百出!
眾所周知,指標實質就是位址!乙個變數的位址即稱為此變數的「指標」。如果有這樣一種變數:它的儲存單元裡存放的是其它變數的位址!我們就稱之為「指標變數」。(請注意兩者之間的區別:兩個完全不同的概念!)
我們都知道,陣列名和函式名就是它們的入口位址。同理,乙個變數名其實也是此變數的所在位址!c語言中有一種運算子為「&」:取址運算子。因為陣列名與函式名本身代表的就是位址,通常不會對並且也不能對它們進行取址操作或其它運算操作(其實對於函式名的直接引用與對它取址是等價的)。這也是它們被稱為「常量」的原因!但對於乙個變數來講,情況就不一樣了。
要想獲得它的位址,就必須進行「&」運算,儘管它本身表示的也是位址值!而對變數直接進行引用得到卻是它所在的記憶體單元的資料內容!「指標變數」作為一種變數當然也不能例外!
只不過它與其它普通變數的差別是,它的內容是其它變數(包括「指標變數」)的位址,在win32上,它的大小恒為32位,4byte。而普通變數則不會有大小上的限制!對指標變數所指向的位址的資料內容的獲取則是通過操作符「*」。
在理解上我們將「提領操作符*」視為型別的一部分,並且這種資料型別是一種變數位址型別(均對每乙個「*」而言)!
只要明白了以上常識,「指標」將不會再是程式設計中的「攔路虎」!
從記憶體的儲存映象的角度來講,c的規則陣列(不包括通過資料結構設計的多維陣列)不存在多維,也就是說所有的陣列本質上都是一維的,而一級指標就等價於一維陣列!關鍵的不同在於多維陣列與一維陣列語義上的差別!
而我們理解多維陣列通常將之形象地描述成「矩陣」形式。
更為精確的理解是多維數
組的每個元素就是乙個陣列,如此遞迴下去直至最後每個元素是乙個簡單的變數型別,最終得到的就是
乙個特殊的一維陣列!
看如下乙個例程:
#include
#include
void main()
,};
int* p=(int*)a;-------------------@
}
注:語句@在.c檔案中可以寫成int* p=a;但會給出警告;若是在.cpp檔案中寫成int* p=a;是通不過編譯的!所以規範起見,最好在任何時候都要進行強制型別轉換!
通過進行除錯,在wathch視窗中檢視變數的記憶體位址情況如下:
從圖上可以看出二維陣列在記憶體單元中是線性增長的。倘若此時有乙個二級指標int** q=null;如何通過q來操作二維陣列a[2][3]呢?
通過q=a;如何呢?在.c檔案中可以編譯通過,但會給出警告。若是在.cpp檔案中則不會編譯通過!我相信很多人的第一反應是加上強制型別轉換:q=(int**)a;如此以來,程式編譯、鏈結暢通無阻,連警告也沒有!但一執行就會出問題:這是當然的!下面進行詳細分析。。。。。。
根據我上面講述的:q可視為int**型別,且是int*變數的位址型別變數!對q (指標變數)的引用,得到是的其(即q)記憶體單元的資料,即int*變數的位址,*q則是獲取q所指向的int*變數型別位址的內容,相當於int* q變數q的直接引用,得到是int型別變數的位址。q所佔的記憶體為4byte,*q所佔的記憶體也為4byte。一切都清楚了。
現在來分析二維陣列a的資料型別。我們知道指標與陣列的聯絡的常見具體應用有兩種:一種是「陣列指標」:形如
(*ptr);
另外一種是「指標陣列」:形如
*ptr
。兩者之間的區別想必大家都清楚。如果我定義乙個:「陣列指標
」並初始化:int (*pp)[3]=a;那麼通過pp完全可以操作a[2][3]。來分析一下「陣列指標」(*ptr)[size]
,ptr
所指的物件是有size個某種資料型別值的陣列。而ptr本身又是一級指標,一級指標又等價於一維陣列。a[2][3]的低維是乙個維度為3的一維陣列。高維是乙個維度為2的一維陣列,不難理解,正如前面所述:二維陣列的每個元素是乙個一維陣列,相當於一維陣列的兩次巢狀。比如變數a[0]是乙個維度為3的一維陣列,a[1]亦是一樣。這樣一來,高維的那一部分可視為乙個指標!
乙個膽大的設想出來了:二維陣列本質上就等同於「陣列指標」!
這種想法雖然無懈可擊,但想歸想,事實是怎樣的還得驗證。現借用c++的型別識別,得出兩者的資料型別:(以下
語句需用標頭檔案)
cout<
cout<
輸出結果為:
int (*)[3](換行)int (*)[3]
兩者完全相同,與設想一致!
現在回到問題上來,q=(int**)a;強制轉換成功,但卻不可能正確執行!
原因已浮出水面:q這個位址單元存放的是int*型別的「指標變數」的位址,而二維陣列a骨子裡卻是乙個「陣列指標」。兩者完全是「八竿子打不著」!想一想它們的記憶體分布情況,前者(位址)所指向的記憶體大小恒為4byte,後者(位址)所指向的記憶體大小是隨著你定義的陣列維數而不斷變化的!即使通過強制型別轉換成功,q的記憶體值就是a所代表的位址,但這個位址僅僅是乙個位址,而q的記憶體值不僅要求是乙個位址,而且還必須是乙個「指標變數」的位址!只有這樣通過*q(前面說過:*q則是獲取q所指向的int*變數型別位址的內容,即乙個int變數的位址)才能操作乙個普通變數的位址,否則就是用「*」來操作普通變數,想一下int x=250;*x表示的是什麼呢?
或許有人會問:你不是說二維陣列實質上就是一維陣列嗎,怎麼二維陣列實質上
又是「陣列指標」?這裡有必要強調一下:我是從它們的儲存映象上來講的,但編譯器的語義實現上兩者是絕不能劃等號的!你能夠將乙個二維陣列賦值給乙個一維陣列嗎?顯然是不行的!因此我們這樣想:語句
q=(int**)a;
是將乙個一維陣列(等價於一級指標)賦給乙個二級指標(要通過「&」賦一級指標的位址才行),地球人都知道這是行不通的!雖然乍聽起來還蠻合理的,其實此般理解無異於穿鑿附會。剛才解釋過,兩者的語義迥異!不過,這樣理解似乎更能深刻且方便地知道那樣做錯在**了,呵呵。。。。。。
二維陣列與二級指標
今日在論壇上看到有人問到指標 級指標和二維陣列 我們知道char array abcdef array是陣列的首位址,那麼在二維陣列中array當然也是陣列的首位址,看看這個定義char array 3 怎麼知道的呢?定義這樣乙個陣列,在vc除錯視窗中 我們看到 array 0x64324234 a...
二級指標與二維陣列
include 輸入輸出標頭檔案。include 本程式需要用到malloc free函式,引用該標頭檔案。int main 定義二維陣列a,並賦值從1 12.int p null 定義二維指標。int i,j p int malloc sizeof int 3 要訪問的陣列有三行,所以申請三個一維...
二維陣列與二級指標
首先,記憶體是連續的,二維陣列中的資料塊在記憶體中也是一塊連續的順序的儲存的空間。二維陣列名相當於指標,但是它指向的是乙個陣列型別。int a 4 5 那麼二維陣列a 在記憶體中的分配應該是順序分配 5 5 5 5 此處 5 表示乙個長度為5的一維陣列 二維陣列名 a 指向一維陣列a 0 a相當於乙...