Ich habe einen starken Java / Groovy-Hintergrund und wurde einem Team zugewiesen, das eine ziemlich große C-Codebasis für eine Verwaltungssoftware unterhält.
Einige Probleme, wie das Behandeln von Blobs in der Datenbank oder das Generieren von Berichten in PDF und Excel, wurden in den Java-Webdienst ausgelagert.
Als Java-Entwickler bin ich jedoch ein bisschen verwirrt über einige Aspekte des Codes:
- es ist wortreich
- Es gibt viele große Methoden (Methode mit mehr als 2000 Zeilen)
- Es gibt keine fortgeschrittenen Datenstrukturen (ich vermisse List, Set und Map sehr)
- keine Trennung von Bedenken (SQL wird fröhlich rund um den Code gemischt)
Infolgedessen habe ich das Gefühl, dass das Geschäft in Tonnen von technischem Code verborgen ist und mein Gehirn, das mit objektorientierter und einer Prise funktionaler Programmierung geformt wurde, sich nicht wohl fühlt.
Die gute Seite des Projekts ist, dass der Code einfach ist: Es gibt kein Framework, keine Manipulation des Bytecodes zur Laufzeit, kein AOP. Und der Server kann gleichzeitig über 10.000 Benutzern mit einer einzigen Maschine antworten, indem er weniger Arbeitsspeicher benötigt, als Java zum Spucken von "Hallo Welt" benötigt.
Ich möchte lernen, wie man C-Code entsprechend den allgemein anerkannten modernen Prinzipien schreibt. Gibt es allgemein anerkannte Prinzipien, wie modernes C geschrieben und strukturiert werden sollte?
Etwas wie das Äquivalent des Buches 'Effective Java', aber für C.
Bearbeiten Sie im Licht der Antworten und Kommentare:
- Ich werde versuchen, meine Denkweise an C-Code anzupassen und nicht, sie in OOP zu spiegeln.
- Ich habe begonnen, die empfohlenen Coding-Style-Guides aus dem Kommentar (The GNU Coding Standards und The Linux Kernel Coding Style) zu lesen.
- Ich werde dann versuchen, meinen Mitarbeitern diesen Codestil vorzuschlagen. Der schwierigste Teil könnte darin bestehen, die Mitarbeiter davon zu überzeugen, dass eine große Methode in kleinere Teile aufgeteilt werden kann und dass die Wiederholung derselben vier Zeilen Fehlerbehandlungscode mithilfe einer Methode vermieden werden kann.
quelle
Antworten:
Ich kann Ihrer Frage entnehmen, dass das Problem nicht darin besteht, dass der Code altes C ist, sondern nur eine schlechte Programmierung. Die meisten Probleme, die Sie erwähnt haben, wie Ausführlichkeit, über 2000 Zeilenfunktionen oder keine Trennung von Bedenken, gelten für jede Sprache, C oder Java gleichermaßen.
Ausführlichkeit wurde im Zusammenhang mit der Fehlerbehandlung erwähnt. Sie haben kein Beispiel angegeben, sodass ich nur daran erinnern kann, dass Fehlerbehandlungscode auch Code ist . Es gibt keine Entschuldigung für sich wiederholende Abschnitte des Boilerplate-Codes. Faktor es aus; Entweder für eine Funktion oder (wenn es sich nicht lohnt, eine separate Funktion zu erstellen) führen Sie das
goto Error;
Muster aus und verschieben Sie die Fehlerbehandlung und die Ressourcenbereinigung in einenError:
Abschnitt am unteren Rand der Funktion.Wenn die Weitergabe des Fehlers über die Anrufkette das Problem zu sein scheint, fragen Sie sich: Muss die Funktion dort oben wirklich wissen, dass ein kleiner Kerl hier unten ein Problem hatte? Die in einer Sprache integrierten Ausnahmemechanismen machen dies einfach. Im Allgemeinen ist es jedoch besser, Ausnahmen frühzeitig (in jeder Sprache) zu behandeln, damit die Fehlerbedingung die Logik von Code auf hoher Ebene nicht beeinträchtigt. Und wenn die Funktion dort oben wirklich wissen muss, gibt es Möglichkeiten, Ausnahmen mit
setjmp
und zu emulierenlongjmp
.Ich denke, das einzige wirklich C-bezogene Problem, das erwähnt wird, ist das Fehlen von Standardcontainern. Zwar
Set
kann im Allgemeinen durch ein sortiertes Array undMap
(zum größten Teil) durch ein Array von Paaren oder einstruct
(wenn Sie den Schlüsselsatz vorher kennen,map[key] = value
wird er zus.key = value
) ersetzt werden, aber die Tatsache ist, dass der Standard keinen dynamischen Array-Container enthält Bibliothek. In C99 können Sie mindestens ein Array mit variabler Länge für den Stack deklarieren (int array[len]
), dies muss jedoch imlen
Voraus berechnet werden (normalerweise nicht schwer), und Sie können es natürlich nicht als stapelzugeordnetes Objekt zurückgeben. Die meisten Projekte schreiben ihren eigenen dynamischen Array-Container oder übernehmen einen Open-Source-Container.Abschließend möchte ich darauf hinweisen, dass ich dort war. Ich war der Java-Programmierer, der zu C ++ und reinem C übergegangen ist. Ich würde empfehlen, "Buch X zu lesen, um gutes C zu lernen", aber es gibt keine, so wie es keine für Java gibt. Der Weg vorwärts ist, alle Feinheiten der Sprache und der Standardbibliothek in sich aufzunehmen. googeln Sie viel, lesen Sie viel und programmieren Sie viel, bis Sie anfangen, in C zu denken. Der Versuch, Dinge in C zu schreiben, wie Sie es in Java tun würden, ist ebenso frustrierend wie der Versuch, einen Satz in einer Fremdsprache mit Wörtern zu schreiben, die direkt von Ihrer Mutter übersetzt wurden Zunge; Sie und der Leser werden zusammenzucken. Die gute Nachricht ist, dass es langsam ist, gute Programme zu lernen, aber eine andere Sprache schnell zu lernen. Also, wenn Sie anständigen Code in Java schreiben,
quelle
setjmp()
/longjmp()
als gültiges Werkzeug zu sehen: Es wird nicht einmal versucht, eine Bereinigung durchzuführen. Allfällige Zuordnungen gehen verloren, gehaltene Sperren werden nicht aufgehoben, geöffnete Dateien werden nicht geschlossen und vorübergehende Dateninkonsistenzen werden dauerhaft. Meiner Meinung nach ist dieses Funktionspaar im Grunde der schlimmste Hack, der jemals erfunden wurde, mit der einzigen Rechtfertigung, dass es möglich war, es zu implementieren. Letztendlich gibt es in C nur eine gültige Methode zur Fehlerbehandlung: explizite Fehlercodes.setjmp/longjmp
scheint ein Fisch in C kein Wasser mehr zu haben und ich habe ihn nie benutzt. Ich fühlte mich gezwungen, sie nur wegen der zahlreichen Tutorials / Bibliotheken im Internet aufzunehmen, um Ausnahmen nachzuahmen, also dachte ich, dass es Leute gibt, die sie tatsächlich nutzen.Ich würde Ihnen empfehlen, vorsichtig zu sein, ob dies Ihre Zeit und das Geld des Unternehmens wert ist, um Ressourcen für die "Modernisierung" einer funktionierenden Software mit geringer Codekomplexität auszugeben und wer eine gute Leistung erbringt. Es ist sehr wahrscheinlich, dass Sie selbst neue Bugs einführen, zumal es ein System zu sein scheint, mit dem Sie nicht vertraut sind.
Wenn Sie diesen Weg immer noch einschlagen möchten, würde ich Folgendes vorschlagen:
An diesem Punkt werden Sie entscheiden, ob es sich lohnt, dies zu erkunden. Wenn die Unternehmenskultur einen Misserfolg nicht belohnt, erhalten Sie grünes Licht von einem höheren oder einem Manager.
Ich denke, das ist eine ziemlich gute Straßenkarte und bringt Sie dorthin, wo Sie es brauchen. Ohne die Einzelheiten dieses Projekts zu kennen, ist es schwierig, Ihnen viel zu helfen. Bitte verwerfen Sie meinen Haftungsausschluss nicht als übermäßig alarmierend. Unzählige exzellente Programmierer haben den Staub überwunden, indem sie versucht haben, ein bestehendes Projekt in ihre Lieblingssprache umzuschreiben oder "moderne" Tools zu verwenden. Das ist eine Entscheidung, die sorgfältig durchdacht werden muss, und ich fordere Sie dringend auf, nicht auf eigene Faust zu handeln, ohne die Unterstützung des Managements oder die Unterstützung Ihrer Kollegen.
quelle
Wenn Sie eine höhere Sprache bevorzugen, gibt es einige Sprachen wie C ++ oder Objective-C, die sich problemlos mit C-Code mischen lassen.
Alternativ sind C und C ++ einigermaßen kompatibel. Möglicherweise können Sie die gesamte Codebasis mit wenigen Änderungen einfach als C ++ kompilieren - Sie haben gelegentlich die Variable "class" oder "template", die Sie umbenennen müssen. In der Praxis ist dies jedoch alles. (sizeof ('a') unterscheidet sich in C und C ++, aber ich glaube nicht, dass ich das jemals benutzt habe).
Wenn Sie diesen Weg gehen, denken Sie daran, dass der nächste Betreuer mit C ++ möglicherweise nicht zu fließend ist. Lass dich nicht mitreißen. Nutzen Sie C ++, aber nur so weit, dass ein C-Programmierer es leicht verstehen kann.
quelle
malloc
) ist schlechte Praxis in C. Die Bedeutung betrachtetconst
undinline
ist auch sehr unterschiedlich zwischen C und C ++, und natürlich C ++ nicht versteht__restrict
. Behandle die Sprachen nicht als austauschbar, auch nicht in der Teilmenge der Quellen, die in beiden kompiliert werden.Grundsätzlich ist das Schreiben von gutem C-Code dasselbe wie das Schreiben von gutem C ++ - oder Java-Code: Sie möchten eine Klasse, verwenden Sie a
struct
. Wenn Sie eine Vererbung wünschen, schließen Sie die Basisstruct
als namenloses erstes Mitglied ein. Wenn Sie virtuelle Funktionen möchten, fügen Sie einen Zeiger zu einem statischenstruct
Funktionszeiger hinzu. Und so weiter usw. Genau das macht C ++ unter der Haube. Der einzige Unterschied besteht darin, dass es in C explizit ist. Somit können Sie in C perfekt objektorientiert programmieren sind gewöhnt an.Der Punkt ist, dass es bei guter Programmierung um Paradigmen geht, nicht um Sprachmerkmale. Es ist zwar immer schön, wenn Ihre Sprachfunktionen die gewünschten Paradigmen gut unterstützen, die Sprachfunktionen jedoch nicht erforderlich sind. Sobald Sie dies bemerken, können Sie guten Code in so ziemlich jeder Sprache schreiben (abgesehen von einigen esoterischen Sprachen wie Brainfuck oder INTERCAL).
Natürlich bleibt das Problem bestehen, dass die Standard-C-Bibliothek keine dieser raffinierten Containerklassen enthält, an die Sie gewöhnt sind. Leider bedeutet dies, dass Sie entweder Ihre eigenen verwenden müssen oder diesen Mangel mithilfe von dynamisch zugewiesenen Arrays umgehen müssen. Aber ich wette, Sie werden bald feststellen, dass alles, was Sie wirklich brauchen, dynamische Arrays (
malloc()
) und verknüpfte Listen / Bäume sind, die über Zeiger-Member in Ihren Klassen implementiert werden.quelle