本專案實作一個在終端機中運行的下雪動畫,具備以下特色:

透過條件編譯處理平台差異:
#if defined(__GNUC__)
// Linux 實作
#include <unistd.h>
#define sleep_ms(m) usleep(m * 1000)
static void __curup(int height) {
while (++i < height) printf("\033[1A"); // ANSI 轉義碼移動游標
}
#else
// Windows 實作
#include <Windows.h>
#define sleep_ms(m) Sleep(m)
static void __curup(int height) {
COORD cr = {0,0};
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), cr);
}
#endif
使用一維陣列模擬二維像素矩陣,結構體一次性配置記憶體減少碎片:
struct screen {
int frate; // 幀週期(ms)
int width; // 螢幕寬度
int height; // 螢幕高度
char *pix; // 像素緩衝區:0=無雪花, 1+=雪花數量
};
// 一次 malloc 配置結構體 + 像素緩衝區
scr = malloc(sizeof(struct screen) + sizeof(char)*width*height);
scr->pix = ((char *)scr) + sizeof(struct screen);
雙重指標銷毀函式避免野指標:
void screen_destory(struct screen** pscr) {
if (NULL == pscr || NULL == *pscr) return;
free(*pscr);
*pscr = NULL; // 關鍵:釋放後立即置空
}
#define _INT_SHEAD (1<<2) // 每 4 格一個雪花
static void __snow_head(char* snow, int len) {
memset(snow, 0, len);
int r = 0;
for (;;) {
int t = rand() & (_INT_SHEAD - 1); // 位運算取模,等價 rand() % 4
if (r + t >= len) break;
snow[r + t] = 1;
r += _INT_SHEAD;
}
}
#define _INT_SWING 3 // 擺動範圍:-1, 0, +1
static void __snow_next(struct screen* scr, int idx) {
int width = scr->width;
char* psnow = scr->pix + width*(idx - 1); // 上一行
char* snow = psnow + width; // 當前行
memset(snow, 0, width);
for (int i = 0; i < width; ++i) {
for (int t = psnow[i]; t > 0; --t) { // 處理疊加雪花
int j = i + rand() % _INT_SWING - 1; // -1, 0, +1
j = (j < 0) ? width - 1 : (j >= width ? 0 : j); // 環狀邊界
++snow[j];
}
}
}
int screen_draw_snow(struct screen* scr) {
static int __speed = 0;
if (++__speed != _INT_VSNOW) return 1; // 速率控制
__speed = 0;
for (int idx = scr->height - 1; idx > 0; --idx)
__snow_next(scr, idx);
__snow_head(scr->pix, scr->width);
return 0;
}
void __flash_snow_buffer(struct screen* scr, char* buf) {
for (;; sleep_ms(scr->frate)) {
if (screen_draw_snow(scr)) continue; // 未到繪製時機
// 將像素緩衝區轉為可列印字元
int rt = 0;
for (int i = 0; i < scr->height; ++i) {
for (int j = 0; j < scr->width; ++j)
buf[rt++] = scr->pix[i*scr->width + j] ? '*' : ' ';
buf[rt++] = '\n';
}
buf[rt - 1] = '\0';
puts(buf);
__curup(scr->height); // 游標回頂端,實現動畫覆蓋
}
}
完整原始碼已整合於文章末尾(含跨平台巨集、資料結構、演算法實作與主程式)。
新增主控台專案,貼上程式碼按 F5 執行。

gcc -g -Wall snow.c -o snow.out
./snow.out

編譯提示:Linux 需連結
-lm數學庫(若使用rand()通常不需要),Windows 須確保專案設定為主控台應用程式。
文中穿插探討 NULL、0、'\0'、"0"、false 的異同,以及 for(;;) 與 while(true) 的組合語言等價性——這正是 C 語言「簡單語法,深邃語義」的縮影,也是成為資深開發者的必修課。