Ricerca…


introduzione

Un'affermazione è un predicato che la condizione presentata deve essere vera nel momento in cui l'asserzione viene rilevata dal software. Più comuni sono le asserzioni semplici , che vengono convalidate al momento dell'esecuzione. Tuttavia, le asserzioni statiche vengono controllate al momento della compilazione.

Sintassi

  • affermare (espressione)
  • static_assert (espressione, messaggio)
  • _Static_assert (espressione, messaggio)

Parametri

Parametro Dettagli
espressione espressione di tipo scalare.
Messaggio stringa letterale da includere nel messaggio di diagnostica.

Osservazioni

Sia assert che static_assert sono macro definite in assert.h .

La definizione di assert dipende dalla macro NDEBUG che non è definita dalla libreria standard. Se NDEBUG è definito, assert è un no-op:

#ifdef NDEBUG
#  define assert(condition) ((void) 0)
#else
#  define assert(condition) /* implementation defined */
#endif

L'opinione varia a seconda che NDEBUG debba essere sempre utilizzato per le compilation di produzione.

  • Il campo professionale sostiene che assert chiamate abort e che i messaggi di asserzione non sono utili per gli utenti finali, quindi il risultato non è utile all'utente. Se si verificano condizioni fatali per il controllo del codice di produzione, è necessario utilizzare le normali condizioni if/else e exit o quick_exit per terminare il programma. Al contrario di abort , questi permettono al programma di fare un po 'di pulizia (tramite funzioni registrate con atexit o at_quick_exit ).
  • Il campo di concentramento sostiene che le chiamate di assert non dovrebbero mai attivare il codice di produzione, ma se lo fanno, la condizione che viene controllata significa che c'è qualcosa di drammaticamente sbagliato e il programma si comporterebbe in modo peggiore se l'esecuzione dovesse continuare. Pertanto, è meglio avere le asserzioni attive nel codice di produzione perché se sparano, l'inferno si è già scatenato.
  • Un'altra opzione consiste nell'utilizzare un sistema di asserzioni home-brew che esegue sempre il controllo ma gestisce gli errori in modo diverso tra lo sviluppo (dove l' abort è appropriata) e la produzione (dove un 'errore interno imprevisto - contattare l'assistenza tecnica' potrebbe essere più appropriato).

static_assert espande in _Static_assert che è una parola chiave. La condizione viene verificata al momento della compilazione, quindi la condition deve essere un'espressione costante. Non è necessario che questo sia gestito in modo diverso tra sviluppo e produzione.

Presupposto e Postcondizione

Un caso d'uso per l'asserzione è precondizione e post-condizionale. Questo può essere molto utile per mantenere invariabile e progettare per contratto . Ad esempio, una lunghezza è sempre zero o positiva, quindi questa funzione deve restituire uno zero o un valore positivo.

#include <stdio.h>
/* Uncomment to disable `assert()` */
/* #define NDEBUG */
#include <assert.h>

int length2 (int *a, int count)
{
    int i, result = 0;

    /* Precondition: */
    /* NULL is an invalid vector */
    assert (a != NULL);
    /* Number of dimensions can not be negative.*/ 
    assert (count >= 0);

    /* Calculation */
    for (i = 0; i < count; ++i) 
    {
        result = result + (a[i] * a[i]);
    }

    /* Postcondition: */
    /* Resulting length can not be negative. */
    assert (result >= 0);
    return result;
}

#define COUNT 3

int main (void)
{
    int a[COUNT] = {1, 2, 3};
    int *b = NULL;
    int r;
    r = length2 (a, COUNT);
    printf ("r = %i\n", r);
    r = length2 (b, COUNT);
    printf ("r = %i\n", r);
    return 0;
}

Asserzione semplice

Un'asserzione è un'affermazione usata per affermare che un fatto deve essere vero quando viene raggiunta quella linea di codice. Le asserzioni sono utili per garantire che le condizioni previste siano soddisfatte. Quando la condizione passata a un'affermazione è vera, non c'è azione. Il comportamento su false condizioni dipende dai flag del compilatore. Quando le asserzioni sono abilitate, un falso input provoca l'arresto immediato del programma. Quando sono disabilitati, non viene intrapresa alcuna azione. È pratica comune abilitare le asserzioni nelle build interne e di debug e disabilitarle nelle build di rilascio, sebbene le asserzioni siano spesso abilitate nel rilascio. (Se la terminazione è migliore o peggiore degli errori dipende dal programma.) Le asserzioni dovrebbero essere utilizzate solo per rilevare errori di programmazione interni, che in genere significa essere passati parametri non validi.

#include <stdio.h>
/* Uncomment to disable `assert()` */
/* #define NDEBUG */
#include <assert.h>

int main(void)
{
    int x = -1;
    assert(x >= 0);

    printf("x = %d\n", x);   
    return 0;
}

Possibile output con NDEBUG indefinito:

