Lehre C – Eingebettet in die Wissenschaft

Neulich erwähnte Neel Krishnaswami, dass er im Herbst die C-Klasse in Cambridge unterrichten wird, und fragte mich, ob ich einen Rat zu diesem Thema hätte. Natürlich tue ich das! Tatsächlich dauerte die Antwort so lange, dass daraus ein Blog-Beitrag entstand.

Mein Hauptgedanke ist, dass wir C auf eine Weise lehren müssen, die den Schülern hilft, zu verstehen, warum ein sehr großer Teil der kritischen Software-Infrastruktur, einschließlich fast aller Betriebssysteme und eingebetteten Systeme, in C geschrieben ist, und gleichzeitig die verheerende zentrale Rolle anerkennt, die es spielt hat in unserem anhaltenden Albtraum der Computersicherheit eine Rolle gespielt.

Es gibt jede Menge Lesematerial da draußen. Für die Grundlagen empfehle ich den Studierenden dennoch den Kauf von K&R. Die Leute sagen Gutes über C Programming: A Modern Approach; Ich habe es nur überflogen. Für Fortgeschrittene in C habe ich kein besseres Buch gelesen als „Expert C Programming“, obwohl es wie K&R ziemlich alt ist. „The Practice of Programming“ ist ein wirklich tolles Buch, auch wenn es sich nicht ganz speziell auf C bezieht. Ich habe nicht alles gelesen, aber nach allem, was ich gesehen habe, ist Modern C eine sehr gute Ressource, wobei AFAIK von allen die beste Behandlung für undefiniertes Verhalten darstellt C-Buch. Die C-FAQ enthält viel gutes Material.

Zur ergänzenden Lektüre müssen sich die Schüler natürlich alle drei Teile von Chris Lattners Aufsatz über undefiniertes Verhalten ansehen, und auch meinen.

Welche Version von C sollten wir lehren? Wahrscheinlich eine gemeinsame Untergruppe von C99 und C11. In einer ersten C-Klasse besteht keine Notwendigkeit, auf erweiterte C11-Funktionen wie das gleichzeitige Speichermodell einzugehen.

Wir möchten, dass die Studierenden die Frage beantworten können: Ist C eine geeignete Wahl zur Lösung dieses Problems? Wir brauchen etwas Vorlesungsmaterial über den Platz von C in der modernen Welt und müssen uns auch die Zeit nehmen, hochwertigen C-Code zu lesen, vielleicht beginnend mit Redis, Musl oder Xv6. Insbesondere Musl eignet sich gut für den Unterricht, da es viele nette kleine Funktionen enthält, die isoliert verstanden werden können. Von jeder dieser Funktionen aus können wir eine Diskussion über Kompromisse zwischen Portabilität, Effizienz, Wartbarkeit, Testbarkeit usw. starten. Wenn Rich Felker (der Musl-Autor) etwas auf eine bestimmte Weise getan hat, gibt es dafür wahrscheinlich einen guten Grund und wir sollten in der Lage sein, darüber nachzudenken es raus. Wir können auch den supertollen Compiler-Explorer von Matt Godbolt verwenden, um den von verschiedenen Compilern generierten Code anzusehen. Die leichte bis nicht vorhandene Laufzeitunterstützung von C ist einer der Hauptvorteile von C für den realen Systemaufbau und bedeutet auch, dass generierter Code verstanden werden kann, ohne an so etwas wie einen Garbage Collector denken zu müssen.

Lesen Sie auch  Google zoomt in den Kampf gegen ChatGPT: Dadurch verdunstet der Marktwert von Low-Level-Falschantworten um 100 Milliarden

