Condividi tramite


Asserzioni

Un'istruzione di asserzione specifica una condizione che si prevede sia true in un punto del programma.Se tale condizione non è true, l'asserzione avrà esito negativo, l'esecuzione del programma viene interrotta e Finestra di dialogo asserzione non riuscita.

Visual C++ supporta le istruzioni di asserzione basati sui seguenti costrutti:

  • Asserzioni MFC per programmi MFC.

  • ATLASSERT per programmi che utilizzano ATL.

  • Asserzioni CRT per programmi che utilizzano la libreria di runtime del linguaggio C.

  • La funzione di asserzione ANSI per altri programmi C/C++.

È possibile utilizzare asserzioni per rilevare errori logici, verificare i risultati di un'operazione e delle condizioni di errore del test che devono essere gestite.

In questo argomento

Funzionamento delle asserzioni

Asserzioni di debug e nelle build di rilascio

Effetti collaterali di utilizzare asserzioni

Asserzioni CRT

Asserzioni MFC

  • MFC ASSERT_VALID e CObject::AssertValid

  • Limitazioni di AssertValid

Utilizzo di asserzioni

  • Rilevamento di errori logici

  • Verificare i risultati

  • Individuare errori non gestiti

Funzionamento delle asserzioni

Quando il debugger si interrompe a causa di un MFC o asserzione della libreria di runtime del linguaggio C, quindi se il database di origine è disponibile, il debugger passa al punto del codice sorgente in cui l'asserzione si è verificata.Il messaggio di asserzione viene visualizzato in Finestra di output che nella finestra di dialogo Asserzione non riuscita è.Se si desidera salvare il messaggio relativo all'asserzione per poterlo utilizzare successivamente, è possibile copiarlo dalla finestra Output in una finestra di testo.La finestra Output può contenere anche altri messaggi di errore.Esaminare questi messaggi attentamente, poiché forniscono indizi della causa dell'esito negativo dell'asserzione.

Asserzioni di utilizzo per rilevare gli errori durante lo sviluppo.In generale, utilizza un'asserzione per ciascun presupposto.Ad esempio, se si presuppone che un argomento non è NULL, utilizza un'asserzione per verificare tale ipotesi.

In questo argomento

Asserzioni di debug e nelle build di rilascio

Le istruzioni di asserzione vengono compilate solo se _DEBUG è definito.In caso contrario, il compilatore considera le asserzioni come istruzioni nulle.Pertanto, le istruzioni di asserzione non richiedono sovraccarico o riduzione delle prestazioni nella versione finale del programma e consentono di evitare mediante le direttive di #ifdef.

Effetti collaterali di utilizzare asserzioni

Quando si aggiungono asserzioni al codice, accertarsi che tali asserzioni non abbiano effetti secondari.Ad esempio, si consideri la seguente asserzione che modifica il valore di nM :

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

Poiché l'espressione di ASSERT non viene valutata nella versione di rilascio del programma, nM presenterà valori differenti nelle versioni di debug e di rilascio.Per evitare questo problema in MFC, è possibile utilizzare la macro di VERIFICHI anziché ASSERT.VERIFY valuta l'espressione in tutte le versioni ma non verifica il risultato della versione finale.

È necessario prestare particolare attenzione all'utilizzo di chiamate di funzione in istruzioni di asserzione, poiché la valutazione di una funzione può avere effetti secondari imprevisti.

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

VERIFY chiama myFnctn sia nel debug che nelle versioni finali, pertanto è accettabile da utilizzare.Tuttavia, l'utilizzo di VERIFY impone il sovraccarico di una funzione inutile nella versione di rilascio.

In questo argomento

Asserzioni CRT

Il file di intestazione CRTDBG.H definisce le macro _ASSERT e _ASSERTE per il controllo delle asserzioni.

Macro

Risultato

_ASSERT

Se l'espressione specificata ha valore FALSE, il nome file e il numero di riga di _ASSERT.

_ASSERTE

Come _ASSERT, con l'aggiunta della rappresentazione in formato stringa dell'espressione asserita.

_ASSERTE è più potente perché riporta l'espressione asserita che è stata valutata FALSE.Ciò può essere sufficiente per identificare il problema senza fare riferimento al codice sorgente.La versione di debug dell'applicazione conterrà tuttavia una costante stringa per ciascuna espressione asserita mediante _ASSERTE.Se si utilizzano numerose macro _ASSERTE, queste espressioni in formato stringa richiederanno una notevole quantità di memoria.Se questo costituisce un problema, utilizzare _ASSERT per risparmiare memoria.

Quando _DEBUG è definito, la macro di _ASSERTE viene definita come segue:

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

