作者tropical72 (藍影)
看板C_and_CPP
標題Re: [問題] 指標與字元轉換問題
時間Thu Oct 13 01:22:30 2011
※ 引述《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