C Language
Bewering
Zoeken…
Invoering
Een bewering is een predicaat dat de gepresenteerde toestand waar moet zijn op het moment dat de bewering door de software wordt aangetroffen. De meest voorkomende zijn eenvoudige beweringen , die worden gevalideerd tijdens de uitvoering. Statische beweringen worden echter tijdens het compileren gecontroleerd.
Syntaxis
- assert (expressie)
- static_assert (expressie, bericht)
- _Static_assert (expressie, bericht)
parameters
Parameter | Details |
---|---|
uitdrukking | expressie van scalair type. |
bericht | letterlijke tekenreeks die moet worden opgenomen in het diagnostische bericht. |
Opmerkingen
Zowel assert
als static_assert
zijn macro's gedefinieerd in assert.h
.
De definitie van assert
hangt af van de macro NDEBUG
die niet wordt gedefinieerd door de standaardbibliotheek. Als NDEBUG
is gedefinieerd, is assert
een no-op:
#ifdef NDEBUG # define assert(condition) ((void) 0) #else # define assert(condition) /* implementation defined */ #endif
De meningen lopen uiteen of NDEBUG
altijd moet worden gebruikt voor productiecompilaties.
- De pro-camp beweert dat
assert
abort
en bewaarberichten niet nuttig zijn voor eindgebruikers, dus het resultaat is niet nuttig voor de gebruiker. Als u fatale voorwaarden heeft om de productiecode in te checken, moet u normaleif/else
voorwaarden gebruiken enexit
ofquick_exit
om het programma te beëindigen. In tegenstelling totabort
, stellen deze programma's het programma in staat enige opruimingen te doen (via functies die zijn geregistreerd bijatexit
ofat_quick_exit
). - Het kamp beweert dat
assert
nooit in productiecode mogen afvuren, maar als ze dat doen, betekent de aangevinkte voorwaarde dat er iets dramatisch mis is en het programma zich slechter zal gedragen als de uitvoering doorgaat. Daarom is het beter om de beweringen actief te hebben in de productiecode, want als ze schieten, is de hel al losgebroken. - Een andere optie is om een huisbrouwsysteem van beweringen te gebruiken dat altijd de controle uitvoert, maar fouten anders behandelt tussen ontwikkeling (waar
abort
geschikt is) en productie (waar een 'onverwachte interne fout - neem contact op met Technische Ondersteuning').
static_assert
breidt uit naar _Static_assert
wat een trefwoord is. De aandoening wordt gecontroleerd tijdens het compileren, waardoor condition
moet een constante expressie. Het is niet nodig dat dit anders wordt behandeld tussen ontwikkeling en productie.
Voorwaarde en postconditie
Eén use case voor bewering is voorwaarde en postconditie. Dit kan zeer nuttig zijn om invariant en ontwerp per contract te onderhouden. Voor een voorbeeld is een lengte altijd nul of positief, dus deze functie moet een nul of positieve waarde retourneren.
#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;
}
Eenvoudige bewering
Een bewering is een bewering die wordt gebruikt om te beweren dat een feit waar moet zijn wanneer die regel code wordt bereikt. Beweringen zijn nuttig om ervoor te zorgen dat aan de verwachte voorwaarden wordt voldaan. Wanneer de voorwaarde die aan een bewering wordt doorgegeven, waar is, is er geen actie. Het gedrag onder valse omstandigheden is afhankelijk van compilervlaggen. Wanneer beweringen zijn ingeschakeld, zorgt een valse invoer ervoor dat het programma onmiddellijk wordt gestopt. Als ze zijn uitgeschakeld, wordt er geen actie ondernomen. Het is gebruikelijk om beweringen in interne en debug-builds in te schakelen en uit te schakelen in release-builds, hoewel beweringen vaak zijn ingeschakeld in release. (Of beëindiging beter of slechter is dan fouten, is afhankelijk van het programma.) Beweringen mogen alleen worden gebruikt om interne programmeerfouten op te vangen, wat meestal betekent dat er slechte parameters worden doorgegeven.
#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;
}
Mogelijke uitvoer met NDEBUG
niet gedefinieerd:
a.out: main.c:9: main: Assertion `x >= 0' failed.
Mogelijke uitvoer met NDEBUG
:
x = -1
Het is een goede gewoonte om NDEBUG
globaal te definiëren, zodat u uw code eenvoudig kunt compileren met alle beweringen aan of uit. Een eenvoudige manier om dit te doen, is NDEBUG
definiëren als een optie voor de compiler, of definiëren in een gedeelde configuratiekop (bijv. config.h
).
Statische bewering
Statische beweringen worden gebruikt om te controleren of een voorwaarde waar is wanneer de code wordt gecompileerd. Als dit niet het geval is, moet de compiler een foutmelding geven en het compilatieproces stoppen.
Een statische bewering is er een die tijdens het compileren wordt gecontroleerd, niet tijdens de uitvoering. De voorwaarde moet een constante expressie zijn en als false resulteert in een compilerfout. Het eerste argument, de voorwaarde die is aangevinkt, moet een constante uitdrukking zijn en het tweede een letterlijke tekenreeks.
In tegenstelling tot assert is _Static_assert
een trefwoord. Een gemakkelijke macro static_assert
is gedefinieerd 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 */
Vóór C11 was er geen directe ondersteuning voor statische beweringen. In C99 kunnen statische beweringen echter worden geëmuleerd met macro's die een compilatiefout zouden veroorzaken als de compilatietijd niet waar was. In tegenstelling tot _Static_assert
moet de tweede parameter een juiste _Static_assert
, zodat hiermee een variabelenaam kan worden gemaakt. Als de bewering mislukt, wordt de variabelenaam weergegeven in de compilerfout, omdat die variabele is gebruikt in een syntactisch onjuiste arraydeclaratie.
#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 */
Vóór C99 kon je geen variabelen op willekeurige locaties in een blok declareren, dus je zou uiterst voorzichtig moeten zijn met het gebruik van deze macro, ervoor zorgen dat het alleen verschijnt waar een variabeledeclaratie geldig zou zijn.
Bewering van onbereikbare code
Wanneer tijdens de ontwikkeling bepaalde codepads buiten het bereik van de besturingsstroom moeten worden voorkomen, kunt u assert(0)
om aan te geven dat een dergelijke voorwaarde onjuist is:
switch (color) {
case COLOR_RED:
case COLOR_GREEN:
case COLOR_BLUE:
break;
default:
assert(0);
}
Wanneer het argument van de macro assert()
false evalueert, schrijft de macro diagnostische informatie naar de standaardfoutstroom en wordt het programma afgebroken. Deze informatie bevat het bestand en het regelnummer van de assert()
-instructie en kan zeer nuttig zijn bij het opsporen van fouten. Beweringen kunnen worden uitgeschakeld door de macro NDEBUG
definiëren.
Een andere manier om een programma te beëindigen wanneer er een fout optreedt, is met de standaardbibliotheekfuncties exit
, quick_exit
of abort
. exit
en quick_exit
nemen een argument dat aan uw omgeving kan worden doorgegeven. abort()
(en dus assert
) kan een echt ernstige beëindiging van uw programma zijn, en bepaalde opruimingen die anders aan het einde van de uitvoering zouden worden uitgevoerd, worden mogelijk niet uitgevoerd.
Het primaire voordeel van assert()
is dat het automatisch foutopsporingsinformatie afdrukt. Het aanroepen van abort()
heeft het voordeel dat het niet als een assert kan worden uitgeschakeld, maar het mag er niet voor zorgen dat foutopsporingsinformatie wordt weergegeven. In sommige situaties kan het nuttig zijn om beide constructies samen te gebruiken:
if (color == COLOR_RED || color == COLOR_GREEN) {
...
} else if (color == COLOR_BLUE) {
...
} else {
assert(0), abort();
}
Wanneer asserts zijn ingeschakeld , drukt de assert()
-oproep foutopsporingsinformatie af en wordt het programma beëindigd. Uitvoering bereikt nooit de aanroep abort()
. Wanneer asserts zijn uitgeschakeld , doet de call assert()
niets en wordt abort()
aangeroepen. Dit zorgt ervoor dat het programma altijd eindigt voor deze foutconditie; inschakelen en uitschakelen beweert alleen effecten of de foutopsporingsuitvoer al dan niet wordt afgedrukt.
U mag nooit een dergelijke assert
achterlaten in de productiecode, omdat de foutopsporingsinformatie niet nuttig is voor eindgebruikers en omdat abort
over het algemeen een veel te ernstige beëindiging is die quick_exit
verhindert die zijn geïnstalleerd voor exit
of quick_exit
om te worden uitgevoerd.
Bevestig foutmeldingen
Er is een truc die een foutmelding samen met een bewering kan weergeven. Normaal zou u code zoals deze schrijven
void f(void *p)
{
assert(p != NULL);
/* more code */
}
Als de bewering mislukte, zou een foutmelding lijken
Bewering mislukt: p! = NULL, bestand main.c, regel 5
U kunt echter ook logische EN ( &&
) gebruiken om een foutmelding te geven
void f(void *p)
{
assert(p != NULL && "function f: p cannot be NULL");
/* more code */
}
Als de bewering niet slaagt, zal een foutmelding zoiets als dit lezen
Bewering mislukt: p! = NULL && "functie f: p kan niet NULL zijn", bestand main.c, regel 5
De reden waarom dit werkt, is dat een letterlijke tekenreeks altijd evalueert naar niet-nul (waar). Het toevoegen van && 1
aan een Booleaanse uitdrukking heeft geen effect. Het toevoegen van && "error message"
heeft dus ook geen effect, behalve dat de compiler de volledige expressie zal weergeven die is mislukt.