?
拋出問題
最近碰到一個(gè)問題點(diǎn),這里跟大家分享一下。有一個(gè)二維數(shù)組,我想把它傳給一個(gè)函數(shù)。于是我把函數(shù)接口定義出來了,如下:
?
int?array[2][3]?=?{1,2,3,4,5,6}; void?fun(int?**array)?{ ??array[0][0]?=?5; }
?
當(dāng)我試圖直接把數(shù)組名傳給函數(shù)時(shí)候,fun(array)編譯會(huì)報(bào)錯(cuò),大概意思就是類型不匹配。既然類型不匹配,那我就直接強(qiáng)轉(zhuǎn)成你所需要的類型,于是我又做了調(diào)整,fun((int **)array),這下確實(shí)不報(bào)錯(cuò)了。但是此時(shí)我還沒意識(shí)到問題的嚴(yán)重性。不出意外的情況下意外還是發(fā)生了,只要進(jìn)入到這個(gè)函數(shù)后,程序就掛了。那你知道是什么原因嗎?如果不清楚就往下看吧...
指針
先從指針說起,指針是一個(gè)特殊的變量, 它里面存儲(chǔ)的數(shù)值被解釋成為內(nèi)存里的一個(gè)地址。要搞清一個(gè)指針需要搞清指針的四方面的內(nèi)容:指針的類型、 指針?biāo)赶虻念愋?、 指針的值或者叫指針?biāo)赶虻膬?nèi)存區(qū)、 指針本身所占據(jù)的內(nèi)存區(qū)。
指針的類型
只要把指針聲明語句里的指針名字去掉,剩下的部分就是這個(gè)指針的類型。例如:
?
int*ptr;??????//指針的類型是?int* char*ptr;?????//指針的類型是?char* int**ptr;?????//指針的類型是?int** int(*ptr)[3];?//指針的類型是?int(*)[3]
?
指針?biāo)赶虻念愋?/p>
只須把指針聲明語句中的指針名字和名字左邊的指針聲明符*去掉, 剩下的就是指針?biāo)赶虻念愋?。例如?/p>
?
int*ptr;??????//指針?biāo)赶虻念愋褪?int char*ptr;?????//指針?biāo)赶虻牡念愋褪?char int**ptr;?????//指針?biāo)赶虻牡念愋褪?int* int(*ptr)[3];?//指針?biāo)赶虻牡念愋褪?int()[3]
?
指針的值
在32位程序里,所有類型的指針的值都是一個(gè)32位整數(shù),因?yàn)?2位程序里內(nèi)存地址全都是32位長(zhǎng)。
指針本身所占用的內(nèi)存大小
意思是指針本身占了多大的內(nèi)存,在32位平臺(tái)里,指針本身占據(jù)了4個(gè)字節(jié)的長(zhǎng)度??梢允褂胹izeof(指針的類型)測(cè)試。
一維數(shù)組
對(duì)于一個(gè)一維數(shù)組int array[10],數(shù)組名代表一個(gè)常量地址,該地址指向第一個(gè)元素。以下兩種情況數(shù)組名不能當(dāng)指針使用。
&
對(duì)數(shù)組名取址,int *p_array = &array,&這個(gè)運(yùn)算符也很有講究的,暫時(shí)不多說了。
sizeof
sizeof(array)計(jì)算的是整個(gè)數(shù)組在內(nèi)存中所占用的空間。
二維數(shù)組
二維數(shù)組本質(zhì)上是以數(shù)組作為數(shù)組元素的數(shù)組,即“數(shù)組的數(shù)組”。假設(shè)我們定義了一個(gè)二維數(shù)組int array[2][3] = {1,2,3,4,5,6}。
網(wǎng)上有很多地方都再說數(shù)組名array和array[0]、&array[0]以及&array[0][0]是等效的。那我們代碼測(cè)試一下。
?
printf("%#x,%#x,%#x,%#x ",array,array[0],&array[0],&array[0][0]); Terminal: 0x404008,0x404008,0x404008,0x404008
?
因?yàn)檫@幾種寫法輸出地址都是相同的,所以有的同學(xué)自然就認(rèn)為這幾種寫法就是一樣的。雖然地址相同,但是實(shí)際意義是有區(qū)別的,我們繼續(xù)看下面的代碼。
?
//這里重新定義了指針變量,能夠方便的知道右值得類型 int?*p_array1?=?array[0]; int?*p_array2?=?&array[0][0]; int?(*p_array3)[3]?=?&array[0]; int?(*p_array4)[3]?=?array; ?? printf("%#X,%#X,%#X,%#X,%#X ",array,++p_array1,?++p_array2,?++p_array3,?++p_array4); Terminal: 0X404008,0X40400C,0X40400C,0X404014,0X404014
?
根據(jù)以上實(shí)驗(yàn)分析能夠看出:array[0]與&array[0][0]指針類型相同,都是int *,地址存放的是int數(shù)據(jù),當(dāng)指針自增1時(shí)地址都偏移了一個(gè)int類型的大小。
&array[0]與array指針類型相同,都是int (*)[3],首先它是一個(gè)數(shù)組指針,這個(gè)指針指向一個(gè)數(shù)組,數(shù)組中數(shù)據(jù)的類型為int型。當(dāng)指針自增1時(shí)地址都偏移了一個(gè)數(shù)組的長(zhǎng)度(即3個(gè)int數(shù)據(jù)的大小)。
所以說array只和&array[0]真正意義等效。那怎么去理解這幾種表達(dá)呢 ?
表示 | 含義 |
---|---|
array | 是一個(gè)數(shù)組指針,類型為int (*)[3]。指向二維數(shù)組中第一個(gè)元素(元素是一維數(shù)組),指針?biāo)赶虻膬?nèi)存大小為一維數(shù)組的長(zhǎng)度 |
array[0] | 是一個(gè)指針,類型為int *。就相當(dāng)于一個(gè)一維數(shù)組名,指向一維數(shù)組中第一個(gè)元素的地址,指針?biāo)赶虻膬?nèi)存大小為一個(gè)數(shù)據(jù)長(zhǎng)度 |
&array[0] | 是一個(gè)數(shù)組指針,類型為int (*)[3]。相當(dāng)于對(duì)一維數(shù)組取地址。指針?biāo)赶虻膬?nèi)存大小為一維數(shù)組的長(zhǎng)度 |
&array[0][0] | 是一個(gè)指針,類型為int *,是對(duì)二維數(shù)組中第一個(gè)數(shù)據(jù)取地址,注意是數(shù)據(jù)不是元素,指針?biāo)赶虻膬?nèi)存大小為一個(gè)數(shù)據(jù)長(zhǎng)度 |
如以上能夠理解清楚,那么文中的問題應(yīng)該就能夠自己分析清楚了。
二級(jí)指針
先定義一個(gè)二級(jí)指針int **p,首先p是一個(gè)指針,在這個(gè)地址中存放的數(shù)據(jù)是指向一個(gè)整形數(shù)據(jù)的地址。
問題解答
接著看文章中的問題,把一個(gè)二維數(shù)組強(qiáng)轉(zhuǎn)成二級(jí)指針傳給了函數(shù)。注意二維數(shù)組名的類型是一個(gè)數(shù)組指針和二級(jí)指針完全不是一個(gè)東西。那么會(huì)出現(xiàn)什么問題呢?
?
int?array[2][3]?=?{1,2,3,4,5,6}; int?main(int?argc?,char?**argv)?{ ??int?**p_data?=?(int?**)array; ??printf("%#x,?%d ",?p_data,?*p_data); } Terminal: 0x404008,?1
?
地址 | 數(shù)據(jù) |
---|---|
0x404008 | 1 |
0x40400C | 2 |
0x404010 | 3 |
0x404014 | 4 |
0x404018 | 5 |
0x40401C | 6 |
看上面的例子,array的地址為0x404008,當(dāng)把一個(gè)二維數(shù)組強(qiáng)轉(zhuǎn)成二級(jí)指針的時(shí)候。p_data地址中存放的數(shù)據(jù)為1,因?yàn)槎S數(shù)據(jù)中第一個(gè)數(shù)據(jù)就是1。根據(jù)二級(jí)指針的定義,這個(gè)數(shù)據(jù)1又會(huì)當(dāng)成一個(gè)地址,該地址指向的內(nèi)存才是最終的數(shù)據(jù)。
但是呢,這個(gè)地址1其實(shí)是個(gè)數(shù)據(jù),并不是真正的地址。如果訪問地址1中的數(shù)據(jù),就屬于非法訪問地址了,可能會(huì)進(jìn)入異常。
二維數(shù)據(jù)當(dāng)函數(shù)入?yún)?/p>
通過以上學(xué)習(xí)我們已經(jīng)知道二維數(shù)組名就是一個(gè)數(shù)組指針,我們函數(shù)就可以像下面這樣聲明。
?
void?fun(int?array[][3],?int?row); void?fun(int?(*p_array)[3],?int?row); void?fun(int?row,?int?column,?int?array[row][column]);
?
實(shí)參與入?yún)?/p>
最后在看下,應(yīng)該如何定義與實(shí)參相對(duì)應(yīng)的形參的數(shù)據(jù)類型。
含義 | 實(shí)參 | 形參 |
---|---|---|
二維數(shù)組(數(shù)組的數(shù)組) | int array[4][6] | int (*array)[6] |
指針數(shù)組(數(shù)組中的數(shù)據(jù)是指針) | int *array[6] | int **array |
數(shù)組指針(指向數(shù)組的指針) | int (*array)[6] | int (*array)[6] |
二級(jí)指針(指針的指針) | char **array | char **array |
?
審核編輯:湯梓紅
評(píng)論