BARR-Vorgaben als Richtlinien für den Programmierstil

Autor / Redakteur: Frank Büchner * / Michael Eckstein

Standards zur Entwicklung von sicherheitskritischer Software fordern die Einhaltung eines Programmierstils. Solche Programmierstile sind oft proprietär und die Prüfung ihrer Einhaltung ist meist aufwendig. Die frei verfügbaren BARR-Vorgaben versprechen Abhilfe.

Firmen zum Thema

Guter Stil: Richtlinien für den Programmierstil sind für die Verständlichkeit wichtig – und die BARR-Vorgaben sind eine gute Grundlage dafür.
Guter Stil: Richtlinien für den Programmierstil sind für die Verständlichkeit wichtig – und die BARR-Vorgaben sind eine gute Grundlage dafür.
(Bild: gemeinfrei / Pixabay )

Ein einheitlicher Programmierstil innerhalb eines Projekts oder einer Organisation ist wichtig: Er sorgt für die leichtere Verständlichkeit des Quellcodes durch andere Beteiligte als den Ersteller, beispielsweise bei einem Review oder einer Inspektion. Auch bei späteren Arbeiten am Quellcode, beispielsweise Funktionserweiterungen oder Portierungen, ist ein einheitlicher Programmierstil sehr wertvoll, weil er die (Wieder-) Einarbeitung in den Quellcode erleichtert. Somit geht es beim Programmierstil eher um das „Aussehen“ eines Programms, also beispielsweise wo die geschweiften Klammern platziert sind, die Namensgebung von Variablen und Funktionen, die Einrückungstiefe und ähnliches.

Nicht zum Programmierstil zählen in diesem Beitrag Programmiervorgaben, welche die Sprache einschränken, um undefiniertes Verhalten des Programms zu vermeiden. Diese schlagen sich natürlich schlussendlich ebenfalls im Aussehen des Programms nieder. Insofern ist die Zuordnung einer Programmiervorgabe nicht immer eindeutig.

Bildergalerie
Bildergalerie mit 5 Bildern

Alle Standards zur Entwicklung von sicherheitskritischer Software wie IEC 61508, ISO 26262, EN 50128 oder IEC 62304 fordern mehr oder weniger nachdrücklich die Einhaltung eines Programmierstils im Sinne dieses Beitrags. Andere Anforderungen schränken die Sprache ein (siehe Bild 1).

Programmierstilforderungen in Standards

IEC 61508 [1] fordert in Teil 3 in Abschnitt 7.4.4.12: „Programmiersprachen … müssen in Übereinstimmung mit angemessenen Programmierrichtlinien verwendet werden.“ Im folgenden Abschnitt (7.4.4.13) wird klar, was die IEC 61508 unter Programmierrichtlinie versteht: Es geht über das hinaus, was wir in diesem Beitrag unter Programmierstil verstehen; es geht nämlich auch um die Vermeidung von unsicheren Sprachmerkmalen.

Allerdings wird in diesem Abschnitt auch die „Codeverständlichkeit“ ausdrücklich genannt, und dies ist ein Programmierstilziel. Laut Tabelle A.4 (in Teil 3) sind Entwurfs- und Programmierrichtlinien empfohlen für SIL 1 und besonders empfohlen für SIL 2 bis 4. In Tabelle B.1 wird die Verwendung von Programmierrichtlinien für alle SIL besonders empfohlen. In Abschnitt C.2.6.2 von Teil 7 der IEC 61508 wird ausgeführt, dass Programmierrichtlinien auch zu Verständlichkeit des Codes beitragen sollen, so dass er lesbar, verständlich und prüfbar ist. Hierzu zählen dann beispielsweise Vereinbarungen zur Namensgebung. Und genau darum geht es beim Programmierstil.

