C語言指針詳述(2):數組與指針

第六章 數組和指針的關係

1.指針數組:一個數組裏存放的都是同一個類型的指針,通常我們把他叫做指針數


組。


int * a[10];它裏邊放了10個int * 型變量,由於它是一個數組,已經在棧區分配了1


0個(int * )的空


間,也就是32位機上是40個byte,每個空間都可以存放一個int型變量的地址,這個


時候你可以爲這


個數組的每一個元素初始化,或者單獨做個循環去初始化它。


一個指針數組中的指針可以爲任何同一類型,例如可以爲結構指針、函數指針等

struct a

{

int a;


int b;

}

typedef struct a STRUCTA


STRUCTA* b[4];//一個存放了4個STRUCTA*型指針的數組,其大小爲4*sizeof


(STRUCTA*)=16


typedef void(*FUNC)();


FUNC funarr[4];//定義了一個函數指針數組


2.結構數組

struct a

{

int a;

int b;

}a[12];//定義了一個結構數組,大小爲12*sizeof(struct a) = 48;


3.數組指針 : 一個指向一維或者多維數組的指針;


int (*b2) [20]; //二級指針;b2指向一個大小爲20*sizeof(int)的int型數組


int(*b3) [30] [20]; //三級指針――>指向三維數組的指針;


4.數組的數組名具備一些指針的用法,近似於指針,


例如:int array[12][31];//數組的嵌套定義,array是一個含有12個數組類型元素的數


組,其中的每一個


元素是一個int數組,array的類型是int[12][31],其指向的類型是int[31],可以理解爲


array是指向數組


的指針。


array[1]是一個含有31個int型元素的數組,它的類型是int[31],它指向的類型是int;


int I;

I = array[4][7] 等同於 I = *(array[4]+7) 等同於 I = *(*(array +4)+7)


int *p; int **q; int (*ptr1)[31]; int (*ptr2)[41];


p = array[2];//正確,p指向數組array[2]中下標爲0的元素


p = array; // 錯誤,array是二維數組,其類型爲“數組的數組”,類型不匹配


q = array; //錯誤,array不是指向指針的指針


ptr1 = array; // 正確,同是指向數組int[31]的指針。


ptr2 = array;//錯誤,array是指向int[31]的指針,ptr2是指向int[41]的指針。


例九:

char*str[3]={


"Hello,thisisasample!",


"Hi,goodmorning.",


"Helloworld"

};


chars[80];


strcpy(s,str[0]);//也可寫成strcpy(s,*str);


strcpy(s,str[1]);//也可寫成strcpy(s,*(str+1));


strcpy(s,str[2]);//也可寫成strcpy(s,*(str+2));


上例中,str是一個三單元的數組,該數組的每個單元都是一個指針,這些指針各指向


一個字符串。把


指針數組名str當作一個指針的話,它指向數組的第0號單元,它的類型是char**,


指向的類型是


char*。


*str也是一個指針,它的類型是char*,它所指向的類型是char,它指向的地址是字符


串"Hello,thisisasample!"的第一個字符的地址,即'H'的地址。 str+1也是一個指針,它


指向數組的第1號


單元,它的類型是char**,它指向的類型是char*。


*(str+1)也是一個指針,它的類型是char*,它所指向的類型是char,它指向


"Hi,goodmorning."的第一


個字符'H',等等。


5.下面總結一下數組的數組名的問題。聲明瞭一個數組TYPE array[n],則數組名稱


array就有了兩重含


義:第一,它代表整個數組,它的類型是TYPE[n];第二 ,它是一個指針,該指針的


類型是TYPE*,該


指針指向的類型是TYPE,也就是數組單元的類型,該指針指向的內存區就是數組第0


號單元,該指針自


己佔有單獨的內存區,注意它和數組第0號單元佔據的內存區是不同的。該指針的值是


不能修改的,即


類似array++的表達式是錯誤的。


在不同的表達式中數組名array可以扮演不同的角色。


在表達式sizeof(array)中,數組名array代表數組本身,故這時sizeof函數測出的是整個


數組的大小。


在表達式*array中,array扮演的是指針,因此這個表達式的結果就是數組第0號單元的


值。sizeof


(*array)測出的是數組單元的大小。


表達式array+n(其中n=0,1,2,....。)中,array扮演的是指針,故array+n的結果


