Sök…


Introduktion

En påstående är ett predikat att det presenterade villkoret måste vara sant i det ögonblick som påståendet stöter på programvaran. Vanligtvis är enkla påståenden som valideras vid utförandet. Statiska påståenden kontrolleras emellertid vid sammanställningstidpunkten.

Syntax

  • assert (uttryck)
  • static_assert (uttryck, meddelande)
  • _Static_assert (uttryck, meddelande)

parametrar

Parameter detaljer
uttryck uttryck av skalartyp.
meddelande strängbokstav som ska inkluderas i diagnosmeddelandet.

Anmärkningar

Både assert och static_assert är makron som definieras i assert.h .

Definitionen av assert beror på makro NDEBUG som inte definieras av standardbiblioteket. Om NDEBUG är definierat är assert ett no-op:

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

Yttrandet varierar om NDEBUG alltid ska användas för produktionssammansättningar.

  • Den pro-lägret hävdar att assert samtal abort och påstående meddelanden inte bra för slutanvändare, så resultatet är inte till hjälp för användaren. Om du har dödliga förhållanden för att checka in produktionskod bör du använda vanliga if/else villkor och exit eller quick_exit att avsluta programmet. I motsats till abort tillåter dessa programmet att göra en sanering (via funktioner registrerade med atexit eller at_quick_exit ).
  • Con-campen hävdar att assert aldrig ska avfyras produktionskod, men om de gör det betyder villkoret att det är något dramatiskt fel och att programmet kommer att bete sig sämre om exekveringen fortsätter. Därför är det bättre att ha påståendena aktiva i produktionskoden eftersom om de skjuter har helvetet redan lossnat.
  • Ett annat alternativ är att använda ett hembryggningssystem av påståenden som alltid utför kontrollen men hanterar fel annorlunda mellan utveckling (där abort är lämpligt) och produktion (där ett "oväntat internt fel - kontakta teknisk support" kan vara mer lämpligt).

static_assert expanderar till _Static_assert som är ett nyckelord. Villkoret kontrolleras vid sammanställningstiden, så att condition måste vara ett konstant uttryck. Det finns inget behov av att detta hanteras annorlunda mellan utveckling och produktion.

Förkonditionering och postkondition

Ett användningsfall för påståenden är förkonditionering och postkondition. Detta kan vara mycket användbart för att upprätthålla invariant och design genom kontrakt . För ett exempel är en längd alltid noll eller positiv så denna funktion måste returnera ett noll eller positivt värde.

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

Enkel påstående

En påstående är ett uttalande som används för att hävda att ett faktum måste vara sant när den kodraden nås. Påståenden är användbara för att säkerställa att förväntade villkor uppfylls. När villkoret som överförs till ett påstående är sant, finns det ingen åtgärd. Uppträdandet på falska förhållanden beror på kompilatorflaggor. När påståenden är aktiverade orsakar en falsk inmatning ett omedelbart programstopp. När de är inaktiverade vidtas inga åtgärder. Det är vanligt att aktivera påståenden i interna och debug-build och inaktivera dem i release-build, även om påståenden ofta aktiveras i release. (Huruvida avslutningen är bättre eller sämre än fel beror på programmet.) Påståenden bör endast användas för att fånga interna programmeringsfel, vilket vanligtvis innebär att passera dåliga parametrar.

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

Möjlig utgång med NDEBUG undefined:

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

Möjlig utgång med NDEBUG definierad:

x = -1

Det är god praxis att definiera NDEBUG globalt, så att du enkelt kan kompilera din kod med alla påståenden antingen på eller av. Ett enkelt sätt att göra detta är att definiera NDEBUG som ett alternativ för kompilatorn, eller definiera det i en delad konfigurationshuvud (t.ex. config.h ).

Statisk påstående

C11

Statiska påståenden används för att kontrollera om ett villkor är sant när koden sammanställs. Om det inte är det krävs kompilatorn att utfärda ett felmeddelande och stoppa kompileringsprocessen.