ISO 26262 [2] aus dem Automotive-Bereich nennt in Tabelle 1 von Teil 6 Themen, die durch Kodierrichtlinien abgedeckt werden sollen („Topics to be covered by modelling and coding guidelines“), explizit die Verwendung von Regeln zum Programmierstil („Use of style guides“) und die Verwendung von Namenskonventionen („Use of naming conventions“). Regeln zu Programmierstil sind empfohlen für ASIL A und besonders empfohlen für ASIL B bis D. Namenskonventionen sind besonders empfohlen für alle ASIL. Wie bei IEC 61508 gehen auch bei ISO 2626 die Kodierrichtlinien in Tabelle 1 über Regeln zum Programmierstil hinaus, beispielsweise wird u.a. auch strenge Typisierung gefordert („enforcement of strong typing“).

DIN EN 50128 [3] für Bahnanwendungen schreibt in Abschnitt 7.3.4.25 „Codierstandards müssen entwickelt werden und spezifizieren gute Programmierpraxis, wie in Tabelle A.12 festgelegt“. In Tabelle A.12 ist die Maßnahme „Leitlinie für Codierstil“ besonders empfohlen für alle SIL. In Abschnitt D.15 wird Programmierstil mit Formatierungen und Benennungsvereinbarungen erläutert. Wie bei IEC 61508 gehen auch bei DIN EN 50128 die Kodierstandards in Tabelle A.12 über Regeln zum Programmierstil hinaus, beispielsweise werden unbedingte Sprünge verboten.

Die IEC 62304 [4] aus dem Medizin-Bereich schreibt in Abschnitt B.5.5 („Software unit implementation and verification“), dass Kodierstandards genutzt werden sollen, um einen bevorzugten Programmierstil festzulegen („coding standards should be used to specify a preferred coding style“). Als Beispiel für Kodierstandards wird Verständlichkeit („understandability“) ausdrücklich genannt. Die IEC 62304 ist allgemein nicht sehr detailliert in ihren Anforderungen, somit gibt es keine weiteren Erläuterungen zum Programmierstil.

Alle betrachteten Standards zur Entwicklung von sicherheitskritischer Software fordern mehr oder weniger nachdrücklich die Einhaltung eines Programmierstils im Sinne dieses Beitrags. Andere Anforderungen schränken die Sprache ein (siehe Bild 1).

Der Programmierstandard BARR-C:2018

Im Folgenden werden Vorgaben zum Programmierstil aus dem „Embedded C Coding Standard BARR-C:2018“ (kurz „BARR“) von Michael Barr [5] betrachtet. Dieser Programmierstandard ist kostenlos im Internet verfügbar. BARR enthält nicht nur Vorgaben zum Programmierstil, sondern auch Vorgaben, die die Sprache C einschränken oder den Entwicklungsprozess betreffen. BARR umfasst insgesamt 143 Vorgaben, davon betreffen mehr als die Hälfte, nämlich 79 Vorgaben, den Programmierstil und den Entwicklungsprozess.

BARR hat eine Vorgeschichte (Bild 2): In 2009 wurde der „Netrino’s Embedded C Coding Standard“ publiziert, der im Jahr 2013 unter dem Namen „Embedded C Coding Standard“ erneut veröffentlich wurde. Alle wurden von Michael Barr bzw. der Barr Group herausgegeben.

Die erste Edition von BARR (Netrino von 2009) berücksichtigt MISRA-C:2004 [6]; die aktuelle Edition (BARR-C:2018) berücksichtigt MISRA C:2012 [7] (vergleiche Bild 2).

BARR führt schon in seiner Einleitung aus, dass die Absicht dieses Programmierstandards die Verminderung der Anzahl von Programmierfehlern in eingebetteter Software ist. Das betrifft laut BARR nicht nur die spracheinschränkenden Vorgaben, sondern auch die stilistischen Vorgaben. Dabei sollen nicht die persönlichen Vorlieben der Autoren des Standards zum Tragen kommen, sondern, falls mehrere alternative Vorgaben für einen bestimmten Programmieraspekt möglich waren, wurde diejenige Alternative gewählt, die die größte Fehlerreduzierung versprach. Das betrifft explizit auch Vorgaben zum Stil, beispielsweise wie geschweifte Klammern gesetzt werden.

