2007年11月28日 星期三

C語言中 union 用法

今天上C語言課的時候,老師突然問我怎麼把 float 在記憶體裡面的值印出來,當時我第一個想到的方法是先把變數存成 float,然後用 memcpy 複製到 char[4] 的陣列裡,然後從第四個開始 (因為x86是 Little Endian 排列) 印出來,就可以看到 float 在記憶體裡面存放的方式 (IEEE 754),參考程式如下:
int i;
float num=-1;
unsigned chat n[4];
memcpy(n, &num, sizeof(float);
for(i=3;i>=0;i--)
  printf("%x ", n[i]);

除了這方法外,老師另外告訴我一個 union (同構) 的方式,他的原理就是把同一塊記憶體,以不同型態的方式去看待他,有點像 OO 裡的 polymorphism。
老師說當初發明這個的用途是因為以前記憶體空間比較不足,因此許多變數共用同一塊記憶體比較節省,不過還有一個功能就是實現類似 OO 的 polymorphism,這點我還不是很清楚,以下就第一點先做範例說明:

int i;
union {
  float num;
  unsigned char n[4];
  } q;
//q.num=4;
q.num=-1;
for(i=3;i>=0;i--)
  printf("%x ", q.n[i]);

可以得到一樣的結果
IEEE 754:
-1 = -1.0*2^+127 = 1,0111 1111,0000...23個
所以結果: 1,011=B, 1111=F, 1000=8, 0000....=0,00,00 ==>BF800000

10 則留言:

jswirl 提到...

q.n?

張晁睿 提到...

謝謝你的提醒,已經修正了

匿名 提到...

q.num=4;??

張晁睿 提到...

修正了~謝謝提醒

cl4rk 提到...

float x;

x=-1;
int i;
for (i=3;i>=0;i--)
{
printf("%x",((unsigned char*)&x)[i]);
}

這樣更快XD

張晁睿 提到...

謝謝提供更好方法!!

育維的網誌 提到...

memcpy(n, &num, sizeof(float));

匿名 提到...

#include

union data {
int vi;
double vd;
};

int main(void)
{
union data a;

a.vi = 11,a.vd = 22.0;
printf("a = (%d, %f)\n", a.vi, a.vd);
printf("a = (%d)\n",sizeof(a));
a.vd = 22.0,a.vi = 11;
printf("a = (%d, %f)\n", a.vi, a.vd);
printf("a = (%d)\n",sizeof(a));
return 0;
}

不好意我遇到不懂地方

輪出1 0, 22
sizeof 8
輪出1 11,22
sizeof 8

整個a 是不是只佔了 一個8 而double 就佔了一個八
那int 要存到那裡去

第一個輪出可以理解
第二個輪出就不懂了

匿名 提到...

修改一下 ,倒數第8行
輪出2 11,22
sizeof 8

張晁睿 提到...

回覆 匿名:
int 為 4 bytes, double 為 8 bytes
由於 little-endian 排列方式, int 會放在 double 的 byte 3~0 的位置
再來參考 ieee754 定義, https://en.wikipedia.org/wiki/Double-precision_floating-point_format
第一次的結果, 先放 int 再放 double, 原本 int 的位置會被覆蓋成 0
第二次的結果, 先放 double 再放 int, int造成的內容在小數點後40幾位數以後
而預設 %f 只會顯示到小數點後第6位, 所以還是看到 22.0, int 的部份也可正常顯示 11

Related Posts Plugin for WordPress, Blogger...