第六章 數組和指針的關係
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
C語言指針詳述(2):數組與指針
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.