1. 程式人生 > >C語言可變引數在巨集定義中的應用

C語言可變引數在巨集定義中的應用

       在C語言的標準庫中,printf、scanf、sscanf、sprintf、sscanf這些標準庫的輸入輸出函式,引數都是可變的。在除錯程式時,我們可能希望定義一個引數可變的輸出函式來記錄日誌,那麼用可變引數的巨集是一個不錯的選擇。

C99中規定巨集也可以像函式一樣帶可變的引數,如:

#define LOG(format, ...) fprintf(stdout, format, __VA_ARGS__)
其中,...表示可變引數列表,__VA_ARGS__在預處理中,會被實際的引數集(實參列表)所替換。
同時gcc還支援帶可以變引數名的方式(注意:VC不支援):
#define LOG(format, args...) fprintf(stdout, format, args)
同樣,args在預處理過程中,會被實際的引數集所替換。其用法和上面的方式一樣,只是引數的符號有變。

需要注意的是,上述兩種方式的可變引數不能省略,儘管可以傳一個空引數進去。說到這裡,有必要提一下“##”連線符號的用法,“##”的作用是對token進行連線,上例中format,args,__VA_ARGS都可以看作是token,如果token為空,“##”則不進行連線,所以允許省略可變引數。對上述2個示例的改造:

#define LOG(format, ...) fprintf(stdout, format, ##__VA_ARGS__)
#define LOG(format, args...) fprintf(stdout, format, ##args)
即然引數可以省略,那麼用巨集定義一個開關,實現一個輸出日誌的函式就簡單了:
#ifdef DEBUG
#define LOG(format, ...) fprintf(stdout, ">>>>>" format "<<<<", ##__VA_ARGS__)
#else
#define LOG(format, ...)
#endif
在開發階段時,在編譯選項中加入一個DEBUG巨集,即會在程式中使用了LOG巨集的地方輸出日誌,否則只是呼叫了一個空的LOG巨集而已,不會有任何輸出。假設原始檔為test.c,gcc編譯時加上-DDEBUG(-D表示預定巨集,在預編譯的時候由編譯器定義)即在判斷DEBUG巨集時條件成立,從而達到輸出日誌的目的:
gcc -o test test.c -DDEBUG

示例:在Android的NDK開發中,列印log的巨集:

需要在android.mk檔案中載入日誌模組,並新增DEBUG選項:

LOCAL_LDLIBS 	:= -llog	#新增日誌模組
LOCAL_CFLAGS	+= -DDEBUG

NDKTest.cpp

#include <jni.h>
#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)
{
	JNIEnv *env;
	jclass cls;
	if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK)
	{
		LOGE("init javavm failed!");
		return JNI_ERR;
	}
	cls = env->FindClass("com/learn/ndk/SampleModel");
	if (cls == NULL)
	{
		LOGE("can't find com.learn.ndk.SampleModel.");
		return JNI_ERR;
	}
	class_com_learn_ndk_MainActivity = (jclass)env->NewWeakGlobalRef(cls);
	env->DeleteLocalRef(cls);
	return JNI_VERSION_1_4;
}