Wir müssen wahrscheinlich auch etwas Zeit damit verbringen, uns mit dem schlechten alten C zu befassen, mit dem die Welt funktioniert, auch wenn wir nicht stolz darauf sind. Wir können in OpenSSL und im PHP-Interpreter Dateien finden, die Ihnen das Gehirn versengen würden, obwohl sie milliardenfach am Tag ausgeführt werden, oder wir können immer auf einen alten Standby-Modus wie glibc zurückgreifen – einen Blick lohnt sich allein schon wegen des Missbrauchs des Präprozessors. Aber vielleicht bin ich gemeinnützig: Pascal Cuoq (der einen Entwurf dieses Artikels liest) weist zu Recht darauf hin, dass „sogar das, was wie schlichte Dummheit erscheint, oft auf technische Kompromisse zurückzuführen ist.“ Versucht das Projekt, unter MS-DOS mit DJGPP, mit C90-Compilern, unter VMS oder allen dreien gleichzeitig kompilierbar zu bleiben?“ Und es ist wahr, dass wir gut daran tun würden, den Schülern klarzumachen, dass die technischen Zwänge in der realen Welt oft nicht den Umständen ähneln, die wir ihnen in der Schule vermitteln.

Die zweite wichtige Sache, die jeder Schüler lernen sollte, ist: Wie kann ich verhindern, dass ich mich an den zahlreichen und gravierenden Mängeln von C verbrenne? Dies ist eine Entwicklungsumgebung, in der nur Paranoide überleben können; Wir möchten einen modernen C-Programmierstil hervorheben und uns stark auf die (glücklicherweise ausgezeichnete) Sammlung von Tools verlassen, die uns bei der Entwicklung von gutem C helfen.

Die statische Analyse ist die erste Verteidigungslinie; Die Schüler müssen eine gute Auswahl an -W-Flags verwenden und sich dann daran gewöhnen, Dinge ohne Warnungen zu kompilieren. Es sollte auch ein stärkeres Tool wie der Clang-Statikanalysator verwendet werden. Auf der dynamischen Seite muss der gesamte von den Studierenden eingereichte Code in Bezug auf ASan, UBSan und MSan sauber sein. tis-interpreter hält den Code auf einem noch höheren Niveau; Ich hatte noch keine Schüler, die dieses Tool nutzten, aber ich denke, es ist eine tolle Sache, es auszuprobieren. Da dynamisches Testen durch die Qualität der Testfälle begrenzt ist, müssen sich die Studierenden daran gewöhnen, die Ausgabe eines Code-Coverage-Tools zu verwenden, um Lücken in der Testabdeckung zu finden. Für C sind viele Abdeckungstools verfügbar, aber ich verwende normalerweise nur gcov, da es allgegenwärtig und problemlos ist.

Lesen Sie auch  Mit bis zu 1.000 US-Dollar Rabatt auf die besten Geräte von Google, Samsung und Motorola an diesem Black Friday sind faltbare Geräte endlich erschwinglich

Undefiniertes Verhalten mithilfe von Desinfektionsmitteln zu lehren ist ein Kinderspiel: Das Tool gibt den Schülern genau das Feedback, das sie brauchen. Die andere Möglichkeit, undefiniertes Verhalten zu lehren, indem wir uns mit seinen Konsequenzen befassen, ist etwas, mit dem wir etwas Zeit verbringen sollten, aber es erfordert eine andere Art des Denkens und wir werden wahrscheinlich nicht erwarten, dass die Mehrheit der Schüler alles versteht die Feinheiten – selbst erfahrene professionelle C-Programmierer sind sich dieser oft nicht bewusst.

Fehler zu erkennen und etwas dagegen zu unternehmen, ist ein wirklich wichtiger Teil des Programmierens, über den wir in der Schule normalerweise nicht viel unterrichten. Da die C-Klasse darauf ausgelegt ist, diese Probleme nicht unter den Teppich zu kehren, ist eine C-Klasse ein großartiger Ort, um Schüler auf den richtigen Weg zu bringen. Sie sollten eine Goto-Kette implementieren müssen.

Etwas, das ich in diesem Beitrag weglasse, ist der Inhalt der Aufgaben, die wir den Studenten geben – das hängt hauptsächlich von den spezifischen Zielen des Kurses ab und davon, wie er in den umfassenderen Lehrplan passt (In welchem ​​Jahr sollen die Studenten den Kurs belegen). ? Welchen Hintergrund haben sie in Mathematik und Naturwissenschaften? Welche Sprachen beherrschen sie bereits?). Ich habe C immer als Nebeneffekt des Unterrichtens von Betriebssystemen, eingebetteten Systemen oder etwas in dieser Richtung unterrichtet. In einem Kurs, dessen Hauptziel C ist, haben wir mehr Freiheit und könnten uns mit mehr Bereichen befassen. Bildverarbeitung und kryptografische Algorithmen würden zum Beispiel richtig Spaß machen und auch die alten Reserven, Datenstrukturen, können im Unterricht gut eingesetzt werden.

