(WS19-01)Wetterballon Styleguide: Unterschied zwischen den Versionen

Aus Verteilte Systeme - Wiki
Wechseln zu: Navigation, Suche
(Die Seite wurde neu angelegt: „=C-Styleguide= == Dateien == === Header=== <syntaxhighlight lang="c"> **tl;dr:** > Keine C-Datei ohne zugehörigen Header. > `#ifndef`-Block benutzen.…“)
 
(C-Styleguide)
 
Zeile 8: Zeile 8:
 
> Keine C-Datei ohne zugehörigen Header.
 
> Keine C-Datei ohne zugehörigen Header.
 
> `#ifndef`-Block benutzen.
 
> `#ifndef`-Block benutzen.
  +
> Nur für Header benötigte `#include`-Anweisungen (z. B. für Datentypen).
 
> Enthält Großteil der Dokumentation.
 
> Enthält Großteil der Dokumentation.
 
</syntaxhighlight>
 
</syntaxhighlight>
Zeile 29: Zeile 30:
 
<syntaxhighlight lang="c">
 
<syntaxhighlight lang="c">
 
> **tl;dr:**
 
> **tl;dr:**
> Nur Header inkludieren.
+
> Nur Header inkludieren, keine C-Dateien.
 
> Sinnvolle Reihenfolge beim Inkludieren beachten.
 
> Sinnvolle Reihenfolge beim Inkludieren beachten.
 
</syntaxhighlight>
 
</syntaxhighlight>
 
C-Dateien liefern die Implementierung von Funktionalität, die von der zugehörigen Headern vorgegeben wird. Und das Beste: Der Linker findet halbwegs automatisch die korrekten Quelltextdateien. Damit muss nicht ihr gesamter Code in andere C-Dateien inkludiert werden.
 
C-Dateien liefern die Implementierung von Funktionalität, die von der zugehörigen Headern vorgegeben wird. Und das Beste: Der Linker findet halbwegs automatisch die korrekten Quelltextdateien. Damit muss nicht ihr gesamter Code in andere C-Dateien inkludiert werden.
  +
  +
Die hier vorgeschlagene Reihenfolge für `#include`-Anweisungen ist:
  +
1. durch diese Datei implementierter Header
  +
2. System-Header
  +
3. Projekt-Header
   
 
== Einrückungen==
 
== Einrückungen==

Aktuelle Version vom 4. Dezember 2019, 13:43 Uhr

C-Styleguide

Dateien

Header

 **tl;dr:**  
> Keine C-Datei ohne zugehörigen Header.  
> `#ifndef`-Block benutzen.  
> Nur für Header benötigte `#include`-Anweisungen (z. B. für Datentypen). 
> Enthält Großteil der Dokumentation.

H-Dateien sind sinnvoll, um die Funktionalität einer Softwarekomponente leserlich aber vollständig zu definieren und die tatsächliche Implementierung austauschbar zu halten. Konstanten, Structs und Typedefs sind auch vorwiegend in Headern anzusiedeln.

Da der Header die Definition der Funktionalität übernimmt, enthält er auch die entsprechende Dokumentation.

Damit ein Header nicht mehrfach inkludiert wird, ist es nötig, den Präprozessor zu nutzen. Der symbolische Name setzt sich wie folgt zusammen: `<PROJEKT>_<PFAD>_<DATEI>_H_`. Der Code im Header ist also so ähnlich eingeschlossen wie:

#ifndef WETTERBALLON_INCLUDE_SENSORS_MCP9808_H_
#define WETTERBALLON_INCLUDE_SENSORS_MCP9808_H_

// Code...

#endif

Quelltextdateien

> **tl;dr:**  
> Nur Header inkludieren, keine C-Dateien.
> Sinnvolle Reihenfolge beim Inkludieren beachten.

