C++基礎(十二)一個巨集使用的坑

yedawei_1發表於2020-12-30

雖然C++一直在強調儘量少用巨集,但是巨集確實是個好東西,會使程式碼很簡潔,使人慾罷不能。

近期出現一個無法理解的異常,找了很長時間,結果發現是呼叫的別人的巨集導致的,差點吐血。

實際工作中,日誌是定位程式錯誤的常用手段之一。不同等級的日誌輸出,代表不同的錯誤級別,有一些僅僅是警告,對程式執行無任何影響,則只需要列印出來即可;但有一些是嚴重的錯誤,會對後續程式的執行影響很大,除了列印資訊外,最好還能直接斷言,提示開發人員。

程式實際執行時,一般錯誤日誌都是寫到日誌檔案中;而開發環境下,直接列印到控制檯更方便檢視。想要實現這個功能,用巨集來控制再方便不過。

開發環境visual studio 2013, 程式碼如下:

#include <iostream>
#include <assert.h>
#ifdef _DEBUG
	#define LOG_I(x) std::cout << "警告:" << x << std::endl
	#define LOG_E(x) std::cout << "錯誤:" << x << std::endl; assert(false)
#else
	//有關檔案的操作省略
#endif
int _tmain(int argc, _TCHAR* argv[])
{
	LOG_I("有瑕疵");
	LOG_E("有錯誤");


	system("pause");
	return 0;
}

這裡很簡單,如果定義了_DEBUG巨集,即開發除錯環境,則將日誌列印到控制檯;否則,將日誌寫到對應檔案中(簡單起見,此處省略)。注意,巨集定義最後面是沒有分號的。

編譯執行,結果如下:

LOG_E巨集除了列印錯誤日誌外,還會觸發斷言,這裡這樣使用沒任何問題。

但是,日誌的列印,一般都會有條件判斷,即某錯誤條件成立後,才列印日誌。程式碼調整如下:

#include <iostream>
#include <assert.h>
#ifdef _DEBUG
	#define LOG_I(x) std::cout << "警告:" << x << std::endl
	#define LOG_E(x) std::cout << "錯誤:" << x << std::endl; assert(false) 
#else
	//有關檔案的操作省略
#endif
int _tmain(int argc, _TCHAR* argv[])
{
	int iValue;
	std::cout << "請輸入一個偶數:";
	std::cin >> iValue;
	if (iValue % 2 == 0)
		LOG_I("資料正確");
	else
		LOG_E("資料錯誤");


	system("pause");
	return 0;
}

執行,輸入1,結果如下:

和預想的一致,似乎沒什麼問題。但是如果輸入的2,什麼情況呢?

納尼?發生了什麼?

仔細看程式碼,用具體程式碼替換掉巨集,if語句的程式碼就是如下:

問題就出在LOG_E巨集上,裡面有多條語句。展開後,只有第一條語句在else作用域內,第二條則一定會指定。

想要解決該怎麼辦?有兩種方式:

1、在條件判斷語句後面加上{}

調整後的程式碼如下:

#include <iostream>
#include <assert.h>
#ifdef _DEBUG
	#define LOG_I(x) std::cout << "警告:" << x << std::endl
	#define LOG_E(x) std::cout << "錯誤:" << x << std::endl; assert(false) 
#else
	//有關檔案的操作省略
#endif
int _tmain(int argc, _TCHAR* argv[])
{
	int iValue;
	std::cout << "請輸入一個偶數:";
	std::cin >> iValue;
	if (iValue % 2 == 0)
	{
		LOG_I("資料正確");
	}		
	else
	{
		LOG_E("資料錯誤");
	}

	system("pause");
	return 0;
}

執行後結果如下:

結果正確!

但如果你是巨集的設計者,則需要為你設計的巨集負責,用下面的方式更合適。

2、多條語句的巨集,用{}將多條語句括起來

修改程式碼如下:

#include <iostream>
#include <assert.h>
#ifdef _DEBUG
	#define LOG_I(x) std::cout << "警告:" << x << std::endl
#define LOG_E(x) { std::cout << "錯誤:" << x << std::endl; assert(false); }
#else
	//有關檔案的操作省略
#endif
int _tmain(int argc, _TCHAR* argv[])
{
	int iValue;
	std::cout << "請輸入一個偶數:";
	std::cin >> iValue;
	if (iValue % 2 == 0)
		LOG_I("資料正確");
	else
		LOG_E("資料錯誤");

	system("pause");
	return 0;
}

執行結果如下:

沒問題!

有關巨集的使用,一定要謹慎,特別是和條件判斷語句搭配使用。

巨集後面是否有";"也需要注意,和條件語句一起,也可能產生意想不到的問題。

 

 

相關文章