1.c語言中,有時需要使用只讀數(shù)組,也就是程序從數(shù)組中讀取數(shù)值,但是程序不向數(shù)組中寫數(shù)據(jù),在這種情況下聲明并初始化數(shù)組時,建議使用const。
2.變量和數(shù)組都是自動類型的,不同的存儲類有時具有不同的屬性,自動類型就是如果沒有進(jìn)行初始化,一些存儲類的變量和數(shù)組會把他們的存儲單元設(shè)置為0;
#include <stdio.h>
#define SIZE 4
int main(int argc, const char * argv[]) {
int some_data[SIZE]={1492,1066};
int i;
printf("%s%s\n","i","some_data[i]");
for (i=0; i<SIZE; i++) {
printf("%d--%d\n",i,some_data[i]);
}
return 0;
}
輸出結(jié)果
isome_data[i]
0--1492
1--1066
2--0
3--0
從上面的結(jié)果我們可以看出,編譯器做的很好,當(dāng)數(shù)值數(shù)目小于數(shù)組元素數(shù)目時,多余的數(shù)組元素被初始化為0。也就是說,如果不初始化數(shù)組,數(shù)組元素和末初始化的普通變量一樣,其中存儲的是無用的數(shù)值;但是如果部分初始化數(shù)組,未初始化的元素則被設(shè)置為0;
如果初始化列表中項目的個數(shù)大于數(shù)組大小,編譯器會毫不留情地認(rèn)為這是一個錯誤。一種做法就是你可以省略括號中的數(shù)字,從而讓編譯器自動匹配數(shù)組大小和初始化列表中的項目數(shù)目
3.指定初始化項目
傳統(tǒng)的我們一般會這樣初始化數(shù)組:
int arr[6]={0,0,0,0,0,211};
但我們也可以在初始化列表中使用帶有方括號的元素下標(biāo)可以指定某個特定的元素
int arr[6]={[5]=211};
在初始化一個或多個元素后,未經(jīng)初始化的元素都將被設(shè)置為0
4.指針和數(shù)組
short dates[4];
short*pti;
short index;
double bills[4];
double*ptf;
pti=dates;
ptf=bills;
for (index=0; index<4; index++) {
printf("pointer +%d:%p %p\n",index,pti+index,ptf+index);
}
輸出結(jié)果
pointer +0:0x7fff5fbff738 0x7fff5fbff710
pointer +1:0x7fff5fbff73a 0x7fff5fbff718
pointer +2:0x7fff5fbff73c 0x7fff5fbff720
pointer +3:0x7fff5fbff73e 0x7fff5fbff728
指針的數(shù)值就是它所指向的對象的地址,地址的內(nèi)部表示方式是由硬件來決定的
在指針錢運(yùn)用運(yùn)算符*就可以得到該指針?biāo)赶虻膶ο蟮臄?shù)值
對指針加1,等價于對指針的值加上它所指向的對象的字節(jié)大小
dates+2==&dates[2]; //相同的地址
*dates+2 == dates[2]; // 相同的值
這些關(guān)系總結(jié)了數(shù)組和指針間密切的關(guān)系,可以用指針標(biāo)示數(shù)組的每個元素,并得到每個元素的數(shù)值,
順便提一下,請注意區(qū)分(dates+2)和dates+2。間接運(yùn)算符的優(yōu)先級高于+,因此后者等價于:(dates)+2
*(dates+2 ) /* dates 的第3個元素的值*/
*dates+2 /*第一個元素的值和2相加*/
賦值:可以把一個地址賦給指針。通常使用數(shù)組名或地址運(yùn)算符&來進(jìn)行地址賦值。
int urn[5]={100,200,300,400,500};
int*ptr1,*ptr2,ptr3;
ptr1=urn;
ptr2=&urn[2];
printf("ptr1=%p,*ptr1 =%d,&ptr1=%p\n",ptr1,*ptr1,&ptr1);
//指針加法
ptr3=*(ptr1+4);
printf("ptr1+4=%p,*(ptr4+3)=%d\n",ptr1+4,*(ptr1+3));
//遞增指針
ptr1++;
printf("ptr1=%p,*ptr1=%d,&ptr1=%p\n",ptr1,*ptr1,&ptr1);
//遞減指針;
ptr2--;
printf("ptr2=%p,*ptr2=%d,&ptr2=%p\n",ptr2,*ptr2,&ptr2);
--ptr1;//恢復(fù)為初始值
++ptr2;//恢復(fù)為初始值
printf("ptr1=%p,ptr2=%p\n",ptr1,ptr2);
//一個指針減去另一個指針
printf("ptr2=%p,ptr1=%p,ptr2-ptr1=%ld\n",ptr2,ptr1,ptr2-ptr1);
//一個指針減去一個整數(shù)
printf("ptr3=%d,ptr3-2=%d\n",ptr3,ptr3-2);
輸入結(jié)果為:
*p1=100,*p2=100,*p3=300
*p1++=100,*++p2=200,(*p3)++=300
*p1=200,*p2=200,*p3=301
ptr1=0x7fff5fbff6e0,*ptr1 =100,&ptr1=0x7fff5fbff678
ptr1+4=0x7fff5fbff6f0,*(ptr4+3)=400
ptr1=0x7fff5fbff6e4,*ptr1=200,&ptr1=0x7fff5fbff678
ptr2=0x7fff5fbff6e4,*ptr2=200,&ptr2=0x7fff5fbff670
ptr1=0x7fff5fbff6e0,ptr2=0x7fff5fbff6e8
ptr2=0x7fff5fbff6e8,ptr1=0x7fff5fbff6e0,ptr2-ptr1=2
ptr3=500,ptr3-2=498
賦值:可以把一個地址賦給指針。通常使用數(shù)組名或地址運(yùn)算符&來進(jìn)行地址賦值。上面例子中,把數(shù)組urn的起始地址賦給ptr1。變量ptr2得到的是數(shù)組第三個也即最后一個元素的地址。注意:地址應(yīng)該和指針類型兼容,不能把一個double類型的地址賦給一個指向int的指針
求值或取值:運(yùn)算符可取出指針指向地址中存儲的數(shù)值。因此,ptr1開始為100,即存儲在地址0x7fff5fbff6e0中的值。
取指針地址:指針變量同其他變量一樣具有地址和數(shù)值,使用運(yùn)算符&可以得到存儲指針本身的地址。本例中,ptr1被存儲在內(nèi)存地址0x7fff5fbff678中,該內(nèi)存單元的內(nèi)容是0x7fff5fbff6e0,即urn的地址。
將一個整數(shù)加給指針:可以使用+運(yùn)算符來把一個整數(shù)加給一個指針,或者把一個指針加給一個正數(shù)。兩種情況下,這個正數(shù)都會和指針?biāo)割愋偷淖止?jié)數(shù)相乘,然后所得的結(jié)果會加到初始地址上。于是,ptr4的結(jié)果等同于&urn【4】。如果相加的結(jié)果超出了初始指針?biāo)赶虻臄?shù)組的范圍,那么這個結(jié)果是不確定的,除非超出數(shù)組最后一個元素的地址能夠確保是有效的。
增加指針的值:可以通過一般的加法或增量運(yùn)算符來增加一個指針的值。對指向某數(shù)組元素指針做增量運(yùn)算,可以讓指針指向該數(shù)組的下一個元素。因此,ptr++運(yùn)算把ptr1加上數(shù)值4,使ptr1指向urn[1]。現(xiàn)在ptr1的值是0x7fff5fbff6e4,*ptr的數(shù)值為200.請注意ptr1本身的地址仍然是0x7fff5fbff678。別忘了,變量不會因為它的值得變化而移動位置。
從指針中減去一個整數(shù):可以使用-運(yùn)算符來從一個指針中減去一個整數(shù)。指針必須是第一個操作數(shù),或者是一個指向整數(shù)的指針。這個正數(shù)都會和指針?biāo)割愋偷淖止?jié)數(shù)相乘,然后所得到的結(jié)果會從初始地址中國減掉。于是,ptr3-2的結(jié)果等同于&urn[2].因為ptr3是指向&urn[4]的。如果相減的結(jié)果超出了初始指針?biāo)赶虻臄?shù)組的范圍,那么這個結(jié)果是不確定的,除非超出數(shù)組最后一個元素的地址能夠確保是有效的。
減小指針的值:指針當(dāng)然也可以做減量運(yùn)算。ptr2自減1之后,它將不再指向第三個元素,而是指向第二個數(shù)組元素。請注意,你可以使用前綴和后綴形式的增量和減量運(yùn)算符。對指針ptr1和ptr2都指向同一個元素urn【1】,直到他們被重置。
5.關(guān)于const的內(nèi)容
兩個函數(shù)
void show_array(const double ar[],int n);
void mult_array(double ar[],int n,double mult);
double rates[5]={88.99,110.22,59.45,183.22,340.21};
const double locked[4]={0.08,0.075,0.0725,0.07};
像show_array這樣的函數(shù)可以接受普通數(shù)組和常量數(shù)組的名稱作為實際的參數(shù),因為這兩種參數(shù)都可以賦給指向常量的指針
show_arrary(rates,5);
show_arrary(locked,4);
但是,像mult_arrary()這樣的函數(shù)不能接受常量數(shù)組的名稱作為參數(shù)
mult_array(rates,5,1.2);
mult_array(locked,4,1.2);
函數(shù)參量定義中使用const,不僅可以保護(hù)數(shù)據(jù),而且使函數(shù)可以使用聲明為const的數(shù)組
關(guān)于const的用法還有很多,例如,你可以使用關(guān)鍵字const來聲明并初始化指針,以保證指針不會指向別處,關(guān)鍵在于const的位置
這樣的指針仍然 可以用于修改數(shù)據(jù),但它只能指向最初賦給它的地址,不能修改所指向的地址
double * const pc1 = rates; //pc1指向數(shù)組的開始處
*pc1 = 92.99;//允許
//pc1=&rates[2]; // 不允許
指向常量的指針不能用于修改數(shù)值,但是指向的地址是可以改變的
const double locked[4]={0.08,0.075,0.0725,0.07};
const double*pc=rates; //pc指向數(shù)組開始處
//*pc=320.0; //不允許
//pc[2]=222.22; //不允許
pc=locked;//允許
pc=&rates[4];//允許
pc++;//讓pc指向rates[1]這是允許的
rates[0]=99.99; //允許 因為rates不是常量
也可以使用兩個const來創(chuàng)建指針,這個指針既不可以更改所指向的地址,也不可以修改所指向的數(shù)據(jù)
const double* const pc2 = rates;
// pc2 = &rates[2];//不允許
// *pc2 = 92.99 //不允許
6.指針和多維數(shù)組
函數(shù)是通過指針來處理多維數(shù)組的
來看一個例子:
假設(shè)有如下聲明:
int zippo[4][2]; 整數(shù)數(shù)組的數(shù)組
數(shù)組名zippo同時也是數(shù)組首元素的地址。在本例中,zippo的首元素本身又是包含兩個int的數(shù)組,因此zippo也是包含兩個int的數(shù)組的地址。下面從指針地址進(jìn)一步分析:
①因為zippo是數(shù)組首元素的地址,所以zippo的值和&zippo[0]相同。另一方面,zippo[0]本身是包含兩個整數(shù)的數(shù)組,因此zippo[0]的值同其首元素的地址&zippo[0][0]相同。簡單地說,zippo[0]是一個整數(shù)大小對象的地址,而zippo是兩個整數(shù)大小對象的地址。因為整數(shù)和兩個整數(shù)組成的數(shù)組開始于同一個地址,因此zippo和zippo[0]具有相同的數(shù)值。
②對一個指針加1,會對原來的數(shù)值加上一個對應(yīng)類型大小的值。在這方面,zippo和zippo[0]是不一樣的,zippo所指向?qū)ο蟮拇笮∈莾蓚€int,而zippo[0]所指向?qū)ο蟮拇笮∈且粋€int,因此zippo+1和zippo[0]+1的結(jié)果不同。
③對一個指針取值得到的是該指針?biāo)赶驅(qū)ο蟮臄?shù)值。因為zippo[0]是其首元素zippo[0][0]的地址,所以(zippo[0])代表存儲在zippo[0][0]中的數(shù)值,即一個int數(shù)值。同樣zippo代表其首元素zippo[0]的值,但是zippo[0]本事就是一個int數(shù)的地址,即&zippo[0][0],因此zippo是&zippo[0][0]。對這兩個表達(dá)式同時應(yīng)用取值元算符將得到zippo等價于&zippo[0][0],后者簡化后即為一個int數(shù)zippozippo[0][0]。簡言之,zippo是地址的地址,需要兩次取值才可以得到通常的數(shù)值。地址的地址或指針的指針是雙重間接的典型例子。
總結(jié)完的關(guān)系如下:
// zippo[0] 表示首元素的地址
// zippo[0][0] 表示元素的值
// *zippo表示首元素的值
// &zippo[0][0]= zippo[0]=*zippo=zippo
// *(zippo[0])表示存儲在zippo[0][0]中的數(shù)值
// *(zippo[0])=zippo[0][0] = **zippo = *&zippo[0][0]
下面我們看一下這個輸出結(jié)果
printf("8-----*(*(zippo+2)+1)=%d\n",*(*(zippo+2)+1));
結(jié)果為:
8-----*(*(zippo+2)+1)=3
讓我們來分步來建立這個表達(dá)式
zippo //第一個大小為2個int的元素地址
zippo+2 //第3個大小為2個int的元素地址
*(zippo+2)第3個元素,即包含2個int值得數(shù)組,因此也是其第一個元素(int值)的地址
*(zippo+2)+1包含2個int值的數(shù)值的第2個元素的地址
*(*(zippo+2)+1)數(shù)組第三行第2個int(zippo[2][1])的值
7.指向多維數(shù)組的指針
如何聲明一個指向二維數(shù)組的指針變量pz?例如,在編寫處理像zippo這樣的數(shù)組的函數(shù)時,就會用到這類指針。指向int的指針可以勝任嗎?不可以。這種指針只是和zippo[0]兼容,因為它們都指向一個單個int值。但是zippo是其首元素的地址,而該首元素又是包含兩個int值的數(shù)組。因此,pz必須指向一個包含兩個int值的數(shù)組,而不是指向一個單個int值。下面是正確的代碼:
int(*pz)[2];//pz指向一個包含2個int值的數(shù)組(使用圓括號的原因是是表達(dá)式中[]的優(yōu)先級高于*)
zippo[m][n]==*(*(zippo+m)+n)
pz[m][n]=*(*(pz+m)+n)
指針之間的賦值規(guī)則比數(shù)組類型的賦值更嚴(yán)格。例如,你可以不需要進(jìn)行類型轉(zhuǎn)換就直接把一個int數(shù)值賦給一個double類型的變量。但對于指針來說,這樣的賦值是不允許的
??
int *pt;
int(*pa)[3];
int ar1[2][3];
int ar2[3][2];
int **p2;
pt = &ar1[0][0];
pt = ar1[0];
pt = ar1;//非法
pa = ar1;
pa = ar2;//非法
p2 = &pt;
*p2 = ar2[0];
p2 = ar2;//非法
8.函數(shù)和多維數(shù)組
void sum_rows(int ar[][COLS],int rows)
請注意下面的聲明是不正確的
int sum2(int ar[][],int rows);//錯誤的聲明
編譯器會把數(shù)組符號轉(zhuǎn)換成指針符號。這就意味著,ar[1]會被轉(zhuǎn)換成ar+1。編譯器這樣轉(zhuǎn)換的時候需要知道ar所指向?qū)ο蟮臄?shù)據(jù)大小。
int sum2(int ar[][4],int rows);//合法聲明
就表示ar指向由4個int值構(gòu)成的數(shù)組,也就是16個字節(jié)長的對象,所以ar+1表示”在這個地址上加16個字節(jié)大小“。
也可以如下這樣在另一對方括號中填寫大小,但編譯器將忽略之:
int sum2(int ar[3][4],int rows)//合法聲明,但3將被忽略
一般地,聲明N維數(shù)組的指針時,出了最左邊的方括號可以留空之外,其他都需要填寫數(shù)值
int sum4(int ar[][12][20][30],int rows);