C Language
Påstående
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
samtalabort
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 vanligaif/else
villkor ochexit
ellerquick_exit
att avsluta programmet. I motsats tillabort
tillåter dessa programmet att göra en sanering (via funktioner registrerade medatexit
ellerat_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
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 */
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.