Es gibt 38 Vorgaben, denen von BARR besondere Fehlervermeidung attestiert wird. Diese sind mit dem Attribut „keep bugs out“ gekennzeichnet. Da Vorgaben zum Stil unter Programmierern oft kontrovers diskutiert werden, weist BARR darauf hin, dass Quellcode nicht einem Programmierer gehört, auch wenn er ihn geschrieben hat, sondern einer Firma oder einem Auftraggeber. Und dass die Lesbarkeit, Verständlichkeit, Zuverlässigkeit, Effizienz und eventuell die Portierbarkeit von Quellcode wichtiger ist als die Vorlieben eines Programmierers.

Die Vorgaben von BARR sind in Abschnitten zusammengefasst, wobei die einzelnen Vorgaben mit Kleinbuchstaben bezeichnet sind, immer beginnend bei a. Um eine Vorgabe eindeutig zu bezeichnen, muss man immer die Nummer des Abschnitts mit angeben. Beispielsweise bezeichnet 6.1.e die Vorgabe aus dem Kapitel 6 („Procedure Rules“), Abschnitt 6.1 („Naming Conventions“), Vorgabe e, die übrigens „No function name shall contain any uppercase letters“ lautet.

Verhältnis von MISRA und BARR

Die gute Nachricht ist: Der Programmierstandard BARR hat das Ziel, kompatibel mit MISRA [7] zu sein. Die bessere Nachricht ist jedoch, dass BARR MISRA ergänzt. Denn Vorgaben zum Programmierstil sind praktisch nicht in MISRA enthalten, andererseits erwartet MISRA (in Abschnitt 5.2.2 auf Seite 9 von MISRA C:2012 von 2013), dass lokale Vorgaben zum Programmierstil eingesetzt werden, weil (a) anerkannt wird, dass ein einheitlicher Programmierstil die Verständlichkeit von Code fördert und (b) MISRA keine Vorgaben macht, die rein den Programmierstil betreffen. Das bedeutet, dass die Einhaltung von Vorgaben zum Programmierstil für die Konformität zu MISRA eigentlich notwendig ist.

Leider klappt es mit der Kompatibilität von BARR und MISRA nicht ganz. Beispielsweise schränkt die Vorgabe 1.7.d von BARR die Verwendung des Schlüsselworts „continue“ ein („it is preferred practice to avoid all use of the continue keyword“); eine solche Einschränkung gibt es in MISRA C:2012 nicht (mehr), wohl aber in MISRA-C:2004.

Weiterhin erlaubt MISRA C:2012 C90 und C99 als Sprachstandard, wohingegen BARR nur C99 (erlaubt Vorgabe 1.1.a). Auch die Schlüsselworte „auto“ und „register“ sind in BARR verboten (Vorgaben 1.7.a und 1.7.b), was in MISRA nicht der Fall ist.

MISRA ist präziser und ausgearbeiteter als BARR

Beim Vergleich von MISRA und BARR fällt zudem auf, dass MISRA wesentlich präziser und ausgearbeiteter ist als BARR. In MISRA spielt bei den Vorgaben die Prüfbarkeit durch statische Analysewerkzeuge immer mit; für alle Vorgaben ist angegeben, ob sie entscheidbar sind oder nicht. Ebenso ist für alle Vorgaben ist angegeben, ob zur Untersuchung das jeweilige Quellmodul (single translation unit) ausreicht oder nicht (system). Diese Informationen fehlen in BARR.

In MISRA sind alle Vorgaben einer der drei Klassen mandatory, required, advisory zugeordnet und die Verbindlichkeit der Vorgaben wird konsistent durch die Verwendung von „shall“ (für mandatory und required) und „should“ (für advisory) angegeben. BARR betrachtet alle seine Vorgaben als mandatory, verwendet aber nicht durchgehend „shall“, sondern ab und zu auch „should“. Ferner werden Angaben wie „it is preferable“ verwendet, die die Verbindlichkeit offenlassen.

