數組是C內建的基本數據結構,數組表示法和指針表示法緊密關聯。一種常見的錯誤認識是數組和指針徹底能夠互換,儘管數組名字有時能夠當作指針來用,但數組的名字不是指針。數組和指針的區別之一就是,儘管數組的名字能夠返回數組地址,可是名字不能做爲賦值操做的目標。html
數組是能用索引訪問的同質元素連續集合。數組的元素在內存中是相鄰的,並且元素都是同一類型的。數組的長度是固定的,realloc函數和變長數組提供了應對長度須要變化的數組的技術。數組
一維數組是線性結構,用一個索引訪問成員。取決於不一樣的內存模型,數組的長度可能會有不一樣。數據結構
int vector[5];
數組的名字只是引用了一塊內存,對數組使用sizeof操做符會獲得爲該數組分配的字節數。要知道數組的元素數,只要對其除以元素長度便可。函數
int vector[5]; printf("vector is %p\nsize is %d\n", &vector, sizeof(vector)); //vector is 0xbffb8c08 //size is 20
能夠用一個塊語句初始化一維數組。post
int vector[5] = {1,2,3,4,5};
聲明一個二維數組。能夠將二維數組當作數組的數組,只使用一個下標訪問二維數組獲得的是對應行的指針。url
int matrix[2][3] = {{1,2,3},{4,5,6}}; printf("0 is %p\n1 is %p\n", &matrix[0], &matrix[1]); //0 is 0xbf984044 //1 is 0xbf984050
能夠看到,兩個地址恰好差12個字節,也就是matrix數組一行的長度。 spa
把數組地址賦給指針。指針
int* pv = vector;
注意這種寫法和&vector[0]是等價的,不一樣於&vector是返回的整個數組的指針。一個是數組指針,一個是整數指針,指針類型不一樣。pv實際表明的是一個地址,*(pv+i)的寫法等價於pv[i],前者稱做指針表示法,後者稱做數組表示法。htm
printf("pv[0] is %d\n*(pv+1) is %d\n ",pv[0],*pv); //pv[0] is 1 //*(pv+1) is 1
pv指針包含一個內存塊的地址,方括號表示法會取出pv中包含的地址,用指針算數運算符把索引i加上,而後接引新地址返回其內容。給指針加上一個整數實際是給它加了整數與數據類型長度的乘積,這一點對數組名字也一樣適用,*(pv+1)和*(vector+1)是等價的。所以數組表示法能夠理解爲「偏移並解引」操做。vector[i]和*(vector+i)兩種方法結果相同,僅僅在實現上略有差異,能夠忽略。注意指針能夠做爲左值,可是數組名字不能夠做爲左值使用,vector=vector+1是錯誤的寫法。blog
若是從堆上分配內存並把地址賦給一個指針,那就確定能夠對指針使用下標並把這塊內存當作一個數組。用malloc建立的已有數組的長度能夠經過realloc函數來調整。C99支持變長數組,可是變長數組只能在函數內部聲明。因此若是數組的生命週期須要比函數長,或者你沒有使用C99,那就只能使用realloc。
下面這個函數接受用戶的輸入,而且使用realloc函數動態的申請所需的內存,按回車結束輸入。
char* getLine(void) { const size_t sizeIncrement = 10; char* buffer = malloc(sizeIncrement); char* currentPosition = buffer; size_t maximumLength = sizeIncrement; size_t length = 0; int character; if(currentPosition == NULL){return NULL;} while(1) { character = fgetc(stdin); if(character == '\n'){break;} if(++length >= maximumLength) { maximumLength += sizeIncrement; char* newBuffer = realloc(buffer,maximumLength); if(newBuffer == NULL){free(buffer); return NULL;} currentPosition = newBuffer + (currentPosition - buffer); buffer = newBuffer; } *currentPosition++ = character; } *currentPosition = '\0'; printf("buffer is %s\n", buffer); return buffer; } getLine();
將一維數組做爲參數傳遞給函數實際是經過值來傳遞數組的地址,咱們須要告訴函數數組的長度,不然函數只有一個地址,不知道數組到底有多長。對於字符串來講,能夠依靠NUL字符來判斷其長度。對於有些數組則沒法判斷。
聲明一個指向數組的指針和指針數組、數組指針是不一樣的。指向數組的指針是一個指針指向數組下標爲0的元素,指針數組是一個元素爲指針的數組,數組指針是數組類型的指針。
int vector[2] = {1,2}; int* pv1 = vector;//指向數組的指針 int* pv2[2];//指針數組,能夠用一個循環來爲每一個指針分配內存 int (*pv3)[2];//數組類型的指針
如今再來區分一下數組表示法和指針表示法在指針數組的應用。
int* array[5]; array[0] = (int*) malloc (sizeof(int)); *array[0] = 100; *(array+1) = (int*) malloc (sizeof(int)); **(array+1) = 200;
在這裏array[1]和*(array+1)是等價的,實際都是指針類型,使用malloc函數爲指針在堆上分配內存,解引指針來爲數據賦值,所以**(array+1)其實不難理解,一個*解引獲得一個指針,對指針再解引才獲得數據,而[]括號前面已經 解釋過,就是至關於一個取地址和加索引的操做。固然,還可使用array[0][0]來代替*array[0]。
能夠將多維數組的一部分看作子數組,好比二維數組的每一行當作一個一維數組。數組按行-列順序存儲,第二行的第一個元素的內存地址緊跟在第一行最後一個元素後面。
int matrix[2][3] = {{1,2,3},{4,5,6}}; int (*pmat)[3] = matrix;//3是二維數組的列數 printf("size of pmat[0] is %d\n", sizeof(pmat[0])); //size of pmat[0] is 12
能夠看到,該數組指針的第一個元素的長度是12個字節,也就是說是第一行的長度。若是要訪問第一行第一個元素,須要用pmat[0][0]來訪問。array[i][j]等於 array+i*sizeof(row) + j* sizeof(element)。sizeof(row)是一行的總大小,sizeof(element)是單個元素的大小,array是array[0][0]的地址。
給函數傳遞數組參數時,要考慮如何傳遞數組的維數以及每一維度的大小。下面是兩種傳遞二維數組的方法:
void display2DArray(int arr[][3], int rows){} void display2DArray(int (*arr)[3], int rows){}
兩種方法都指定了行數。若是傳遞的數組維度超過了二維,除了一維的部分,須要指定其它維度的長度。若是傳遞一個array3D[3][2][4]的數組:
void display3DArray(int (*arr)[2][4], int rows){}
當使用malloc分別爲二維數組的不一樣子數組分配內存時,可能致使內存分配不連續的問題。使用塊語句一次性初始化不會有這個問題。使用malloc來爲二維數組分配連續的內存有兩種策略。假設二維數組有三行四列。第一種一次性分配全部內存3*4*sizeof(element),而後使用的時候經過前面提到的如何訪問array[i][j]的方法手動計算要訪問的內存地址。第二種方法分爲兩步。第一步先使用malloc分配一塊內存用來存放指向二維數組每一行的指針的指針。第二步爲array[0][0]的指針分配全部內存,而後計算array[1][0]和array[2][0]的位置,分別給這兩個關鍵指針賦值,這樣就能夠根據每一行的指針來使用下標訪問了。
int rows = 3; int columns = 5; //第一種方法 int* matrixx = (int*) malloc (rows * columns * sizeof(int)); //第二種方法 int **matrixy = (int**) malloc (rows * sizeof(int*)); matrixy[0] = (int*) malloc (rows * columns * sizeof(int)); int i = 1; while(i<rows) { i++; matrixy[i] = matrix[0] + i * columns; }
不規則數組是每一行的列數不同的二維數組。可使用複合字面量來初始化不規則數組。
(const int) {100} (int [3]) {10, 20, 30}
不規則數組的訪問和維護都比較麻煩,使用前應慎重考慮。