Ich lasse auch Build-Systeme und Versionskontrolle weg. Sie sollten diese nutzen.

In einigen Kursen werde ich den Studierenden Zugriff auf die Testinfrastruktur gewähren, die zur Bewertung ihres Codes verwendet wird. Dadurch machen Aufgaben viel mehr Spaß und die Schüler sind viel zufriedener. Manchmal gebe ich ihnen ein paar Testfälle und behalte die guten Tests (und die Fuzzer) für mich. Die Idee besteht darin, dass es bei der Aufgabe nicht nur um die Implementierung, sondern auch um das Testen geht. Das stresst die Schüler, ist aber weitaus realistischer.

Lesen Sie auch  Ukrainer greifen die Moskauer Flotte an und treffen ein anderes Schiff mit Seedrohnen. Kiew warnt: „Sechs russische Häfen im Visier“

Pascal bemerkt: „C wird meist sehr schlecht gelehrt, und ein Schüler, der gut in der Pflege von C-Code werden möchte, muss vieles verlernen, was ihm (normalerweise) im Unterricht gesagt wurde.“ Das ist bedauerlicherweise wahr – viele Lehrer haben in den vergangenen Jahrzehnten C gelernt und dann eine veraltete Sprache unterrichtet, weil sie es beispielsweise versäumt haben, den Missbrauch von Präprozessoren zu verhindern. Der gravierendste häufige Fehler besteht darin, den Schülern bei der Arbeit mit einem C-Compiler ihre Seite der Abmachung nicht bewusst zu machen. Ich spreche natürlich von undefiniertem Verhalten (und in geringerem Maße von nicht spezifiziertem und durch die Implementierung definiertem Verhalten). Als konkretes Beispiel habe ich zahlreiche Kurse zum Thema „Computersysteme: Die Perspektive eines Programmierers“ unterrichtet. In vielerlei Hinsicht ist dies ein ausgezeichnetes Buch, aber (sogar in der 3. Auflage) ignoriert es nicht nur undefiniertes Verhalten, sondern, noch schlimmer, es lehrt die Schüler explizit, dass vorzeichenbehaftete ganze Zahlen in C beim Überlauf ein Zweierkomplementverhalten zeigen:

Diese Behauptung, dass ein positiv vorzeichenbehafteter Überlauf umläuft, ist nach dem C-Standard weder korrekt noch steht er im Einklang mit dem beobachteten Verhalten von GCC oder LLVM. Dies ist keine akzeptable Behauptung in einem beliebten C-basierten Lehrbuch aus dem Jahr 2015. Ich kann zwar während der Vorlesung Probleme im Buch beheben, aber das ist nicht sehr zufriedenstellend, und nicht alle Dozenten haben die Zeit und das Fachwissen.

Man könnte argumentieren, dass wir C nicht länger unterrichten sollten, und ich würde sicherlich zustimmen, dass C wahrscheinlich eine schlechte Erst- oder Zweitsprache ist. Andererseits werden wir, selbst wenn wir in einer Situation wären, in der keine neuen Projekte in C geschrieben werden sollten (dieser Tag kommt, aber langsam – wahrscheinlich noch mindestens ein Jahrzehnt), für viele immer noch bei der Beibehaltung von C stecken bleiben Jahrzehnte. Eine beliebige CS-Absolventin hat ziemlich gute Chancen, im Laufe ihrer Karriere auf C zu stoßen. Aber darüber hinaus wird die Nische der Systemprogrammierung auch nach der Ablösung von C bestehen bleiben. Vieles von dem, was wir lernen, wenn wir glauben, C zu lernen, ist Low-Level-Programmierung, und das ist wichtig.

Vielen Dank an Pascal Cuoq und Robby Findler für die Kommentare zu den Entwürfen dieses Artikels.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.