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++
- javascript基礎(事件的冒泡)(三十二)JavaScript事件
- 十二、pytorch的基礎知識PyTorch
- C++基礎::為什麼不能cout一個string?C++
- 好用的一個object c 巨集Object
- HttpUnit的基礎使用(一)HTTP
- 理解C++ 巨集C++
- JavaSE基礎知識分享(十二)Java
- Go 之基礎速學 (十二) golang 的一些小總結Golang
- C++ 基礎C++
- c++基礎C++
- 使用 NSUserDefaults 儲存字典的一個坑
- 『動善時』JMeter基礎 — 6、使用JMeter傳送一個最基礎的請求JMeter
- 前端之路---入坑篇之基礎中的基礎html前端HTML
- Java 基礎(十二)異常機制Java
- javascript基礎(Math物件)(二十二)JavaScript物件
- c/c++巨集指令C++
- RxJava基礎使用(一)RxJava
- C++基礎::StreamC++
- 【C++】C++基礎知識C++
- C++基礎::一些介面彙總C++
- 基礎!使用map返回一個新的陣列要注意哦陣列
- javascript基礎(延時呼叫)(四十二)JavaScript
- javascript基礎(控制流程(迴圈 while,for))(十二)JavaScriptWhile
- java基礎學習之十二:控制流Java
- 一個C++巨集定義與列舉定義重複的編譯錯誤C++編譯
- C++巨集定義#defineC++
- DataBinding基礎使用一
- C++基礎::STL中的定理C++
- 0.開個新坑,湍流基礎理論入門
- ARKit 入坑 1 基礎篇
- C++中巨集定義#define的用法C++
- C++ | VS2017 C++專案配置使用的常見巨集定義C++
- CodeIgniter中使用CSRF TOKEN的一個坑
- Java開發者使用C++寫程式踩的坑JavaC++