Misra C
StartSeite | Neues | TestSeite | ForumSeite | Teilnehmer | Kategorien | Index | Hilfe | Einstellungen | Ändern
MISRA-C (Motor Industry Software Reliability Association)
ist ein Programmierstandard aus der Automobilindustrie für die Programmiersprache C.
Der Misra-Standard besteht aus den sogenannten Misra-Regeln.
1998 erschienen 127 Regeln, 2004 141 Regeln.
Diese Regeln wurden erarbeitet auf Basis von Fehlern von C-Programmierern.
Die Regeln bestehen aus Verboten und Nichtverwendungsempfehlungen von C-Sprachmitteln.
Misra will durch die Beachtung der Regeln erreichen, daß C-Programme nun sicherer
programmiert werden, so daß einer Verwendung in sicherheitskritischer Umgebung
(SIL1 .. SIL4) nichts mehr im Wege steht.
Die Misra-Regeln verbieten eigentlich alles, was C ausmacht:
- Pointer-Arithmetik, jegliche Pointertyp-Konversion
- Variadische Funktionen, stdio.h komplett
- Rekursion, Komma-Operator (auch bei for(;;))
- continue; break nur 1-mal im Loop
- return nur 1-mal am Funktionsende
- offsetof, setjmp, longjmp, signal
- Dynamische Speicherallokation
- ...
Kritik | |
| Dies ist eine absichtliche Schimpftirade, die aber nicht im Blutrausch geschrieben wurde.
Ein sehr guter C-Programmierer erkennt nach einigen Minuten des Lesens einer Misra-Regel-Liste, welch
großen Schaden die Befolgung dieser Regeln bewirkt.
Siedend heiß steigt die Wut auf, wenn klar wird, daß auch noch Geld dafür bezahlt wird, großen
Schaden erleiden zu dürfen.
Die nachfolgend aufgeführten Links zeigen ungefähr 50 Berichte und Kommentare kontra Misra.
Es wird klar - glasklar - daß Misra-C tatsächlich massiv schädlich und grober Unfug ist - befördert von großer Inkompetenz! |
|
|
Nachfolgend gilt Standard MISRA-C:2004, der sich auf C90 bezieht.
2.3 Kommentare dürfen nicht verschachtelt werden.
- Das ist korrekt; Kein C-Standard definiert(e) verschachtelte Kommentare.
2.2 Es darf nur »/* dieser */« Kommentar verwendet werden.
- Das ist korrekt; Der bezogene C-Standard definiert gar keinen anderen Kommentar. Jedoch ein neuerer C-Standard definiert immerhin seit 1999 den »//« Zeilenkommentar, der die Programmierer dringend dazu animieren soll, mehr Kommentare zu schreiben, was auch eingetreten ist, dort, wo »//« verwendet werden darf.
2.4 Code-Teile sollten nicht auskommentiert werden.
- Es ist wichtig und erhöht das Verständnis, Code-Teile auskommentieren zu können! Daran ist konkret erkennbar, was einmal zusätzlich oder alternativ implementiert war und (an jener Stelle) eventuell wieder aktiv gesetzt werden kann.
5.5 Kein Objekt- oder Funktions-Identifizierer mit statischer Speicherdauer sollte wiederbenutzt werden.
- Unklar in mehreren Hinsichten. Regelprüfprogramme verwenden diese Regel nicht.
5.6 Kein Identifizierer in einem Namensraum sollte gleichlautend wie ein Identifizierer in einem anderen Namensraum sein, mit Ausnahme von Struktur- und Union-Mitgliedern.
- Unklar in mehreren Hinsichten. Genaue Kenntnis des C-Standard erforderlich. Regelprüfprogramme verwenden diese Regel nicht.
5.7 Kein Identifizierer-Name sollte wiederbenutzt werden.
- Inwiefern nicht? Vollkommen unklar! Regelprüfprogramme verwenden diese Regel nicht.
6.3 Anstelle der Basistypen (»char« ... »long double«) sollten »typedef«-Namen verwendet werden, die Größe und Vorzeichenbehaftung anzeigen.
- Zusätzlich zu den Basistypen wäre besser. Beispielsweise »BOOL CHAR BYTE INT2 INT4 UNS2 UNS4 MESS TIME«. Es ist Unfug, die Basistypen zu verbannen. Es gibt viele vollkommen sichere (lokale) Verwendungen für die Basistypen, die dann dadurch zudem vollkommen portabel sind.
8.4 Falls Objekte oder Funktionen mehr als einmal deklariert werden, müssen ihre Typen kompatibel sein.
- Wo jeweils? Unklar. Zudem meist genaue Kenntnis des C-Standard erforderlich. Grundsätzlich sind beliebig viele gleiche Deklarationen ein und desselben Objekts möglich.
8.8 Ein externes Objekt oder Funktion darf nur in einer Datei deklariert werden.
- Unklar. Müssen Deklarationen in genau eine Datei geschrieben werden, die dann in alle ».c«-Dateien eines Projektes inkludiert wird? Regelprüfprogramme verwenden diese Regel nicht.
8.10 Alle Deklarationen und Definitionen von Objekten und Funktionen auf Dateiebene müssen interne Bindung haben, sofern externe Bindung nicht erforderlich ist.
- Okay. Siehe 8.11
8.11 Der Speicherklassenspezifizierer »static« muss verwendet werden in Definitionen und Deklarationen, die interne Bindung haben.
- Verwirrend. Die interne Bindung entsteht erst durch »static«. Diese Regel sollte besser mit Regel 8.10 verschmolzen werden.
8.12 Wenn ein Array mit externer Bindung deklariert ist, muß seine Größe explizit angegeben oder durch Initialisierung implizit bekannt sein.
- Verwirrend! Bei einer »extern«-Deklaration ist eine Initialisierung nicht möglich. Nur bei Definition möglich.
10.6 Ein Suffix »U« muß angewendet werden bei allen Konstanten des Typs »unsigned«.
- Verwirrend und unvollständig. Der vorzeichenbehaftete Typ wird erst durch den Suffix vorzeichenlos. Im Regelfall können über 99% aller Integerkonstanten ganz sicher als »int« belassen werden, auch bei Verknüpfung mit vorzeichenlosen Operanden. »int«-Werte bis »32767« sind portabel sicher. Auch hier aufpassen: »~0u«, »ULong= 1u<<15«. »1<<15« ergäbe hier »0xFFFF8000ul«, auf einer Plattform mit 16 Bit »int«. Diese Regel sollte mit Regel 10.5 verschmolzen und erweitert werden: Überall (nicht nur bei Konstanten) wo es zu einer Erweiterung (durch »int«-Promotion oder Verknüpfung mit breiteren Typen) mit unerwünschtem Erhalt des Vorzeichens kommen kann, muß die Vorzeichenbehaftung entfernt werden.
11.1 Konversionen dürfen nicht stattfinden zwischen einem Funktionszeiger und irgendeinem anderen Typ als einem integralen Typ.
- Verwirrend. Ein Funktionszeiger darf also beispielsweise einem Ganzzahl-Objekt vom Typ »char« zugewiesen werden. Das ist ganz erstaunlich.
11.2 Konversionen dürfen nicht stattfinden zwischen einem Objektzeiger und irgendeinem anderen Typ als einem integralen Typ, einem anderen Objektzeigertyp, oder einem »void«-Zeiger.
- Verwirrend und stark einschränkend. Hinsichtlich des integralen Typs gilt das gleiche wie bei Regel 11.1. Es dürfen keine Lib-Funktionen verwendet werden, deren Name mit »mem« beginnt und auch keine sonstigen Funktionen, die einen Parameter des Typs »void*« besitzen. Der C-Standard verlangt, daß bei Konversionen zu und von »void*« keine Wertänderung passieren darf.
12.10 Der Komma-Operator darf nicht verwendet werden.
- Ein Totalverbot ist übertrieben und unbegründet. In der Kopfzeile einer »for«-Schleife und in »if«-Zweigen mit wenig Text und ohne »{ }« sollte der Komma-Operator mindestens erlaubt sein. Zudem kann er den (angeblichen!) booleschen Kontext »if (b=a,++a, a>4)« auf elegante Art beseitigen. Siehe auch 13.6 unten.
13.1 Zuweisungsoperatoren dürfen nicht in Ausdrücken verwendet werden, die einen booleschen Wert ergeben.
- Unklar. Falls »if (a = b)« gemeint ist, so wäre das falsch. Der C-Standard definiert gar keinen booleschen Kontext! Es liegt hier vielmehr ein skalarer Kontext vor, der irgendeine Ganzzahl oder Gleitkommazahl mit beliebigem Wert erwartet. Einen booleschen Wert vom Typ »int« erzeugen die Vergleichs- und die logischen Operatoren.
13.4 Der kontrollierende Ausdruck einer »for«-Schleife darf nicht irgendein Gleitkomma-Objekt enthalten.
- Hin und wieder ist es notwendig, genau dies doch zu tun. Es kann nicht immer mittels eines mitlaufenden Ganzzahl-Objekts vermieden werden. Und was ist daran problematisch, bezüglich Sicherheit, einen Gleitkommawert bei jedem Durchlauf zu prüfen? Das kann sicherer sein als Vermeidungskonstrukte zu implementieren.
13.6 Iterativ verwendete Variablen in einer »for«-Schleife dürfen nicht im Schleifenkörper verändert werden.
- Entweder der Komma-Operator ist erlaubt oder es müssen öfter zwangläufig mehrere iterativ verwendete Variablen im Körper verändert werden! Das Verbot von Zeigerarithmetik fördert dies zusätzlich. Regelprüfprogramme verwenden diese Regel nicht. Beispiel: Eine Variable wird bei jedem Durchlauf um 1 erhöht, eine weitere wird um 4 erhöht, eine weitere wird um 1 reduziert, ein Struktur-Zeiger wird inkrementiert. Alle diese Variablen sollten in der Kopfzeile der Schleife links initialisiert und rechts verändert werden, was wegen des Verbots des Komma-Operators jedoch nicht möglich ist. Nebenbei bemerkt ist eine solche Verwendung eines Struktur-Zeigers sehr performant.
14.3 Vor der Arbeit des Präprozessors, eine Leeranweisung darf nur alleine in einer Zeile stehen; ein Kommentar darf folgen, wenn nach der Leeranweisung zunächst ein Whitespace-Zeichen folgt.
- Etwas verwirrend. Jedenfalls ist »{ ; }« optisch bedeutend auffälliger als ein einzelnes »;« allein in einer Zeile. Mehrere Leeranweisungen »;;;;;« wären ebenfalls auffälliger als ein einzelnes.
14.4 Die »goto«-Anweisung darf nicht verwendet werden.
- In eher seltenen Fällen kann »goto« außerordentlich hilfreich sein! Vermeidungs-Code ist in diesen Fällen meistens problematisch, unübersichtlich und unsicher. Und ein anderes Konzept deshalb wäre sehr viel aufwendiger, anspruchsvoller und zeitraubend. Beim EmbeddedProgramming? ist »goto« nahezu unverzichtbar, da sehr ressourcenschonend. Es ist nicht sinnvoll, daß ein Microcontroller mit beispielsweise 2 MIPS und 4096 Byte RAM an seine Grenzen stößt und seine Aufgabe nicht mehr (ganz richtig) erfüllen kann, nur weil im Code auf »goto« (und andere Anweisungen) verzichtet werden mußte. Es ist sehr gefährlich, einen Microcontroller in die Nähe (einer) seiner Ressourcengrenzen zu fahren. Es kann (sporadisch) undefiniertes Verhalten entstehen.
14.5 Die »continue«-Anweisung darf nicht verwendet werden.
- Dieses Verbot ist nicht nachvollziehbar. Es ist sehr prägnant, übersichtlich und sicher, als erste Zeile des Schleifenkörpers beispielsweise »if (sfco->a==0 || sfco->id==0) continue;« zu codieren. Der Vorteil ist, daß an einer einzigen Zeile die gesamte Aktion und deren Grund ersichtlich ist. Vermeidungs-Code hat selten diesen Vorteil und wirkt hier unnatürlich und verkrampft.
14.6 Jede Iterations-Anweisung betreffend, es darf höchstens eine »break«-Anweisung vorhanden sein, um eine Schleife zu beenden.
- Falls zwei oder mehr »break;« gebraucht werden, wird die Situation problematisch. Vermeidungs-Code kann sehr häßlich und der Übersichtlichkeit abträglich sein.
14.7 Eine Funktion darf nur einen einzigen Punkt des Verlassens an ihrem Ende besitzen.
- Dies ist ein schädliches Verbot, besonders im Zusammenhang damit, daß »goto« verboten ist. Siehe auch 14.5. Sehr oft muß programmiert werden, daß eine Funktion in ihrem vorderen Teil verlassen werden kann mit verschiedenen Rückgabewerten, aufgrund einer Prüfung ihrer Parameterwerte mit negativem Ergebnis. Oft ist es sinnvoll, eine Funktion nach »default:« in einem »switch« zu verlassen. Es gibt auch Funktionen, die konzeptionell hunderte von »return value;« enthalten. Compiler generieren (auf Assemblerebene) in solchen Fällen oft automatisch Sprünge zu einem Exitpunkt. Es sind auch Funktionen bekannt, die mehrere Abteilungen haben, mit jeweils einem eigenen Exit. Wenn nur ein »return« am Ende erlaubt ist, wird Vermeidungs-Code in der Regel eine sehr einengende problematische Angelegenheit werden. Es ist übel, wenn man aufgrund fehlender Sprachmittel einer Programmiersprache (MISRA-C) überall zu unnatürlichen Ersatzkonzepten gezwungen wird. Die natürliche Reaktion darauf besteht darin, daß man sich eine geeignetere Programmiersprache aussucht. Andere Programmiersprachen erlauben ebenfalls die beliebige Verwendung einer return-Anweisung. Von dort ist allerdings nicht bekannt, daß wichtige Sprachmittel durch Verwendungsverbote stark beschnitten oder geraubt werden.
14.10 Alle »if ... else if« Konstruktionen müssen mit einem »else«-Zweig beendet werden.
- Die meisten »if«-Anweisungen haben auf natürliche Weise gar keinen Bedarf an einem »else«-Zweig. Es ist Unfug, zu verlangen, daß in einer Quelle hunderte solcher Zweige mit einer Leeranweisung (siehe 14.3) stehen, wenn es niemals sinnvollen false-Code geben kann. Warum sollte hier »if (Sig) ErrXI?(2, Sig);« ein »else«-Zweig folgen?
15.3 Der letzte Absatz einer »switch«-Anweisung muß der »default«-Absatz sein.
- Dadurch wird in diesem Kontext die Sicherheit reduziert! Es ist öfter sinnvoll und besonders sicher, den »default«-Absatz als ersten zu plazieren und ihn (beispielsweise) auf einen Initialisierungs-Absatz durchfallen zu lassen. Das Durchfallen ist allerdings durch Regel 15.2 verunmöglicht. Es ist zwar sinnvoll, einen »default:« zu erzwingen. Aber wieso unbedingt als letzten Absatz?
16.1 Funktionen mit einer variablen Anzahl von Argumenten dürfen nicht definiert werden.
- Solche Funktionen sind im Regelfall Problemlöser mit hoher Übersichtlichkeit. Als Ersatz können mehrere bis viele Funktionen mit einer steigenden Anzahl und/oder unterschiedlichem Typ von Argumenten implementiert werden, oder es wird an jeder Aufrufstelle zuvor ein Argumente-Array gefüllt und übergeben. Vermeidungs-Code ist also - eigentlich wie immer - unelegant, aufwendiger und unübersichtlich.
16.2 Funktionen dürfen nicht sich selbst aufrufen, weder direkt noch indirekt.
- Rekursivität ist ein gewaltiges Sprachmittel mit hoher Eleganz, Einfachheit und Übersichtlichkeit. Nichtrekursiver Vermeidungs-Code ist im Regelfall zwei- bis dreimal aufwendiger und unsicher(er), da alles, was bei Rekursion automatisch im Hintergrund abläuft, manuell nachgebildet werden muß, wobei die maximal abzuspeichernde Kontrolldatenmenge vorher nicht bekannt ist und mathematisch bestimmt werden muß, sofern das möglich ist. Wenn eine eindeutige Vorherbestimmung nicht möglich ist, muß ausgiebig getestet werden und danach das Doppelte angesetzt werden, das gegen Überlauf abgesichert werden muß. Auch hier bewirkt eine MISRA-Regel folglich das Gegenteil von dem, was MISRA eigentlich erreichen will.
17.4 Indexierter Zugriff auf ein Array ist die einzige erlaubte Art von Zeigerarithmetik.
- Zeigerarithmetik ist ein zu starkes Sprachmittel als daß es so sehr zurückgedrängt werden sollte. Mindestens Inkrement und Dekrement per »++ --« sollte zusätzlich erlaubt sein. Die Vorteile von Zeigerarithmetik sind eminent, so daß Programmierer sie erlernen sollten. Zeigerarithmetik verbieten und die Programmierer in diesem Punkt inkompetent zu belassen, ist der falsche Weg.
18.1 Alle Struktur- und Union-Typen müssen am Ende der Übersetzungseinheit komplett sein.
- Unklar. Genaue Kenntnis des C-Standard erforderlich.
18.2 Ein Objekt darf nicht einem überlappenden Objekt zugewiesen werden.
- Unklar. Überlappt das Objekt vor oder nach der Zuweisung? Objekte können einander nicht zugewiesen werden, Werte von Objekten schon. Regelprüfprogramme verwenden diese Regel nicht.
18.3 Ein Speicherbereich darf nicht für beziehungslose Zwecke verwendet werden.
- Unklar. Regelprüfprogramme verwenden diese Regel nicht.
18.4 Unionen dürfen nicht verwendet werden.
- In genau spezifiziertem Kontext können Unionen außerordentlich hilfreich sein und die Übersichtlichkeit und Sicherheit erhöhen. Microcontroller werden von Compiler-Herstellern unterstützt, auch indem ihre Register und Registerteile durch Unionen zugänglich gemacht (gemappt) werden. Diese Aufgabe selbst ohne Unionen durchzuführen, mittels z.B. 4000 Ausdrücken unter jeweiliger Verwendung von »& | >> << (cast)« wäre ganz sicher unsicherer und sehr unvernünftig.
19.6 »#undef« darf nicht verwendet werden.
- Die Compiler-Option »-U« für den gleichen Zweck wurde gewiß nicht grundlos implementiert. Wenn »#undef« innerhalb eines wohldefinierten Konzeptes verwendet wird, ist nichts dagegen einzuwenden. Makros aus dem Include-Verzeichnis (oder vergleichbar) sollten allerdings nicht wegdefiniert werden.
19.12 Die Präprozessor-Operatoren »#« oder »##« dürfen höchstens einmal vorkommen in einer Makro-Definition.
- Es kommt nicht selten vor, daß »##« vielfach benötigt wird, wenn vielfach vorkommende Variablennamen mit einheitlichem Namensteil hergestellt werden müssen. Und wenn dieser Operator 100-mal in einer Definition vorkommt - was ist so gefährlich daran? Eine einfachere Funktion eines Operators ist doch kaum vorstellbar.
20.4 Dynamische Heap-Speicher-Allokation darf nicht verwendet werden.
- Es wurden und werden viele Programme realisiert, die ohne »malloc()« und Co. gar nicht machbar wären. Beispielsweise ein Skriptinterpreter könnte bei manchen Verwendungen temporär 500 MiB? Speicher benötigen, im Regelfall jedoch nur 40 KiB?. Es wäre sehr unvernünftig, in einem solchen Programm pauschal statische Arrays von insgesamt 1000 MiB? Größe zu definieren! Ebenso unvernünftig wäre es, statisch nur 200 KiB? zur Verfügung zu stellen. Zudem ist unbekannt, welche Größen die Arrays jeweils später zur Laufzeit haben müssen. Eine andere Art von Programm auf einem Microcontroller könnte während der StartUp?-Phase für eine halbe Sekunde 280 KiB? Speicher benötigen. Anschließend könnte der Speicherbedarf stark unterschiedlich sein, je nach dem, wie viele und welche Kommunikationsprotokolle vom Kunden per Key freigeschaltet wurden und (gleichzeitig) benutzt werden. Ein Protokoll wie z.B. IEC61850 kann durchaus 500+ KiB? benötigen. Compiler-Limits werden mitunter als infinity angegeben. Das bedeutet eine alleinige Abhängigkeit von der auf der jeweiligen Plattform verfügbaren Speichermenge - eben soweit der Speicher reicht. Es wird von vielen Programmen verlangt, daß sie sich arbiträr verhalten. Ganz ohne dynamische Besorgung von Arbeitsspeicher geht es einfach nicht!
20.6 Das Makro »offsetof« in der Bibliothek »stddef.h« darf nicht verwendet werden.
- Der Offset von Struktur-Mitgliedern läßt sich auch auf andere Weise feststellen. Das ist allerdings aufwendig, unübersichtlich, unsicher und nur zur Laufzeit möglich. Das Verbot erscheint unter diesem Aspekt nicht vernünftig. Oder soll das Verbot bedeuten, daß ein Offset grundsätzlich nicht festgestellt werden soll?
20.7 Das Makro »setjmp« und die Funktion »longjmp« dürfen nicht verwendet werden.
- Dieses Sprachmittel ist mächtig und elegant. Mit Hilfe dieses Sprachmittels sind die gewünschten Verhaltensweisen eines Programms schnell, übersichtlich und sehr einfach herstellbar. Oft können Sicherheitsanforderungen genau dadurch erfüllt werden. Ohne dieses Sprachmittel sind gleiche Verhaltensweisen eines Programms nur mit sehr viel größerem Aufwand herstellbar. Zumeist sind andere oder zusätzliche Konzepte vonnöten. Es ist zu bezweifeln, daß der Verzicht auf dieses Sprachmittel sicherere Programme bewirkt.
20.8 Die Signal-Verarbeitung in »signal.h« darf nicht verwendet werden.
- Auf Microcontrollern (ohne Betriebssystem) wird man diese Signale nicht benötigen. Aber ansonsten ist dies die einzige Möglichkeit, beispielsweise ein Programm Aufräumarbeiten erledigen zu lassen, wenn ein Terminierungssignal empfangen wurde. Programme, die eigentlich Signale bräuchten, aber ohne Signale arbeiten (müssen), produzieren oft unerwünschten Schrott im »\TEMP«-Verzeichnis.
20.9 Die Eingabe-/Ausgabe-Bibliothek »stdio.h« darf in Produktions-Code nicht verwendet werden.
- Die Funktion »snprintf« kann allerdings außerordentlich nützlich sein! Es werden auch selbstentwickelte snprintf verwendet, die weitere Formate unterstützen, beispielsweise zentrierte Ausgabe (:), Auffüllen mit Leerzeichen ($), Ausgabe mit Gleitpunkt bei Integer (,w.n), etc. Bei Microcontrollern benutzen Funktionen wie »Print_lcd« und »Print_uart« die Funktion snprintf. Die Verwendung des printf-Formats ist einfach, übersichtlich und universell. Viele Compiler warnen (bei stdio-Funktionen), falls das Format nicht zu den Argumenten paßt.
Die MISRA-Regeln sind entstanden aufgrund von Fehlern, die von C-Programmierern
gemacht wurden.
Diese Zusammenstellung ist ein Verdienst von MISRA.
Die Art der aktuellen Verwendung dieser statistischen Resultate ist jedoch
im Endeffekt wahrscheinlich nicht nützlich, sondern schädlich.
Wenn diese lange Liste von MISRA-Regeln (40 von über 140) mit C-Sachverstand
gelesen wird, kommt zuerst Verwunderung auf, die nach einer Weile des Lesens
in blankes Entsetzen umschlägt.
Es ist durchaus möglich, über 30 Jahre C-Programmierung zu betreiben
und nicht einen einzigen Fehler zu begehen, der im Zusammenhang mit irgendeiner
MISRA-Regel und deren Begründung steht!
Das heißt, es ist niemals ein Fehler passiert,
aufgrund von mangelndem Verständnis eines Sprachmittels der Programmiersprache C.
Es wurden eben - ganz einfach - Sprachmittel solange nicht
eingesetzt, solange sie nicht vollkommen verstanden waren.
Oder die Sprachmittel wurden nur insoweit ausgeschöpft wie es zum jeweiligen
Kenntnisstand paßte.
Fehler anderer Art sind durchaus zu Hunderten passiert.
Nämlich Algorithmus-Fehler: Es wurden (kreative) Überlegungen
vergessen oder falsch angestellt.
Und genau zur Minimierung von Algorithmus-Fehlern ist das vollständige
Ausschöpfen aller Sprachmittel geeignet.
Die Erschaffer von Programmiersprachen haben ganz sicher jedes Sprachmittel
genauestens überlegt, weil sie wissen, was wie benötigt wird, um im jeweiligen
Rahmen alle Programmierprobleme möglichst zügig, effizient, elegant
und übersichtlich lösen zu können.
Bei Befolgung aller MISRA-Regeln jedoch wird die Sprache C so stark reduziert,
daß es sich nicht mehr um eine leistungsfähige Programmiersprache handelt.
Es müssen schlechtere Konzepte verwendet und der Quellcode unübersichtlich
aufgeblasen werden, um die fehlenden Sprachmittel zu ersetzen.
Es fehlt der Raum für Kreativität - man kämpft mit dem Wust.
Es ist zu bezweifeln, daß dies zu sichereren Programmen führt.
Außerdem sind solche Programme zur Laufzeit oft beträchtlich weniger
performant, bis hin zur Unbrauchbarkeit.
Die Sprachen ADA und PEARL werden für sichere Programmierung empfohlen.
Beide Sprachen stellen beispielsweise »RETURN;« als auch
»RETURN (expr);« zur Verfügung, mit den gleichen
vielfältigen Verwendungsmöglichkeiten wie in C.
ADA enthält auch »goto« und »exit« (break), wobei
eine »exit«-Anweisung auch aus beliebig vielen
verschachtelten Schleifen herausspringen kann.
Jedoch hier gibt es keinerlei Verbote, um Sicherheit zu gewährleisten.
Diese Sprachen sind für sichere Programmierung geeignet, unter anderem
gerade wegen ihrer vorhandenen Sprachmittel.
Es ist grundlegend, daß die Wahrscheinlichkeit von Softwarefehlern mit der Gesamtmenge
und mit den Teilmengen an Codezeilen steigt.
Je komplexer und unübersichtlicher ein Quellcode(teil) ist, desto wahrscheinlicher
sind Fehler darin. Umgekehrt ist schlanker und übersichtlicher Code potentiell sicherer.
Des Weiteren waren in der Vergangenheit weit überwiegend Algorithmus-Fehler
(versäumte oder falsche Überlegungen) Ursache von Schäden durch fehlerhafte Software
(Bugs,
Ariane).
Die Befolgung aller MISRA-Regeln verursacht (indirekt) potentiell unsicherere Software.
Verwender unterliegen dem gefährlichen Trugschluß, daß ihre Software allein durch Befolgung
der MISRA-Regeln sicher oder sicherer ist.
Verwendungsverbot von Sprachmitteln ist hier ein falscher Ansatz.
Die stärkste positive Wirkung bezüglich Sicherheit wird erzielt, wenn beim Schreiben
eines jeden Codeabschnittes dessen Aufgaben und Wechselwirkungen gründlichst von einem
erfahrenen und talentierten Programmierer überlegt und berücksichtigt werden
und dieser Programmierer die verwendete Programmiersprache meisterhaft beherrscht
und verwendet und die Hardware-Plattform sehr gut kennt.
Oje-Effekt
Ein prominenter C-Experte, der nicht genannt werden wollte, hat 2001 gesagt:
"MISRA C is a shack built on a swamp!"
(Misra ist eine Bretterbude, gebaut auf Sumpf.)
KategorieC KategorieCee KategorieStandard
StartSeite | Neues | TestSeite | ForumSeite | Teilnehmer | Kategorien | Index | Hilfe | Einstellungen | Ändern
Text dieser Seite ändern (zuletzt geändert: 8. Juni 2014 22:20 (diff))