C99 引入宏可變參數 ... 與 __VA_ARGS__,GCC 另支持 args... 寫法。配合 ## 連接符可實現參數可選。
// 標準寫法 #define LOG(fmt, ...) fprintf(stdout, fmt, __VA_ARGS__) // GCC 擴展 #define LOG(fmt, args...) fprintf(stdout, fmt, args) // 允許省略可變參數(## 吞噬前導逗號) #define LOG(fmt, ...) fprintf(stdout, fmt, ##__VA_ARGS__)
#ifdef DEBUG
#define LOG(fmt, ...) fprintf(stdout, ">>>>>" fmt "<<<<", ##__VA_ARGS__)
#else
#define LOG(fmt, ...) // 空展開,零開銷
#endif
// 使用
LOG("init: %d\n", 42);
// 編譯:gcc -DDEBUG test.c → 輸出 ">>>>>init: 42<<<<"
// 不加 -DDEBUG → 無任何代碼生成
// Android.mk
LOCAL_LDLIBS := -llog
LOCAL_CFLAGS += -DDEBUG
// NDKTest.cpp
#include <android/log.h>
#define LOG_TAG "HelloNDK"
#ifdef DEBUG
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#else
#define LOGI(...) do{}while(0)
#define LOGD(...) do{}while(0)
#define LOGE(...) do{}while(0)
#endif
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
LOGI("JNI_OnLoad");
// ...
}
do{}while(0):空宏安全塊,防止 if (...) LOG(...); else ... 語法錯誤。##__VA_ARGS__:當可變參數為空時吞噬前導逗號,避免 fmt, 語法錯誤。-DDEBUG 控制日志編入,發布版零開銷。可變參數宏是構建類 printf 靈活日志系統的核心技術,廣泛用於嵌入式、移動端、高性能服務端。