C-Dateien liefern die Implementierung von Funktionalität, die von der zugehörigen Headern vorgegeben wird. Und das Beste: Der Linker findet halbwegs automatisch die korrekten Quelltextdateien. Damit muss nicht ihr gesamter Code in andere C-Dateien inkludiert werden.

Die hier vorgeschlagene Reihenfolge für `#include`-Anweisungen ist: 1. durch diese Datei implementierter Header 2. System-Header 3. Projekt-Header

Einrückungen

Allgemein

> **tl;dr:**  
> Einen Tabulator pro Einrückung.  
> Konsequent einrücken.

Tabulatoren haben den Vorteil, dass sie eine minimale Anzahl von Zeichen benötigen, um Einrückungen zu erzeugen, die der Entwickler sich beliebig groß anzeigen lassen kann.

Das Wichtigste ist ohnehin die Gleichmäßigkeit der Einrückungen. Auch dabei ist ein Tabulatorzeichen hilfreich, da eine vergessene Einrückung schneller auffällt, als ein vergessenes Leerzeichen.

Für Zeilenfortsetzungen

> **tl;dr:**  
> Zwei weitere Tabulatoren.

Um zu kennzeichnen, dass Code noch zur darüberliegenden Zeile gehört, ist es nötig, ihn vom umliegenden Code abzuheben. Nur einen Tabulator dazu zu verwenden, hätte den Nachteil, dass man keine Unterscheidung zwischen Zeilenfortsetzungen und Kontextwechseln mehr hätte. Daher wird die etablierte Lösung der doppelten Einrückung verwendet.

Code

Allgemein

> **tl;dr:**  
> Eine Anweisung pro Zeile (aber keinen Zeilenumbruch vor `{`).  
> Keine Zuweisungen in Anweisungen.  
> Leerzeilen müssen nicht gespart werden.  
> Kurze Kommentare an komplexeren Stellen.  
> Möglichst unter 120 Zeichen pro Zeile, Ausnahmen möglich.

Code muss nicht nur geschrieben, sondern auch gelesen und verstanden werden können. Aus diesem Grund sollte pro Zeile maximal ein Vorgang geschehen. Die C-Grammatik wertet Codeblöcke allerdings auch als Anweisung. Das wird hier aber zugunsten des Entwicklers ignoriert, was die öffnende geschweifte Klammer ans Ende der vorigen Zeile zieht. Jede Anweisung innerhalb des Codeblocks ist dennoch einzeln zu halten und entsprechend einzurücken, um auch die Leserlichkeit zu erhalten.

Jedoch ist nicht jedes Mittel zum Zweck von kürzerem Code hilfreich. Innerhalb einer Anweisung darf keine Zuweisung stattfinden, da dies extrem komplexen Code produzieren kann.

Zur semantischen Abgrenzung eignen sich Leerzeilen und Kommentare. Kommentare sollten dabei kurz und aussagekräftig sein. Ist eine Funktionalität auch ohne Kommentar klar erkennbar, braucht es dort auch keinen Kommentar.

Da die Zeiten der 80-Zeichen-Bildschirme vorüber sind, muss man sich auch nicht an dieser Konvention festklammern. Um ein gewisses Maß der Übersichtlichkeit zu wahren, sollten 120 Zeichen trotzdem nur selten überschritten werden.


Präprozessor

> **tl;dr:**  
> Keine `#define`-Anweisungen, nur Konstanten.

Der Präprozessor ist ein großartiges Werkzeug, um die Kompilierung des Programms zu unterstützen und zu steuern. Man sollte es aber nicht übertreiben, vor allem, wenn man C als stark typisierte Sprache nutzt. Es ist daher sowohl für die Leserlichkeit als auch für die Fehlersuche sinnvoll, die Typzuweisung selbst zu übernehmen und auf `#define`-Anweisungen zu verzichten.

Konstantennamen

> **tl;dr:**  
> In Großbuchstaben mit Unterstrichen.

