Assertionen

Eine Assertionsanweisung formuliert eine Bedingung, die Sie erwarten, an einem Punkt im Programm erfüllt sein.Wenn diese Bedingung nicht erfüllt ist, schlägt die Assertion fehl, wird die Programmausführung unterbrochen, und Fehlgeschlagene Dialogfeld der Assertion angezeigt wird.

Visual C++ unterstützt Assertionsanweisungen, die auf Grundlage der folgenden Konstrukte sind:

  • Assertionen für MFC-Programme.

  • ATLASSERT für Programme, die ATL verwenden.

  • CRT-Assertionen für Programme, die die C-Laufzeitbibliothek verwenden.

  • Die ANSI-assert-Funktion für andere C/C++‑Programme.

Sie können Assertionen verwenden, um logische Fehler ermittelt, Ergebnisse eines Vorgangs und der Testen auf Fehlerzustände zu überprüfen, die behandelt sein sollten.

In diesem Thema

Wie Assertionen arbeiten

Assertionen in Debug- und in Releasebuilds

Nebeneffekte der Verwendung von Assertionen

CRT-Assertionen

Assertionen

  • MFC ASSERT_VALID und CObject::AssertValid

  • Einschränkungen von "AssertValid"

Verwenden von Assertionen

  • Verwaltete logische Fehler

  • Überprüfen der Ergebnisse

  • Suchen von nicht behandelten Fehler

Wie Assertionen arbeiten

Wenn der Debugger ein Programm aufgrund eines MFC oder einer C-Laufzeitbibliotheks-Assertion, dann, wenn die Quelle verfügbar ist, der Debugger anstelle in der Quelldatei navigiert, in der die Assertion aufgetreten ist.Die Assertionsmeldung wird in Ausgabefenster und Assertionsfehler im Dialogfeld.Sie können die Assertionsmeldung aus dem Ausgabefenster in ein Textfenster kopieren, wenn Sie sie zu Referenzzwecken behalten möchten.Das Ausgabefenster enthält u. U. weitere Fehlermeldungen.Überprüfen Sie diese Meldungen sorgfältig; häufig enthalten sie Hinweise auf die Ursache des Assertionsfehlers.

Assertionen verwenden, um von Fehlern während der Entwicklung zu erkennen.In der Regel verwenden Sie eine Assertion für jede Annahme.Wenn Sie davon ausgehen, dass ein Argument nicht NULL ist, verwenden Sie eine Assertion, um diese Annahme zu testen.

In diesem Thema

Assertionen in Debug- und in Releasebuilds

Assertionsanweisungen nur kompiliert, wenn _DEBUG definiert ist.Andernfalls behandelt der Compiler Assertionen als NULL-Anweisungen.Daher ist Assertionsanweisungen keinen Aufwand oder Leistungseinbußen in der endgültigen Version auf und ermöglichen es Ihnen, #ifdef-Direktiven zu vermeiden.

Nebeneffekte der Verwendung von Assertionen

Wenn Sie Assertionen in den Code einfügen, sollten Sie sicherstellen, dass die Assertionen keine Nebeneffekte haben.Betrachten Sie beispielsweise die folgende Assertion, die den nM-Wert ändert:

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

Da der ASSERT Ausdruck in der Releaseversion des Programms nicht ausgewertet wird, hat nM unterschiedliche Werte in der Debug- und Releaseversionen in den.Um dieses Problem in MFC zu vermeiden, können Sie das Sie ÜBERPRÜFEN-Makro anstelle ASSERT verwenden.VERIFY ausgewertet, den Ausdruck in allen Versionen jedoch nicht überprüft das Ergebnis in der Releaseversion.

Da die Auswertung einer Funktion unerwartete Nebeneffekte haben kann, sollten Funktionsaufrufe in Assertionsanweisungen mit Vorsicht verwendet werden.

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

VERIFY ruft myFnctn sowohl in der Debug- als auch in der Releaseversion auf, sodass zu verwenden ist zulässig.jedoch mithilfe VERIFY erzwingt den Aufwand eines unnötigen Funktionsaufrufs in der Releaseversion auf.

In diesem Thema

