4.3 指標和陣列之間的恩恩怨怨
指標與陣列的區別:
陣列是陣列,指標是指標,根本就是兩個完全不一樣的東西。當然要是在巨集觀的記憶體角度看,那一段相同型別的連續空間,可以說的上是陣列。
但是你可以嘗試下,定義乙個指標,在其他地方把他宣告成陣列,看看編譯器會不會把兩者混為一談,反過來也不會。
但是為什麼我們會經常弄混呢?第一,我們常常利用指標的方式去訪問陣列。第二,陣列作為函式引數的時候,編譯器會把它退化成為指標,因為函式的引數是拷貝,如果是乙個很大的陣列,拷貝是很浪費記憶體的,所以陣列會被退化成指標(這裡一定要理解好,退化的是陣列成員的型別指標,不一定是陣列指標的哈)。
4.3.1 以指標的形式訪問和以下標的形式訪問
我們先來詳細討論他們之間的一些特點:例如,函式內部有如下定義:
(a)char * p = "abcdef ";
(b)char a[ ] = "123456";
1.以指標的形式訪問指標 && 以下標的形式訪問指標
(a)定義了乙個指標變數p, p本身在棧上佔4個位元組,p裡儲存的是一塊記憶體的首位址。這塊內存在靜態區,其空間大小為7位元組,這塊記憶體也沒有名字,對這塊記憶體的訪問完全是匿名訪問。如果現在讀取字元'e』我們有兩種方式:
(1) 以指標的形式: *(p+4)。先取出p裡儲存位址值,假設為0x0000ff00;再加上4個字元的偏移量,得到新的位址0x0000ff04;然後取出0x0000ff04位址上的內容;
(2)以下標的形式:p[4] 。 編譯器總是把以下標的形式的操作解析為以指標的形式的操作。 道理:同上
2.以指標的形式訪問陣列 && 以下標的形式訪問陣列
(b)定義了乙個陣列a,a擁有7個char型別的元素,其空間大小為7。陣列a本身在棧上面。對a的元素的訪問必須先根據陣列的名字a找到陣列元素的首位址,然後根據偏移量找到相應的值。如果現需讀取字元'5',我們有兩種方式:;
(1)以指標的形式:*(a+4)。a這個時候代表的是陣列首元素的首位址,假設為0x0000ff00; 再加上4個字元的偏移量,得到新的位址0x0000ff04;然後取出0x0000ff04位址上的內容;
(2) 以下標的形式: a[4] 。編譯器總是以下標的形式的操作解析為以指標的形式操作。道理:同上啦!
【注意:上面所說的偏移量4代表的是4個元素,而不是4個位元組;不過這裡剛好是char型別的資料,1字元的大小就為1位元組】
4.3.2 a和&a的區別(
a為陣列首元素位址,&a為陣列的首位址
)
通過上面的分析,我們再來看看這個例子:
mian()
; int *ptr=(int*)(&a+1);
printf("%d,%d",*(a+1),*(ptr-1));
}
列印出來的值為多少?這裡主要是考查關於指標加減操作的理解。
&a+1:取陣列a 的首位址,該位址的值加上sizeof(a)的值,即:&a+5*sizeof(int),也就是下乙個陣列的首位址【顯然當前指標已經越過了陣列的界限】。(int*)(&a+1) :則是把上一步計算出來的位址,強制轉換為int *型別,賦值給ptr ;
*(a+1) :a和&a的值是一樣的,但是意思不一樣;
【也即 a = a[0]);
&a:是陣列的首位址; &a+1:是下乙個陣列的首位址(超過了第乙個陣列) 】
4.3.3 指標和陣列的定義與宣告
1 定義為陣列,宣告為指標
在檔案1中,有定義:char a = "abcdefg";在檔案2中,有宣告:extern char *a;2 定義為首先要說的是,這個是錯誤的!
對於大多數編譯器來說,在檔案1中,a佔7個byte;
在檔案2中,會把a當做乙個指標來處理,佔4個byte。而且,在這4個byte中,分別儲存的是a,b,c,d的ascii碼值。由這四個ascii碼值組成的位址並不是我們所想要的陣列的首元素的位址。而真正的陣列a的首元素的首位址在檔案一中定義陣列的時候被編譯器儲存到了某個不知道的地方。
指標,宣告為
陣列(這個是錯誤的!)
在檔案1中,有定義:char *p = "abcdefg";在檔案2中,有宣告:extern char p;如上面的分析一樣,在檔案1中,指標p中儲存的是一塊靜態區域的位址,假設這塊位址為0x00ff0000。
而在檔案2中,把p當做陣列來訪問,按照char型別取出p[0],p[1],p[2],p[3]分別為0x00,0xff,0x00,0x00,並不是我們所想要的a,b,c,d。
而且如果更改了p[0]等等,那麼本來靜態區域的位址也會丟失。
4.3.4 指標與陣列的對比
指標和陣列的特性總結:
區別要點
指標陣列
儲存的內容
儲存資料的位址,任何存入指標變數p的資料都會被當做位址來處理。p本身的位址由編譯器另外儲存,儲存在哪,我們並不知道
儲存資料,陣列名a代表陣列首元素的首位址沒,而不是陣列的首位址。&a才是整個陣列的首位址。a本身的位址由編譯器另外儲存,儲存在**,我們並不知道。
訪問資料的方式
間接訪問資料,首先取得指標變數p的內容,把它作為位址,然後從這個位址讀/寫資料。
指標以指標的形式訪問*(p+i);也以下標的形式訪問p[i],但其本質都是先取p內容然後加上i*sizeof(型別)位元組作為資料的真正位址
直接訪問資料,陣列名a是整個陣列的名字,陣列內每個元素並沒有名字。只能通過「具名+匿名」的方式來訪問其某個元素,不能把陣列當乙個整體來進行讀/寫操作。陣列能以指標的形式訪問*(a+i);也能以下標的形式訪問a[i],但其本質都是a所代表的陣列首元素的首位址加上i*sizeof(型別)位元組作為資料的真正位址
常用的場合
通常用於動態資料結構
通常用於儲存固定數目且資料型別相同的元素
分配刪除
相關的函式為malloc 和free
隱式分配和刪除
變數含義
通常指向匿名資料(當然也可指向具名資料)
自身即為陣列名
讀書筆記《c語言深度解剖》 4
10.struct關鍵字 這裡struct關鍵字講解得比較少,主要有3點 1 空結構體的大小為1 在gcc下我的輸出是0 2 柔性陣列 在c99中,允許結構最後乙個元素是乙個大小未確定的陣列,這個陣列叫做柔性陣列。但是柔性陣列前面必須至少有乙個其他成員。用sizeof返回結構體大小的時候,並不包含柔...
C語言深度解剖讀書筆記1
關鍵字 意義關鍵字 意義auto 宣告自動變數,預設時編譯器一般預設為auto register 宣告暫存器變數 int宣告整型變數 const 宣告唯讀變數 double 宣告雙精度變數 volatile 說明變數在程式執行中可被隱含地改變 long 宣告長整型變數 typedef 用以給資料型別...
C語言深度解剖讀書筆記3
int p null 和 p null 的區別int p null 可以通過偵錯程式檢視p的值為 0x00000000 這句 的意思是 定義乙個指標變數p,其指向的記憶體裡面儲存的是 int 型別的資料 在定義變數p的同時把p的值設定為 0x00000000,而不是把 p 的值設定為0x000000...