程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 深入理解C指針之四:指針和數組,深入理解c指針數組

深入理解C指針之四:指針和數組,深入理解c指針數組

編輯:關於C語言

深入理解C指針之四:指針和數組,深入理解c指針數組


  數組是C內建的基本數據結構,數組表示法和指針表示法緊密關聯。一種常見的錯誤認識是數組和指針完全可以互換,盡管數組名字有時可以當做指針來用,但數組的名字不是指針。數組和指針的區別之一就是,盡管數組的名字可以返回數組地址,但是名字不能作為賦值操作的目標。

  概述

  數組是能用索引訪問的同質元素連續集合。數組的元素在內存中是相鄰的,而且元素都是同一類型的。數組的長度是固定的,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

   可以用一個塊語句初始化一維數組。

int vector[5] = {1,2,3,4,5};

     聲明一個二維數組。可以將二維數組當做數組的數組,只使用一個下標訪問二維數組得到的是對應行的指針。

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數組一行的長度。

  把數組地址賦給指針。

int* pv = vector;

     注意這種寫法和&vector[0]是等價的,不同於&vector是返回的整個數組的指針。一個是數組指針,一個是整數指針,指針類型不同。pv實際代表的是一個地址,*(pv+i)的寫法等價於pv[i],前者稱作指針表示法,後者稱作數組表示法

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是錯誤的寫法。

  一維數組

  如果從堆上分配內存並把地址賦給一個指針,那就肯定可以對指針使用下標並把這塊內存當做一個數組。用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}

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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved