電腦圖形學中,像素本質上是整數,常拆解為 RGBA 四個 unsigned char 分量。本文展示一個有趣的實驗:建立一個 1000×1000 的 float 陣列,數值從 0 遞增到 1000(步長 0.001),再以指針強制轉換將每個 float 的位元模式直接詮釋為 RGB 三通道,輸出 PPM 圖像。結果呈現出迷人的分形紋理,同時也有力回應了「指針強轉有限制、會出錯」的質疑。
float* 讀是浮點數,用 unsigned char* 讀就是三個字節。memcpy 或直接 fwrite(&float, 1, 3, fp) 即可完成「轉換」,無需逐像素運算。#include
#include
#include
#include
#define DIM 1000
FILE *fp;
void pixel_write(int x, int y) {
static unsigned char color[3];
float t = y + x * 0.001f; // 0 ~ 1000 等差數列
memcpy(color, &t, 3); // 取 float 低 3 字節作為 RGB
fwrite(color, 1, 3, fp);
}
int main() {
fp = fopen("image.ppm", "wb");
if (!fp) return -1;
fprintf(fp, "P6\n%d %d\n255\n", DIM, DIM); // PPM 標頭
for (int j = 0; j < DIM; ++j)
for (int i = 0; i < DIM; ++i)
pixel_write(i, j);
fclose(fp);
return 0;
}
PPM (Portable PixMap) 是極簡圖像格式:
P6 # 二進位 RGB
1000 1000 # 寬 高
255 # 最大色值
[RGB 二進位資料...]
可用 ImageMagick、GIMP 或自寫檢視器(文中提到的 Why 數學圖像生成工具)開啟。
class CPixelFloatConvertInt : public IPixelEquation {
public:
CPixelFloatConvertInt() { m_width = 1000; m_height = 1000; }
const char* GetName() const { return "Float Convert Int"; }
unsigned int CalculatePixel(unsigned int x, unsigned int y) {
float t = y + x * 0.001f;
unsigned int rst = *(unsigned int*)&t; // 強轉為 32-bit 整數
rst |= 0xff000000; // Alpha = 255
return rst; // ARGB 格式
}
};
memcpy 或 union 進行類型雙關;嚴格別名規則下直接 *(int*)&float 屬未定義行為,但 memcpy 完全合規。因為 IEEE 754 設計巧妙,正浮點數的位元模式按無符號整數排序,順序與數值順序一致。這意味著上述陣列若用 qsort 對 unsigned int 排序,結果等同於對 float 排序——這是著名的「浮點數無分支排序」技巧。
指針強制轉換不是「危險的黑魔法」,而是系統程式設計的基礎工具。理解記憶體佈局、遵守嚴格別名規則(用 memcpy/union),即可安全、高效地在不同類型視圖間切換。這個 20 行的小程式,既生成了唯美分形圖,又證明了:「想怎麼轉就怎麼轉,只要你懂位元在幹什麼。」