Ein Paradebeispiel für eine solche unpräzise Vorgabe ist Vorgabe 2.2.b „The most useful comments generally precede a block of code that performs one step of a larger algorithm. A blank line shall follow each such block. The comments in front of the block should be at the same indentation level.“ In der Vorgabe 2.2.b werden zudem mehrere Vorgaben zusammengefasst, die eigentlich nicht miteinander zu tun haben. Wann genau ist ein Kommentar vor einem Block notwendig? Kann der Kommentar auch anders als der Block eingerückt sein? Und die verbindliche Forderung nach einer Leerzeile nach dem Block gehört eigentlich in eine eigene Vorgabe.

Von den 64 Vorgaben in BARR, die nicht den Programmierstil betreffen, haben 10 eine genaue Entsprechung in MISRA; 27 haben eine ungefähre Entsprechung in MISRA; 10 sind ähnlich zu Vorgaben in MISRA und 17 haben keine Entsprechung in MISRA.

Vorgaben für den Programmierstil nach BARR

Die 79 Vorgaben für den Programmierstil kann man wie folgt einteilen [8, 9]:

  • Zeilenlänge: Da gibt es nur eine Vorgabe (1.2.a), die die Zeilenlänge auf 80 Zeichen begrenzt.
  • Horizontale Abstände: Es gibt 13 Vorgaben, alle in Abschnitt 3.1, die die Platzierung von Leerzeichen betreffen, beispielsweise 3.1.m, nach der kein Leerzeichen vor dem Semikolon stehen soll, das eine Anweisung beendet. Der Tabulator ist verboten (3.5.a). Es gibt 5 Vorgaben, die die Ausrichtung (alignment) betreffen, beispielsweise 3.2.b, nach der die Namen von Strukturkomponenten aufeinander ausgerichtet werden sollen. Es gibt 3 Vorgaben, die die Einrückung betreffen, alle in Abschnitt 3.4, beispielsweise 3.4.a, wonach 4 Zeichen pro Einrückungsebene eingerückt werden soll.
  • Vertikale Abstände: Es gibt 6 Vorgaben, die den Zeilenumbruch betreffen, beispielsweise 3.3.a, die höchstens eine Anweisung pro Zeile erlaubt.
  • Anordnung des Codes: Es gibt 6 Vorgaben, die die weitere Anordnung des Codes betreffen, beispielsweise 1.3.b, wo es um das Setzen der Klammern geht. Anhänger des „One True Brace Style“ oder 1TBS müssen jetzt tapfer sein, denn 1.3.b besagt, dass eine öffnende geschweifte Klammer alleine in einer Zeile stehen muss; die dazugehörige schließende geschweifte Klammer muss darunter ebenfalls in einer eigenen Zeile stehen, und zwar in der gleichen Spalte wie die Öffnende. Diese Vorgabe hat das Attribut „keep bugs out“, womit BARR besonders fehlervermeidende Vorgaben gekennzeichnet. Bei 1TBS steht die öffnende geschweifte Klammer am Ende der Zeile, beispielsweise nach der Entscheidung einer if-Anweisung (Bild 3).
    Eine weitere Vorgabe in der Kategorie Code-Anordnung ist 8.6.a, wonach bei Verwendung des Gleichheitsoperators (==) für eine Konstante und eine Variable die Konstante links vom Gleichheitsoperator stehen muss (siehe Bild 4). Die Vorgabe 8.6.a soll vermeiden, dass irrtümlich ein einfaches Gleichheitszeichen anstelle des doppelten Gleichheitszeichens verwendet wird. Das einfache Gleichheitszeichen bewirkt eine Zuweisung; eine Zuweisung an eine Konstante ist aber unzulässig, was bereits durch den Compiler aufgedeckt wird. Diese Vorgabe verhindert auf elegante Weise, durch einen Tippfehler eine Zuweisung in einer Entscheidung zu verwenden.
  • Namensgebung: Es gibt 25 Vorgaben zur Namensgebung. Diese betreffen die Namen von Modulen (in BARR besteht ein Modul aus einer Quelldatei und einer Header-Datei), Namen von Funktionen und Namen von Variablen. Dabei geht es u.a. um die Verwendung von Unterstrichen und Groß- und Kleinbuchstaben, aber beispielsweise auch in 7.1.e um die Länge von Variablennamen, die nicht kleiner als 3 Zeichen sein darf. Die Vorgabe 7.1.j fordert, dass alle globalen Variablen mit dem Buchstaben g beginnen.
  • Abkürzungen: Es gibt 2 Vorgaben zu Abkürzungen und Akronymen, beide in Abschnitt 1.5. Vorgabe 1.5.a besagt, dass nur allgemein bekannte Abkürzungen und Akronyme verwendet werden sollen. Dazu gehört 1.5.b, nachdem eine Tabelle unter Versionskontrolle gepflegt werden muss, die projektspezifische Abkürzungen und Akronyme enthält. Diese beiden Vorgaben fördern natürlich die Verständlichkeit, jedoch ist ihre Einhaltung nur durch Analyse des Quellcodes allein nicht zu prüfen; sie betreffen auch den Entwicklungsprozess.
  • Kommentare: Es gibt 11 Vorgaben zu Kommentaren, beispielsweise fordert 2.2.c, dass man vermeiden soll, das Offensichtliche zu kommentieren.
  • Quelldateien: Es gibt 6 Vorgaben zu Quelldateien, alle in Abschnitt 4.3. Beispielsweise fordert 4.3.f, dass keine Quelldatei eine andere Quelldatei inkludieren darf.