是一個指針,它


的類型是TYPE*,它指向的類型是TYPE,它指向數組第n號單元。故sizeof(array+n)測


出的是指針類型


的大小。



例十:


int array[10];


int(*ptr)[10];


ptr=&array;


上例中ptr是一個指針,它的類型是int(*)[10],他指向的類型是int[10] ,我們用整個數


組的首地址來初


始化它。在語句ptr=&array中,array代表數組本身。


本節中提到了函數sizeof(),那麼我來問一問,sizeof(指針名稱)測出的究竟是指針自身類型的大小呢還


是指針所指向的類型的大小?答案是前者。例如:


int(*ptr)[10];


則在32位程序中,有:


sizeof(int(*)[10])==4


sizeof(int[10])==40


sizeof(ptr)==4


實際上,sizeof(對象)測出的都是對象自身的類型的大小,而不是別的什麼類型的大小。

6 數組與指針的不同

引例:
文件1:
int mango[100];
文件2:

extern int *mango;

............. //一些引用mango[i]的代碼

錯誤:把數組的定義等同於指針的外部聲明。



6.1聲明與定義

搞清楚這個問題之前,澄清一些重要概念:

C語言中的對象必須有且只有一個定義,但它可以有多個extern聲明。



概念
區別

定義
只能出現在一個地方,確定對象的類型並分配內存,用於創建新的對象。例如:int a[12]
相當於特殊聲明,它爲對象分配內存。

聲明
可多次出現,描述對對象的類型,用於指代其他地方定義的對象。例如:extern int a[]
相當於普通聲明,它所說明的並非自身,而是描述其他地方創建的對象。




由於extern聲明不爲對象分配內存,所以不必提供關於數組長度的信息。對於多維數組,需要提供除最左邊一維之外其他維的長度——給編譯器足夠的信息產生相應代碼。



6.2輸組與指針的訪問過程

char a[9] = “abckefg”; c = a[i];

編譯器符號表具有一個地址9980

運行時步驟1:取i的值,將它與9980相加;

運行時步驟2:取地址[9980+i]的內容。

Char *p; c = *p;

編譯器符號表有一個符號p,它的地址是4624

運行時步驟1:取地址4624的內容,就是’5081’;

運行時步驟2:取地址5081的內容。



6.3定義爲指針,但以數組方式引用的過程

char *p = “abchdefs” c = p[i]

編譯器符號表有個p,地址爲4624

運行時步驟1:取地址4624的內容,即’5081’;

運行時步驟2:取得I的值,並將它與5081相加;

運行時步驟3:取地址[5081+i]的內容。



如果將p聲明爲指針,那麼不管p原來是定義爲指針還是數組,都會按照上面的三個步驟操作。

如:char *p[10];

另一個文件中extern char *p;

當用p[I]提取這個聲明中的內容時,實際上得到的是一個字符,而編譯器卻把它當作一個指針……





6.4指針和數組的其他區別
指針
數組

保存數據地址
保存數據

間接訪問數據,首先取得指針的內容,把它作爲地址,然後從這個地址提取數據。如果指針有個下標[I],就把指針的內容加上I作爲地址,從中提取數據
直接訪問數據,a[I]只是簡單的以a+I爲地址取的數據。

通常用於動態數據結構
通常用於存儲固定數目且數據類型相同的元素

相關函數爲malloc(),free()
隱式分配和刪除

通常指向匿名數據
自身即爲數據名




7 數組與指針相同時

7.1輸組與指針的可交換性

數組 聲明: extern 如:extern char a[];不能改寫成指針形式

定義,如 char a[10];不能改寫成指針形式

函數參數,如func(char a[]);可隨意選擇數組或指針

在表達式中使用 如c = a[I];隨意選擇數組或者指針形式



規則1:“表達式中的數組名”就是指針。

規則2:C語言把數組下表作爲指針偏移量

規則3:作爲函數參數的數組名等同於指針



數組與指針可交換性總結:

1. 用a[I]這樣的形式對數組進行訪問總是被編譯器解釋爲像*(a+1)這樣的指針訪問。

2. 指針始終就是指針。它絕不可以改寫成數組。可以用下標形式訪問指針,一般都是指針作爲函數參數時,而且實際傳遞給函數的是一個數組。