Se l'espressione asserita ha valore FALSE, verrà chiamato _CrtDbgReport per fornire informazioni sull'esito negativo dell'asserzione (utilizzando una finestra di dialogo per impostazione predefinita).Se si sceglie Riprova nella finestra di messaggio, _CrtDbgReport restituisce 1 e _CrtDbgBreak chiama il debugger con DebugBreak.

ww5t02fa.collapse_all(it-it,VS.110).gifControllo dell'integrità della memoria heap

L'esempio che segue utilizza _CrtCheckMemory per verificare la presenza di eventuali problemi della memoria heap:

_ASSERTE(_CrtCheckMemory());

ww5t02fa.collapse_all(it-it,VS.110).gifVerifica della validità del puntatore

L'esempio che segue utilizza _CrtIsValidPointer per controllare che un determinato intervallo di memoria sia valido per la lettura o la scrittura.

_ASSERTE(_CrtIsValidPointer( address, size, TRUE );

L'esempio che segue utilizza _CrtIsValidHeapPointer per verificare che un puntatore punti a memoria presente nell'heap locale, ovvero all'heap creato e gestito da questa istanza della libreria di runtime del linguaggio C. Una DLL può avere la propria istanza della libreria, e pertanto il proprio heap, all'esterno dell'heap dell'applicazione.Questa asserzione rileva non solo gli indirizzi nulli o esterni ai limiti, ma anche i puntatori a variabili statiche, variabili di stack e altre locazioni di memoria non locali.

_ASSERTE(_CrtIsValidPointer( myData );

ww5t02fa.collapse_all(it-it,VS.110).gifControllo di un blocco di memoria

L'esempio che segue utilizza _CrtIsMemoryBlock per verificare che un blocco di memoria si trovi nell'heap locale e presenti un tipo di blocco valido.

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

In questo argomento

Asserzioni MFC

MFC definisce la macro ASSERT per il controllo delle asserzioni.Definisce anche i metodi di CObject::AssertValid e di MFC ASSERT_VALID per controllare lo stato interno di CObjectoggetto derivato da.

Se l'argomento della macro MFC ASSERT restituisce zero o false, la macro interrompe l'esecuzione del programma e avvisa l'utente; in caso contrario, l'esecuzione continua.

Quando un'asserzione ha esito negativo, viene visualizzata una finestra di messaggio indica il nome del file di origine e il numero di riga dell'asserzione.Se si sceglie Riprova nella finestra di dialogo, una chiamata a AfxDebugBreak farà sì che l'esecuzione passi al debugger.A questo punto, è possibile esaminare lo stack di chiamate e utilizzare altre utilità del debugger per determinare perché l'asserzione non è riuscita.Se è stato abilitato Debug JITe il debugger non era già in esecuzione, la finestra di dialogo consente di avviare il debugger.

Di seguito viene illustrato come utilizzare ASSERT per controllare il valore restituito di una funzione:

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

È possibile utilizzare ASSERT con la funzione IsKindOf per fornire il controllo di tipo per gli argomenti della funzione:

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

La macro di ASSERT non scrive codice nella versione di rilascio.Se è necessario valutare l'espressione nella versione di rilascio, utilizzare la macro VERIFY anziché ASSERT.

ww5t02fa.collapse_all(it-it,VS.110).gifMFC ASSERT_VALID e CObject::AssertValid

Il metodo CObject::AssertValid consente di eseguire controlli di runtime dello stato interno di un oggetto.Anche se non è necessario eseguire l'override di AssertValid quando si deriva la classe da CObject, mediante questa operazione è possibile rendere la classe più affidabile.AssertValid deve eseguire asserzioni su variabili membro dell'oggetto per verificare che contengano valori validi.È ad esempio opportuno controllare che le variabili membro del puntatore non abbiano valore NULL.

L'esempio che segue illustra come dichiarare una funzione AssertValid:

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

Quando si esegue l'override di AssertValid, chiamare la versione della classe base di AssertValid prima di eseguire i controlli.Utilizzare quindi la macro ASSERT per controllare i membri che appartengono unicamente alla classe derivata, come illustrato di seguito:

#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

Se una o più delle variabili membro memorizza oggetti, è possibile utilizzare la macro ASSERT_VALID per verificarne la validità interna (se le rispettive classi operano l'override di AssertValid).

Si consideri ad esempio una classe CMyData, che memorizza un CObList in una delle proprie variabili membro.La variabile CObList, m_DataList, memorizza una raccolta di oggetti CPerson.Una dichiarazione abbreviata di CMyData sarà analoga alla seguente:

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

L'override di AssertValid in CMyData sarà analogo al seguente:

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

CMyData utilizza il meccanismo di AssertValid per verificare la validità degli oggetti memorizzati nel proprio membro dati.La funzione AssertValid di CMyData che esegue l'override richiama la macro ASSERT_VALID per la propria variabile membro m_pDataList.

La verifica della validità non termina a questo livello poiché la classe CObList esegue l'override di AssertValid.Questo override esegue un'ulteriore verifica della validità sullo stato interno della lista.Una verifica di validità condotta su un oggetto CMyData conduce quindi a ulteriori verifiche di validità per gli stati interni dell'oggetto CObList memorizzato.

Con poche operazioni aggiuntive è possibile aggiungere verifiche di validità anche per gli oggetti CPerson memorizzati nell'elenco.È possibile derivare una classe CPersonList da CObList ed eseguire l'override di AssertValid.Nell'override si chiama CObject::AssertValid e poi si itera l'operazione sulla lista, chiamando AssertValid su ciascun oggetto CPerson memorizzato.La classe CPerson illustrata all'inizio di questo argomento esegue già l'override di AssertValid.

Si tratta di un meccanismo potente quando si operano compilazioni per il debug.Quando si effettuano in seguito compilazioni per il rilascio, il meccanismo viene disattivato automaticamente.

ww5t02fa.collapse_all(it-it,VS.110).gifLimitazioni di AssertValid

La generazione di un'asserzione conferma che l'oggetto non è corretto e l'esecuzione verrà interrotta.Tuttavia, la mancanza di asserzione indica solo che nessun problema è stato trovato, ma l'oggetto non garantisce la validità.

In questo argomento

Utilizzo di asserzioni

ww5t02fa.collapse_all(it-it,VS.110).gifRilevamento di errori logici

È possibile impostare un'asserzione su una condizione che deve essere True in base alla logica del programma.L'asserzione non ha alcun effetto finché non si verifica un errore logico.

Si supponga ad esempio di simulare molecole di gas in un contenitore e che la variabile numMols rappresenti il numero totale di tali molecole.Questo numero non può essere minore di zero, pertanto è possibile includere un'istruzione di asserzione MFC del seguente tipo:

ASSERT(numMols >= 0);

In alternativa, includere un'asserzione CRT analoga alla seguente:

_ASSERT(numMols >= 0);

Queste istruzioni non hanno alcun effetto se il programma opera correttamente.Se un errore logico fa sì che numMolsa essere minore di zero, tuttavia, arresti di asserzione l'esecuzione del programma e visualizzazioni Finestra di dialogo Asserzione non riuscita.

In questo argomento

ww5t02fa.collapse_all(it-it,VS.110).gifVerificare i risultati

Le asserzioni risultano utili per testare le operazioni con risultati non sono ovvi a rapidamente.

Si consideri ad esempio il codice che segue, che aggiorna la variabile iMols sulla base del contenuto della lista collegata cui mols punta:

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

Il numero di molecole conteggiato da iMols deve essere sempre minore o uguale al numero totale delle molecole, numMols.L'esame visivo del ciclo non permette di verificare che ciò si verifichi necessariamente, pertanto si ricorre a un'istruzione di asserzione dopo il ciclo per verificare la conformità a tale condizione.

In questo argomento

ww5t02fa.collapse_all(it-it,VS.110).gifIndividuare errori non gestiti

È possibile utilizzare asserzioni per rilevare eventuali condizioni di errore in un punto del codice in cui tutti gli errori dovrebbero essere stati gestiti.Nell'esempio che segue, una routine grafica restituisce un codice di errore oppure, in caso di esito positivo, il valore zero.

myErr = myGraphRoutine(a, b);

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

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

Se il codice di gestione degli errori funziona correttamente, l'errore dovrebbe essere gestito e myErr impostato nuovamente su zero prima di raggiungere l'asserzione.Se myErr presenta un altro valore, l'asserzione avrà esito negativo, il programma verrà interrotto e verrà visualizzata la finestra di dialogo Finestra di dialogo Asserzione non riuscita.

Le istruzioni di asserzione non sostituiscono tuttavia il codice di gestione degli errori.L'esempio che segue riporta un'istruzione di asserzione che può condurre a problemi nel codice della versione finale:

myErr = myGraphRoutine(a, b);

/* No Code to handle errors */

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

Questo codice si basa sull'istruzione di asserzione per gestire la condizione di errore.Di conseguenza qualsiasi codice di errore restituito da myGraphRoutine risulterà non gestito nel codice della versione finale.

In questo argomento

Vedere anche

Riferimenti

Asserzioni nel codice gestito

Concetti

Sicurezza del debugger

Altre risorse

Debug del codice nativo