Dass Konstanten unveränderlich sind und damit auch semantisch eine besondere Rolle einnehmen, sollte auch beim Lesen des Codes gut sichtbar sein. Um sie also von Variablen abzuheben, werden sie ausschließlich in Großbuchstaben geschrieben, wobei Wörter im Namen durch Unterstriche getrennt werden.

Variablen- und Funktionsnamen

> **tl;dr:**  
> In Kleinbuchstaben mit Unterstrichen.

Da die Schreibweise von Variablen und Funktionen von keiner anderen Vorgabe abhängig ist, wird sich hier an den allgemeinen C-Standard gehalten indem Kleinbuchstaben mit Unterstrichen zwischen Wörtern im Namen verwendet werden.

Prototypen

> **tl;dr:**  
> Vollständig definieren, auch mit `void`.

In Headern werden die Prototypen der zu implementierenden Funktionen definiert. Diese beschreiben den vollständigen Funktionskopf mit allen Typen und Variablen. Aus genau diesem Grund sind sie vollständig zu definieren. Möglicherweise wird dies auch durch Compiler-Flags vorausgesetzt.

Globale Variablen

> **tl;dr:**  
> Sind zu vermeiden.  
> Wenn nötig, mit `g_`-Präfix.

Globale Variablen steigern die Codekomplexität ungemein. Daher sind sie im Allgemeinen zu vermeiden. Sollten sie doch nötig sein, wird das Präfix `g_` angehängt, um den Kontext der Variable klarzustellen.

Lokale Variablen

> **tl;dr:**  
> Nur im nötigen Kontext definieren.  
> Zu Beginn des Kontexts definieren (außer Schleifen, s. u.).  
> Beim Deklarieren initialisieren.

Variablen speichern Werte, solange sie gebraucht werden. Wird ein Wert nicht mehr benötigt, hat die Variable auch keinen Nutzen mehr und der Speicher kann anderweitig genutzt werden. Daher sollten Variablen nur im kleinstmöglichen Kontext existieren.

Für die Leserlichkeit lohnt es sich dabei, die Variablen direkt zu Beginn ihres Kontexts zu deklarieren. Eine Ausnahme ist, dass Zählervariablen eine so geringe Wichtigkeit besitzen, dass sie nicht zusammen mit den anderen Variablen zusammen angelegt werden.

Um undefiniertes Verhalten zu vermeiden, sind Variablen bei ihrer Definition bereits mit einem Standardwert zu initialisieren.

Pointer

> **tl;dr:**  
> `*` am Namen, nicht am Typen.

In der Grammatik der Sprache C bindet der `*`-Operator an den Variablenname, nicht an den Typen. Dies lässt sich eins-zu-eins in der Syntax wiedergeben.

Vergleiche

> **tl;dr:**  
> Möglichst eine Konstante vor dem Vergleichsoperator verwenden.

Vergleiche sind essenziell für Fehlerbehandlung und weitere Grundfunktionalitäten eines Programms. Daher ist es nicht hilfreich, wenn ein simpler Tippfehler Grund für eine große Fehlersuche werden kann. Aus genau diesem Grund ist gefordert, auf der linken Seite des Vergleichsoperators eine Konstante stehen zu haben. Sollte eines der `=`-Zeichen im Gleichheitsoperator vergessen werden, wird dies vom Compiler sofort bemerkt.

goto

> **tl;dr:**  
> Nur beim Beenden einer Funktion.  
> Aussagekräftige Labels.

`Do you know da way of da deval?` - Uganda Knuckles

Die `goto`-Anweisung ist nur in Ausnahmefällen zulässig, in denen eine Funktion so komplex ist, dass es unverhälnismäßig schwer ist, Fehlerzustände angemessen zu behandeln. Dann und nur dann darf zum korrekten Beenden der Funktion `goto` verwendet werden. Dabei sollte immer klar sein, aus welchem Grund zum gewählten Label gesprungen wird.