Re: [問題] 指標問題

看板C_and_CPP (C/C++)作者 (閉上眼的魚)時間11年前 (2012/07/21 16:23), 編輯推噓12(1205)
留言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 > ┌────┬────┬────┬────┬────┬──┬───┐ Addr0x12000x12040x12080x120b0x1210 │... │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 去存。 ┌────────────────────────────┐ │ ↓ ┌────┬────┬────┬────┬────┬──┬───┐ Addr0x12000x1204 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 , 所以造成這種結果 ) ┌────────────────────────────┐ ↓ │ ┌────┬────┬────┬────┬────┬──┬───┐ Addr0x12000x1204 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。 ┌───────────────────────┐ ↓ │ ┌────┬────┬────┬────┬────┬──┬───┐ Addr0x1200 0x12040x1208 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
專業推!THX!!!
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
始終覺得P++該理解為,先遞增,再取遞增前的值
07/22 15:17, 10F

07/22 16:05, , 11F
解釋的很棒,不過我總覺得這種圾垃code不應該寫出來
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
真的是垃圾code...覺得這種考題超沒營養的...
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
文章代碼(AID): #1G2cRmDn (C_and_CPP)
討論串 (同標題文章)
本文引述了以下文章的的內容:
0
6
完整討論串 (本文為第 12 之 13 篇):
1
1
4
13
5
21
13
58
5
39
1
28
1
9
文章代碼(AID): #1G2cRmDn (C_and_CPP)