Re: [問題] 指標問題
看板C_and_CPP (C/C++)作者EdisonX (閉上眼的魚)時間11年前 (2012/07/21 16:23)推噓12(12推 0噓 5→)留言17則, 13人參與討論串12/13 (看更多)
※ 引述《newkey (key)》之銘言:
: C語言
: 因為用打得有點難標示
: 所以用成圖片網址
: http://ppt.cc/LMxz
: 不太懂*++P 是什麼意思
: 除了第一個 *P = 5 之外
: 起它的不知道如何下手
: 想請問高手詳解
: 謝謝
好久沒畫圖了,把該講的一起講, 一開始情況是這樣的
int A[5];
int *P
for (i=0; i<=4; i++)
A[i] = 5-i;
< 注意我還沒寫 P = A >
┌────┬────┬────┬────┬────┬──┬───┐
Addr│0x1200 │0x1204 │0x1208 │0x120b │0x1210 │... │0x1300│
├────┼────┼────┼────┼────┼──┼───┤
Var │ A[0] │ A[1] │ A[2] │ A[3] │ A[4] │... │ P │
├────┼────┼────┼────┼────┼──┼───┤
內容│ 5 │ 4 │ 3 │ 2 │ 1 │... │ ??? │
└────┴────┴────┴────┴────┴──┼───┤
幾種特性先說明清楚 < 不保證完全符合標準,歡迎補充 >
由於是一套記憶體的觀念,所以前面幾點很廢話,指標的部份前面會打 *。
(1) 習慣上 Address 編號是 16 進位,且是 8 碼,如 0x90001010 ,
考慮到可視範圍,所以縮成 4 碼。
(2) 一個 Address byte 假設可以存 8bits,
也就是在 0x8f00 這位置上它可能存的是
Address 0x8f00
內容 001100100
下面的 00110100 是二進位,如果以「無號數解釋這個 byte」,
實際上就等於是 00110100(2) = 32 + 16 + 4 = 52(10)
(3) 現在看標記的 0x1200, A[0],
假設 int 是佔 4 bytes (也假設 CHAR_BITS = 8),
由於 A[0] 本質上存的也是 int ,
故從 0x1200~0x1203 都是 A[0] 的資料 (所以有 32 個 0/1)
(4) A[0], A[1], ... , A[4] 本身的 Address 並沒實際太大意義,
這個 compiler 會幫我們規劃好,可以保證的是,這五個元素的
位址是連續的,一個 int 吃 4 bytes,所以 int A[5] 共吃了
5 * sizeof(int) = 5 * 4 = 20 bytes。
* (5) 再來是 P 了。所有的指標,存的都是「Addr」,注意到我在上面說了,
Addr 本身也只是一串數字,簡化問題,在 32bits CPU 底下,
表達一個位址(Address) 就是用 32bits 之無號數去表示,
所以上面的 P ,是從 0x1300~0x1303 都是存 P ,它存的是,
記憶體裡面其他的位址值。
註 : 這部份深入會講到 32/64 bits CPU 對 指標/整數 之配置模型,
在 64bits CPU 底下,指標可能以 32bits 去存,
也可能以 64bits 去存,這部份略過不深入。
* (6) 同樣的,我們也不在意 compiler 給 P 實際上的位址是在哪裡,
因我們通常較在意的是,「P 存的是誰的位址」。
接下來可以講操作了。
(A) P = A
將 陣列 A 的「第一個元素位址」,存入指標,
第一個元素位址值是 A[0],位址值是 0x1200 ,
所以存入 0x1200 ,但別忘了剛說過,
實際上 address 是給 32 bits 去存。
┌────────────────────────────┐
│ ↓
┌────┬────┬────┬────┬────┬──┬───┐
Addr│0x1200 │0x1204 │0x1208 │0x120b │0x1210 │... │0x1300│
├────┼────┼────┼────┼────┼──┼───┤
Var │ A[0] │ A[1] │ A[2] │ A[3] │ A[4] │... │ P │
├────┼────┼────┼────┼────┼──┼───┤
內容│ 5 │ 4 │ 3 │ 2 │ 1 │... │0x1200│
└────┴────┴────┴────┴────┴──┼───┤
所以這動作也相當於下面這行
P = &A[0]; // P = A, 將 A 陣列第一個元素位址給 P
(B) printf("%d", *P);
這動作主要拆二個部份去看,
(B.1) 先去看 P 裡面存的位址是多少 : 0x1200
(B.2) 將那位址的內容,拉出來 : 因為 0x1200 裡面放的值是 5,所以輸出為 5。
所以實際印出的是 A[0] 的值 ( 剛剛已把 A 的位址給 P , 所以造成這種結果 )
┌────────────────────────────┐
↓ │
┌────┬────┬────┬────┬────┬──┬───┐
Addr│0x1200 │0x1204 │0x1208 │0x120b │0x1210 │... │0x1300│
├────┼────┼────┼────┼────┼──┼───┤
Var │ A[0] │ A[1] │ A[2] │ A[3] │ A[4] │... │ P │
├────┼────┼────┼────┼────┼──┼───┤
內容│ 5 │ 4 │ 3 │ 2 │ 1 │... │0x1200│
└────┴────┴────┴────┴────┴──┼───┤
上面 * 二步驟的行為,稱為 dereference (中文是叫提取吧?)。
(C) printf("%d", *P++);
這又要拆二步驟,這裡的 ++ 是寫在後面,屬「後置」,所以是
「先 dereference ,再將指標加 1(這動作等一下會講)」,
deference 動作剛剛解過了,所以會先印出 0x1200 裡面的東西,也就是 A[0]。
「將指標加 1」這動作有趣些,想一下剛剛 P 的宣告是什麼
int *P;
P 是一個「指向 整數 的指標」,所以 P+1 時,移動是 向後移動一個整數大小,
又知道 int 是 4bytes( 剛剛假設過) ,
所以移動是從 0x1200 移動一個 int,變成0x1204。
┌───────────────────────┐
↓ │
┌────┬────┬────┬────┬────┬──┬───┐
Addr│0x1200 │ 0x1204 │0x1208 │0x120b │0x1210 │... │0x1300│
├────┼────┼────┼────┼────┼──┼───┤
Var │ A[0] │ A[1] │ A[2] │ A[3] │ A[4] │... │ P │
├────┼────┼────┼────┼────┼──┼───┤
內容│ 5 │ 4 │ 3 │ 2 │ 1 │... │0x1204│
└────┴────┴────┴────┴────┴──┼───┤
那如果是 P-1 呢?沒錯,是向前移動一個整數大小。
注意到我曾非常強調,
P 是一個「指向 整數 的指標」,所以 P+1 時,移動是 向後移動一個整數大小。
這點非常重要。
(D) 其他
其他的動作都可照推,重點只在於 dereference 和 移動順序而已
(D.1) *++P : 先將 P+1 , 再做 dereference。
P = 0x1208 , 輸出 3
(D.2) ++*P : 先將 P 做 dereference 後,將該位址 +1 再輸出
P = 0x1208 , 將 0x1208 內容 (即 A[2]) 加 1 後輸出,
所以 A[2] = 3+1 = 4 , 輸出 4。
(D.3) (*P)++
有先用括號了,所以先將 P dereference 後 ,得到 0x1208,
由於 ++ 是後置,所以先輸出 0x1208(即 A[2]) 內容 = 4,
再將 0x1208 內容 +1 ,執行完時 A[2] = 5
重點只有二個
(1) ++、-- 是對「指標」,還是數值?
(2) 要先「取值」,還是要先「移動指標」
弄清楚就不是問題了,
下面的 D 部份沒再一個一個畫,原因是認為接下來的東西讓你畫會更有感覺,
有興趣的話可把這例題最後的記憶體配置圖畫出來。
這裡弄清楚的話,學到副函式什麼 call by value、call by pointer 也不再是難事了。
--
「自從我學了 C# , 人都變聰明 , 考試都考一百分」
「自從我學了 VB , 皮膚都變好 , 人也變漂亮了 」
「自從我學了 Java , 明顯變壯 , 個子也變高了 」
「自從我學了 C++ , 內分泌失調 , 頭都禿了... 」
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 113.196.160.141
推
07/21 18:02, , 1F
07/21 18:02, 1F
推
07/21 18:25, , 2F
07/21 18:25, 2F
推
07/21 19:17, , 3F
07/21 19:17, 3F
推
07/21 20:06, , 4F
07/21 20:06, 4F
推
07/21 22:14, , 5F
07/21 22:14, 5F
推
07/21 22:24, , 6F
07/21 22:24, 6F
推
07/22 00:17, , 7F
07/22 00:17, 7F
推
07/22 00:25, , 8F
07/22 00:25, 8F
推
07/22 00:26, , 9F
07/22 00:26, 9F
推
07/22 15:17, , 10F
07/22 15:17, 10F
推
07/22 16:05, , 11F
07/22 16:05, 11F
→
07/22 16:05, , 12F
07/22 16:05, 12F
→
07/22 16:06, , 13F
07/22 16:06, 13F
→
07/22 16:06, , 14F
07/22 16:06, 14F
→
07/22 16:07, , 15F
07/22 16:07, 15F
→
07/22 16:07, , 16F
07/22 16:07, 16F
推
07/23 14:10, , 17F
07/23 14:10, 17F
討論串 (同標題文章)
C_and_CPP 近期熱門文章
PTT數位生活區 即時熱門文章