C語(yǔ)言數(shù)組與指針詳解
由于數(shù)據(jù)的表現(xiàn)形式多種多樣,還有字符型和其它的數(shù)值類(lèi)型,因此僅有基本數(shù)據(jù)類(lèi)型是不夠的。是否可以通過(guò)基本數(shù)據(jù)類(lèi)型的組合抽象構(gòu)造其它的數(shù)據(jù)類(lèi)型呢?下面是小編為大家?guī)?lái)的C語(yǔ)言數(shù)組與指針詳解的知識(shí),歡迎閱讀。
1.數(shù)組
(1)數(shù)組的聲明
我們知道,一個(gè)基本數(shù)據(jù)類(lèi)型的變量只能存儲(chǔ)一個(gè)數(shù)據(jù),比如:
int data = 0x64;
如果需要存儲(chǔ)一組int型數(shù)據(jù)呢?比如,1、2、3,則至少需要3個(gè)變量data0、data1、data2。比如:
int data0 = 1, data1 = 2, data2 =3;
由于數(shù)據(jù)的表現(xiàn)形式多種多樣,還有字符型和其它的數(shù)值類(lèi)型,因此僅有基本數(shù)據(jù)類(lèi)型是不夠的。是否可以通過(guò)基本數(shù)據(jù)類(lèi)型的組合抽象構(gòu)造其它的數(shù)據(jù)類(lèi)型呢?答案是可以的,構(gòu)造數(shù)據(jù)類(lèi)型數(shù)組就是這樣產(chǎn)生的。
從概念的視角來(lái)看,int型整數(shù)1、2和3都是相同的數(shù)據(jù)類(lèi)型,data0、data1和data2三個(gè)變量的共性是data,其差異性是下標(biāo)不一樣。因此可以將data0、data1和data2抽象為一個(gè)名字,然后用下標(biāo)區(qū)分這些變量的集合——data[0]、data[1]和data[2]。如果有以下聲明:
intdata[3]; //解讀為data是int數(shù)組(元素個(gè)數(shù)3)
那么data[3]就成了存放3個(gè)int型數(shù)據(jù)1、2、3的data[0]、data[1]和data[2]所組成的數(shù)組,即可分別對(duì)data[0]、data[1]和data[2]賦值:
data[0] = 1, data[1] =2, data[2] = 3;
當(dāng)然,也可以按照以下方式聲明一個(gè)數(shù)組并進(jìn)行初始化:
intdata[3] = {1, 2, 3};
通常將data稱(chēng)為數(shù)組(變量)名,data[0]、data[1]和data[2]被稱(chēng)為變量。因而可以說(shuō),數(shù)組是將相同類(lèi)型數(shù)據(jù)的若干變量按有序的形式組織起來(lái),用一個(gè)名字命名,然后用下標(biāo)區(qū)分這些變量的集合。
由于數(shù)組是建立在其它類(lèi)型的基礎(chǔ)上,因此C將數(shù)組看作構(gòu)造類(lèi)型,在聲明數(shù)組時(shí)必須說(shuō)明其元素的類(lèi)型。比如,int類(lèi)型的數(shù)組、float類(lèi)型的數(shù)組或其它類(lèi)型的數(shù)組。而其它類(lèi)型也可以是數(shù)組類(lèi)型,在這種情況下,創(chuàng)建的是數(shù)組類(lèi)型的數(shù)組,簡(jiǎn)稱(chēng)數(shù)組的數(shù)組。
(2)下標(biāo)與變量的值
在這里,定義了一個(gè)名為data的數(shù)組類(lèi)型變量,它是由存放3個(gè)int型數(shù)據(jù)1、2、3的變量data[0]、data[1]和data[2]組成的。通常又將數(shù)組的各個(gè)變量稱(chēng)為數(shù)組的元素,而數(shù)組的元素是按照順序編號(hào)的,這些元素的編號(hào)又稱(chēng)為數(shù)組元素的下標(biāo)。
由于有了下標(biāo),因此數(shù)組元素在內(nèi)存中的位置就被唯一確定下來(lái)了。下標(biāo)總是從0開(kāi)始的,最后一個(gè)元素的下標(biāo)為元素的個(gè)數(shù)減1(即2),data[0]叫第1個(gè)元素,data[1]叫第2個(gè)元素,data[2]叫第3個(gè)元素,也就意味著所有的元素在內(nèi)存中都是連續(xù)存儲(chǔ)的。
直觀(guān)上,數(shù)組是由下標(biāo)(或稱(chēng)為索引)和值所組成的序?qū),?duì)于每個(gè)有定義的下標(biāo)都存在一個(gè)與其關(guān)聯(lián)的值,在數(shù)學(xué)上稱(chēng)為映射。除了創(chuàng)建新數(shù)組外,大多數(shù)語(yǔ)言對(duì)數(shù)組只提供兩種標(biāo)準(zhǔn)操作:一個(gè)操作是檢索一個(gè)值,另一個(gè)操作是存儲(chǔ)一個(gè)值。
函數(shù)Create(data, size)創(chuàng)建一個(gè)新的具有適當(dāng)大小的空數(shù)組,初始時(shí)數(shù)組的每一項(xiàng)都沒(méi)有定義。Retrieve操作接受一個(gè)數(shù)組data和一個(gè)下標(biāo)index,如果下標(biāo)合法,則該操作返回與下標(biāo)關(guān)聯(lián)的值,否則產(chǎn)生一個(gè)錯(cuò)誤。Store操作接受一個(gè)數(shù)組data、一個(gè)下標(biāo)index和一個(gè)項(xiàng)item的集合,即項(xiàng)是value值的集合,有時(shí)也將值(value)稱(chēng)為項(xiàng)(item),返回在原來(lái)數(shù)組中增加新的序?qū)蟮臄?shù)組。
顯然,int系的任何常量表達(dá)式都可以作為數(shù)組元素的下標(biāo)。比如:
int array[3+5]; // 合法
int array['a']; //表示int array[97];
上述定義之所以合法,因?yàn)楸硎驹貍(gè)數(shù)的常量表達(dá)式在編譯時(shí)就具有確定的意義,與變量的定義一樣明確地分配了固定大小的空間。
雖然使用符號(hào)常量增強(qiáng)了數(shù)組的靈活性,但如果定義采用了以下的形式:
int n = 5;
int array[n]; //非法
因?yàn)闃?biāo)準(zhǔn)C認(rèn)為數(shù)組元素的個(gè)數(shù)n不是常量,雖然編譯器似乎已經(jīng)“看到”了n的值,但intarray[n]要在運(yùn)行時(shí)才能讀取變量n的值,所以在編譯期無(wú)法確定其空間大小。使用符號(hào)常量定義數(shù)組長(zhǎng)度的正確形式如下:
#define N 10
int array[N];
即可根據(jù)實(shí)際的需要修改常量N的值。
由于數(shù)組元素下標(biāo)的有效范圍為0~N-1,因此data[N]是不存在的,但C語(yǔ)言并不檢查下標(biāo)是否越界。如果訪(fǎng)問(wèn)了數(shù)組末端之后的元素,訪(fǎng)問(wèn)的就是與數(shù)組不相關(guān)的內(nèi)存。它不是數(shù)組的一部分,使用它肯定會(huì)出問(wèn)題。C為何允許這種情況發(fā)生呢?這要?dú)w功于C信任程序員,因?yàn)椴粰z查越界可以使運(yùn)行速度更快,所以編譯器沒(méi)有必要檢查所有的下標(biāo)錯(cuò)誤。因?yàn)樵诔绦蜻\(yùn)行之前,數(shù)組的下標(biāo)可能尚未確定,所以為了安全起見(jiàn),編譯器必須在運(yùn)行時(shí)添加額外代碼檢查數(shù)組的每個(gè)下標(biāo)值,但這樣會(huì)降低程序的運(yùn)行速度。C相信程序員能編寫(xiě)正確的代碼,這樣的程序運(yùn)行速度更快。但并不是所有的程序員都能做到這一點(diǎn),越界恰恰是初學(xué)者最容易犯的錯(cuò)誤,因此要特別注意下標(biāo)的范圍不能超出合理的界限。
(3)變量的地址與類(lèi)型
當(dāng)將變量data[0]、data[1]和data[2]作為&的操作數(shù)時(shí),&data[0]是指向變量data[0]的指針,&data[1]是指向變量data[1]的指針,&data[2]是指向變量data[2]的指針。data[0]、data[1]和data[2]變量的類(lèi)型為int,&data[0]、&data[1]和&data[2]指針的類(lèi)型為int *const,即指向常量的指針,簡(jiǎn)稱(chēng)常量指針,其指向的值不可修改。比如:
int a;
int * const ptr = &a;
ptr = NULL; //試圖修改,則編譯報(bào)警
&a = NULL; //試圖修改,則編譯報(bào)警
同理,&data是指向變量data的指針,那么data是什么類(lèi)型?
按照聲明變量的規(guī)約,將標(biāo)識(shí)符data取出后,剩下的“int [3]”就是data的類(lèi)型,通常將其解釋為由3個(gè)int組成的數(shù)組類(lèi)型,簡(jiǎn)稱(chēng)數(shù)組類(lèi)型。其目的是告訴編譯器需要分配多少內(nèi)存?3個(gè)元素的整數(shù)數(shù)組,data類(lèi)型測(cè)試程序詳見(jiàn)程序清單 1.20。
程序清單 1.20 data類(lèi)型測(cè)試程序
1 #include
2
3 void f(int x);
4 int main(int argc, char *argv[])
5 {
6 int data[3];
7 f(data);
8 return 0;
10 }
通過(guò)編譯器提示的警告,“funtion: 'int' differ in levels ofindirection from 'int [3]'”,說(shuō)明數(shù)組變量data的類(lèi)型為不是int而是int [3]數(shù)組類(lèi)型。由于在設(shè)計(jì)C語(yǔ)言時(shí),過(guò)多地考慮了開(kāi)發(fā)編譯器的便利。雖然設(shè)計(jì)編譯器更方便了,卻因?yàn)楦拍畹哪:o初學(xué)者造成了理解上的困難。實(shí)際上數(shù)組應(yīng)該這樣定義:
int[3] data;
即int是與[3]結(jié)合的。&data到底是什么類(lèi)型?
當(dāng)data作為&的操作數(shù)時(shí),則&data是指向data的指針。由于data的類(lèi)型為int [3],因此&data是指向“int [3]數(shù)組類(lèi)型”變量data的指針,簡(jiǎn)稱(chēng)數(shù)組指針。其類(lèi)型為int (*)[3],即指向int [2]的指針類(lèi)型。為何要用“()”將“*”括起來(lái)?
如果不用括號(hào)將星號(hào)括起來(lái),那么“int (*)[3]”就變成了“int *[3]”,而int*[3]類(lèi)型名為指向int的指針的數(shù)組(元素個(gè)數(shù)3)類(lèi)型,這是設(shè)計(jì)編譯器時(shí)約定的語(yǔ)法規(guī)則。
&data的類(lèi)型到底是不是“int (*)[3]”?其驗(yàn)證程序范例詳見(jiàn)程序清單 1.21。
程序清單 1.21 &data類(lèi)型測(cè)試程序
1 #include
2 int main(int argc, char *argv[])
3 {
4 int data[3];
5 int b = &data;
6 return 0;
7 }
通過(guò)編譯器提示的警告,“'int' differ in levels ofindirection from 'int (*)[3]'”,說(shuō)明&data的類(lèi)型為int (*)[3]。
(4)sizeof(data)
將如何尋找相應(yīng)的數(shù)組元素呢?常用的方法是通過(guò)“數(shù)組的基地址+偏移量”算出數(shù)組元素的地址。在這里,第一個(gè)元素&data[0]的地址稱(chēng)為基地址,其偏移量就是下標(biāo)值和每個(gè)元素的大小sizeof(int)相乘。假設(shè)數(shù)組元素&data[0] 的地址為A,且在內(nèi)存中的實(shí)際地址為0x22FF74,那么&data[1]的值為:當(dāng)data作為sizeof的操作數(shù)時(shí),其返回的是整個(gè)數(shù)組的長(zhǎng)度。在這里,sizeof(data)的大小為12,即3個(gè)元素占用的.字節(jié)數(shù)為4×3=12,系統(tǒng)會(huì)認(rèn)為&data+1中的“1”,偏移了一個(gè)數(shù)組的大小,因此&data +1是下一個(gè)未知的存儲(chǔ)空間的地址(即越界)。在小端模式下,數(shù)組在內(nèi)存中的存儲(chǔ)方式詳見(jiàn)圖 1.10。
A + 1×sizeof(int) = (unsignedint)data + 4 = 0x22FF74 + 4 = 0x22FF78
&data[2]的值為:
A + 2×sizeof(int) = (unsigned int)data+ 8 = 0x22FF74 + 8 = 0x22FF7C
實(shí)際上,當(dāng)在C語(yǔ)言中書(shū)寫(xiě)data[i]時(shí),C將它翻譯為一個(gè)指向int的指針。Data是指向data[0]的指針,data+i是指向data[i]的,因此不管data數(shù)組是什么類(lèi)型,總有data+i等于data[i],于是*(data+i)等于data[i],其相應(yīng)的測(cè)試范例程序詳見(jiàn)程序清單 1.22。
程序清單 1.22變量的地址測(cè)試程序
1 #include
2 int main(int argc, char *argv[])
3 {
4 int data[3]= {1, 2, 3};
5 printf("%x, %x, %x, %x, %x",&data[0], &data[1], &data[2], &data, &data+1);
6 return0;
7 }
實(shí)踐證明,雖然&data[0]與&data的類(lèi)型不一樣,但它們的值相等。同時(shí)也可以看出,數(shù)組的元素是連續(xù)存儲(chǔ)的。如果將數(shù)組變量占用內(nèi)存的大小除以數(shù)組變量中一個(gè)元素所占用空間的大小,便可得到數(shù)組元素的個(gè)數(shù)。即:
int numData = sizeof(data) / sizeof(data[0]);
當(dāng)然,也可以使用宏定義計(jì)算數(shù)組元素的個(gè)數(shù):
#define NELEMS(data)(sizeof(data) / sizeof(data[0]))
當(dāng)數(shù)組作為函數(shù)的參數(shù)時(shí),C語(yǔ)言函數(shù)的所有參數(shù)必須在函數(shù)內(nèi)部聲明。但是,由于在函數(shù)內(nèi)部并沒(méi)有給數(shù)組分配新的存儲(chǔ)空間,因此一維數(shù)組的容量只在主程序中定義。顯然,如果函數(shù)需要得到一維數(shù)組的大小,則必須將它以函數(shù)參數(shù)的形式傳入函數(shù)中,或?qū)⑺鳛槿肿兞吭L(fǎng)問(wèn)。
2. 規(guī)約
標(biāo)準(zhǔn)C規(guī)定:除了“在聲明中”或“當(dāng)一個(gè)數(shù)組名是sizeof或&的操作數(shù)”之外,只要數(shù)組名出現(xiàn)在表達(dá)式中,這編譯器總是將數(shù)組名解釋為指向該數(shù)組的第一個(gè)元素的指針。
雖然data在表達(dá)式中解讀為指向該數(shù)組首元素data[0]的指針,但實(shí)際上data被解讀為&data[0]或等價(jià)于“*data==data[0]”,因此data與&data[0]的值相等,且它們的類(lèi)型都是int *const,即一個(gè)數(shù)組名是一個(gè)不可修改的常量指針(左值)。
根據(jù)指針運(yùn)算規(guī)則,當(dāng)將一個(gè)整型變量i和一個(gè)數(shù)組名相加時(shí),其結(jié)果是指向數(shù)組第i個(gè)元素的指針,即data+index==&data[index],*(data+index)==data[index],因此習(xí)慣性地將:
int * ptr = &data[0];
寫(xiě)成下面這樣的形式:
int *ptr = data;
由于data的類(lèi)型是不可修改的常量指針int*const,因此任何試圖使數(shù)組名指向其它地方的行為都是錯(cuò)誤的。比如:
data ++; //錯(cuò)誤
雖然data有地址,但其類(lèi)型為int *const,因此不能對(duì)data++賦值,同樣也不能反過(guò)來(lái)給data賦值。比如:
data = ptr;
類(lèi)型地,象下面這樣的表達(dá)式也是非法的:
intdata[3];
data= {1, 2, 3};
但可以將data復(fù)制給指針變量ptr,通過(guò)改變指針變量達(dá)到目的。比如:
#define N 10
int data[N];
int *ptr;
for(ptr = data; ptr < data + N; ptr ++)
sum+= *ptr;
for語(yǔ)句中的條件p
int sum = 0;
while(ptr < data +N)
sum += *ptr++;
其中,*ptr++等價(jià)于*(ptr++),它的含義為自增前表達(dá)式的值是*ptr,即ptr當(dāng)前指向的對(duì)象,以后再自增ptr。
【C語(yǔ)言數(shù)組與指針詳解】相關(guān)文章:
C語(yǔ)言指針數(shù)組的概念10-22
C語(yǔ)言中指針與數(shù)組的區(qū)別10-05
C語(yǔ)言指針用法詳解10-19
C語(yǔ)言指針函數(shù)和函數(shù)指針詳解06-10
C語(yǔ)言指針和數(shù)組基礎(chǔ)知識(shí)10-04
C語(yǔ)言之復(fù)雜指針詳解10-26
C語(yǔ)言指針和數(shù)組的學(xué)習(xí)方法10-24
C++ this指針詳解10-01
C語(yǔ)言指針教學(xué)11-05