3. 在特定的上下文環境中,也就是它作爲函數的參數(也只有這種情況),一個數組的聲明可以看作是一個指針。作爲函數參數的數組(就是在一個函數調用中)始終會被編譯器修改成爲之鄉第一個元素的指針。

4. 因此,當把一個數組定義爲函數參數時,可以選擇把它定義爲數組,也可定義爲指針。不管用那種方法,在函數內部事實上獲得的都是一個指針。

5. 在其他所有情況中,定義和聲明必須匹配。如果定義了一個數組,在其他文件對他進行聲明時也必須把它聲明爲數組,指針也是如此。



7.2多維數組

例:

int apricot[2][3][5];

int (*q)[2][3][5] = &apricot;

int (*p)[3][5] = apricot;

int (*r)[5] = apricot[i];

int *t = apricot[I][j];

int u = apricot[I][j][k];



初始化:

只有字符串常量纔可以初始化指針數組

char * vegetables[] = { “carrot”,“celery”,“corn”,“cilantro”,“crispy”};

指針數組不可以由非字符串的類型直接初始化:

int *weights[] = {{1,2,3,4,5}, {6,7,8},}; //無法編譯成功

可以這樣做:

int row_1[] = {1,2,3,4,5,-1}; //-1是行結束標誌。

Int row_2[] = {6,7,8,-1};

Int *weights[] = {Row_1,Row_2,};



7.3 向函數傳遞一個一維數組

任何一維數組都可以作爲函數實參,形參被改寫爲指向數組第一個元素的指針,所以需要一個約定來提示數組的長度。一般有兩個基本方法:

1, 增加一個額外的參數,表示元素數目(argc就起這個作用)

2, 賦予數組最後一個元素一個特殊的值,提示他是數組的尾部(字符串結尾的‘/0’字符就是起這個作用)。這個特殊值必須不會做爲正常的元素值在數組中出現。



7.4 使用指針向函數傳遞一個多維數組

C語言無法表達“這個數組的邊界在不同的調用中可以變化”這個概念。C語言必須知道數組邊界,從而爲下標引用產生正確代碼。

二維或多維數組無法在C語言中作一般的形參。無法向函數傳遞一個普通的多維數組。

可以向函數傳遞預先確定長度的特殊數組。



方法1,my_function( int my_array[10][20] );//只能處理10行20列的數組。調用時參數也要匹配

方法2,my_function( int my_array[][20] );//只能處理20列的數組,調用時參數也要匹配

由於必須提供數組除最左邊一維以外的所有維的長度,所以形參只能省略第一個長度。

方法3,my_function( char **my_array,…… );

此方法使用前提,必須在調用之前將二維數組改爲一個指向向量的指針數組。必須是指針數組,而且必須是指向字符串的指針數組。



總結:如果多維數組各維的長度是一個完全相同的固定值,那麼把它傳給一個函數毫無問題。如果情況更一般些,也更常見一些,就是作爲函數的參數的數組的長度是任意的,分析如下:

1, 一維數組——沒有問題,但須包括一個計數值或者是一個能夠標識越界位置的結束符。被調用的函數無法檢測數組參數的邊界。

2, 二維數組——不能直接傳遞給寒暑,但可以把矩陣改寫爲一個一維的指針數組,並使用相同的下標表示法。對於字符串來說,這樣是可以的,對於其他類型,需要增加一個計數值或者能夠標識越界位置的結束符。同樣,他依賴於調用函數和被調用函數之間的約定。

3, 三維或更多維——都無法使用。必須把它分解爲幾個維數更少的數組。

例1:

int main( int argc, char *argv[] )//由於字符串以’/0’作爲結束標誌,所以只需要一個argc計數有多少個字符串即可。

例2:

int arr[2][3] = {{1,2,3},{4,5,6}};//將要進行傳遞的二維數組

做法1:

int *p[2] = {arr[0],arr[1]};將二維數組改造成指針數組,每個指針指向一個一維數組

void fun(int rownum,int colnum,int *array[]);

fun(2,3,p);//傳遞一個二維數組必須在傳遞之前將其轉化爲一個指針數組,傳遞時還要傳遞該二維數組的行數和列數。

做法2:

int *p[3] = {arr[0],arr[1],NULL};將二維數組改造成指針數組,每個指針指向一個一維數組

void fun(in

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章