Abweichungen

Alle Vorgaben des Programmierstandards BARR werden von ihm selbst als zwingend einzuhalten erachtet. Manche Vorgaben, bei denen es um numerische Größen wie Zeilenlänge und die Einrückungstiefe geht, dürfen an die Erfordernisse des Projekts angepasst werden. Wird ansonsten gegen eine Vorgabe verstoßen, so muss dieser Verstoß vom „project manager“ genehmigt werden und diese Genehmigung muss mit Namen und Begründung als Kommentar im Quellcode dokumentiert werden.

Werkzeugunterstützung

Bei Werkzeugunterstützung zum Erzielen eines einheitlichen Programmierstils denkt man natürlich zunächst an Formatierer, sogenannte „code beautifier“, also Programme, die vorgegebenen Quellcode so umwandeln, dass er einem bestimmten Programmierstil entspricht. Auch BARR empfiehlt solche Werkzeuge, beispielsweise um die richtige Einrückungstiefe herzustellen. Auch empfiehlt BARR, durch geeignete Einstellung des Editors dafür zu sorgen, dass bei Eingabe eines Tabulators dieser sofort durch die korrekte Anzahl von Leerzeichen ersetzt wird.

Soll ein Projekt initial konform zu einem Programmierstil gemacht werden, kann ein Formatierer viel Arbeit einsparen. Nicht vergessen darf man aber den Aufwand, den Formatierer für den gewünschten Programmierstil zu konfigurieren. Aber während der täglichen Arbeit ist es jedoch unsinnig, immer wieder den neu geschriebenen Quellcode mittels Fomatierer konform zum Programmierstil zu machen. Der Programmierstil sollte schon beim Schreiben des Codes eingehalten werden. Darüber hinaus können Formatierer viele Vorgaben von BARR nicht prüfen bzw. umsetzen.

Deswegen wird in BARR häufig „code review“ als Methode angegeben, wie die Einhaltung der Vorgaben geprüft werden soll. Ein Code-Review ist eine manuelle Tätigkeit und deshalb sehr aufwendig. Was eigentlich benötigt wird, ist ein statisches Analysewerkzeug, das die Einhaltung der Vorgaben aus BARR automatisiert prüft und auf Verletzungen hinweist. Ein solches Werkzeug ist das statische Code-Analyse-Werkzeug ECLAIR [10]. ECLAIR findet mögliche Laufzeitfehler, prüft MISRA-Regeln und berechnet Metriken.