a.out: main.c:9: main: Assertion `x >= 0' failed.

Possibile output con NDEBUG definito:

x = -1

È buona norma definire NDEBUG livello globale, in modo da poter facilmente compilare il codice con tutte le asserzioni NDEBUG o disattivate. Un modo semplice per farlo è definire NDEBUG come opzione per il compilatore, o definirlo in un'intestazione di configurazione condivisa (es. config.h ).

Asserzione statica

C11

Le asserzioni statiche vengono utilizzate per verificare se una condizione è vera quando il codice è compilato. In caso contrario, è richiesto al compilatore di inviare un messaggio di errore e interrompere il processo di compilazione.

Un'asserzione statica è quella che viene verificata al momento della compilazione, non il tempo di esecuzione. La condizione deve essere un'espressione costante e se false genererà un errore del compilatore. Il primo argomento, la condizione che viene controllata, deve essere un'espressione costante e il secondo una stringa letterale.

A differenza _Static_assert , _Static_assert è una parola chiave. Una macro di convenienza static_assert è definita in <assert.h> .

#include <assert.h>

enum {N = 5};
_Static_assert(N == 5, "N does not equal 5");
static_assert(N > 10, "N is not greater than 10");  /* compiler error */
C99

Prima di C11, non esisteva alcun supporto diretto per asserzioni statiche. Tuttavia, in C99, le asserzioni statiche potevano essere emulate con macro che avrebbero attivato un errore di compilazione se la condizione di compilazione era falsa. A differenza di _Static_assert , il secondo parametro deve essere un nome di token appropriato in modo che possa essere creato un nome di variabile con esso. Se l'asserzione fallisce, il nome della variabile viene visualizzato nell'errore del compilatore, poiché tale variabile è stata utilizzata in una dichiarazione di array sintatticamente errata.

#define STATIC_MSG(msg, l) STATIC_MSG2(msg, l)
#define STATIC_MSG2(msg,l) on_line_##l##__##msg
#define STATIC_ASSERT(x, msg) extern char STATIC_MSG(msg, __LINE__) [(x)?1:-1]
 
enum { N = 5 };
STATIC_ASSERT(N == 5, N_must_equal_5);
STATIC_ASSERT(N > 5, N_must_be_greater_than_5); /* compile error */

Prima di C99, non si potevano dichiarare variabili in posizioni arbitrarie in un blocco, quindi si dovrebbe essere estremamente cauti nell'usare questa macro, assicurandosi che appaia solo dove una dichiarazione di variabile sarebbe valida.

Asserzione di codice irraggiungibile

Durante lo sviluppo, quando determinati percorsi di codice devono essere impediti dalla portata del flusso di controllo, è possibile utilizzare assert(0) per indicare che tale condizione è errata:

switch (color) {
    case COLOR_RED:
    case COLOR_GREEN:
    case COLOR_BLUE:
        break;

    default:
        assert(0);
}

Ogni volta che l'argomento della macro assert() valutato falso, la macro scriverà le informazioni diagnostiche sul flusso di errore standard e quindi interromperà il programma. Queste informazioni includono il numero di file e di riga assert() e possono essere molto utili nel debugging. Gli avvisi possono essere disabilitati definendo la macro NDEBUG .

Un altro modo per terminare un programma quando si verifica un errore sono con le funzioni di libreria standard exit , quick_exit o abort . exit e quick_exit accettano un argomento che può essere passato al tuo ambiente. abort() (e quindi assert ) può essere una terminazione molto grave del programma, e alcune pulizie che altrimenti verrebbero eseguite alla fine dell'esecuzione, potrebbero non essere eseguite.

Il vantaggio principale di assert() è che stampa automaticamente le informazioni di debug. Calling abort() ha il vantaggio che non può essere disattivato come un assert, ma non può causare la visualizzazione di informazioni di debug. In alcune situazioni, l'utilizzo di entrambi i costrutti può essere utile:

if (color == COLOR_RED || color == COLOR_GREEN) {
   ...
} else if (color == COLOR_BLUE) {
   ...
} else {
   assert(0), abort();
}

Quando gli assert() sono abilitati , la chiamata assert() stamperà le informazioni di debug e terminerà il programma. L'esecuzione non raggiunge mai la chiamata abort() . Quando gli assert() sono disabilitati , la chiamata assert() non fa nulla e viene abort() . Ciò garantisce che il programma termini sempre per questa condizione di errore; l'abilitazione e la disabilitazione asserisce solo gli effetti indipendentemente dalla stampa dell'output di debug.

Non si dovrebbe mai lasciare una tale assert nel codice di produzione, poiché le informazioni di debug non sono utili per gli utenti finali e perché l' abort è in genere una terminazione troppo grave che impedisce l'esecuzione di gestori di pulizia installati per l' exit o di quick_exit .

Segnala messaggi di errore

Esiste un trucco in grado di visualizzare un messaggio di errore insieme a un'asserzione. Normalmente, si dovrebbe scrivere un codice come questo

void f(void *p)
{
    assert(p != NULL);
    /* more code */
}

Se l'asserzione falliva, un messaggio di errore sarebbe simile

Asserzione non riuscita: p! = NULL, file main.c, riga 5

Tuttavia, è possibile utilizzare AND logico ( && ) per fornire anche un messaggio di errore

void f(void *p)
{
    assert(p != NULL && "function f: p cannot be NULL");
    /* more code */
}

Ora, se l'asserzione fallisce, un messaggio di errore leggerà qualcosa del genere

Asserzione non riuscita: p! = NULL && "funzione f: p non può essere NULL", file main.c, riga 5

La ragione per cui questo funziona è che una stringa letterale valuta sempre a non zero (true). Aggiungere && 1 a un'espressione booleana non ha alcun effetto. Pertanto, l'aggiunta di && "error message" non ha alcun effetto, ad eccezione del fatto che il compilatore visualizzerà l'intera espressione che ha avuto esito negativo.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow