Ich finde, dass die Verwendung von assert(...)
meinen Code kürzer und einfacher zu lesen macht, im Gegensatz zu langen if..else..
Blöcken. Gibt es jedoch gute technische Gründe, assert(...)
im Versandcode nicht zu verwenden, wenn dasselbe den Wert return
testet, während weniger Code verwendet wird?
Nachdem ich diesen Artikel gelesen habe, werde ich meine Ansichten über assert
:
Ja, es ist in Ordnung, assert
zu verwenden, wenn etwas absolut die Bedingung erfüllen sollte, die Sie geltend machen.
In vielen Sprachen können Sie benutzerdefinierte Fehler bei der Assertion auslösen. C ohne "Ausnahmen" kann zu Fehlern führen, die ein wenig schwieriger zu diagnostizieren sind, ohne direkt auf die betreffende Quelle zu schauen.
Während die anderen Antworten einige gute Informationen enthalten, scheint keine (mir) direkt auf die von Ihnen gestellte Frage eingegangen zu sein.
IMO, ja, es gibt einen ziemlich bedeutenden Mangel, assert
s im (meisten) Produktionscode zu verlassen: Sie haben keine Kontrolle über die angezeigte Fehlermeldung, die oft ziemlich unheimlich aussieht.
Anstelle von etwas wie:
%Vor%Ich gebe dem Kunden lieber etwas wie:
%Vor% assert()
ist für Dinge gedacht, die unter normalen Umständen niemals falsch sein sollten.
if
- else
ist für Dinge gedacht, die unter normalen Umständen manchmal falsch sein können, weil der else
-Anteil dafür ausgelegt ist, solche Situationen korrekt zu behandeln.
Kurz gesagt, if
-Anweisungen sind dafür vorgesehen, Abstürze zu vermeiden; assert()
wurde entwickelt, um sie zu verursachen .
Ich denke, dass die Verwendung von assert
für Laufzeitfehler eine schlechte Form ist, insbesondere weil sie der Konvention widerspricht:
assert
wird normalerweise aus Nicht-Debug-Builds kompiliert. Sie möchten möglicherweise alle Ihre Builds mit der Erwartung kompilieren, dass assert
immer aktiviert ist, aber jeder, der später Ihren Code erbt, versteht das möglicherweise nicht. Es wird noch schlimmer, wenn Sie Code schreiben, bei dem Ihre assert
s Nebenwirkungen haben (wie assert((fp = fopen(filename, "r")) != NULL)
).
Ebenso ist assert
für logische Fehler gedacht, nicht für Laufzeitfehler. Wenn Sie assert
verwenden, um Laufzeitfehler zu überprüfen, beginnen Sie falsch darzustellen, wie sich Ihr Code für jeden verhalten soll, der versucht, ihn zu lesen. Betreuer werden nicht in der Lage sein, leicht zwischen logischen Fehlern und möglichen Laufzeitfehlern zu unterscheiden, und die Unterscheidung ist nützlich, wenn über das Codeverhalten nachgedacht wird.
Und möchten Sie wirklich, dass Ihr Programm abrupt beendet wird und einen Kern ausgibt, wenn ein Benutzer versehentlich ein Eingabefeld leer lässt? Das ist unglaublich benutzerfeindlich.
Wenn Sie glauben, dass die Verwendung von assert
weniger Code ist, könnten Sie versuchen, Ihr eigenes, separates Makro zu schreiben. Das würde vermeiden, die erwartete Semantik von assert
und die Feindseligkeit zu ändern. Zum Beispiel etwas wie:
Behauptungen sind gut. Kompilierzeit-Behauptungen sind noch besser. Hinweis:
BOOST_STATIC_ASSERT()
. static_assert()
. static_assert()
: Hat GCC eine eingebaute Kompilierzeit-Assertion?
Wenn Ihre Umgebung noch keine statische Assertion hat, hier ein Vorschlag.
Das unten angegebene Makro ASSERT()
kann an einer beliebigen Stelle im Code platziert werden, außer:
#ifndef...#endif
wrapper. Wenn Sie etwas in die Mitte einer Strukturdefinition stecken wollen, müssen Sie das langweilige, hässliche, dreizeilige Konstrukt #if...#error...#endif
verwenden. Und wenn Sie dies tun, hat der Preprozessor eine viel begrenztere Vorstellung davon, was ein "konstanter Ausdruck" ist.
Dies ist eine Verfeinerung von Ideen aus dem Web, vor allem von Ссылка . Diese Definition ist kürzer als BOOST_STATIC_ASSERT()
. Und ich glaube, ist besser als Linus Vorschlag für eine verbesserte BUILD_BUG_ON()
. Und der do{...}while(0)
-Wrapper, den Sie normalerweise sehen, ist hier völlig unzutreffend, da er zulässige Speicherorte begrenzt.
Dies ist auch einfacher als Google Compile_ASSERT / CompileAssert. Der 'sizeof bitfield'-Trick scheint auch gut zu sein, von Linux BUILD_BUG_ON_ZERO()
, aber nicht sein nutzloses Geschwister BUILD_BUG_ON()
.
Es gibt viele Vorschläge für die Verwendung von Arrays mit einem negativen Index. Aber mit GCC erkennen die meisten von ihnen kein nicht-konstantes arg (was leicht genug ist, um irrtümlich zu tun), mit Ausnahme des 'extern int foo [expression]', das auch ein '' gibt. unbenutzte Variable 'Warnung. Aber typedef int array[expression]
scheint auch gut zu sein: siehe unten.
Die Definition:
%Vor%Ebenso gut, glaube ich, ist die folgende Variante, aber sie ist um fünf Zeichen länger:
%Vor% Es gibt auch das do{switch(0){case 0:case(e):;}}while(0)
-Konstrukt, das ich nicht untersucht habe.
Manchmal braucht man eine Variante, um den Fall zu behandeln, dass zufällig zwei verschiedene Header-Dateien vorkommen, um zwei ASSERT () in derselben Zeile oder ebenso für eine Quelldatei und eine Header-Datei zu haben. Sie könnten dies über __COUNTER__
behandeln, aber dies wird von einigen Compilern nicht unterstützt (und ist hässlicher). Und wir können __FILE__
nicht verwenden, weil es normalerweise nicht zu einem gültigen C-Token expandiert (z.B. hat es einen Punkt c oder Punkt h). Die Mozilla-Version Ссылка stellt fest, dass solche Konflikte "selten sein sollten", aber sie werden es tun sehr ärgern Sie Ihre Teamkollegen, wenn es passiert. Dies kann auch verwendet werden, um mehrere ASSERTS
in einem mehrzeiligen Makro zu behandeln, wobei sich __LINE__
nicht ändert.
Die nächste Variante, ASSERT_zero(),
, ähnelt BUILD_BUG_ON_ZERO(),
mit dem 'sizeof bitfield'-Trick. Dies ergibt entweder:
e
falsch oder So kann es an Stellen verwendet werden, an denen eine Anweisung nicht möglich ist, etwa in der Mitte eines Ausdrucks.
%Vor% IMO keine der Antworten hier sagt der wichtigste Teil: Assert Staaten Programmierer Annahmen. assert(x)
sagt etwas wie "x ist zu diesem Zeitpunkt immer wahr, du kannst das überprüfen, wenn du" willst. Wenn der Benutzer einen Assertionsfehler sieht, bedeutet dies, dass Sie etwas falsch gemacht haben.
Was Sie wahrscheinlich brauchen, ist eine Funktion, die fast dasselbe macht wie Assert, aber sowohl im Release- als auch im Debug-Modus. Die Verwendung von check(x)
würde bedeuten, dass "Überprüfen Sie, ob x wahr ist. Wenn dies nicht der Fall ist, informieren Sie den Benutzer und beenden Sie"
Ich teile nicht den Punkt von denen, die sagen, dass es gültig ist. Sobald ich durch die Tatsache geschwankt habe, dass Qt's Behauptungen wie folgt definiert wurden:
%Vor%und ich habe so etwas wie
geschrieben %Vor% Wenn ich es im Veröffentlichungsmodus befolgt habe, war ich ziemlich verwirrt von a
wurde nie initialisiert.
Wie Java Ausnahmen assert
gibt ein fail-fast . Das ist eine sehr gute Methode, die viel Entwicklungszeit und -aufwand spart. Aber der Versandcode sollte schnelle Releases als Web-Anwendung haben, und nicht so etwas wie eine CD-Distribution. Außerdem können Ausnahmen in Java abgefangen, protokolliert und gut dargestellt werden.
Tags und Links c coding-style if-statement assert