Wikipedia · einfach zusammengefasst · Stand
Toter Code
Toter Code (englisch dead code) ist in der Programmierung ein Begriff für Teile eines Computerprogramms, „die an keiner Stelle im Programm verwendet werden“.
Inhalt5 Abschnitte
Begriff und Abgrenzung
Toter Code (englisch dead code) bezeichnet in der Programmierung Teile eines Computerprogramms, „die an keiner Stelle im Programm verwendet werden“. Dazu können Anweisungen oder Befehle ebenso gehören wie nicht verwendete Datendeklarationen. Im erweiterten Sinn ist toter Code eine Sonderform redundanten, also überflüssigen Codes.
Davon abzugrenzen ist unerreichbarer Code: Dieser kann durch keinen möglichen Kontrollfluss erreicht werden und wird deshalb nicht ausgeführt. Umgangssprachlich wird auch unerreichbarer Code oft als toter Code bezeichnet. Außerdem gibt es unnötigen Code, etwa eine Bedingung, die immer wahr ist. Er gehört nicht zwingend zu den genannten Kategorien, wird aber ebenfalls häufig als toter Code bezeichnet.
Bedeutung und Risiken
Alle Formen toten Codes gelten als unerwünscht und als Mangel der Softwarequalität. Häufig weist er auf einen Programmfehler hin; ihn zu entdecken ist deshalb ein Ziel beim Softwaretest.
Toter Code kann aber auch absichtlich vorhanden sein, etwa um einen vorläufigen oder früheren Quelltextteil zu erhalten. Seine Ergebnisse werden zwar nicht verwendet, dennoch kann der Code Ausnahmebehandlungen auslösen oder globale Status beeinflussen. Wird er entfernt, kann sich daher die Programmausgabe verändern und es können unbeabsichtigte Fehler entstehen. Ob solcher Code dann noch als toter Code gilt, ist umstritten.
Typische Formen am Beispiel
In der Funktion foo(int x) ist int y = 100 / x; toter Code, weil y nie verwendet wird. Bei x gleich Null löst die Division jedoch eine Ausnahme aus. Das Entfernen ändert also die Funktionalität; laut Beispiel sollte das Auslösen der Ausnahme aber ohnehin nie Teil der Funktionalität sein und ist deshalb fehlerhaft.
int z = x * x; und das spätere return x * x; sind redundant, weil x dazwischen nicht verändert wird und beide Ausdrücke immer dasselbe Ergebnis haben. Stattdessen wäre return z; angebracht. Die Bedingung if (z >= 0) ist unnötig, da ein Quadrat stets mindestens 0 ist. Weil die Bedingung immer wahr ist und im Block die Methode verlassen wird, ist das folgende return -1; unerreichbar.
Eine redundanzfreie Fassung der Funktion lautet: int foo (int x) { return x*x; }.
Entstehung und Erkennung
Toter Code kann durch Programmierfehler, unvollständige Tests oder einen bei einer Fehlerkorrektur neu eingeführten Fehler entstehen. Weitere Ursachen sind Code, der wegen seiner Vermischung mit funktionalem Code nicht entfernt wurde, vergessene Löschungen, veränderte Eingabedaten oder absichtlich unerreichbar gemachter Code, der später wiederverwendet werden soll. Auch Debugging-Konstrukte und Reste von Entwicklungscode zählen dazu. In den letzten fünf dieser Fälle ist der unerreichbare Code eine Altlast: Er war früher sinnvoll, wird aber nicht mehr benötigt.
Ein typisches absichtliches Beispiel ist eine Kontrollausgabe mit #define DEBUG 0. Während der Entwicklung kann DEBUG auf einen von Null verschiedenen Wert gesetzt werden, damit etwa der Wert von a ausgegeben wird. Bei DEBUG gleich 0 erkennt und entfernt der Präprozessor den nun toten Abschnitt.
Das Auffinden ist eine Form der statischen Codeanalyse. Sie untersucht die Ablaufsteuerung, um Code unabhängig von Variablen und anderen Laufzeitbedingungen zu erkennen. Werkzeuge finden viele tote und unerreichbare Teile; in Java sind einige Formen unerreichbaren Codes verboten und führen zu Kompilierungsfehlern. In großen Projekten können ganze Module schwer erkennbar sein, etwa wenn Testgerüste Code noch als „lebendig“ erscheinen lassen oder Vertragspflichten seine Auslieferung verlangen. Profiling liefert nur eine Heuristik für potenziell unerreichbaren Code, keinen Beweis; verdächtige Stellen müssen danach mit stärkerer Analyse oder von Hand geprüft werden.
Entfernung und Grenzen
Seit Mitte der 1990er Jahre erkennen und entfernen Compiler und Linker unbenutzte Abschnitte mit dead code elimination. Seit Mitte der 2010er Jahre zeigen auch IDEs unbenutzten Code beim Editieren an; Debugger können im aktuellen Durchlauf nicht mehr erreichbaren Code abgedunkelt darstellen.
Die Entfernung erfolgt meist durch Löschen der betreffenden Stellen, verlangt aber Vorsicht wegen möglicher Seiteneffekte, die nicht unmittelbar erkennbar sind. Compiler arbeiten deshalb konservativ: Bei Mehrdeutigkeit über das Programmverhalten wird Code nicht entfernt. Toter Code kann durch Variablenanalyse, unerreichbarer Code durch Datenflusskontrolle erkannt werden. Compilertransformationen wie common subexpression elimination können Code selbst unerreichbar machen.
Constant folding und einfache Flussanalyse zeigen beispielsweise bei int i = 2 + 1; if (i == 4) foo();, dass foo() unerreichbar ist. Wie viel Code gefunden wird, hängt stark von der Ausgereiftheit der Analyse ab; einer vollständigen Analyse zur Kompilierungszeit setzen theoretische Grenzen der Entscheidbarkeitsproblematik Grenzen. Im November 2010 wurde Microsoft kritisiert, weil eine spezielle dead code elimination den Internet Explorer in einem bekannten JavaScript-Benchmark besonders schnell erscheinen ließ, während die Ergebnisse in anderen Benchmarks eher im Mittelfeld lagen.