C++基礎(十二)一個巨集使用的坑
雖然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;
}
執行結果如下:
沒問題!
有關巨集的使用,一定要謹慎,特別是和條件判斷語句搭配使用。
巨集後面是否有";"也需要注意,和條件語句一起,也可能產生意想不到的問題。
相關文章
- 今天踩了一個基礎坑
- 十二個坑爹的Python陷阱Python
- 逆向基礎(十二)
- 如何使用C++語言列印一個平行四邊形 - NOIP基礎C++
- 十二、pytorch的基礎知識PyTorch
- JavaSE基礎知識分享(十二)Java
- Go 之基礎速學 (十二) golang 的一些小總結Golang
- c++基礎C++
- 使用 NSUserDefaults 儲存字典的一個坑
- C++ | VS2017 C++專案配置使用的常見巨集定義C++
- 『動善時』JMeter基礎 — 6、使用JMeter傳送一個最基礎的請求JMeter
- 前端之路---入坑篇之基礎中的基礎html前端HTML
- DataBinding基礎使用一
- RxJava基礎使用(一)RxJava
- C++基礎 constC++
- 【C++】C++基礎知識C++
- 基礎!使用map返回一個新的陣列要注意哦陣列
- 『與善仁』Appium基礎 — 15、使用Appium的第一個DemoAPP
- C++的基礎學習5C++
- C++中巨集定義#define的用法C++
- C++基礎總結C++
- C++基礎知識C++
- C++基礎語法C++
- C++基礎學習第一天C++
- 0.開個新坑,湍流基礎理論入門
- 又踩坑了!BigDecimal使用的5個坑!Decimal
- Golang基礎速度二十二(終結篇)Golang
- Visual C++ MFC 中常用巨集的含義C++
- (Redis基礎教程之十二) 如何解決Redis中的故障Redis
- foreach使用引用時的一個坑-foreach原始碼分析原始碼
- 分享一個composer的坑
- 【01】C到C++(基礎)C++
- C++基礎知識整理C++
- c++基礎三(變數)C++變數
- C++基礎學習6C++
- C++基礎資料二C++
- C++基礎學習1C++
- Spring基礎使用(一)--------IOC、Bean的XML方式SpringBeanXML