看板 C_and_CPP 關於我們 聯絡資訊
※ 引述《netsky (芒果冰)》之銘言: 恕刪。 : printf("%d",c); 我覺得這行有問題,應該是做 *c 才有問的價值。 為引述此問題,我想先跳個 tone , 看另一個程式碼做為引述 , 沒興趣的話直接拉到 貮、 原問題 去看解答說明就好。 聲明的是 , 下面程式碼目前在少數場合還是看得到 , 都還算是符合標準。 ---- 壹、 pointer hack unsigned i, x=0x12345678; char *p=(char*)&x; for(i=0; i!=sizeof(x); ++i) printf("address(%08x) : %02hhx", &p[i], p[i]); 在環境為 little endian , 執行出來的結果如下 address(0012ff54) : 78 address(0012ff55) : 56 address(0012ff56) : 34 address(0012ff57) : 12 在記憶體裡面長這樣 x頭 Var. x0 x1 x2 x3 ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐ Addr │0x54│0x55│0x56│0x57│ │ │ │ │ │ │ │ ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐ Value │0x78│0x56│0x34│0x12│ │ │ │ │ │ │ │ └──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴ 然而在解釋 unsigned x 的時候,它是從 x0 開始,往後抓了 4 bytes 到 x3, 且注意到 , 它的順序,一開始高位元部份,是存在較底位部份, for 4bytes, (1) 抓 x0, 放在最低1bytes , 0x ?? ?? ?? 78 (2) 抓 x1, 放在次低1bytes , 0x ?? ?? 56 78 (3) 抓 x2, 放在次高1bytes , 0x ?? 34 56 78 (4) 抓 x3, 放在最高1bytes , 0x 12 34 56 78 這是 little endian 特性。 於是才會顯示出 0x12345678。 意思是如果今天再多弄一個 unsigned short* 出來的話, unsigned i, x=0x12345678; unsigned short *p=(unsigned short *)&x; for(i=0; i!=sizeof(x)/sizeof(unsigned short); ++i) printf("address(%08x) : %04hx\n", &p[i], p[i]); 在 p[0] 時,會從開頭位置 x0,抓取 unsigned short (2bytes) 大小之資料, 從上面原則,於是從 x0 開始抓 2 bytes, 得到 0x5678, 而 p[1] 時,會從開頭位置 x2,抓取 unsigned short (2bytes) 大小之資料, 為何是從 x2 開始?簡單的說,因為 p 指向 unsigned short 2bytes, 所以 p[1] = address start of p + 1 * sizeof(unsigned short); 在實際上,記憶體之位置長這樣 |<-0*short->|<-1*short->| p[0] p[1] Var. x0 x1 x2 x3 ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐ Addr │0x54│0x55│0x56│0x57│ │ │ │ │ │ │ │ ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐ Value │0x78│0x56│0x34│0x12│ │ │ │ │ │ │ │ └──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴ 注意到 p[0] 是對到 x0, p[1] 是對到 x2 , 故 p[1] 取出為 0x1234。 比較有得講的是 p[1]。事實上,compiler 在計算 p[0]、p[1] 位置時, 有一個重要的參考依據,就是 pointer 指向的資料型態, 假設為 Type* pointer ,而 pointer[i] ,取到的位置是 pointer[i] 位置 = pointer 開頭位置 + Type大小 * i, 因 pointer 現在是 unsigned short, 於是 pointer[1] 位置 = 0x54 + sizeof(unsigned short)*1 = 0x56 這裡和 array 計算位置方式沒什麼兩樣,但重點是「翻譯」的地方, 在 little endian 的部份,data type 使用 byte 數確定後,是從後面翻回來的。 ---- 貮、 原問題 再把上面最重要的結論說一次 在 little endian 的部份,data type 使用 byte 數確定後,是從後面翻回來的。 回到原問題,將 code 改如下所述 char a[6]={0,1,2,3,4,5}; char *b; int *c; b=a; b+=2; c=(int*)b; printf("*c = %d (%08x)\n", *c, *c); 現在一行一行看 ※ char a[6] = {0,1,2,3,4,5}; Var. a0(a) a1 a2 a3 a4 a5 ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐ Addr │0x58│0x59│0x5a│0x5b│0x5c│0x5d│ │ │ │ │ │ ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐ Value │0x00│0x01│0x02│0x03│0x04│0x05│ │ │ │ │ │ └──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴ ※ b=a; b Var. a0(a) a1 a2 a3 a4 a5 ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐ Addr │0x58│0x59│0x5a│0x5b│0x5c│0x5d│ │ │ │ │ │ ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐ Value │0x00│0x01│0x02│0x03│0x04│0x05│ │ │ │ │ │ └──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴ ※ b+=2; b Var. a0(a) a1 a2 a3 a4 a5 ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐ Addr │0x58│0x59│0x5a│0x5b│0x5c│0x5d│ │ │ │ │ │ ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐ Value │0x00│0x01│0x02│0x03│0x04│0x05│ │ │ │ │ │ └──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴ ※ c=(int*)b; b, c Var. a0(a) a1 a2 a3 a4 a5 ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐ Addr │0x58│0x59│0x5a│0x5b│0x5c│0x5d│ │ │ │ │ │ ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐ Value │0x00│0x01│0x02│0x03│0x04│0x05│ │ │ │ │ │ └──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴ 接著確定 c 的資料範圍,4 bytes |<------- c ---------->| Var. a0(a) a1 a2 a3 a4 a5 ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐ Addr │0x58│0x59│0x5a│0x5b│0x5c│0x5d│ │ │ │ │ │ ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐ Value │0x00│0x01│0x02│0x03│0x04│0x05│ │ │ │ │ │ └──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴ 最後再從後面翻回來, 得到 *c = 0x05040302,這就是答案。 而 address of (*c) , 本身就和 a[2] 一樣,沒什麼好討論, 故認為原 po 應是筆誤寫錯。 ---- 有興趣看一下進階題 unsigned char a[]={ 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f }; unsigned char *b=a+3; unsigned int *c=(unsigned int*)b; c+=2; printf("*c=%08x\n", *c); 上面兩點看懂的話,這個也沒問題了,更難一點的考題是, 用 float* 去接,最後問 %.15e , *c 值是多少, 這應該會死更多人。 以上若有誤或其他意見,請指正 。 -- No matter how gifted you are, alone, can not change the world. -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 180.177.78.41
loveme00835:好人跳出來了, 不過文中一直看到littleshan大的ID... 10/13 01:26
james732:真的跳出來了,非推不可! 10/13 01:32
james732:float的話就要懂IEEE 754的格式了... 10/13 02:23
james732:這個沒查書我還真寫不出來 orz 10/13 02:23
cutecpu:unsigned int *c = (unsigned int *)b; 10/13 02:27
tropical72:謝謝 cutecpu 指正。 10/13 02:28
※ 編輯: tropical72 來自: 180.177.78.41 (10/13 02:45)
netsky:謝 10/13 10:47
angleevil:我發現james732最近都在偷懶當好人. 10/13 11:46
james732:最近花在看電影的時間比寫程式還長XD 10/13 11:56
angleevil:樓上最近花在認識女生的時間很長.這是好現象 10/13 11:58
james732:都是一個人看,哭哭 10/13 11:59
wemee:長知識了 我現在才知道C語言是用little endian 10/13 20:20
wemee:再看原PO問題時 我一直用big endian一直覺得答案怪怪的 10/13 20:21
priv:小時候是因為用pctools才曉得little endian這種碗糕XD 10/13 20:21
littleshan:C 並沒有規定 little endian 或 big endian 10/13 20:48
tropical72:big/little endian 是 depends on machine. 10/13 21:12
tropical72:而 c language 有些問題也是 depends on machine. 10/13 21:12
tropical72:像這題便是假定該 machine 為 little endian. 10/13 21:13
james732:推樓上解說XD 10/13 21:13
loveme00835:又一直看到little大的id, 我眼花了... 10/13 21:14
danny8376:不過目前主流的pc cpu(x86)都是little endian 10/13 23:15
danny8376:當然如果不是x86的cpu就另當別論了 10/13 23:16