CRT-Assertionen

Die Headerdatei Crtdbg.h enthält Definitionen der Makros "_ASSERT" und "_ASSERTE", die zur Überprüfung von Assertionen verwendet werden.

Makro

Ergebnis

_ASSERT

Dateiname und Zeilennummer von _ASSERT, wenn der angegebene Ausdruck mit FALSE ausgewertet wird.

_ASSERTE

Identisch mit _ASSERT, zusätzlich jedoch eine Zeichenfolgenentsprechung des Ausdrucks, für den die Assertion ausgeführt wurde.

_ASSERTE ist leistungsfähiger, da der überwachte Ausdruck gemeldet wird, wenn er mit FALSE ausgewertet wurde.Auf diese Weise lässt sich das Problem u. U. bereits ohne Zuhilfenahme des Quellcodes identifizieren.In der Debugversion der Anwendung ist jedoch für jeden mit _ASSERTE überprüften Ausdruck eine Zeichenfolgenkonstante enthalten.Bei Verwendung vieler _ASSERTE-Makros können diese Zeichenfolgenausdrücke beträchtlichen Speicherplatz belegen.Falls dies zu Problemen führt, verwenden Sie _ASSERT, um Speicherplatz zu sparen.

Wenn _DEBUG definiert ist, wird das Makro _ASSERTE definiert, wie folgt:

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

Wenn der Ausdruck bei der Assertionsauswertung FALSE ergibt, wird _CrtDbgReport aufgerufen und gibt den Assertionsfehler (standardmäßig in einem Meldungsdialogfeld) aus.Wenn Sie Wiederholen im Meldungsdialogfeld auswählen, gibt _CrtDbgReport 1 zurück und _CrtDbgBreak ruft den Debugger durch DebugBreak auf.

ww5t02fa.collapse_all(de-de,VS.110).gifÜberprüfen des Heaps auf Beschädigungen

Im folgenden Beispiel wird _CrtCheckMemory verwendet, um den Heap auf Beschädigungen zu überprüfen:

_ASSERTE(_CrtCheckMemory());

ww5t02fa.collapse_all(de-de,VS.110).gifÜberprüfen der Gültigkeit von Zeigern

Im folgenden Beispiel wird anhand von _CrtIsValidPointer überprüft, ob in einen bestimmten Speicherbereich geschrieben bzw. daraus gelesen werden kann.