Darüber hinaus kann ECLAIR in der Version 3.8.1 von den 79 Vorgaben zum Programmierstil in BARR 66 Vorgaben (also ca. 84%) automatisch prüfen. Von allen 143 Vorgaben in BARR kann diese ECLAIR-Version 121 prüfen (also ca. 85%). Bei manchen der stilistischen Vorgaben in BARR ist die Prüfung jedoch verzwickt: Beispielsweise fordert Vorgabe 2.2.a unter anderem, dass Kommentare in korrekter Schreibweise („proper spelling“) verfasst sind. Dies übersteigt die Fähigkeiten der meisten statischen Analysewerkzeuge. ECLAIR kann ein Wörterbuch verwenden, um richtig von falsch geschriebenen Worten in Kommentaren zu unterscheiden (Bild 5).

Bei manchen der Vorgaben in BARR sind statischen Analysewerkzeugen jedoch Grenzen gesetzt: Wie soll ein Werkzeug herausfinden, ob ein Kommentar die Vorgabe 2.2.c („avoid explaining the obvious“) verletzt?

Fazit

Ein Programmierstil ist durch die Standards für sicherheitskritische Software gefordert. Auch für nicht-sicherheitskritische Software ist ein Programmierstil in Bezug auf Verständlichkeit und somit auf Wartbarkeit und Weiterentwicklung sehr zu empfehlen. Proprietäre Standards haben den Nachteil, dass sie zunächst einmal erarbeitet und dokumentiert werden müssen, was erheblichen Aufwand und die notwendige Expertise erfordert. Nicht selten führt dieser Prozess zu Spannungen unter den Beteiligten. Ist ein proprietärer Standard einmal eingeführt, erfordert die Prüfung seiner Einhaltung üblicherweise größeren Aufwand, weil dies durch manuellen Code-Review erfolgen muss, weil normalerweise kein (Off-the-shelf-) Werkzeug existiert, das die Prüfung automatisieren könnte.

Die Verwendung eines allgemein verfügbaren Programmierstils, wie er in BARR enthalten ist, bietet viele Vorteile: Zunächst existiert der Programmierstil und muss nicht erst erarbeitet werden. Wenn der allgemein verfügbare Programmierstil als Expertenmeinung anerkannt wird, entfallen auch die Spannungen über die Gestaltung einzelner Regeln, beispielsweise, wie die geschweiften Klammern zu setzen sind. Gibt es ein Werkzeug für die automatisierte Prüfung von Vorgaben des Programmierstils, kann sehr viel manueller Aufwand eingespart werden. Für BARR ist beispielsweise ECLAIR ein solches Werkzeug. Und nicht zuletzt wird durch einen allgemein bekannten Programmierstil die Einarbeitung von neuen Mitarbeitern in ein Projekt erleichtert, wenn sie diesen Programmierstil schon kennen.

Literatur- und Quellenverzeichnis

[1] IEC 61508 (VDE 0803), Funktionale Sicherheit sicherheitsbezogener, elektrischer/elektronischer/programmierbarer elektronischer Systeme, 2011.

[2] ISO 26262, Road Vehicles – Functional Safety, 2018.

[3] DIN EN 50128 (VDE 0831-128):2012-03, deutsche Fassung von EN 50128:2011.

[4] IEC 62304, Edition 1.1, 2015-06, VDE Verlag GmbH.

[5] Embedded C Coding Standard by Michael Barr, Edition BARR-C:2018, www.barrgroup.com

[6] MISRA-C:2004, Guidelines for the use of the C language in critical systems, Mira Limited, Edition 2, Oktober 2004

[7] MISRA C:2012, Guidelines for the use of the C language in critical systems, Horiba Mira Limited, Edition 3, März 2013.

[8] Roberto Bagnara, Michael Barr, Patricia M. Hill: BARR-C:2012 and MISRA C:2012: Synergy Between the Two Most Widely Used C Coding Standards, Proceedings EWC, 2020.

[9] Roberto Bagnara, BARR-C:2012 and MISRA C:2012: Synergy Between the Two Most Widely Used C Coding Standards, Webinar by BUGSENG, March 17th, 2020, Aufzeichnung auf YouTube, abgerufen 22. Oktober 2020

[10] ECLAIR: Statisches Analysewerkzeug von BUGSENG, mehr Info: www.hitex.de/eclair

* Frank Büchner ... ist Principal Engineer Software Quality bei der Hitex GmbH in Karlsruhe.

(ID:47569662)