深刻理解C指針之四:指針和數組

原文: 深刻理解C指針之四:指針和數組

  數組是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}

    不規則數組的訪問和維護都比較麻煩,使用前應慎重考慮。

相關文章
相關標籤/搜索