C 語言預設會幫我們作一些型別轉換的動作, 一般的型別轉換不只會更換資料型別, 更會使資料內容有所變動, 例如:
float fVar = 1.2;
int32_t iVar;
iVar = fVar; // iVar 值為整數 1
但是萬一我們不想要這個預設的換轉, 只是想要把 fVar 的數值 (以 float 格式) 存放在變數 iVar 所佔用的記憶體中, 而又不想動用 union 設定呢? (因為使用 union 程式或多或少都需要修改, 萬一 compiler 不支援 Unnamed Structure and Union Fields (或者 Anonymous unions, C11 的功能), 則需要更動的幅度就會更大.)
答案是: (取值)<-(指標轉型)<-(取址)
// 寫在等號右手邊
iVar = *(int32_t*)&fVar;
// 或者, 寫在等號左手邊
*(float*)&iVar = fVar;
或者是反過來, 想要把 iVar 的數值 (以int32_t 格式) 存放在變數 fVar 所佔用的記憶體中.
// 寫在等號右手邊
fVar = *(float*)&iVar;
// 或者, 寫在等號左手邊
*(int32_t*)&fVar = iVar;
(取值)<-(指標轉型)<-(取址)
看似很複雜, 其實只是告訴 compiler: 我要用 (新指定的方式) 存取原本變數所在的位址, 所以看似很複雜的 C 程式碼, 但是編譯出來的目的碼並不會膨脹, 也沒有轉來轉去的問題.
Compiler 不支援 Anonymous unions 時改用 union 程式無論是使用整數型態存取, 或者使用浮點數型態存取的部份都需要修改, 寫法如下:
float fVar = 1.2;
union {
int32_t i;
float f;
} iVar;
iVar.f = fVar; // 浮點數型態的要改
iVar.i = 123; // 整數型態的也要改
Compiler 有支援 Anonymous unions 時, 只需要更動使用浮點數型態存取的部份, 寫法如下:
float fVar = 1.2;
union {
int32_t iVar;
float iVarf;
};
iVarf = fVar; // 浮點數型態的要改
iVar = 123; // 整數型態的不用改
應用實例: 平方根倒數快速演算法
此即讀者 ">w0" 留言所說的那個演算法:
float Q_rsqrt( float number )
{
long i;
float x2, y;
const float threehalfs = 1.5F;
x2 = number * 0.5F;
y = number;
i = * ( long * ) &y; // evil floating point bit level hacking
i = 0x5f3759df - ( i >> 1 ); // what the fuck?
y = * ( float * ) &i;
y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
return y;
}
第 9 行即是最前面提到的 (取值).
這裡我不多解釋這個函數, 有興趣的讀者可以參閱:
相關文章
- C 語言:型別轉換 (Type Casting) 技巧之一
- C 語言:型別轉換 (Type Casting) 技巧之二 (不同型別資料之轉換)
- C 語言:型別轉換 (Type Casting) 技巧之三 指定位址 (資料結構, 函數)
- C 語言:型別轉換 (Type Casting) 技巧之四 (結構運算子 "->" 和 "." 互換)
文章標籤
全站熱搜
留言列表