Looking for c Keywords? Try Ask4Keywords

C LanguageUndefiniertes Verhalten


Einführung

In C führen einige Ausdrücke zu undefiniertem Verhalten . Der Standard wählt explizit aus, nicht zu definieren, wie sich ein Compiler verhalten soll, wenn er auf einen solchen Ausdruck stößt. Daher kann ein Compiler das tun, was er für richtig hält, und kann nützliche Ergebnisse, unerwartete Ergebnisse oder sogar einen Absturz verursachen.

Code, der UB aufruft, funktioniert möglicherweise auf einem bestimmten System mit einem bestimmten Compiler wie beabsichtigt, wird aber wahrscheinlich nicht auf einem anderen System oder mit einem anderen Compiler, einer Compilerversion oder Compilereinstellungen funktionieren.

Bemerkungen

Was ist undefiniertes Verhalten (UB)?

Nicht definiertes Verhalten ist ein Begriff, der im C-Standard verwendet wird. Der C11-Standard (ISO / IEC 9899: 2011) definiert den Begriff undefiniertes Verhalten als

Verhalten bei Verwendung eines nicht portablen oder fehlerhaften Programmkonstrukts oder fehlerhafter Daten, für das diese Internationale Norm keine Anforderungen auferlegt

Was passiert, wenn mein Code UB enthält?

Dies sind die Ergebnisse, die aufgrund von undefiniertem Verhalten gemäß Standard auftreten können:

ANMERKUNG Das mögliche undefinierte Verhalten reicht vom Ignorieren der Situation mit unvorhersehbaren Ergebnissen über das dokumentierte Verhalten der Übersetzung (mit oder ohne Ausgabe einer Diagnosemeldung) während der Übersetzung oder Programmausführung bis zum Abbruch einer Übersetzung oder Ausführung (mit der Option Ausgabe einer Diagnosemeldung).

Das folgende Zitat wird häufig verwendet, um (weniger formell) Ergebnisse zu beschreiben, die auf undefiniertes Verhalten zurückzuführen sind:

"Wenn der Compiler [einem bestimmten undefinierten Konstrukt] begegnet, ist es legal, Dämonen aus der Nase fliegen zu lassen."

Warum gibt es UB?

Wenn es so schlimm ist, warum haben sie es nicht einfach definiert oder durch Implementierung definiert?

Undefiniertes Verhalten bietet mehr Möglichkeiten zur Optimierung; Der Compiler kann zu Recht davon ausgehen, dass jeder Code kein undefiniertes Verhalten enthält, wodurch Laufzeitprüfungen vermieden und Optimierungen durchgeführt werden können, deren Gültigkeit kostspielig ist oder nicht nachgewiesen werden kann.

Warum ist UB schwer zu finden?

Es gibt mindestens zwei Gründe, warum undefiniertes Verhalten Fehler verursacht, die schwer zu erkennen sind:

  • Der Compiler muss nicht - und kann Sie im Allgemeinen nicht zuverlässig - vor undefiniertem Verhalten warnen. Ein entsprechendes Erfordernis würde direkt gegen den Grund für undefiniertes Verhalten verstoßen.
  • Die unvorhersehbaren Ergebnisse beginnen sich möglicherweise nicht genau an der Stelle zu entwickeln, an der das Konstrukt auftritt, dessen Verhalten undefiniert ist. Undefiniertes Verhalten verfälscht die gesamte Ausführung und ihre Auswirkungen können jederzeit auftreten: Während, nach oder sogar vor dem undefinierten Konstrukt.

Betrachten Sie eine Null-Zeiger-Dereferenzierung: Der Compiler ist nicht für die Diagnose einer Null-Zeiger-Dereferenzierung erforderlich und könnte dies auch nicht, da zur Laufzeit jeder in eine Funktion übergebene Zeiger oder in einer globalen Variablen Null sein kann. Und wenn die Null-Zeiger-Dereferenzierung auftritt, schreibt der Standard nicht vor, dass das Programm abstürzen muss. Das Programm kann vielmehr früher, später oder überhaupt nicht abstürzen. Es könnte sich sogar so verhalten, als ob der Nullzeiger auf ein gültiges Objekt zeigte, und sich völlig normal verhält, nur unter anderen Umständen zum Absturz.

Im Falle von Null-Zeiger-Dereferenzierung unterscheidet sich die Sprache C von verwalteten Sprachen wie Java oder C #, in denen das Verhalten der Null-Zeiger-Dereferenzierung definiert ist : Es wird genau zu der Zeit eine Ausnahme ausgelöst ( NullPointerException in Java, NullReferenceException in C #) Diejenigen, die aus Java oder C # stammen, könnten daher fälschlicherweise glauben, dass ein C-Programm mit oder ohne Ausgabe einer Diagnosemeldung abstürzen muss .

Zusätzliche Information

Es gibt mehrere solcher Situationen, die klar unterschieden werden sollten:

  • Ausdrücklich undefiniertes Verhalten. Hier weist der C-Standard explizit darauf hin, dass Sie sich nicht im Grenzbereich befinden.
  • Implizit undefiniertes Verhalten, bei dem der Standard einfach keinen Text enthält, der ein Verhalten für die Situation vorsieht, in die Sie Ihr Programm eingebracht haben.

Bedenken Sie auch, dass das Verhalten bestimmter Konstrukte an vielen Stellen durch den C-Standard absichtlich undefiniert ist, um Raum für Compiler- und Bibliotheksimplementierer zu lassen, um eigene Definitionen zu entwickeln. Ein gutes Beispiel sind Signale und Signalhandler, bei denen Erweiterungen von C, wie beispielsweise der Betriebssystemstandard POSIX, weitaus detailliertere Regeln definieren. In solchen Fällen müssen Sie nur die Dokumentation Ihrer Plattform überprüfen. Der C-Standard kann Ihnen nichts sagen.

Beachten Sie auch, dass wenn undefiniertes Verhalten in einem Programm auftritt, das nicht bedeutet, dass nur der Punkt, an dem undefiniertes Verhalten aufgetreten ist, problematisch ist, vielmehr wird das gesamte Programm bedeutungslos.

Aufgrund solcher Bedenken ist es für die Personenprogrammierung in C wichtig (vor allem, da Compiler uns nicht immer vor UB warnen), zumindest mit den Dingen vertraut zu sein, die undefiniertes Verhalten auslösen.

Es sei darauf hingewiesen, dass es einige Tools gibt (z. B. statische Analysewerkzeuge wie PC-Lint), die das Erkennen von undefiniertem Verhalten unterstützen, aber auch hier nicht alle Vorkommen von undefiniertem Verhalten erkennen können.

Undefiniertes Verhalten Verwandte Beispiele