C/C++ 判斷提示
判斷提示陳述式指定想要在您的程式中的一個點,則為 true 的條件。 如果該條件不為真,表示判斷提示失敗,會中斷程式執行,以及判斷提示已失敗對話方塊就會出現。
Visual C++ 的支援以下列建構函式為基礎的判斷提示陳述式:
您可以使用判斷提示來捕捉邏輯錯誤,檢查作業的結果,測試應該已處理的錯誤情況。
本主題中
How assertions work
Assertions in Debug and Release builds
Side effects of using assertions
CRT assertions
MFC assertions
MFC ASSERT_VALID and CObject::AssertValid
Limitations of AssertValid
Using assertions
Catching logic errors
Checking results
Testing error conditions
判斷提示的運作方式
當偵錯工具暫停因為 MFC 或 C 執行階段程式庫判斷提示時,然後如果來源是可供使用,偵錯工具會巡覽至發生判斷提示的原始程式檔中的點。 判斷提示訊息會出現在兩個輸出] 視窗 和 判斷提示已失敗對話方塊。 您可以將複製的判斷提示訊息輸出視窗至文字視窗,如果您想要儲存它供未來參考。 輸出視窗可能包含其他錯誤訊息。 這些訊息仔細地檢查,因為它們提供了造成判斷提示失敗的線索。
使用判斷提示來在開發期間偵測到錯誤。 一般而言,使用每個假設一個判斷提示。 例如,如果您假設引數不是 NULL,使用判斷提示來測試該假設。
In this topic
在偵錯和發行組建的判斷提示
判斷提示陳述式編譯時,才_DEBUG所定義。 否則,編譯器會判斷提示視為 null 陳述式。 因此,判斷提示陳述式對造成沒有的額外負荷,或效能成本在最終發行程式中,並可讓您避免使用#ifdef指示詞。
使用判斷提示的副作用
當您的程式碼加入判斷提示時,請確定判斷提示不會有副作用。 例如,請考慮下列判斷提示的修改nM值:
ASSERT(nM++ > 0); // Don't do this!
因為ASSERT在您的程式的發行版本中無法評估運算式 nM偵錯和發行版本中會有不同的值。 若要避免這個問題,在 MFC 中的,您可以使用驗證 巨集,而不是 ASSERT. VERIFY評估所有版本中的運算式,但不會檢查在發行版本的結果。
是尤其要特別注意使用判斷提示陳述式中的函式呼叫,因為評估函式可以有未預期的副作用。
ASSERT ( myFnctn(0)==1 ) // unsafe if myFnctn has side effects
VERIFY ( myFnctn(0)==1 ) // safe
VERIFY呼叫 myFnctn在偵錯和發行版本中,所以是可接受的使用。 不過,使用VERIFY加諸在發行版本中不必要的函式呼叫的負荷。
In this topic
CRT 判斷提示
CRTDBG。H 標頭檔定義 _assert 狀況和 _asserte 的判斷提示巨集的判斷提示檢查。
巨集 |
結果 |
---|---|
_ASSERT |
如果指定的運算式評估為 FALSE,檔名和行號的 _ASSERT. |
_ASSERTE |
與相同 _ASSERT,再加上運算式,判斷提示的字串表示。 |
_ASSERTE因為它會報告已判斷提示是 FALSE 的運算式,則是功能更強大。 這可能是不足以識別問題,而不參照到的原始碼。 然而,您的應用程式的偵錯版本會包含每個運算式使用字串常數 _ASSERTE. 如果您使用許多**_ASSERTE巨集,這些字串運算式會佔用大量的記憶體。 如果這證實會產生問題,使用_ASSERT**以節省記憶體。
當_DEBUG定義, _ASSERTE巨集定義,如下所示:
#define _ASSERTE(expr) \
do { \
if (!(expr) && (1 == _CrtDbgReport( \
_CRT_ASSERT, __FILE__, __LINE__, #expr))) \
_CrtDbgBreak(); \
} while (0)
如果已判斷提示的運算式評估為 FALSE, _CrtDbgReport 呼叫以報告 (根據預設,使用訊息對話方塊) 的判斷提示失敗。 如果您選擇再試一次 在 [訊息] 對話方塊中, _CrtDbgReport,則傳回 1 和 _CrtDbgBreak透過偵錯工具會呼叫 DebugBreak.
檢查堆積損毀
下列範例會使用 _CrtCheckMemory 檢查堆積損毀:
_ASSERTE(_CrtCheckMemory());
檢查指標有效性
下列範例會使用 _CrtIsValidPointer 來檢查指定的記憶體範圍是否有效來讀取或寫入。
_ASSERTE(_CrtIsValidPointer( address, size, TRUE );
下列範例會使用 _CrtIsValidHeapPointer 來驗證指標指向本機的堆積中的記憶體 (堆積所建立和管理這個執行個體的 C 執行階段程式庫 — DLL 可以擁有自己的程式庫,因此它自己的堆積,外部應用程式的堆集的執行個體)。 這個判斷提示就會攔截不只 null 或超出範圍的位址,但也靜態變數、 堆疊變數和任何其他非本機記憶體的指標。
_ASSERTE(_CrtIsValidPointer( myData );
檢查記憶體區塊
下列範例會使用 _CrtIsMemoryBlock ,請確認記憶體區塊本機的堆積中,且具有有效的區塊型別。
_ASSERTE(_CrtIsMemoryBlock (myData, size, &requestNumber, &filename, &linenumber));
In this topic
MFC 判斷提示
MFC 定義 ASSERT 的巨集,判斷提示檢查。 它也會定義MFC ASSERT_VALID和 CObject::AssertValid進行檢查的內部狀態的方法 CObject-衍生物件。
如果引數的 MFC ASSERT巨集評估成零或 false,巨集中止程式執行,系統會通知使用者 ; 否則,會繼續執行。
判斷提示失敗時,訊息對話方塊顯示原始程式檔和行號的判斷提示的名稱。 如果您在對話方塊中選擇 [重試] 方塊中,呼叫 AfxDebugBreak 會使偵錯工具中斷執行。 此時,您可以檢查呼叫堆疊,並使用其他的偵錯工具設施,來決定 [判斷提示失敗的原因。 如果您已啟用時間僅偵錯,且偵錯工具已經不在執行,[] 對話方塊可以啟動偵錯工具。
下列範例示範如何使用ASSERT來檢查函式的傳回值:
int x = SomeFunc(y);
ASSERT(x >= 0); // Assertion fails if x is negative
您可以使用判斷提示與 IsKindOf 提供型別檢查函數的引數的函式:
ASSERT( pObject1->IsKindOf( RUNTIME_CLASS( CPerson ) ) );
ASSERT巨集,會產生發行版本中的沒有程式碼。 如果您要評估的版本中的運算式,請使用驗證而不是判斷提示的巨集。
MFC ASSERT_VALID 和 CObject::AssertValid
CObject::AssertValid 方法會提供執行階段會檢查物件的內部狀態。 雖然您不需要覆寫AssertValid時衍生您的類別,從 CObject,您可以讓您的類別更可靠這麼。 AssertValid應該在所有物件的成員變數,以確認它們包含有效的值上都執行判斷提示。 例如,它應該檢查指標成員變數不是 NULL。
下列範例示範如何宣告AssertValid函式:
class CPerson : public CObject
{
protected:
CString m_strName;
float m_salary;
public:
#ifdef _DEBUG
// Override
virtual void AssertValid() const;
#endif
// ...
};
當您覆寫 AssertValid、 呼叫的基底類別版本 AssertValid在執行您自己檢查之前。 然後如下所示,請檢查您的衍生類別的唯一成員使用 ASSERT 巨集:
#ifdef _DEBUG
void CPerson::AssertValid() const
{
// Call inherited AssertValid first.
CObject::AssertValid();
// Check CPerson members...
// Must have a name.
ASSERT( !m_strName.IsEmpty());
// Must have an income.
ASSERT( m_salary > 0 );
}
#endif
如果任何成員變數儲存物件時,您可以使用ASSERT_VALID巨集,以測試其內部的有效性 (如果它們的類別覆寫 AssertValid)。
例如,假設類別 CMyData,哪些商店 CObList 在它的成員變數的其中一個。 CObList變數, m_DataList,存放一堆 CPerson物件。 縮寫的宣告CMyData看起來像這樣:
class CMyData : public CObject
{
// Constructor and other members ...
protected:
CObList* m_pDataList;
// Other declarations ...
public:
#ifdef _DEBUG
// Override:
virtual void AssertValid( ) const;
#endif
// And so on ...
};
AssertValid中覆寫 CMyData看起來像這樣:
#ifdef _DEBUG
void CMyData::AssertValid( ) const
{
// Call inherited AssertValid.
CObject::AssertValid( );
// Check validity of CMyData members.
ASSERT_VALID( m_pDataList );
// ...
}
#endif
CMyData使用 AssertValid機制來測試物件儲存在其資料成員的有效性。 覆寫AssertValid的 CMyData叫用 ASSERT_VALID自己 m_pDataList 成員變數的巨集。
有效性測試不會停止在此層級因為類別CObList也會覆寫 AssertValid. 這個覆寫執行額外的有效性測試清單的內部狀態。 因此,有效測試上CMyData物件會導致預存的內部狀態的額外的有效性測試 CObList清單物件。
透過一些工作,您可以為加入有效性測試CPerson也儲存在] 清單中的物件。 您無法衍生類別CPersonList的 CObList而覆寫 AssertValid. 在覆寫時,您會呼叫 CObject::AssertValid,然後逐一查看清單中,呼叫 AssertValid上每個 CPerson清單中儲存的物件。 CPerson已經本主題開頭所示的類別會覆寫 AssertValid.
當您建置偵錯時,這是強大的機制。 當您接下來建置發行時,此機制會自動關閉。
AssertValid 的限制
觸發判斷提示表示物件一定是壞,而且會停止執行。 然而,缺乏判斷提示表示只沒有發現問題,但不是保證物件是很好。
In this topic
使用判斷提示
攔截邏輯錯誤
您可以設定必須根據您的程式邏輯,則為 true 的條件判斷提示。 除非發生邏輯錯誤,判斷提示就會有任何作用。
例如,假設您模擬瓦斯分子中一種容器和變數numMols代表分子總數。 這個數字不能小於零,因此您可以包含像這樣的 MFC 判斷提示陳述式:
ASSERT(numMols >= 0);
或者,您可能會包含像這樣的 CRT 判斷提示:
_ASSERT(numMols >= 0);
如果您的程式運作正常,這些陳述式會執行任何動作。 如果邏輯錯誤造成numMols 小於零,不過,判斷提示暫停程式執行,並顯示 判斷提示已失敗對話方塊.
In this topic
檢查結果
判斷提示是有價值的測試的作業的結果可能不明顯的快速視覺檢視。
例如,請考慮下列的程式碼,更新變數iMols所指的連結清單的內容為基礎 mols:
/* This code assumes that type has overloaded the != operator
with const char *
It also assumes that H2O is somewhere in that linked list.
Otherwise we'll get an access violation... */
while (mols->type != "H2O")
{
iMols += mols->num;
mols = mols->next;
}
ASSERT(iMols<=numMols); // MFC version
_ASSERT(iMols<=numMols); // CRT version
分子數計算iMols都必須小於或等於總數分子, numMols. 迴圈的視覺檢視不會顯示這一定會大小寫,所以判斷提示陳述式用於迴圈之後來測試該條件。
In this topic
尋找未處理的錯誤
您可以使用判斷提示在程式碼中測試錯誤條件,在某個點也就是其中的任何錯誤就應該處理。 在下列範例中,一個圖形常式會傳回錯誤代碼或成功的零。
myErr = myGraphRoutine(a, b);
/* Code to handle errors and
reset myErr if successful */
ASSERT(!myErr); -- MFC version
_ASSERT(!myErr); -- CRT version
如果錯誤處理程式碼正常運作,錯誤應該會處理和myErr重設為零之前,判斷提示就會到達。 如果myErr有另一個值,判斷提示失敗,則程式會中止, 判斷提示已失敗對話方塊就會出現。
判斷提示陳述式沒有替代的錯誤處理程式碼,不過。 下列範例會顯示在最後發行程式碼中將造成判斷提示陳述式:
myErr = myGraphRoutine(a, b);
/* No Code to handle errors */
ASSERT(!myErr); // Don't do this!
_ASSERT(!myErr); // Don't do this, either!
這段程式碼依賴判斷提示陳述式,來處理錯誤狀況。 錯誤的任何程式碼所傳回的結果是, myGraphRoutine會在最後發行程式碼中未處理。
In this topic