_ASSERTE(_CrtIsValidPointer( address, size, TRUE );

Im folgenden Beispiel wird anhand von _CrtIsValidHeapPointer überprüft, ob ein Zeiger auf Speicher im lokalen Heap zeigt. (Es handelt sich um den Heap, der von dieser Instanz der C-Laufzeitbibliothek generiert und verwaltet wird. Eine DLL kann über eine eigene Bibliotheksinstanz und daher auch über einen eigenen Heap außerhalb des Anwendungsheaps verfügen).Diese Assertion erkennt nicht nur NULL-Adressen oder Adressen außerhalb des gültigen Bereichs, sondern auch Zeiger auf statische Variablen, Stapelvariablen und sonstigen nicht lokalen Speicher.

_ASSERTE(_CrtIsValidPointer( myData );

ww5t02fa.collapse_all(de-de,VS.110).gifÜberprüfen eines Speicherblocks

Im folgenden Beispiel wird anhand von _CrtIsMemoryBlock sichergestellt, dass ein Speicherblock sich im lokalen Heap befindet und über einen gültigen Blocktyp verfügt.

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

In diesem Thema

Assertionen

Das ASSERT-Makro für die Assertionsüberprüfung ist in MFC definiert.Sie definiert auch die MFC ASSERT_VALID und CObject::AssertValid-Methoden zum Überprüfen des internen Zustands von CObject von abgeleitetes Objekt.

Wenn das Argument des ASSERT-MFC-Makros 0 oder false ergibt, stellt das Makro die Programmausführung ein und weist den Benutzer; andernfalls wird die Ausführung fortgesetzt.

Wenn eine Assertion fehl, wird ein Meldungsdialogfeld den Namen der Quelldatei sowie die Zeilennummer der Assertion an.Wenn Sie im Dialogfeld auf Wiederholen klicken, wird AfxDebugBreak aufgerufen, die Ausführung unterbrochen und der Debugger aufgerufen.An diesem Punkt können Sie die Aufrufliste untersuchen und weitere Debuggerfunktionen verwenden, um festzustellen, warum die Assertion fehlgeschlagen ist.Wenn Sie Just-In-Time-Debuggen aktiviert haben und der Debugger nicht bereits ausgeführt wurde, kann der Debugger über das Dialogfeld.

Im folgenden Beispiel wird gezeigt, wie ASSERT verwendet, um den Rückgabewert einer Funktion zu überprüfen:

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

Um eine Typüberprüfung für die Funktionsargumente vorzunehmen, können Sie ASSERT mit der IsKindOf-Funktion verwenden:

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

Das ASSERT-Makro keinen Code in der Releaseversion.Wenn Sie den Ausdruck in der Releaseversion auswerten müssen, verwenden Sie anstelle von ASSERT das VERIFY-Makro.

ww5t02fa.collapse_all(de-de,VS.110).gifMFC ASSERT_VALID und CObject::AssertValid

Die CObject::AssertValid-Methode ermöglicht es, den internen Zustand eines Objekts zur Laufzeit zu überprüfen.Es ist zwar nicht erforderlich, AssertValidbeim Ableiten der Klasse aus CObject zu überschreiben, Sie können so jedoch die Zuverlässigkeit der Klasse erhöhen.AssertValid sollte Assertionen für alle Membervariablen Objekts ausführen, um sicherzustellen, dass sie gültige Werte enthalten.Beispielsweise sollte überprüft werden, ob alle Membervariablen in Form von Zeigern ungleich NULL sind.

Im folgenden Beispiel wird die Deklaration einer AssertValid-Funktion erläutert:

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

Wenn Sie AssertValid überschreiben, rufen Sie die Basisklassenversion von AssertValid auf, bevor Sie eigene Überprüfungen vornehmen.Überprüfen Sie dann mit dem ASSERT-Makro wie folgt die Gültigkeit der Member, die nur in der abgeleiteten Klasse vorkommen:

#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

Wenn durch eine der Membervariablen Objekte gespeichert werden, können Sie deren interne Gültigkeit mit dem ASSERT_VALID-Makro testen (falls die betreffende Klasse AssertValid überschreibt).

Durch die CMyData-Klasse wird beispielsweise CObList in einer ihrer Membervariablen gespeichert.Die CObList-Variable m_DataList speichert eine Auflistung von CPerson-Objekten.Die Deklaration von CMyData lautet in den wesentlichen Teilen wie folgt:

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

Die AssertValid-Überschreibung in CMyData sieht wie folgt aus:

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

CMyData verwendet den AssertValid-Mechanismus, um die Gültigkeit der in ihrem Datenmember gespeicherten Objekte zu testen.Durch das AssertValid-Überschreiben von CMyData wird das ASSERT_VALID-Makro für die eigene m_pDataList-Membervariable aufgerufen.

Die Gültigkeitsüberprüfung wird auf dieser Ebene nicht auf, da die Klasse CObList auch AssertValid überschreibt.Diese Überschreibung bewirkt weitere Gültigkeitsüberprüfungen des internen Listenzustands.Auf diese Weise führt eine Gültigkeitsüberprüfung für ein CMyData-Objekt zu weiteren Gültigkeitsüberprüfungen, mit denen der interne Zustand des gespeicherten CObList-Listenobjekts getestet wird.

Mit geringem Mehraufwand könnten die Gültigkeitsüberprüfungen auch auf die in der Liste gespeicherten CPerson-Objekte ausgeweitet werden.Sie könnten eine CPersonList-Klasse von CObList ableiten und AssertValid überschreiben.Hierbei würden Sie CObject::AssertValid aufrufen und dann die Liste durchlaufen, wobei für jedes in der Liste gespeicherte CPerson-Objekt AssertValid aufgerufen würde.Durch die am Anfang dieses Themas dargestellte CPerson-Klasse wird AssertValid bereits überschrieben.

Dieses Verfahren ist beim Erstellen von Debugbuilds äußerst hilfreich.Beim späteren Erstellen eines Releasebuilds wird das Verfahren automatisch deaktiviert.

ww5t02fa.collapse_all(de-de,VS.110).gifEinschränkungen von "AssertValid"

Eine ausgelöste Assertion weist darauf hin, dass das Objekt mit Sicherheit fehlerhaft ist, und die Ausführung wird angehalten.Es gibt ein Mangel an Assertion nur an, dass kein Problem gefunden wurde, das Objekt ist nicht gewährleistet, dass gut zu sein.

In diesem Thema

Verwenden von Assertionen

ww5t02fa.collapse_all(de-de,VS.110).gifVerwaltete logische Fehler

So kann eine Assertion für eine Bedingung festgelegt werden, die entsprechend der Programmlogik true ergeben muss.Die Assertion bleibt so lange wirkungslos, bis ein logischer Fehler auftritt.

Angenommen, Sie simulieren die Bewegungen von Gasmolekülen in einem Behälter, und die numMols-Variable gibt die Gesamtanzahl der Moleküle an.Diese Zahl darf nie kleiner als 0 (null) sein, sodass Sie die folgende MFC-Assertionsanweisung einfügen können:

ASSERT(numMols >= 0);

Sie können auch eine CRT-Assertion wie die folgende einschließen:

_ASSERT(numMols >= 0);

Diese Anweisungen bleiben wirkungslos, solange das Programm einwandfrei funktioniert.Wenn ein logischer Fehler numMols bewirkt, dass sein kleiner als null, stellt jedoch die Assertion die Ausführung des Programms ein und zeigt Dialogfeld "Assertionsfehler" an.

In diesem Thema

ww5t02fa.collapse_all(de-de,VS.110).gifÜberprüfen der Ergebnisse

Assertionen sind für das Testen von Operationen wertvoll, deren Ergebnisse nicht von einer schnellen Blick offensichtlich sein.

Dies ist beispielsweise beim folgenden Code der Fall, in dem die iMols-Variable abhängig vom Inhalt der verketteten Liste, auf die mols zeigt, aktualisiert wird.

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

Die Anzahl der von iMols gezählten Moleküle muss immer kleiner oder gleich numMols, d. h. der Gesamtanzahl der Moleküle, sein.Durch die visuelle Prüfung dieser Schleife kann nicht ermittelt werden, dass dies immer zutrifft, sodass diese Bedingung nach der Schleife mit einer Assertionsanweisung überprüft wird.

In diesem Thema

ww5t02fa.collapse_all(de-de,VS.110).gifSuchen von nicht behandelten Fehler

Assertionen ermöglichen es Ihnen, den Code an den Stellen, an denen alle Fehler bereits behandelt worden sein sollten, auf Fehlerzustände zu testen.Im folgenden Beispiel gibt eine Grafikroutine einen Fehlercode oder, bei erfolgreicher Ausführung, 0 (null) zurück.

myErr = myGraphRoutine(a, b);

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

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

Wenn der Fehlerbehandlungscode einwandfrei funktioniert, sollte der Fehler behandelt und myErr vor Erreichen der Assertion auf 0 (null) zurückgesetzt werden.Wenn myErr jedoch einen anderen Wert hat, schlägt die Assertion fehl, das Programm wird angehalten, und Dialogfeld "Assertionsfehler" wird geöffnet.

Assertionsanweisungen sind allerdings kein Ersatz für Fehlerbehandlungscode.So kann die Assertionsanweisung im folgenden Beispiel im Releaseversionscode u. U. Probleme bereiten:

myErr = myGraphRoutine(a, b);

/* No Code to handle errors */

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

Bei diesem Code wird davon ausgegangen, dass der Fehlerzustand durch die Assertionsanweisung behandelt wird.Dies würde im Releaseversionscode dazu führen, dass alle von myGraphRoutine zurückgegebenen Fehlercodes nicht behandelt würden.

In diesem Thema

Siehe auch

Referenz

Assertionen in verwaltetem Code

Konzepte

Debuggersicherheit

Weitere Ressourcen

Debuggen von systemeigenem Code