Hallo und willkommen.
In diesem Vortrag möchte ich über das Debugging sprechen.
Dies ist ein sehr wichtiges Konzept, das Sie unbedingt verstehen müssen. Sie müssen einige der Werkzeuge und
Methoden verstehen, die Sie einsetzen können, um Fehler zu finden und zu beheben.
Beim Debugging geht es also darum, Fehler in einem Programm zu finden und zu beheben.
In der Regel Logikfehler.
Ich werde Ihnen den Unterschied zwischen Logik- und Syntaxfehlern erklären, aber auch Compiler-Syntaxfehler
können dazu gehören.
Es ist der gesamte Prozess für Syntaxfehler.
Beim Debuggen geht es darum, zu verstehen, was der Compiler Ihnen mitteilt.
Sie sollten sich immer auf die Behebung des ersten entdeckten Problems konzentrieren.
Ich möchte einige der üblichen Compilerfehler und -warnungen durchgehen, die Sie möglicherweise erhalten, aber eines
der Dinge, die Sie immer über den Compiler wissen sollten, ist, dass der Compiler Ihnen eine Reihe von Fehlern
anzeigen kann.
Sie sollten sich immer auf den ersten Fehler konzentrieren, da dieser erste Fehler zu weiteren Fehlern führen kann.
Es kann also sehr irreführend sein, wenn Sie in einem Programm fünf Fehler sehen, obwohl der Compiler Ihnen sagt, dass Sie vielleicht nur einen
Fehler haben und dieser erste Fehler die anderen Fehler verursacht.
Es ist also wichtig, dass Sie sich immer den ersten Fehler ansehen und diesen zuerst beheben.
In diesem sehr vereinfachten Beispiel gibt es mehrere Fehler in diesem Code.
Wir haben keine.
Wir haben keine beginnende linke Klammer, geschweifte Klammer.
Wir haben kein Semikolon.
Wir geben eine Zeichenkette zurück.
Wenn wir dies speichern und kompilieren, sehen Sie einige rote Fehler.
Wenn wir es kompilieren, zeigt es uns an, dass wir drei Fehler haben.
Sie sollten niemals nur den letzten Fehler in Zeile sieben auswählen, auch wenn es sich in diesem Fall um einen tatsächlichen Fehler handelt.
Sie wollen immer nur den ersten Fehler beheben.
Sie sehen also den ersten Fehler, der hier aufgelistet ist, und beheben diesen ersten Fehler, dann kompilieren Sie neu und führen es erneut
aus.
Der Grund dafür, dies zu tun, ist jedoch nur, dass der Fehler auf der obersten Ebene behoben werden soll, weil dieser Fehler andere Probleme
verursachen kann.
Konzentrieren Sie sich also immer auf die Behebung des ersten Fehlers. Die Fehlersuche und -behebung kann sehr komplex
sein, von der Suche nach einfachen Fehlern bis hin zur Erfassung großer Datenmengen für die Analyse.
Das kommt ganz darauf an.
Manchmal hat man außer der Protokollierung keine weiteren Informationen zur Verfügung, und in diesem Fall muss man
die Dinge analysieren.
Manchmal gibt es Fehler, die sich nicht wiederholen lassen.
Man kann es nicht reproduzieren, weil es so selten vorkommt.
In der Regel muss man dann Protokolle oder eine andere Variante davon analysieren.
Die Fähigkeit zum Debuggen ist für einen Programmierer eine wesentliche Fähigkeit.
Es ist eine echte Problemlösungskompetenz, die Ihnen eine Menge Zeit und Geld sparen kann.
Es ist also eine wichtige Fähigkeit, die man lernen muss.
Das ist eine wichtige Fähigkeit, die es zu entwickeln gilt.
Das erfordert Übung.
Sie werden nicht einfach zu Ihnen kommen.
Es gibt bestimmte Techniken und Methoden, die Sie anwenden können, um die Fehlersuche
zu verbessern.
Ich kenne eine ganze Reihe von Entwicklern, die wirklich gut darin sind, Software und Code zu schreiben.
Sie verstehen die Sprache sehr gut, aber ihre Debugging-Fähigkeiten sind sehr schlecht.
Es kann sein, dass sie auf einen Fehler stoßen und ihr erster Gedanke, diesen Fehler zu lösen, völlig daneben geht.
Und auch hier gilt: Übung macht den Meister.
Man wird immer besser im Debuggen und versteht, wie man Probleme findet und behebt,
aber das muss man auch lernen.
Die Wartungsphase ist die teuerste Phase des Software-Lebenszyklus.
Die Wartungsphase beginnt, nachdem Sie Ihr Produkt veröffentlicht haben, und Sie müssen es nun pflegen.
Die Pflege bedeutet, dass neue Fehler behoben und neue Funktionen hinzugefügt werden.
Wenn Windows zum Beispiel ein neues Betriebssystem wie Windows 10 herausbringt, ist die Produktion dieser Software
noch nicht abgeschlossen.
Es wird wahrscheinlich zehn Jahre lang im Einsatz sein.
Und es liegt immer noch in der Verantwortung von Microsoft, wenn neue Fehler auftauchen oder sie neue Fehler für diese Software finden, diese zu
beheben.
Aus diesem Grund werden automatische Aktualisierungen und ähnliche Maßnahmen durchgeführt.
Und dann werden sie auch feststellen, dass das Betriebssystem mit neuen Funktionen aufwartet, wenn sich der Markt
und die Technologie ändern.
Die Wartung ist also sehr wichtig.
Daher ist es sehr wichtig, dass Sie Ihre Fehler finden und beheben.
Allerdings sind Fehler unvermeidbar.
Jede Software hat Bugs.
Niemand kann perfekten Code schreiben, und es geht auch nicht unbedingt darum, perfekten Code zu schreiben.
Es ist nur so, dass sich die Dinge mit der Zeit ändern.
Das kann also auch zu Fehlern führen.
Sie müssen verstehen, dass die Tatsache, dass eine Software Fehler aufweist, nicht bedeutet, dass der Code schlecht geschrieben ist.
Das ist irgendwie unvermeidlich.
Es ist also in Ordnung, Fehler zu haben, und wenn man sie findet und behebt, ist das sogar noch besser, weil man das
Gefühl hat, etwas erreicht zu haben.
Häufige Probleme.
Und wir werden mehr über spezifische Probleme im Zusammenhang mit der See sprechen.
Die häufigsten Probleme bei der Fehlersuche sind jedoch Logikfehler.
Logikfehler haben nicht mit dem Gegenteil der Art von Syntaxfehlern zu tun, die Ihr Code tatsächlich kompiliert.
Es funktioniert, aber es tut nicht das, was es eigentlich tun sollte.
Es gibt also eine Art von Logik oder beabsichtigter Funktionalität, die nicht korrekt ist.
Vielleicht haben Sie eine IF-Anweisung, und Sie springen bei der falschen Bedingung in die if-Anweisung.
Vielleicht haben Sie eine Schleife und die Schleife wird nicht die richtige Anzahl von Malen ausgeführt oder Sie sortieren nicht
richtig.
Die Funktionalität ist nicht mehr gegeben, aber der Compiler sagt: "Hey, alles sieht gut aus.
Hier ist ein Beispiel für einen logischen Fehler.
Ein Logikfehler liegt vor, wenn die Syntax korrekt ist, aber die gewünschten Ergebnisse aufgrund eines logischen Problems nicht
korrekt sind.
In diesem Code sehen Sie also, dass die for-Schleife ein Semikolon am Ende hat, was syntaktisch korrekt ist.
Aber Sie erwarten, dass diese for-Schleife fünfmal durchlaufen wird.
Ist es aber nicht.
Es wird nur eine Laufzeit geben.
Wenn wir also build sagen und dieses Projekt bauen, sehen wir eine Warnung, was gut ist und man sollte immer alle Warnungen
beheben.
Aber es wurde erfolgreich kompiliert.
Wenn wir dieses Projekt ausführen, werden Sie feststellen, dass es tatsächlich nur einmal ausgeführt wird und
Hello World einmal ausgibt.
Dies ist also ein Beispiel für einen logischen Fehler.
Syntaxfehler stehen in direktem Zusammenhang mit dem Compiler.
Das sind Dinge, die nicht den Regeln der Sprache entsprechen.
Sie haben kein Semikolon am Ende einer Aussage.
Sie haben kein Ende, richtig?
Klammern mit links, Klammern.
Sie haben eine Klammer vergessen.
Sie verwenden nicht das richtige Schlüsselwort und so weiter.
Syntaxfehler werden in der Regel vom Compiler verursacht Logikfehler werden vom Programmierer während
der Testphase verursacht.
Speicherkorruption ist ebenfalls ein häufiges Problem, insbesondere in C.
Wenn Sie Ihren Speicher nach der korrekten Zuweisung nicht freigeben, können Sie Dinge
wie "Dangling References" haben, und Sie können auch Speicherprobleme im Zusammenhang mit Speicher haben, der nicht verwendet wird.
Sie können Speicherlecks haben.
Hier ist ein Beispiel für einen Speicherfehler.
In diesem speziellen Code haben wir tatsächlich ein Speicherleck, daher haben wir noch nicht über Zeiger gesprochen.
Wir haben noch nicht über die dynamische Speicherzuweisung gesprochen.
Aber in C können Sie Speicher zuweisen, während Ihr Programm läuft.
Sie können eine malloc-Funktion verwenden.
Und so markieren wir hier, wir weisen Speicher zu, aber wir geben ihn nie wieder frei.
Wir können also diesen Code erstellen und ausführen und alles ist gut, aber wir beheben den Fehler nicht wirklich.
Um diesen Speicherfehler zu korrigieren, müssten wir also free sagen und dann free
und X, um den Zeiger freizugeben.
Aber wenn Sie Speicher zuweisen, geben Sie ihn nicht wieder frei.
Das ist ein Speicherleck.
Ein weiteres Beispiel wäre, wenn wir den Speicher nicht zuweisen, wenn wir einen Zeiger erstellen und dann versuchen,
ihn zuzuweisen, kommt es zu einer Speicherbeschädigung, da der Speicher nicht zugewiesen ist und wir versuchen,
ihm einen Wert zuzuweisen.
Das war nur ein kleiner Ausschnitt aus einigen Beispielen für Speicher, Korruption, Leistung und Skalierbarkeit.
Dies könnte ein Problem sein, wenn Ihre Funktionalität zwar beabsichtigt ist und Ihr Code syntaktisch korrekt ist, aber
nicht sehr effizient ist oder Sie nicht die nötige Hardware haben, um sie zu unterstützen.
Wenn Sie also einen Server betreiben und nicht genügend Hardware haben, um alle Anfragen zu unterstützen,
wird er sehr, sehr langsam laufen.
Oder wenn Sie Ihr Programm so geschrieben haben, dass es mit zehn Benutzern funktioniert, aber wenn es viele Benutzer hat, ist es vielleicht
nicht mehr so schnell, weil die Datenbank ins Stocken gerät oder was auch immer der Fall sein mag.
Der Code funktioniert zwar, ist aber nicht für die Handhabung vieler, vieler Nutzer oder was auch immer
ausgelegt.
Mangelnder Zusammenhalt.
Dies kann ein Problem in Ihrem Code sein, das die Qualität verringert, weil Ihr Code zu viel tut oder eine
Funktion zu viel tut.
Es handelt sich nicht um einen einzelnen Schwerpunkt oder gar eine Anwendung, die zu viele Funktionen hat,
die nie genutzt werden, so dass dies wahrscheinlich ein Redesign erfordern würde.
Hier ist ein Beispiel für ein Programm, das nicht modularisiert ist, das nicht zusammenhängend ist.
Sie haben eine große Funktion, die viel zu viele Dinge tut, Hunderte von Codezeilen, Tausende von Codezeilen.
Sie möchten eine Funktion in Hilfsfunktionen, in einzelne Module, aufteilen.
Das macht Ihr Programm viel kohärenter und einfacher zu pflegen.
Anstatt also eine große Hauptmethode zu haben, die die Initialisierung, das Lesen der Eingabe, das Finden von etwas, das
Finden der Differenz, das Finden des Produkts und das Finden des Quotienten vornimmt, könnten Sie all diesen Code erstellen
und in Hilfsmodule packen und den Code trennen, so dass die Trennung helfen kann, Ihre Fehler viel einfacher zu finden und zu beheben.
Zusammenhalt ist in C immer wichtig und wird in der Regel dort, wo man einen hohen Zusammenhalt hat, durch die Bereitstellung von Hilfsmethoden
gelöst.
Enge Kopplung.
Dies hat auch mit der Qualität des Codes zu tun.
Hier gibt es eine Menge Abhängigkeiten.
Kopplung ist etwas anderes als Kohäsion.
Die Kohäsion ist Ihr einziger Schwerpunkt.
Kopplung bedeutet, dass Abhängigkeiten zwischen Funktionen oder Objekten bestehen.
Wir haben über Kopplung gesprochen, wir haben über globale Variablen gesprochen.
Wenn eine globale Variable in vielen verschiedenen Funktionen verwendet wird, kann sie Abhängigkeiten zwischen diesen
Funktionen verursachen.
Wenn Sie also Code schreiben und eine hohe Qualität anstreben, sollten Sie eine
sehr hohe Kohäsion und eine sehr geringe Kopplung haben.
Der Debugging-Prozess.
Das erste, was Sie tun müssen, bevor Sie einen Fehler finden und beheben können, ist, das Problem
zu verstehen.
Das ist vergleichbar mit der Frage, ob man nicht einfach loslegt und sich den Code ansieht, sondern sich mit dem Tester zusammensetzt,
die Anforderungen versteht und begreift, was vor sich geht.
Denn wenn du nicht verstehst, was vor sich geht, wirst du es auch nicht können.
Sie sollten nicht einmal versuchen, das Problem zu lösen.
Sobald Sie den Fehler verstanden haben, versuchen Sie, ihn zu reproduzieren, denn das Reproduzieren des Fehlers kann Ihnen helfen, weitere Informationen
zu sammeln.
Es ist nicht immer möglich, Fehler zu reproduzieren, da sie nur unter sehr seltenen Umständen auftreten können.
So können z.B. Fehler wie Race Conditions oder sogar Speicherkorruption nur in ganz bestimmten Anwendungsfällen
auftreten und es kann sich um intermittierende Probleme handeln.
Und ist das der Fall?
Und Sie können es nicht reproduzieren.
Es wird immer schwieriger, Fehler zu beheben.
Wenn Sie in der Lage sind, das Problem zu reproduzieren, können Sie normalerweise genügend Informationen sammeln, um es zu beheben.
Die meisten Probleme sind behebbar.
Außerdem kann es bei Multithreading-Umgebungen, parallelen Prozessen oder Threading-Problemen problematisch
sein, ein Problem nicht reproduzieren zu können.
Diese sind auch sehr schwer zu debuggen, aber auf jeden Fall versuchen, es zu reproduzieren, sobald Sie herausgefunden
haben, was das Problem tatsächlich in Bezug auf Sie sind in der Lage zu verstehen, sobald Sie in der Lage sind, zu reproduzieren,
wenn Sie nicht sind, dann wollen Sie versuchen, eine Art von Teilen und Erobern Ansatz.
Sie sollten versuchen, den Fehler einzugrenzen und das Problem zu vereinfachen, denn durch die Möglichkeit der Reduzierung erhalten
Sie zwar Informationen, aber nicht unbedingt, in welchem Teil des Codes sich der Fehler befindet.
Man kann also versuchen, das Problem zu vereinfachen, indem man Teile des ursprünglichen Testfalls entfernt, damit
man nicht zu viel testet.
Sie können das Problem sogar noch weiter eingrenzen, indem Sie sagen: "Oh, das passiert nur in dieser speziellen Situation.
Sie können auch Code auskommentieren.
Wir haben über Funktionen gesprochen, die nur Stubs und keine Implementierung haben.
Manchmal wird das bei Tests eingesetzt, um die Problemstellung einzugrenzen.
Wenn Sie Code auskommentieren, haben Sie auch weniger Code, in dem der Fehler tatsächlich auftreten kann.
Wenn Sie also Code auskommentieren oder Änderungen rückgängig machen, kann der Fehler verschwinden, und wenn der Fehler verschwindet, sagt Ihnen
das, dass der Code, den Sie auskommentiert haben, die Ursache gewesen sein könnte.
Im Wesentlichen geht es jedoch darum, ein großes Programm in viele kleine Programme zu verwandeln, entweder durch
Testen oder Auskommentieren.
Eine weitere gängige Technik ist die Durchführung von Unit-Tests für Ihren Code.
Wenn ein Unit-Test fehlschlägt, kann Ihnen das genau sagen, wo die Logik oder das Problem in Ihrem Code liegen könnte.
Sobald Sie den Fehler isoliert haben, können Sie den Ursprung des Problems im Code finden, den spezifischen Code,
der das Problem verursacht.
In manchen Fällen werden dazu Debugging-Tools wie ein Debugger verwendet.
Wir werden über einen Debugger im Detail sprechen, aber je nachdem, welche integrierte Entwicklungs-IDE Sie verwenden, gibt
es normalerweise Debugger, die mit der IDE geliefert werden.
Es gibt auch Debugger, die eigenständig landen können.
Der große wichtige Debugger, den wir für die C-Programmierung verwenden werden, heißt GDB.
Der GNU-Debugger ist ein nettes grafisches Werkzeug, mit dem Sie die Ausführung Ihres Programms
anhalten und Haltepunkte einfügen können.
Wenn Sie dann die Ausführung stoppen, können Sie Zeile für Zeile in Ihrem Code gehen und sich den Wert der Variablen ansehen.
So können Sie genau sehen, was in Ihrem Code vor sich geht, während er ausgeführt wird.
Es ist ein unschätzbares Hilfsmittel.
Es ist einfach so nützlich.
Wir werden den Debugger durchlaufen.
Ich zeige Ihnen, wie Sie Haltepunkte hinzufügen können.
Ich zeige Ihnen, wie Sie eine Beobachtungsfunktion einrichten, damit Sie sich die Veränderungen der Variablen ansehen können, und wie Sie den Code Schritt für Schritt
durchgehen.
Nachdem Sie die Ursache des Problems ermittelt haben, müssen Sie es beheben.
Dies erfordert eine Menge Erfahrung und Übung.
Nur weil Sie die Ursache des Problems gefunden haben, können Sie es möglicherweise nicht allein mit
Ihrer Erfahrung beheben.
Dazu gehört manchmal auch das Redesign oder Refactoring von anderem Code, den Sie vor allem bei Skalierbarkeits-
oder Leistungsproblemen umgestalten müssen, damit Ihre Software besser skalierbar ist.
Es ist also nicht immer einfach, das Problem zu finden und es dann zu beheben.
Das Problem ist manchmal, insbesondere bei noch schwierigeren Problemen wie Threading oder Parallelverarbeitung,
dass diese noch schwieriger zu lösen sind.
Aber je mehr Code Sie schreiben, desto einfacher wird dies.
Und zu guter Letzt müssen Sie wie verrückt testen.
Testen Sie immer viele verschiedene Grenzen.
Gute Fälle, schlechte Fälle dazwischen, um zu überprüfen, ob Sie das Problem tatsächlich behoben haben.
Einige der zur Verfügung stehenden Techniken und Werkzeuge.
Ein gängiges und sehr häufig verwendetes Werkzeug ist die Verwendung von Druckanweisungen.
Sie können Druckanweisungen verwenden, um Ihren Code grundsätzlich nachzuvollziehen.
In C können Sie also die Anweisung print F verwenden, und Sie können sie in mehrere Zeilen Ihres Codes einfügen, um zu zeigen,
wo Sie sich befinden.
Dies ist auch sehr nützlich, wenn Ihr Programm abstürzt.
Sie kann dabei helfen, die Absturzstelle zu identifizieren.
Wenn Sie diese Druckanweisungen hinzufügen, um Ihren Code zu verfolgen, können Sie in den Druckanweisungen Ausgaben
hinzufügen, um den Wert von Variablen zu sehen, da der Wert von Variablen den Zustand beeinflussen und logische
Probleme verursachen kann.
Das Hinzufügen von Druckanweisungen ist also sehr, sehr nützlich.
Es kann den Ablauf der Ausführung zeigen und helfen, den Fehler zu isolieren.
Debugger, wie ich sie in GDB erwähnt habe, können die Ausführung des Programms überwachen.
Sie können es anhalten, zeilenweise ausführen, neu starten, Haltepunkte setzen, Variablen und Speicher überwachen.
Sehr, sehr hilfreich, wenn man versucht zu diagnostizieren, wo ein Problem liegt.
Log-Dateien.
Protokolldateien sind ebenfalls sehr nützlich, insbesondere bei Fehlern, die nicht wiederhergestellt werden können.
Sie können sie zu einem späteren Zeitpunkt analysieren, um genau zu sehen, was zum Zeitpunkt des Fehlers vor sich ging.
Das bedeutet also, dass Sie in Ihrem Code selbst gute Logging-Anweisungen bereitstellen müssen,
damit dies nützlich ist.
Sie können also eine eigene Protokolldatei erstellen und in dieses Protokoll schreiben, während das Programm läuft, so dass Sie im Falle eines
Fehlers diese Protokolle an die Entwickler senden können.
Bei Xerox setzen wir diese Methode häufig ein, denn wenn wir auf Probleme stoßen, können wir sie nicht immer reproduzieren,
oder sie treten erst im Feld auf.
Wir müssen in der Lage sein, auf diese Protokolle zuzugreifen, um zu sehen, was passiert ist, als der Fehler auftrat.
Überwachungssoftware.
Es gibt Software, die die Speichernutzung, die Bedrohung durch den Netzwerkverkehr und Objektinformationen überwachen und zur Laufzeit
analysieren kann.
So können Sie feststellen, ob Sie vielleicht zu viel Speicher zuweisen und diesen nicht löschen.
Wenn Sie ein Tool für die Speichernutzung des Netzwerkverkehrs haben, das Ihnen sagen kann, ob Sie zu
viele Pakete senden oder ob Ihre Software skalierbar ist, wenn Sie einen Anstieg des Netzwerkverkehrs
haben und feststellen, dass sich Ihre Antwortzeit verschlechtert.
Sie können auch Threads und Objektinformationen für die Parallelverarbeitung überwachen, um zu sehen, was in bestimmten Threads
und was in einem anderen Thread vor sich geht.
Das wird Ihnen helfen, Probleme mit dem Gewinde zu diagnostizieren.
Einige andere gängige Debugging-Tools, die Sie verwenden können, um Ihre Fehler zu finden und zu beheben.
Sie können eine Ausnahmebehandlung verwenden.
Viele Programmiersprachen haben eine Ausnahmebehandlung.
Leider hat C keinen Exception-Handler, der tatsächlich Abstürze verhindert und viele Dinge zur Laufzeit
erledigt.
Das gilt zwar nicht für das Sehen, aber es ist trotzdem gut, das zu verstehen.
Trycatch-Blöcke und dergleichen.
Statische Analysatoren.
Es gibt viele statische Analysatoren für C statische Analysatoren zur Verfügung.
Analysieren Sie den Quellcode ohne die eigentliche ausführbare Datei.
Die Ausführung des Programms muss nicht erfolgen.
Es geht Zeile für Zeile vor und prüft den Quellcode.
Und während der Quellcode analysiert wird, sucht er nach bekannten Problemen.
So kann es die Semantik überprüfen.
Sie kann dafür sorgen, dass.
Das war's.
Sie haben keine uninitialisierten Variablen, wenn Sie, wenn Sie neuen Speicher zuweisen, dass Sie es löschen.
Wenn Sie toten Code haben, der nicht wirklich ausgeführt wird, können Sie diesen Code löschen.
Es analysiert auch Dinge wie Race Conditions und Deadlocks.
Bei meiner Arbeit setzen wir einige statische Analysatoren ein.
Wir haben ein Tool namens Coverity, das ziemlich populär ist, und es gibt ein Code-Sonar-Tool, das wir ebenfalls verwenden, und wir lassen
es auf unserer gesamten Baseline, unserem gesamten Quellenset laufen.
Und immer wenn wir neue Dateien und neuen Code einführen, wird dieses Tool auf diesen Code angewendet und ist in der Lage, unmittelbare
Probleme mit dem Code zu identifizieren, z. B. nicht initialisierte Variablen, Speicherlecks und so weiter.
Die Syntax wird nicht analysiert.
Dafür ist der Compiler da.
Es gibt auch eine Reihe von Testsuiten, die eine Reihe umfassender Systemtests von Ende zu Ende durchführen, die Sie mit Ihrer
Software durchführen können.
Testen ist immer wichtig, aber wenn man es irgendwie automatisieren kann, um zum Beispiel bei jedem neuen Build
bestimmte Tests auszuführen, kann das sehr hilfreich sein, und das kann tatsächlich Fehler verhindern,
aber auch bei der Fehlerdiagnose helfen.
Debuggen des Programms nach einem Absturz.
Wir werden über etwas sprechen, das sich Call Stack nennt.
Dies ist ein sehr nützlicher Mechanismus, besonders in einer Sprache wie C, wo man bei einem Absturz
nur Informationen wie Segmentierungsfehler erhält.
Wenn es zu einem Absturz kommt, wird in den meisten Fällen, je nach Absturzursache, eine Kerndatei erzeugt,
die Sie analysieren können, um den Aufrufstapel zu sehen, um festzustellen, wo genau das Programm abgestürzt
ist.
Es zeigt Ihnen die Codezeile an, in der das Programm abgestürzt ist.
Wir werden also etwas über den Aufrufstapel lernen.
Und was die Analyse von Kerndateien angeht, so ist das eher eine fortgeschrittene Technik.
Aber in C kann eine Kerndatei erzeugt werden, die Sie analysieren können.
Das ist eine etwas fortgeschrittenere Technik, aber es ist eine weitere Möglichkeit, Ihre Software zu debuggen.
Wie lassen sich Fehler vermeiden?
Wir wissen, dass es bei der Fehlersuche darum geht, Fehler zu finden und zu beheben, aber wenn man Fehler vermeiden kann, ist das noch besser.
Eine Möglichkeit, Fehler zu vermeiden, besteht darin, qualitativ hochwertigen Code zu schreiben.
Für viele Software-Ingenieure ist dies nicht wirklich ein Schwerpunkt.
Viele Software-Ingenieure schreiben den Code, und solange er funktioniert, ist das alles,
worum sie sich kümmern, oder solange die Ausgabe korrekt ist, dann betrachten sie sich als fertig.
Aber es gibt so etwas wie schlechten Qualitätscode, und den wollen Sie nicht schreiben.
Einiges davon habe ich in den Vorlesungen gezeigt, aber Dinge wie die Verwendung sinnvoller Variablennamen
sind sehr wichtig.
Oder wenn Sie ineffizienten Code schreiben, in dem Sie Schleifen vielleicht mehr als nötig verwenden, oder eine if-Anweisung
verwenden, oder wenn Sie das nicht tun müssen.
All diese Dinge stellen schlechte Codequalität dar.
Wenn Sie eine Funktion haben, die 100 andere Dinge tut und nicht zusammenhängend ist, wenn Sie 5000 Zeilen in einer Funktion
haben, wenn Ihr Code nicht in einer modularen Weise organisiert ist oder wenn Sie viele Abhängigkeiten
zwischen Funktionen haben, dann ist dies alles schlechter Qualitätscode und Sie sollten sich bemühen, dies zu vermeiden.
Sie wollen Techniken einsetzen, die Ihr Programm organisieren.
Es ist einfach zu pflegen, hat einen hohen Grad an Kohäsion, eine geringe Kopplung und Sie verwenden aussagekräftige Variablennamen.
Sehr, sehr wichtig.
Das ist auch deshalb so wichtig, weil die Instandhaltungsphase sehr, sehr teuer ist.
Wenn Sie Code von schlechter Qualität schreiben, wird es viel schwieriger, ihn zu warten.
Es ist schwieriger, Fehler zu isolieren und neue Funktionen hinzuzufügen.
Daher sollten die Mitarbeiter beim Schreiben ihres Codes auf hochwertige Techniken zurückgreifen.
Einheitstests.
Unit-Tests helfen enorm, Fehler zu vermeiden.
Unit-Tests werden in der Regel automatisch ausgeführt, wenn Sie kompilieren Unit-Tests können helfen, Regression zu vermeiden.
Das ist einer der wichtigsten Faktoren.
Von Regression spricht man, wenn man neuen Code schreibt und einen Fehler in etwas einführt, das vorher funktionierte.
Sie haben den alten Code gebrochen.
Das wollen Sie nicht tun.
Wenn eine Software funktioniert und dann etwas Neues, das Sie gemacht haben, sie kaputt macht, wollen Sie sofort
benachrichtigt werden, und Unit-Tests können dabei helfen, weil Unit-Tests auf dem alten Code laufen und sagen: Hey, das
hat funktioniert, jetzt fällt es aus.
Es kann auch Fehler in neuem Code finden, bevor er ausgeliefert wird, denn auch hier wird er nach der Erstellung ausgeführt und
zeigt Ihnen sofort an, dass etwas nicht stimmt.
Es gibt ein Konzept der testgetriebenen Entwicklung, das immer beliebter wird.
Hier schreiben Sie zuerst Ihre Tests und entwickeln erst danach Ihren Code.
Das Ziel dabei ist, dass Ihr erster Test fehlschlägt und Sie dann den Code schreiben, um ihn zum Bestehen
zu bringen.
TDD hat viele Vorteile, und es ist eine Art umgekehrte Denkweise.
Normalerweise schreiben die Leute beim Schreiben von Code zuerst den Code und dann erst den Test.
Aber mit TDD, es gibt Ihnen tatsächlich eine andere Art des Denkens, so dass Sie in wirklich kleinen Stücken arbeiten und
Sie neigen dazu, sehr zusammenhängenden Code zu schreiben und Sie haben nicht den Code, hat nicht eine Menge
von Abhängigkeiten, weil erraten, was?
Wenn Sie in der Lage sind, Code zu testen, bedeutet das, dass der Code nicht so viele Abhängigkeiten hat.
Wenn Ihr Unit-Test sehr, sehr umfangreich ist und Sie all diese verschiedenen Dinge ausprobieren, ist
das normalerweise ein großer Indikator dafür, dass Ihr Code sehr, sehr abhängig von anderen Dingen ist.
Durch den Einsatz von TDD sind Sie also gezwungen, sehr lose gekoppelten Code zu schreiben, der keine Abhängigkeiten aufweist, und
es zwingt Sie, Ihre Funktionen sehr kohärent zu gestalten.
TDD hat eine Reihe von Vorteilen.
Sie wollen auch eine gute Dokumentation erstellen und eine gute Planung vornehmen.
Tauchen Sie nicht einfach in den Code ein und beginnen Sie mit der Umsetzung.
Schreiben Sie Ihren Entwurf auf Papier.
Verwenden Sie Pseudocode.
Richtig.
Planen Sie Ihren Code.
Wenn Sie Ihren Code planen, die Anforderungen verstehen und einen gut durchdachten Entwurf haben, können
Sie viele, viele Fehler vermeiden.
Und ich habe das im Laufe des Kurses immer wieder erwähnt, aber es ist sehr, sehr wichtig, eine Art "Teile
und herrsche" Ansatz zu verfolgen, in kleinen Schritten zu arbeiten und nach jedem Schritt ständig zu testen, nicht 1000 Zeilen Code zu schreiben
und dann zu sagen, okay, kompilieren und testen.
Das ist kein guter Ansatz.
Vermeiden Sie zu viele Änderungen auf einmal.
Nehmen Sie Änderungen schrittweise vor, fügen Sie eine Änderung hinzu und testen Sie sie gründlich, bevor Sie den nächsten
Schritt beginnen.
Dies kann dazu beitragen, alle Arten von Fehlern zu reduzieren, und es schränkt auch Ihre Problemmenge ein.
Wenn Sie also einen Fehler haben, wissen Sie sofort, wo er sich im Code befindet, weil Sie gerade kompiliert
und diese Zeile oder Anzahl von Zeilen hinzugefügt haben.
Ich kann nicht genug betonen, wie wichtig die Fehlersuche beim Schreiben von qualitativ hochwertigem Code ist, um Fehler zu vermeiden.
Diese Problemlösungstechniken werden Ihnen eine Menge Zeit ersparen und Sie zu einem viel besseren Programmierer der Spitzenklasse
machen.
In den nächsten Vorlesungen möchte ich über einige der häufigsten Fehler sprechen, die in der Programmiersprache
C auftreten.
Außerdem möchte ich ein wenig über den Aufrufstapel sprechen.
Ich möchte den Debugger durchgehen und Sie mit den Menüoptionen vertraut machen und Ihnen zeigen, wie Sie Uhren anhalten und
ausführen können usw.
Und ich möchte auch helfen, einige häufige Compiler-Fehler zu identifizieren, die Sie sehen, wie die Compiler, Compiler-Warnungen und Fehler
zu interpretieren.
Was bedeutet es, wenn der Compiler dies sagt?
Was bedeutet es, wenn der Compiler diese Warnung ausgibt?
Ich danke Ihnen.