En statisk påstående är en som kontrolleras vid sammanställningstid, inte körtid. Villkoret måste vara ett konstant uttryck, och om falskt kommer att resultera i ett kompilatorfel. Det första argumentet, villkoret som kontrolleras, måste vara ett konstant uttryck, och det andra en strängbokstavlig.

Till skillnad från _Static_assert är _Static_assert ett nyckelord. En bekväm makro static_assert definieras i <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

Före C11 fanns inget direkt stöd för statiska påståenden. I C99 kan emellertid statiska påståenden emuleras med makron som skulle utlösa ett kompilationsfel om kompileringstidsvillkoret var falskt. Till skillnad från _Static_assert den andra parametern vara ett korrekt tokenamn så att ett variabelnamn kan skapas med det. Om påståendet misslyckas syns variabelns namn i kompilatorfelet, eftersom den variabeln användes i en syntaktiskt felaktig matrisdeklaration.

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

Innan C99 kunde du inte deklarera variabler på godtyckliga platser i ett block, så du måste vara extremt försiktig med att använda detta makro och se till att det bara visas där en variabeldeklaration skulle vara giltig.

Anvisning av oåtkomlig kod

Under utveckling, när vissa kodvägar måste förhindras från kontrollflödes räckvidd, kan du använda assert(0) att indikera att ett sådant tillstånd är felaktigt:

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

    default:
        assert(0);
}

Närhelst argumentet för assert() hämtar falskt kommer makroen att skriva diagnostisk information till standardfelströmmen och avbryta sedan programmet. Denna information inkluderar fil- och assert() påståendet assert() och kan vara till stor hjälp vid felsökning. Anmärkningar kan inaktiveras genom att definiera makro NDEBUG .

Ett annat sätt att avsluta ett program när ett fel uppstår är med standardbiblioteksfunktionerna exit , quick_exit eller abort . exit och quick_exit ta ett argument som kan skickas tillbaka till din miljö. abort() (och därmed assert ) kan vara en riktigt allvarlig avslutning av ditt program, och vissa saneringar som annars skulle utföras i slutet av exekveringen kanske inte utförs.

Den främsta fördelen med att assert() är att den automatiskt skriver ut felsökningsinformation. Att ringa abort() har fördelen att det inte kan inaktiveras som ett påstående, men det kanske inte får någon felsökningsinformation att visas. I vissa situationer kan det vara fördelaktigt att använda båda konstruktionerna tillsammans:

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

När påståenden är aktiverade kommer assert() att skriva ut felsökningsinformation och avbryta programmet. Utförandet når aldrig abort() samtalet. När påståenden är inaktiverade gör assert() -samtalet ingenting och abort() . Detta säkerställer att programmet alltid avslutas för detta feltillstånd; aktivering och inaktivering påstår endast effekter oavsett om felsökningsutskrift skrivs ut eller inte.

Du får aldrig lämna ett sådant assert i produktionskod, eftersom felsökningsinformationen inte är användbar för slutanvändare och eftersom abort allmänhet är en alltför svår avslutning som hindrar saneringshanterare som är installerade för att exit eller quick_exit att köra.

Ange felmeddelanden

Det finns ett trick som kan visa ett felmeddelande tillsammans med en påstående. Normalt skulle du skriva kod som den här

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

Om påståendet misslyckades, skulle ett felmeddelande likna

Påståendet misslyckades: p! = NULL, fil main.c, rad 5

Du kan dock använda logiska OCH ( && ) för att ge ett felmeddelande också

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

Om påståendet misslyckas kommer ett felmeddelande att läsa något liknande

Påståendet misslyckades: p! = NULL && "funktion f: p kan inte vara NULL", fil main.c, rad 5

Anledningen till att detta fungerar är att en strängbokstav alltid utvärderar till icke-noll (sant). Att lägga till && 1 till ett booleskt uttryck har ingen effekt. Således lägger till && "error message" ingen effekt heller, förutom att kompilatorn kommer att visa hela uttrycket som misslyckades.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow