In de broncode van computerprogramma's verwijst dode code of onbereikbare code naar stukken code die nooit uitgevoerd (kunnen) worden maar wel in de broncode aanwezig zijn. Dode code is ongewenst om verscheidene redenen:
Redundante code is code die wel uitgevoerd wordt maar geen invloed heeft op de werking van het programma. Het declareren van een variabele om die vervolgens niet te gebruiken is bijvoorbeeld redundante code.
Sommige compilers proberen bepaalde soorten dode code op te sporen (tijdens de optimalisatiefase) en rapporteren die als fout of waarschuwing aan de programmeur.
Een voorbeeld van dode code:
if (n >= 0) {
// n groter of gelijk aan 0
} else if (n < 0) {
// n kleiner dan 0
} else {
// dood
}
Het onderste gedeelte (de tweede else
) is dood aangezien de code daarin nooit uitgevoerd kan worden.
Dode code kan opgespoord worden met behulp van statische codeanalyse en een analyse van de control flow om te kijken welke stukken code onder geen enkele omstandigheden uitgevoerd kunnen worden. Hierbij wordt rekening gehouden met de waarde van de gebruikte variabelen en de mogelijke omstandigheden tijdens het uitvoeren van het programma.
Een compiler kan optimalisaties uitvoeren om de dode code te verwijderen; dit wordt dode code eliminatie genoemd. Code kan ook dood worden na optimalisaties die door de compiler worden uitgevoerd. Een goede compiler behoort dode code te rapporteren zodat de programmeur kan bepalen wat er met zo'n stuk dode code moet gebeuren.
Er bestaat geen algoritme dat voor een willekeurig stuk broncode kan bepalen of het dood is of niet. Dit is namelijk gelijk aan het oplossen van het stopprobleem. Het is dus niet mogelijk om op correcte wijze en in alle gevallen de dode code op te sporen.
Bepaalde situaties zijn wel eenvoudig op te sporen, zoals:
int n = 2 + 1
if (n == 4) {
// dood
}
Andere situaties vereisen echter meer moeite, zoals:
double n = sqrt(2)
if (n > 2) {
// dood
}
Er kunnen verscheidene redenen zijn waarom de broncode van een computerprogramma dode code kan bevatten.
Tijdens de ontwikkeling van software kunnen de ontwikkelaars tijdelijk stukken code overslaan om iets te testen: in zulke gevallen wordt een stuk code toegevoegd om er voor te zorgen dat een ander stuk niet wordt uitgevoerd. Een voorbeeld:
while (voorwaarde) {
foo();
bar();
}
Om tijdelijk bar();
over te slaan, is het mogelijk de code te veranderen in:
while (voorwaarde) {
foo();
continue;
bar();
}
Het sleutelwoord continue
houdt in dat de executie voortgezet wordt bij de volgende gang door de lus waardoor bar();
overgeslagen wordt. Het is mogelijk dat dit soort tijdelijke veranderingen in de code blijven staan en dus ook in een uitgebrachte versie van het programma zitten.
Andere voorbeelden van dode code zijn: redundante controle op excepties en onverwijderde code die gebruikt is voor debuggen. Dode code kan ook ontstaan doordat er een bepaalde functionaliteit van het programma gepland was maar dat deze slechts gedeeltelijk is geïmplementeerd. De bijbehorende code is dan wel aanwezig maar niet toegankelijk tijdens het uitvoeren van het programma.
Gehele functies of methode kunnen dood zijn als ze niet aangeroepen worden of alleen aangeroepen worden vanuit dode code. Hetzelfde geldt voor klassen in objectgeoriënteerd programmeren.