Re: [問題] 指標和雙重指標考題

看板C_and_CPP作者 (閉上眼的魚)時間11年前 (2012/09/14 22:32), 編輯推噓4(404)
留言8則, 7人參與, 最新討論串3/3 (看更多)
以下是昨天晚上作夢夢到的,夢到某位學生對這題有興趣, < 小弟講指標的講義有講到類似的東西 > 所以大致講一下這是什麼情形。 這題寫法我不知到底合不合標準(是有幾個不完成合標準沒錯,只是一般人這麼用), 所以有幾個 基本假設 先講清楚。 (1) CHAR_BITS = 8 (2) sizeof(char) = 1 < 這不是基本假設了, 是規定 > (3) sizeof(int) = 4 (4) machine memory platfrom is "little endia" (5) Project using Release Mode, not Debug Mode.<depends on compiler> (6) 二補數系統 若這是練習題的話,上面五個假設應是基本要有的。 最後如果你「運氣不好」跑得出結果,請別認為是好事, 該想一下有沒有辦法讓編譯器處理這種壞習慣, 這題目只存在於面試考觀念而已。 : 我最近寫題目 寫到一題 : 不知道答案是什麼 自己用 VC跑 竟然跑不出來 你原本的程式碼有一處是筆誤的,在 VC 下不是「跑不出來」, 而是「無法編譯」、「編譯錯誤」,下次直接把錯誤敘述清楚。 ※ 引述《SNSDpk5566 (5566 超強)》之銘言: : char d[3] = {100,200,300}; : int *p = &d; : int **pn = &p; 假設前三行記憶體內容如下,記憶體編碼應用 8 碼較合適,唯說明以四碼。 ┌────┬────┬────┬────┬──┬───┬───┐ Addr0x1200 │0x1201 │0x1202 │0x1203 │... │0x1300│0x1304│ ├────┼────┼────┼────┼──┼───┼───┤ Var │ d[0] │ d[1] │ d[2] │ ???? │... │*p │**pn │ ├────┼────┼────┼────┼──┼───┼───┤ 內容│ 100 │ 200 300 │ ???? │... │0x12000x1300│ └────┴────┴────┴────┴──┴───┴───┘ hex 0x64 0xc8 0x2c 0xbf < 這個 byte 數值是假設的 > 上面張圖可以講很久了, (1) d[2] = 300, 一個 signed-byte 最多只能存 -128~+127,這已經溢位了, 實際上存多少?不知道,但一種「可能」的數值是 (300-256 = 44), 但無論如何,它不會把溢位多出的部份存到 0x1203。 (2) d[1] = 200, 這也溢位了, 範圍不在 -128~+127,實際上「可能」的數值是 (200-256 = -56)。 (3) 不論 d[1], d[2] 問題,我們都以 16 進位示之,原因是存在 memory 都是二進位, 無關正負號。 (4) 當寫下 *p = &d , 或 *p=(int*)d 的時候,由於 *p 本身是「整數」指標,所以 p 指向記憶體範圍是從 0x1200~0x1203,也就是說,它會用到第 4 個 unknow byte, 上面假設,在記憶體裡面是 0xbf,那問題來了, printf("%08x\n", *p); 答案是多少? 它不是 0x 64 c8 2c bf, 由於是 little-endian 的關係,所以 *p 會被解讀成 0x bf 2c c8 64 : *p-=1; 所以 *p-=1, 會變成 0xbf 2c c8 63,以這例子而言, 實際上就是將 d[0] 扣 1 ,記憶體如下 ┌────┬────┬────┬────┬──┬───┬───┐ Addr0x1200 │0x1201 │0x1202 │0x1203 │... │0x1300│0x1304│ ├────┼────┼────┼────┼──┼───┼───┤ Var │ d[0] │ d[1] │ d[2] │ ???? │... │*p │**pn │ ├────┼────┼────┼────┼──┼───┼───┤ 內容│ 99 │ 200300 │ ???? │... │0x12000x1300│ └────┴────┴────┴────┴──┴───┴───┘ hex 0x63 0xc8 0x2c 0xbf 上述可知,其實完全動不到未知的第 4 個 byte, (Address 0x1203), 所以印出 printf("%d", *p); 的時候會是未知的, 關鍵在於 0x1203 多少不確定, 但前 3 bytes 變化是可以「粗略估計」的,就像上面流程所述。 故這時候如果要問我 *p 是多少,我的回答是 0x??2cc863 至於到目前為止,compiler 真的可以那麼順利讓它轉過去嗎? oh, 不一定。已知一些 compiler 在 debug 時會用 hex speaker, 去檢查使用者寫的程式碼有沒有用到非法空間,如果有做這層檢查的話, 上述在做 int *p = (int*)&d ; 轉型的時候就會失敗了。 然而開啟 release mode (-o2) 時,這個不會再自己去檢查是不是寫到非法空間, 所以 "有機會" 可以轉型成功,反正最後 "以這個例子" ,並不會影響到第 4 個 byte, 會影響到第4個 byte 的情況有兩種: <a> 電腦為 little-endian (一般假設),且 d[0]~d[2] 全都是 0, 所以減法造成了減法上的借位,這時才動用了 0x1203 之內容。 <b> 電腦為 big-endian 時就一定會動到 0x1203 這個 value。至於 Mixed-Endian、 Middle-Endian 小弟沒研究,就不多贅述了。 : p = &d[1]; 這行改變了 p 的內容值,記憶體內容變如下。 ┌────┬────┬────┬────┬──┬───┬───┐ Addr│0x1200 │0x1201 │0x1202 │0x1203 │... │0x1300│0x1304│ ├────┼────┼────┼────┼──┼───┼───┤ Var │ d[0] │ d[1] │ d[2] │ ???? │... │*p │**pn │ ├────┼────┼────┼────┼──┼───┼───┤ 內容│ 99 │ 200 300 │ ???? │... │0x12010x1300│ └────┴────┴────┴────┴──┴───┴───┘ hex 0x63 0xc8 0x2c 0xbf 0xcc : **p+=1; **pn+=1; 這行我認為是筆誤寫錯,應該是要寫 **pn+=1 才對, 不然會造成編譯錯誤。分解步驟: pn --> 取到 pn 的內容,0x1300。 *pn --> 取到 (0x1300) 的內容,0x1201。 **pn --> 取到 (0x1201) 的內容,。 至此,**pn 得到是一個 int, 所以從 0x1201 往後提出 4 bytes 出來, 也就是 0x1201~0x1204 的值,以 little-endian 去解讀, 無奈 0x1204 我們也不知道,只好暫時先假設成 0xcc 了, little-endian 解讀出來後就變成 0xccbf2cc8 ,最後一步.. **pn+=1 --> 將 (0x1201) 的內容 0xccbf2cc8 取出來,做+1動作得 0xccbf2cc9, 再以 little-endian 方式放回到 (0x1201) 裡面去。 ┌────┬────┬────┬────┬──┬───┬───┐ Addr│0x1200 │0x1201 │0x1202 │0x1203 │... │0x1300│0x1304│ ├────┼────┼────┼────┼──┼───┼───┤ Var │ d[0] │ d[1] │ d[2] │ ???? │... │*p │**pn │ ├────┼────┼────┼────┼──┼───┼───┤ 內容│ 99 │ 201300 │ ???? │... │0x12010x1300│ └────┴────┴────┴────┴──┴───┴───┘ hex 0x63 0xc9 0x2c 0xbf 0xcc 一樣,這個時候如果問我 *p , **pn 是多少的話, 我的回答是 0x????2cc9,變前 2 bytes 不確定。 : p--; : 求 *p , **p, d[0] d[1] 最後的解讀就看 printf 輸出格式怎麼處理,就算知道記憶體長怎樣, 輸出格式亂搞還是會得到不同結果,故認為題目就只有寫 求 *p , **p, d[0] d[1] 個人認為是不夠的,printf 格式寫出來才有繼續討論的必要。 ---------------------------- 以上敘述若有誤、或不盡理想的部份,歡迎指正,感謝收聽。 -- 「自從我學了 C# , 人都變聰明 , 考試都考一百分」 「自從我學了 VB , 皮膚都變好 , 人也變漂亮了 」 「自從我學了 Java , 明顯變壯 , 個子也變高了 」 「自從我學了 C++ , 內分泌失調 , 頭都禿了... 」 -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 180.177.76.161 ※ 編輯: EdisonX 來自: 180.177.76.161 (09/14 22:43)

09/14 22:39, , 1F
09/14 22:39, 1F

09/14 22:56, , 2F
怎麼常常都會有這種問題出現...實際使用會用到嗎?
09/14 22:56, 2F

09/14 22:57, , 3F
我還是喜歡用cout,雖然輸出格式化比較麻煩
09/14 22:57, 3F

09/14 23:01, , 4F
早期面試要考指標強不強、little 概念時會拿這個來考。
09/14 23:01, 4F

09/14 23:04, , 5F
0.0 是喔~ 能請問現在的面試是常考什麼啊~
09/14 23:04, 5F

09/14 23:09, , 6F
考container
09/14 23:09, 6F

09/14 23:40, , 7F
看懂了推
09/14 23:40, 7F

09/15 13:20, , 8F
09/15 13:20, 8F
文章代碼(AID): #1GKp_dPx (C_and_CPP)
文章代碼(AID): #1GKp_dPx (C_and_CPP)