C Language
Behauptung
Suche…
Einführung
Eine Assertion ist ein Prädikat, dass die dargestellte Bedingung in dem Moment wahr sein muss, in dem die Software von der Assertion getroffen wird. Am häufigsten sind einfache Zusicherungen , die zur Ausführungszeit überprüft werden. Statische Zusicherungen werden jedoch zur Kompilierzeit geprüft.
Syntax
- behaupten (Ausdruck)
- static_assert (Ausdruck, Nachricht)
- _Static_assert (Ausdruck, Nachricht)
Parameter
Parameter | Einzelheiten |
---|---|
Ausdruck | Ausdruck des Skalartyps. |
Botschaft | String-Literal, das in die Diagnosemeldung aufgenommen werden soll. |
Bemerkungen
Sowohl assert
und static_assert
sind Makros in definierten assert.h
.
Die Definition von assert
hängt von der Makro NDEBUG
, die nicht durch die Standardbibliothek definiert ist. Wenn NDEBUG
definiert ist, assert
ist ein No-op:
#ifdef NDEBUG # define assert(condition) ((void) 0) #else # define assert(condition) /* implementation defined */ #endif
Es gibt unterschiedliche Meinungen darüber, ob NDEBUG
immer für Produktionszusammenstellungen verwendet werden sollte.
- Das Pro-Lager argumentiert , dass
assert
Anrufeabort
und Assertion - Meldungen sind nicht hilfreich für den Endverbraucher, so das Ergebnis Benutzer nicht hilfreich ist. Wenn Sie schwerwiegende Bedingungen zum Einchecken des Produktionscodes haben, sollten Sie normaleif/else
Bedingungen verwenden undexit
oderquick_exit
zumquick_exit
des Programms verwenden. Im Gegensatz zumabort
kann das Programm einige Bereinigungenatexit
(über Funktionen, die mitatexit
oderat_quick_exit
registriertat_quick_exit
). - Das Con-Camp argumentiert, dass
assert
Aufrufe niemals im Produktionscode ausgelöst werden sollten, aber wenn dies der Fall ist, bedeutet dies, dass etwas dramatisch falsch ist und das Programm sich schlechter benimmt, wenn die Ausführung fortgesetzt wird. Daher ist es besser, die Aussagen im Produktionscode zu aktivieren, denn wenn sie feuern, ist die Hölle bereits gebrochen. - Eine andere Möglichkeit besteht darin, ein Selbstbrühsystem für Assertions zu verwenden, das die Prüfung immer vornimmt, jedoch Fehler zwischen Entwicklung (wo
abort
angebracht ist) und Produktion (bei "unerwartetem internem Fehler - wenden Sie sich an den technischen Support" an, möglicherweise angemessener).
static_assert
erweitert sich zu _Static_assert
, einem Schlüsselwort. Die Bedingung wird zur Kompilierzeit geprüft, daher muss die condition
ein konstanter Ausdruck sein. Es ist nicht erforderlich, dass dies in der Entwicklung und in der Produktion anders gehandhabt wird.
Vorbedingung und Nachbedingung
Ein Anwendungsfall für die Behauptung ist Vorbedingung und Nachbedingung. Dies kann sehr nützlich sein, um die Invariante und das vertragliche Design zu erhalten. Für ein Beispiel ist eine Länge immer Null oder positiv, daher muss diese Funktion einen Nullwert oder einen positiven Wert zurückgeben.
#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;
}
Einfache Assertion
Eine Assertion ist eine Aussage, mit der behauptet wird, dass eine Tatsache wahr sein muss, wenn diese Codezeile erreicht ist. Zusicherungen sind hilfreich, um sicherzustellen, dass die erwarteten Bedingungen erfüllt werden. Wenn die an eine Assertion übergebene Bedingung wahr ist, gibt es keine Aktion. Das Verhalten bei falschen Bedingungen hängt von Compilerflags ab. Wenn Assertions aktiviert sind, führt eine falsche Eingabe zum sofortigen Programmstopp. Wenn sie deaktiviert sind, wird keine Aktion ausgeführt. Es ist üblich, Assertions in internen Builds und Debug-Builds zu aktivieren und sie in Release-Builds zu deaktivieren, obwohl Assertions häufig in Release aktiviert werden. (Ob die Beendigung besser oder schlechter als Fehler ist, hängt vom Programm ab.) Assertions sollten nur zum Abfangen von internen Programmierfehlern verwendet werden, was normalerweise bedeutet, dass schlechte Parameter übergeben werden.
#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ögliche Ausgabe mit NDEBUG
undefined:
a.out: main.c:9: main: Assertion `x >= 0' failed.
Mögliche Ausgabe mit NDEBUG
definiert:
x = -1
Es ist NDEBUG
, NDEBUG
global zu definieren, sodass Sie Ihren Code mit allen Zusicherungen entweder NDEBUG
oder ausschalten können. Eine einfache Möglichkeit, dies zu tun, ist die Definition von NDEBUG
als Option für den Compiler oder in einem gemeinsam genutzten Konfigurationsheader (z. B. config.h
).
Statische Assertion
Statische Zusicherungen werden verwendet, um zu überprüfen, ob eine Bedingung erfüllt ist, wenn der Code kompiliert wird. Ist dies nicht der Fall, muss der Compiler eine Fehlermeldung ausgeben und den Kompiliervorgang stoppen.
Eine statische Zusicherung ist eine, die zur Kompilierzeit geprüft wird, nicht zur Laufzeit. Die Bedingung muss ein konstanter Ausdruck sein, und falls false, wird ein Compilerfehler ausgegeben. Das erste Argument, die geprüfte Bedingung, muss ein konstanter Ausdruck sein und das zweite ein Zeichenkettenliteral.
Im Gegensatz zu assert ist _Static_assert
ein Schlüsselwort. Ein komfortables Makro static_assert
ist 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 */
Vor C11 gab es keine direkte Unterstützung für statische Assertions. In C99 konnten statische Assertions jedoch mit Makros emuliert werden, die einen Kompilierungsfehler auslösen würden, wenn die Kompilierzeitbedingung falsch war. Im Gegensatz zu _Static_assert
muss der zweite Parameter ein geeigneter Token-Name sein, damit ein Variablenname erstellt werden kann. Wenn die Assertion fehlschlägt, wird der Variablenname im Compiler-Fehler angezeigt, da diese Variable in einer syntaktisch falschen Array-Deklaration verwendet wurde.
#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 */
Vor C99 konnten Sie keine Variablen an beliebigen Stellen in einem Block deklarieren. Daher müssen Sie bei der Verwendung dieses Makros äußerst vorsichtig sein und sicherstellen, dass nur dort angezeigt wird, wo eine Variablendeklaration gültig ist.
Geltendmachung eines unerreichbaren Codes
Wenn während der Entwicklung bestimmte Codepfade von der Reichweite des Steuerungsflusses ferngehalten werden müssen, können Sie assert(0)
um anzuzeigen, dass eine solche Bedingung fehlerhaft ist:
switch (color) {
case COLOR_RED:
case COLOR_GREEN:
case COLOR_BLUE:
break;
default:
assert(0);
}
Wenn das Argument des assert()
-Makros den Wert false ergibt, schreibt das Makro Diagnoseinformationen in den Standardfehlerstrom und bricht das Programm ab. Diese Informationen enthalten die Datei- und Zeilennummer der assert()
Anweisung und können beim Debuggen sehr hilfreich sein. Asserts können durch Definieren des Makros NDEBUG
.
Eine andere Möglichkeit, ein Programm zu beenden, wenn ein Fehler auftritt, sind die Standardbibliotheksfunktionen exit
, quick_exit
oder abort
. exit
und quick_exit
nehmen ein Argument an, das an Ihre Umgebung zurückgegeben werden kann. abort()
(und damit assert
) kann eine wirklich schwerwiegende Beendigung Ihres Programms sein, und bestimmte Bereinigungen, die andernfalls am Ende der Ausführung durchgeführt würden, werden möglicherweise nicht durchgeführt.
Der Hauptvorteil von assert()
ist, dass Debugging-Informationen automatisch gedruckt werden. Das Aufrufen von abort()
hat den Vorteil, dass es nicht wie ein Assert deaktiviert werden kann, aber möglicherweise keine Debugging-Informationen angezeigt werden. In einigen Situationen kann die Verwendung beider Konstrukte von Vorteil sein:
if (color == COLOR_RED || color == COLOR_GREEN) {
...
} else if (color == COLOR_BLUE) {
...
} else {
assert(0), abort();
}
Wenn Assert assert()
aktiviert sind , gibt der assert()
Aufruf die Debug-Informationen aus und beendet das Programm. Die Ausführung erreicht niemals den abort()
Aufruf. Wenn Asserts deaktiviert sind , macht der assert()
Aufruf nichts und abort()
wird aufgerufen. Dadurch wird sichergestellt, dass das Programm für diese Fehlerbedingung immer beendet wird. Durch Aktivieren und Deaktivieren wird nur die Auswirkung aktiviert, ob die Debug-Ausgabe gedruckt wird.
Sie sollten eine solche assert
niemals im Produktionscode belassen, da die Debug-Informationen für Endbenutzer nicht hilfreich sind und der abort
im Allgemeinen eine viel zu schwerwiegende Beendigung darstellt, die die für exit
oder quick_exit
installierten Cleanup-Handler quick_exit
.
Bestätigen Sie Fehlermeldungen
Es gibt einen Trick, der neben einer Assertion eine Fehlermeldung anzeigen kann. Normalerweise würden Sie Code so schreiben
void f(void *p)
{
assert(p != NULL);
/* more code */
}
Wenn die Zusicherung fehlgeschlagen ist, würde eine Fehlermeldung ähneln
Assertion fehlgeschlagen: p! = NULL, Datei main.c, Zeile 5
Sie können jedoch auch das logische AND ( &&
) verwenden, um eine Fehlermeldung zu geben
void f(void *p)
{
assert(p != NULL && "function f: p cannot be NULL");
/* more code */
}
Wenn die Zusicherung fehlschlägt, wird eine Fehlermeldung in etwa folgendermaßen angezeigt
Assertion fehlgeschlagen: p! = NULL && "Funktion f: p kann nicht NULL sein", Datei main.c, Zeile 5
Der Grund, warum dies funktioniert, ist, dass ein Zeichenfolgenliteral immer den Wert ungleich Null (wahr) ergibt. Das Hinzufügen von && 1
zu einem booleschen Ausdruck hat keine Auswirkung. Das Hinzufügen von && "error message"
hat also keine Auswirkung, außer dass der Compiler den gesamten fehlgeschlagenen Ausdruck anzeigt.