Kontrolní výrazy jazyka C/C++

Příkaz kontrolního výrazu určuje podmínku, kterou očekáváte v určitém okamžiku v programu. Pokud tato podmínka není pravdivá, kontrolní výraz selže, spuštění programu se přeruší a zobrazí se dialogové okno Kontrolní výraz se nezdařilo.

Visual Studio podporuje příkazy kontrolních výrazů jazyka C++, které jsou založené na následujících konstruktorech:

  • Kontrolní výrazy MFC pro programy MFC.

  • ATLASSERT pro programy, které používají ATL.

  • Kontrolní výrazy CRT pro programy, které používají knihovnu runtime jazyka C.

  • Funkce ASSERT ANSI pro ostatní programy C/C++.

    Kontrolní výrazy můžete použít k zachycení chyb logiky, kontrole výsledků operace a testovacích chybových podmínek, které by měly být zpracovány.

V tomto tématu

Jak fungují kontrolní výrazy

Kontrolní výrazy v buildech ladění a vydaných verzí

Vedlejší účinky použití kontrolních výrazů

Kontrolní výrazy CRT

MFC – kontrolní výrazy

Jak fungují kontrolní výrazy

Když se ladicí program zastaví kvůli kontrolnímu výrazu knihovny mfc nebo C za běhu, pak pokud je zdroj dostupný, ladicí program přejde do bodu ve zdrojovém souboru, kde došlo k kontrolnímu výrazu. Zpráva kontrolního výrazu se zobrazí v okně Výstup i v dialogovém okně Kontrolní výraz se nezdařilo . Pokud chcete zprávu kontrolního výrazu uložit pro budoucí referenci, můžete z okna Výstup zkopírovat do textového okna. Okno Výstup může obsahovat i další chybové zprávy. Tyto zprávy pečlivě prozkoumejte, protože poskytují vodítka k příčině selhání kontrolního výrazu.

Pomocí kontrolních výrazů můžete detekovat chyby během vývoje. Jako pravidlo použijte pro každý předpoklad jeden kontrolní výraz. Pokud například předpokládáte, že argument není NULL, použijte k otestování daného předpokladu kontrolní výraz.

V tomto tématu

Kontrolní výrazy v buildech ladění a vydaných verzí

Příkazy kontrolního výrazu se kompilují pouze v případě, že _DEBUG je definován. V opačném případě kompilátor zachází s kontrolními výrazy jako s příkazy null. Příkazy kontrolních výrazů proto v konečném programu vydané verze neukládají žádné režijní náklady ani náklady na výkon a umožňují se vyhnout direktivám using #ifdef .

Vedlejší účinky použití kontrolních výrazů

Když do kódu přidáte kontrolní výrazy, ujistěte se, že kontrolní výrazy nemají vedlejší účinky. Představte si například následující kontrolní výraz, který upraví nM hodnotu:

ASSERT(nM++ > 0); // Don't do this!

Vzhledem k tomu, že výraz ASSERT není vyhodnocen ve verzi vydané verze programu, nM budou mít různé hodnoty ve verzích Debug a Release. Chcete-li se tomuto problému vyhnout v prostředí MFC, můžete místo něj použít makro VERIFY.ASSERT VERIFY vyhodnotí výraz ve všech verzích, ale nekontroluje výsledek ve verzi vydané verze.

Buďte obzvláště opatrní při používání volání funkce v kontrolních příkazech, protože vyhodnocení funkce může mít neočekávané vedlejší účinky.

ASSERT ( myFnctn(0)==1 ) // unsafe if myFnctn has side effects
VERIFY ( myFnctn(0)==1 ) // safe

VERIFY volání myFnctn ve verzích Debug i Release, takže je přijatelné použít. Použití VERIFY však představuje režii nepotřebného volání funkce ve verzi vydané verze.

V tomto tématu

Kontrolní výrazy CRT

The CRTDBG. Soubor záhlaví H definuje _ASSERT a _ASSERTE makra pro kontrolu kontrolních výrazů .

Makro Výsledek
_ASSERT Pokud se zadaný výraz vyhodnotí jako NEPRAVDA, název souboru a číslo _ASSERTřádku .
_ASSERTE Stejné jako _ASSERT, plus řetězcové vyjádření výrazu, který byl asserted.

_ASSERTE je výkonnější, protože hlásí výraz asserted, který ukázal, že je NEPRAVDA. To může stačit k identifikaci problému bez odkazu na zdrojový kód. Ladicí verze vaší aplikace však bude obsahovat řetězcovou konstantu pro každý výraz asserted using _ASSERTE. Pokud používáte mnoho _ASSERTE maker, tyto řetězcové výrazy zabírají značné množství paměti. Pokud se to ukáže jako problém, použijte _ASSERT k uložení paměti.

Při _DEBUG definování _ASSERTE je makro definováno takto:

#define _ASSERTE(expr) \
    do { \
        if (!(expr) && (1 == _CrtDbgReport( \
            _CRT_ASSERT, __FILE__, __LINE__, #expr))) \
            _CrtDbgBreak(); \
    } while (0)

Pokud se výraz asserted vyhodnotí jako NEPRAVDA, _CrtDbgReport je volána k hlášení selhání kontrolního výrazu (ve výchozím nastavení pomocí dialogového okna zprávy). Pokud v dialogovém okně zprávy zvolíte Možnost Opakovat , _CrtDbgReport vrátí hodnotu 1 a _CrtDbgBreak zavolá ladicí program prostřednictvím DebugBreak.

Pokud potřebujete dočasně zakázat všechny kontrolní výrazy, použijte _CtrSetReportMode.

Kontrola poškození haldy

Následující příklad používá _CrtCheckMemory ke kontrole poškození haldy:

_ASSERTE(_CrtCheckMemory());

Kontrola platnosti ukazatele

Následující příklad používá _CrtIsValidPointer k ověření, zda je daná oblast paměti platná pro čtení nebo zápis.

_ASSERTE(_CrtIsValidPointer( address, size, TRUE );

Následující příklad používá _CrtIsValidHeapPointer k ověření ukazatele na paměť v místní haldě (halda vytvořená a spravovaná touto instancí knihovny runtime jazyka C – knihovna DLL může mít svou vlastní instanci knihovny, a proto vlastní haldu mimo haldu aplikace). Tento kontrolní výraz zachytává nejen adresy null nebo mimo hranice, ale také ukazatele na statické proměnné, proměnné zásobníku a jakoukoli jinou nelokalní paměť.

_ASSERTE(_CrtIsValidHeapPointer( myData );

Kontrola bloku paměti

Následující příklad používá _CrtIsMemoryBlock k ověření, že blok paměti je v místní haldě a má platný typ bloku.

_ASSERTE(_CrtIsMemoryBlock (myData, size, &requestNumber, &filename, &linenumber));

V tomto tématu

MFC – kontrolní výrazy

MFC definuje makro ASSERT pro kontrolu kontrolních výrazů. Definuje také MFC ASSERT_VALID a CObject::AssertValid metody pro kontrolu vnitřního stavu -odvozeného objektu CObject.

Pokud se argument makra MFC ASSERT vyhodnotí jako nula nebo nepravda, makro zastaví provádění programu a upozorní uživatele. V opačném případě bude provádění pokračovat.

Pokud kontrolní výraz selže, zobrazí se v dialogovém okně se zprávou název zdrojového souboru a číslo řádku kontrolního výrazu. Pokud v dialogovém okně zvolíte Možnost Opakovat, volání AfxDebugBreak způsobí přerušení provádění ladicímu programu. V tomto okamžiku můžete prozkoumat zásobník volání a pomocí dalších zařízení ladicího programu zjistit, proč kontrolní výraz selhal. Pokud jste povolili ladění za běhu a ladicí program ještě nebyl spuštěný, dialogové okno může spustit ladicí program.

Následující příklad ukazuje, jak zkontrolovat ASSERT návratovou hodnotu funkce:

int x = SomeFunc(y);
ASSERT(x >= 0);   //  Assertion fails if x is negative

Funkci ASSERT s funkcí IsKindOf můžete použít k zajištění kontroly typů argumentů funkce:

ASSERT( pObject1->IsKindOf( RUNTIME_CLASS( CPerson ) ) );

Makro ASSERT nevygeneruje žádný kód ve verzi vydané verze. Pokud potřebujete výraz vyhodnotit ve verzi vydané verze, použijte místo ASSERT makro VERIFY .

MFC ASSERT_VALID a CObject::AssertValid

CObject ::AssertValid metoda poskytuje kontroly za běhu interního stavu objektu. I když při odvození třídy nepotřebujete přepisovat AssertValid , můžete tím svoji třídu CObjectusnadnit. AssertValid by měly provádět kontrolní výrazy pro všechny členské proměnné objektu, aby bylo možné ověřit, zda obsahují platné hodnoty. Měla by například zkontrolovat, že členské proměnné ukazatele nemají hodnotu NULL.

Následující příklad ukazuje, jak deklarovat AssertValid funkci:

class CPerson : public CObject
{
protected:
    CString m_strName;
    float   m_salary;
public:
#ifdef _DEBUG
    // Override
    virtual void AssertValid() const;
#endif
    // ...
};

Při přepsání AssertValidvolejte verzi AssertValid základní třídy před provedením vlastních kontrol. Pak pomocí makra ASSERT zkontrolujte členy jedinečné pro vaši odvozenou třídu, jak je znázorněno zde:

#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

Pokud některé z vašich členských proměnných ukládají objekty, můžete makro použít ASSERT_VALID k otestování jejich vnitřní platnosti (pokud jejich třídy přepisují AssertValid).

Představte si například třídu CMyData, která ukládá CObList v jedné z jejích členských proměnných. Proměnná CObList , m_DataListukládá kolekci CPerson objektů. Zkrácená deklarace CMyData vypadá takto:

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 ...
};

Přepsání AssertValidCMyData vypadá takto:

#ifdef _DEBUG
void CMyData::AssertValid( ) const
{
    // Call inherited AssertValid.
    CObject::AssertValid( );
    // Check validity of CMyData members.
    ASSERT_VALID( m_pDataList );
    // ...
}
#endif

CMyDataAssertValid používá mechanismus k otestování platnosti objektů uložených ve svém datovém členu. Přepsání AssertValidCMyData vyvolá ASSERT_VALID makro pro vlastní m_pDataList členské proměnné.

Testování platnosti se na této úrovni nezastaví, protože třída CObList také přepisuje AssertValid. Toto přepsání provádí další testování platnosti v interním stavu seznamu. Proto test platnosti objektu CMyData vede k dalším testům platnosti pro vnitřní stavy uloženého CObList objektu seznamu.

S další prací můžete také přidat testy platnosti pro CPerson objekty uložené v seznamu. Třídu můžete odvodit CPersonList a CObList přepsat AssertValid. V přepsání byste volali CObject::AssertValid a pak iterovat seznamem a volali AssertValid na každý CPerson objekt uložený v seznamu. Třída CPerson zobrazená na začátku tohoto tématu již přepsána AssertValid.

Jedná se o výkonný mechanismus při sestavování pro ladění. Když následně sestavíte pro vydání, mechanismus se automaticky vypne.

Omezení AssertValid

Aktivovaný kontrolní výraz označuje, že objekt je rozhodně chybný a spuštění se zastaví. Nedostatek kontrolního výrazu však značí pouze, že nebyl nalezen žádný problém, ale objekt není zaručen, že je dobrý.

V tomto tématu

Použití kontrolních výrazů

Zachycení chyb logiky

Kontrolní výraz můžete nastavit pro podmínku, která musí být pravdivá podle logiky programu. Kontrolní výraz nemá žádný vliv, pokud nedojde k chybě logiky.

Předpokládejme například, že simulujete molekuly plynu v kontejneru a proměnná numMols představuje celkový počet molekul. Toto číslo nemůže být menší než nula, takže můžete zahrnout příkaz kontrolního výrazu MFC, například takto:

ASSERT(numMols >= 0);

Nebo můžete zahrnout kontrolní výraz CRT takto:

_ASSERT(numMols >= 0);

Tyto příkazy nedělají nic, pokud váš program funguje správně. Pokud je ale chyba numMols logiky menší než nula, kontrolní výraz zastaví provádění programu a zobrazí dialogové okno Kontrolní výraz, které selhalo.

V tomto tématu

Kontrola výsledků

Kontrolní výrazy jsou cenné pro testovací operace, jejichž výsledky nejsou zřejmé z rychlé vizuální kontroly.

Představte si například následující kód, který aktualizuje proměnnou iMols na základě obsahu propojeného seznamu, na který molsodkazuje:

/* 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

Počet molekul počítaných iMols podle musí být vždy menší než nebo roven celkovému počtu molekul, numMols. Vizuální kontrola smyčky nezobrazuje, že to bude nutně případ, takže příkaz kontrolního výrazu se použije po smyčce k otestování pro danou podmínku.

V tomto tématu

Hledání neošetřených chyb

Kontrolní výrazy můžete použít k testování chybových podmínek v okamžiku v kódu, kde by se měly zpracovat všechny chyby. V následujícím příkladu vrátí grafická rutina kód chyby nebo nulu pro úspěch.

myErr = myGraphRoutine(a, b);

/* Code to handle errors and
   reset myErr if successful */

ASSERT(!myErr); -- MFC version
_ASSERT(!myErr); -- CRT version

Pokud kód pro zpracování chyb funguje správně, měla by být chyba zpracována a myErr před dosažením kontrolního výrazu by se měla obnovit na nulu. Pokud myErr má jinou hodnotu, kontrolní výraz selže, program se zastaví a zobrazí se dialogové okno Kontrolní výraz se nezdařilo.

Příkazy kontrolních výrazů však nejsou náhradou za kód pro zpracování chyb. Následující příklad ukazuje příkaz kontrolního výrazu, který může vést k problémům v konečném kódu vydané verze:

myErr = myGraphRoutine(a, b);

/* No Code to handle errors */

ASSERT(!myErr); // Don't do this!
_ASSERT(!myErr); // Don't do this, either!

Tento kód spoléhá na příkaz kontrolního výrazu pro zpracování chybového stavu. V důsledku toho se veškerý kód chyby vrácený myGraphRoutine v konečném kódu verze neošetřuje.

V tomto tématu