現代編譯器(GCC、Clang、MSVC)會將特定標準庫函數識別為**內建函數**,在編譯期或優化期直接替換為機器指令或常數,無需函數調用開銷。
double a = log2(1.0); // 編譯期直接優化為 0.0
double b = log2(20.0); // 無法編譯期確定,保留運行時調用
原理:編譯器內建數學函數語義,參數為常數時執行常數傳播。
double fabs_opt(double x) { return fabs(x); }
// GCC -O2 可能優化為單條指令:vabs.sd (x86 AVX) / fabs (x87) / fabss (ARM NEON)
// 甚至無需包含 即可編譯通過(隱式內建聲明)。
(int &)a // C++ 參考轉換
*((int *)&a) // C 風格指標轉換 (取址 -> 強轉 -> 解引用)
// 兩者效果相同:將變數 a 的記憶體位址視為 int* 訪問(佔 4 位元組)。
嚴格別名規則警告:C/C++ 標準規定不同類型指標不得指向同一記憶體(`char*` 例外)。違反會導致未定義行為(優化器可能誤判無依賴而錯誤重排)。安全做法:使用 `memcpy` 或 `union` 進行類型雙關。
C99 標準化 `long long` (至少 64 位)。
| 平台/編譯器 | scanf/printf 格式 | 備註 |
|---|---|---|
| Linux (GCC/Clang, glibc) | `%lld` / `%lld` | 標準 C99 |
| Windows (MSVC 古老版本) | `%I64d` | Microsoft 特有格式 |
| Windows (MinGW / MSVC 現代) | `%lld` | 支援 C99 |
可移植寫法 (C99 `
#include
long long val;
scanf("%" SCNd64, &val);
printf("%" PRId64 "\n", val);
標準方法:使用 `union` 進行類型雙關(C 標準允許聯合體成員互訪)。
typedef union {
double value;
struct { uint32_t lsw; uint32_t msw; } parts; // 小端序:低字在前
} ieee_double_shape_type;
#define EXTRACT_WORDS(ix0, ix1, d) \
do { \
ieee_double_shape_type ew_u; \
ew_u.value = (d); \
(ix0) = ew_u.parts.msw; \
(ix1) = ew_u.parts.lsw; \
} while (0)
// 使用範例
double d = 3.14159;
uint32_t high, low;
EXTRACT_WORDS(high, low, d);
printf("高32位: 0x%08X, 低32位: 0x%08X\n", high, low);
位域佈局說明 (IEEE 754 binary64):
msw (高 32 位):符號位 (1) + 指數位 (11) + 尾數高 20 位。lsw (低 32 位):尾數低 32 位。標準 `printf("%.17g")` 足夠精確還原 `double` (53 位尾數 ≈ 15-17 位十進制)。
double d1 = 0.681638760023334166733241952779893935;
printf("%.17g\n", d1); // 標準庫實現通常正確
// 輸出: 0.68163876002333417 (最後一位四捨五入)
原文提及的 `dtoa` 非標準函數(可能源自 BSD `dtoa.c` 或特定庫),標準 C 使用 `printf` 族或 C++ `