C CodingStyleGuide 3.0.10
C CodingStyleGuide 3.0.10
C-Coding Styleguide
-Programmierrichtlinien-
Hochschule Offenburg
Fakultät EMI
Lehrgebiete:
Ingenieur-Informatik/Programmieren 2
Embedded Systems 1+2
Embedded Echtzeitsysteme
Ergeben sich bezüglich den Lehrgebieten unterschiedliche Programmierrichtlinien, werden die beiden Regelvarianten der Richtlinie mit II (Ingenieur-Informatik) und
ES (Embedded Systems 1+2, Embedded Echtzeitsysteme) kenntlich gemacht. Programmierrichtlinien, die nur für Embedded Systems 1+2 und Embedded
Echtzeitsysteme relevant sind, sind ebenso mit ES gekennzeichnet.
Zur Bestimmung einzelner Metriken kommt das SW-Werkzeug Testwell CMT++ zur Anwendung. Dieses SW-Werkzeug ist in der zu verwendenden VM
(BWLehrpool) installiert und dient dazu Metriken, wie z.B. LOCpro (Lines Of Code - program), c% (Kommentardichte) und v(G) (Zyklomatische Komplexität –
Cyclomatic Complexity CC - nach McCabe) zu bestimmen. Empfohlene Regeln (recommended) sind in der Nummernspalte grau hinterlegt.
Allgemeines
Der Code-Editor ist so zu konfigurieren, dass ein Tabulator durch drei Leerzeichen Innerhalb der IDE können Einstellungen für alle oder für
ersetzt wird. Damit ist das Erscheinungsbild vom Sourcecode gleich, wenn dieser in spezifische Sprachen (C/C++) eingegeben werden.
Microsoft Visual Studio 2019:
verschiedenen Editoren geöffnet wird. Tools Options Text-Editor C/C++ Tabs Tab size und
A1 Indent size auf 3, Insert spaces anwählen.
Keil uVision 5:
Edit Configuration Tab size auf 3 und Insert spaces for
tabs anwählen.
Sourcecode wird in englischer Sprache verfasst. // User has to enter the max. value
Das gilt z.B. für Kommentare, Variablen- und Funktionsnamen. int iMaxUserValue;
A2 // Check against limits
BOOL IsMaxValue(int iEnteredValue);
Faustregel: Pro 10 Programmzeilen 1-3 Zeilen Kommentar! (c%: 10-30%)
Auch leere Zeilen tragen zur Lesbarkeit bei.
// increment i -> trivial, don’t comment this
Es sind keine Trivialitäten zu dokumentieren. Alles, was sich aus dem i++;
A3 Programmcode für einen mittelmäßigen Programmierer sofort ergibt, ist nicht zu
dokumentieren! // Is source of timer interrupt match
In Testwell CMT++ ist die Kommentardichte als c% und die Programmzeilen als // register 2?
LOCpro ausgewiesen. Ist c% zu groß oder zu klein, dann wird ein „-“ angezeigt. if (LPC_TIM0->IR & (1<<2))
Kommentare beginnen mit einem //. Mehrzeilige Kommentare /* */ sind nicht // this is okay
erlaubt. Ausnahme: doxygen-Kommentare! (siehe Embedded Systems 1)
A4 Bei projektweiter Suche ist im Suchergebnis sofort ersichtlich, welche Zeile
/* this is
not
auskommentiert ist und welche nicht. okay */
Ein C-Projekt ist in viele *.c- und *.h-Dateien aufzuteilen. Dateinamen beginnen Nqueensmain.c
A5 mit einem Großbuchstaben. Recalgo.c
Recalgo.h
Die Wiederverwendbarkeit der Module wird dadurch erhöht (Baukastenprinzip).
Eine Funktion darf maximal nur 4-40 Programmzeilen enthalten.
A6
In Testwell CMT++ sind die Programmzeilen als LOCpro ausgewiesen.
A7 Eine C-Datei darf maximal nur 4-400 Zeilen enthalten
Eine C-Datei (Modul) ist wie folgt aufgebaut:
#include <stdio.h>
1. Systemheaderdateien (z.B. #include <stdio.h>) #include <conio2.h>
2. User Headerdateien (z.B. #include "Eightqueen.h") #include "Eightqueen.h"
A8
3. Modulspezifische Datentypen
Defines, Konstanten, Enumerationen, Structs, Unions, Bitfields, Typedefs, Makros #define SIZEOFBOARD 8
typedef unsigned short int UINT16_t;
const double cdPI = 3.1415926535;
enum Modes
{
continuous,
stop
};
A10 Projektglobale Variablen müssen in einer c-Datei definiert werden. volatile uint32_t ui32Clock1s;
ES
Projektglobale Variablen können in der c-Datei als modulglobale Variablen int iVal1 = 0; //without encapsulation
gekapselt werden. Der Zugriff erfolgt dann mit Getter- und Setterfunktionen.
static int iVal2 = 0; // with encapsulation
Dadurch können Checkfunktionen (bei den Setter- und ggf. auch bei den
Getterfunktionen) realisiert werden. int GetVal2(void) void SetVal2(int iNewVal2)
Ebenso ergibt sich beim Debuggen ein wesentlicher Vorteil: Setzt man einen { {
A11 return iVal2; if (iNewVal2 < 4711)
Breakpoint in die Setter- oder Getterfunktion, so hält das Programm an, wenn ein
Zugriff erfolgt. Über den Callstack kann dann der Aufruf nachverfolgt werden. } {
iVal2 = iNewVal2;
}
}
Für komplexe Datentypen, Variablen und Funktionen sind aussagekräftige Namen int GetGreatestValue(int iValA, int iValB);
A12 zu vergeben.
BOOL IsValueNegativ(int iValue);
Code wird lesbarer und ersetzt ggf. einen Kommentar.
Code-Layout
Die öffnende { und schließende } geschweifte Klammer beginnt stets in einer neuen for (ui = 0; ui < 10; ui++)
Zeile. {
CL1 // Code
}
Innerhalb der geschweiften Klammern ist der Codeblock um drei Leerzeichen struct SyncMechanism
einzurücken. Dies gilt auch nach case-Labels in einer switch/case-Kontrollstruktur, {
CL2 bei der Deklaration von komplexen Datentypen und ihren Elementen, sowie bei
long int liTimeStamp;
int iNumberOfIntervals;
Kommentaren. (siehe Regel A1: Umwandlung von Tabs in Leerzeichen) };
Nach den Schlüsselworten if, while, for, switch folgt ein Leerzeichen. for ()
CL3 if ()
while ()
Nach einem Funktionsnamen folgt unmittelbar die öffnende runde Klammer ( Good:
(Deklaration, Definition, Aufruf). Call250usTask();
CL4 Bad:
Daran erkennt man, dass dies zusammengehört. Call250usTask ();
Vor oder nach den primären Operatoren ( (), ->, . und [ ]) oder zwischen unären psAddress->iZipCode = asAddressOld[3].iZipCode; //primary
Operatoren und ihrem Operanden sowie bei bitorientierten Operationen (&, |, ^, ~, iNumbers++; //unary (++)
uiVal1 = uiVal2>>2;
<<, >>) und dem logischen ! sind keine Leerzeichen zu verwenden. iVal3 = (4 * iVal4) + 4;
Vor und nach den relationalen (<, <= ,> ,>= ,== , !=), arithmetischen (*, /, +, -, %)
CL5
und logischen Operatoren (&&, ||) ist jeweils ein Leerzeichen einzufügen. Bei einer if ((iA < 10) && (iA != iC))
Zuweisung ist ebenso vor und nach dem = ein Leerzeichen einzufügen (gilt ebenso
für Kurzformen). Zur Erhöhung der Lesbarkeit kann aber bei komplexeren
if (LPC_TIM0->IR & (1<<2))
Ausdrücken von dieser Regel abgewichen werden (Hinzfügen von Leerzeichen).
Die geschweiften Klammern { } stehen bei switch, while, for, do/while, if, else, switch (iSelection)
else/if direkt unter dem ersten Buchstaben des Schlüsselwortes. {
case 10:
while (i < 10)
{
i = GetStatusOfPort();
}
CL6 break;
case 20: // fall through is ok here
case 30:
// commands if iSelection is 20 or 30
break;
default:
break;
}
DV4 Pro Zeile ist immer nur eine Variable/Konstante zu deklarieren/definieren. int* pi1, pi2; // not okay
Bei der Definition einer Zeiger-Variablen/-Konstanten ist zwischen dem Datentyp int * piVal1; // not okay
DV5 int* piVal2; // okay
und dem * kein Leerzeichen einzufügen.
Bei Konstanten ist noch ein c voranzustellen. Bei Pointern ist p und bei Arrays ist const float cfPI = 3.1415F;
ein a dem Präfix voranzustellen. Einem void-Pointer ist ein pv als gesamter Präfix int aiSemester[7];
DV6 int* piSum;
voranzustellen (Sonderrolle, keine void-Variable möglich) char* pacSymbols[];
DV7 Für Laufvariablen können ausnahmsweise i, j, k, l, m und n verwendet werden. int i;
long-Konstanten sind mit einem L, unsigned Konstanten mit einem U und unsigned lNumberOfBits = 221214233L;
DV8 ulprojectcode = 994943848UL;
long-Konstanten sind mit einem UL abzuschließen. Keine Kleinbuchstaben u und l!
Fließkommakonstanten haben immer mindestens eine Ziffer vor und nach dem const float cfPi = 3.1415F;
Dezimalpunkt. Ein F signalisiert den Datentyp float, ohne F ist die Konstante vom const float cfGoldenCut = 0.618F;
DV9 const double cdEuler = 2.7182818284;
Typ double. Der Kleinbuchstabe f ist nicht zu verwenden.
Hexadezimalkonstanten beginnen immer mit 0x (kein großes O!!!). Für A-F sind const unsigned short int cuiValue = 0x1AF3;
DV10
Großbuchstaben zu verwenden.
DV11 Oktalkonstanten (führende 0) sollten nicht verwendet werden. unsigned int uiVal = 072; //072 is octal constant
Wenn Modifizierer verwendet werden (long, short, signed, unsigned), müssen long int liNumberOfBytes; //okay
DV12 long liRange; // not okay
immer die Basistypen (int, float, double, char) angegeben werden.
Implizite Casts (casts durch Compiler) bei Zuweisungen sind zu vermeiden! Nur iVal = fVal; // implicit cast – not okay
DV13 iVal = (int)fVal; // explicit cast - okay
explizite casts verwenden, um zu zeigen, dass dies so gewollt ist.
Zeiger sind vor ihrer ersten Verwendung mit NULL zu initialisieren, sofern dem int* piMemory = NULL;
Zeiger nicht in einer ersten Definition eine gültige Adresse zugewiesen wird.
DV14 // check if pointer is valid
Wenn dies durchgängig erfolgt (mit Regel DV16) kann immer festgestellt werden, ob if (piMemory != NULL)
ein Zeiger gültig ist. NULL ist als ((void*)0) definiert: #define NULL ((void*)0)
Bei malloc immer den sizeof-Operator (keine absoluten Werte) verwenden. piMemory = (int*)malloc(100 * sizeof(int));
DV15
malloc liefert einen Zeiger vom Type void* zurück. Expliziter cast in C++ notwendig
Nach einem free ist der Zeiger ebenso auf NULL zu setzen, um diesen dann wieder als free(piMemory);
DV16 piMemory = NULL;
ungültig zu kennzeichnen.
Eingebettete Zuweisungen sind zu vermeiden. Eingebettete Zuweisungen sind //not okay // okay
entsprechend aufzuspalten. if ((iTotal = GetTotal()) == 10) iTotal = GetTotal();
{ if (iTotal == 10)
DV17 Der Programmcode wird dadurch lesbarer und ist auch weniger fehleranfällig. printf("do it\n"); {
} printf("do it\n");
}
Signed und unsigned Variablen/Konstanten dürfen nicht in einer if (uiVal1 > iVal2) //possible wrong behaviour
DV18
Anweisung/Bedingung verwendet werden.
Die bitorientierten Operatoren (>> und <<) dürfen nicht auf signed Variablen iVal2 = iVal2>>1U; //not okay if iVal2 is negativ
DV19 uiVal3 = iVal3>>1U; //okay
angewendet werden.
Komplexe Datentypen
Variablen von komplexen Datentypen enum, union, struct, bitfield wird als
Präfix vorangestellt:
enum States eProgrammStates;
enum e union MeasureTime uLastTime;
KD1
union u struct Address sCustomerAddress;
struct s struct Flags bfNewFlags;
bitfields bf
Auch bei den komplexen Datentypen gilt: const enum States ceProgrammStates;
Bei Konstanten ist noch ein c voranzustellen. Bei Arrays ist ein a und bei struct Address asCustomerAddress[100];
KD2 struct Address* psCustomerAddress;
Pointern ein p dem Präfix voranzustellen. const struct Name csDefaultName = {"Hans","Mustermann"};
Um Schreibarbeit zu sparen, sind von jedem obigen komplexen Datentyp (KD1) Kurzform Langform
ein Typedef auch für einen Zeiger darauf zu deklarieren. typedef struct Address struct Address
{ {
Bei komplexen Datentypen erspart man sich dann bei den Definitionen und den //elements of struct Address //elements of struct Address
Deklarationen die Verwendung der Schlüsselworte struct, union und enum. }Address_t; };
KD3 typedef struct Adress
Address_t;
typedef Address_t* pAddress_t; typedef Address_t* pAddress_t;
Address_t sMyAddress = {"Badstrasse 24",77652,"Offenburg"};
pAddress_t psMyAddress = &sMyAddress;
KD4 Eigenen Typdefinitionen (typedef) ist als Postfix ein _t nachzustellen. typedef float f32_t;
Auf die Elemente eines Bitfelds sollte nie über eine Maske zugegriffen werden. if (bfstate&0x80000000) // not okay
KD5 Es ist abhängig vom Compiler, wo die Elemente des Bitfelds innerhalb des
Speicherbereichs angelegt werden.
Die Elemente eines Bitfeldes müssen vom Typ unsigned int (II) oder uint32_t struct BitfieldAPSR
(ES) sein. {
KD6 unsigned int uiVal1 : 1;
unsigned int uiVal2 : 3;
};
Bei der Deklaration einer Enumeration ist beim ersten Element der Startwert enum States {Start = 0, Run, End};
KD7
anzugeben, auch wenn dieser 0 ist (Default).
KD8 Bei Funktionszeigern sollte immer mit einem typdef gearbeitet werden. typedef (void)(*pf)(int, char, int*);
KD9 Rückgabewert und Übergabewert sind dabei nach DV3 im neuen Namen zu typedef (uint32_t)(*pf_u32_s32-s8_t)(int32_t, int8_t);
ES hinterlegen. Ebenso ist als Postfix ein _t nachzustellen.
Funktionen
Nach der öffnenden geschweiften Klammer werden alle lokalen Variablen pro char* CopyStringWithoutBlanks(char* pcDest, char* pcSource)
Zeile einzeln definiert und immer initialisiert. Die zum Funktionsrumpf {
// points to the result
gehörenden geschweiften Klammern stehen dabei immer in der ersten Spalte und char* pcDestination;
in einer neuen Zeile.
// number of chars
F1 int iNumberOfChars = 0;
return pcDestination;
}
Funktionen sind vor dem Aufruf zu deklarieren.
F2
Ansonsten greift die implizite Int-Regel (Returnwert wird als int angenommen).
Bei der Funktionsdeklaration sind die optionalen Variablennamen anzugeben, char* CopyStringWithoutBlanks(char* pcDest, char* pcSource);
falls Parameter übergeben werden. Werden keine Parameter übergeben, so ist bei
F3 int GetNumberOfTrials(void);
der Deklaration ein void hinzuzufügen.
Dies dient dem besseren Verständnis beim Lesen der Funktionsdeklaration.
F4 Es ist nur ein return pro Funktion erlaubt und zwar als letzte Anweisung.
F5 Eine Funktion, die keinen Wert zurückgibt (void), hat keine return-Anweisung.
F6 Die maximale Anzahl an Übergabeparameter sollte drei nicht überschreiten. int CalcMedian(int iVal1, int iVal2, int iVal3)
Eine Funktion darf nicht mehr als neun Bedingungen (in if, while, do..while, for) CC = Anzahl Bedingungen + 1
F7 enthalten. Die maximale zyklomatische Komplexität CC ist dann 10.
In Testwell CMT++ ist die zyklomatische Komplexität als v(G) ausgewiesen.
Formale Parameter, die innerhalb einer Funktion nur lesend benutzt werden, sind unsigned int CalcSpeed(const unsigned int cuiVal)
mit dem Schlüsselwort const zu übergeben {
return (10 * cuiVal);
}
Bei Zeigern auf Strukturen kann verfeinert werden: unsigned int calcTorque(const struct Vals* const cpcs)
F8 const beim Datentyp: Die Daten sind konstant (nicht veränderbar) {
const beim Zeiger: Der Zeiger ist konstant. return (ps->uiKm * ps->uiI0);
}
//Alternative version of calcTorque
//First const can be set after datatyp and before *
unsigned int calcTorque(struct Vals const * const cpcs)
F9 Strukturen dürfen nie per value übergeben werden. int32_t CalcMedian(struct VeryBig smybig); //not okay
ES Diese benötigen sonst zu viel Speicher auf dem Stack. int32_t CalcMedian(struct VeryBig* const pcsmybig); //okay
Funktionen, die nur innerhalb eines Moduls (c-Datei) verwendet werden, sind als static int GetSum(int ival1, int ival2);
F10
static (Kapselungsprinzip) in der c-Datei zu deklarieren. Ein Zugriff von
außerhalb des Moduls (c-Datei) ist dann nicht mehr möglich.
Kontrollstrukturen
Sprünge mit goto sind nicht erlaubt. Entsprechend sind Labels außer bei goto Label1; // not okay
K1
switch/case-Kontrollstrukturen nicht notwendig.
K2 break ist nur in switch/case-Kontrollstrukturen erlaubt.
Eine switch/case-Kontrollstrukturen hat immer ein default als letzter Case default:
K3 break;
(Fehlerausgabe etc.). Dieser letzte Case enthält auch ein break.
Ein fallthrough (fehlendes break) in einer switch/case-Kontrollstruktur ist case 1: // fallthrough to case 2, because same behaviour
K4 case 2:
ausführlich zu dokumentieren.
Das Auslassen von Teilen des Schleifenkörpers mittels continue ist nicht erlaubt. //not okay //okay
Dies kann durch einen angepassten Kontrollfluss erreicht werden. while (iStatus < 10) while (iStatus < 10)
{ {
iStatus = GetStatus(); iStatus = GetStatus();
if (iStatus == 4) if (iStatus != 4) //okay
K5 { {
continue; // not okay // other statements
} }
// other statements }
}
Das Abbrechen des Programmes mit exit() ist nur in begründeten Ausnahmefällen if (iEmergency == 1)
(Sicherheit oder Notfall) möglich. Ansonsten ist exit generell zu vermeiden. {
K6 exit(0);
}
Die bedingte Zuweisung ?: (ternärer Operator) ist zu vermeiden, wenn dies iZ = (iA > iB) ? iA : iB; // z = max(a, b)
verständlicher ausdrückt werden kann (als if-else-Abfrage). Ansonsten ist mittels
K7 Kommentar zu beschreiben, was mit der bedingten Zuweisung erreicht werden
soll.
Die bedingte Zuweisung findet sich häufig in Funktionsmakros.
elseif ist aus Gründen der besseren Lesbarkeit nicht erlaubt! // not okay // okay
Dies kann einfach durch Klammerung {} und einem if ersetzt werden. if (iVal == 1) if (iVal == 1)
{ {
iVal = 42; iVal = 42;
} }
elseif (iVal == 2) else
K8 { {
iVal = 79; if (iVal == 2)
} {
iVal = 79;
}
}
Ein Codeblock, der nur aus einer Zeile besteht, ist auch zu klammern. if (iVal == 1)
K9 Der äußerste Codeblock eines case in einer switch/case-Kontrollstruktur ist nicht {
iVal = 42;
zu klammern. }
Ansonsten werden die sowieso schon umfangreichen (LOCpro) switch/case-
Kontrollstrukturen noch länger.
Die Regeln der „Strukturierten Programmierung“ sind einzuhalten.
§1 Eine Funktion hat einen Eingang und einen Ausgang.
§2 Nicht in Abfragen springen.
§3 Nicht aus Abfragen herausspringen.
K10
§4 Nicht in Schleifen springen.
§5 Nicht aus Schleifen herausspringen.
Mit Struktogrammen (Nassi-Shneiderman Diagrammen) können nur strukturierte
Funktionen realisiert werden.
Kurzformen (C-Standard: != 0 für wahr, 0 für falsch) sind zu vermeiden. if (iVal) // better: if (iVal != 0)
K11 Dieses implizite Wissen (!= 0 für wahr) sollte nicht vorausgesetzt werden.
Im C89 Standard gibt es noch kein true und false.
Ist die Anzahl der Schleifendurchläufe vor der Schleife bekannt, so ist die for- statt
K12 die while-Schleife zu verwenden.
While-Schleifen sind in der Regel fehleranfälliger und etwas schwerer lesbar.
Schleifenvariablen (Laufvariablen) dürfen bei einer for-Schleife im Schleifenrumpf for (i = 0; i < 100; i++)
nicht verändert werden. {
//more code
K13 i = iUserInput; // not okay
//more code
}
Fließkommavariablen (double und float) dürfen nicht als Schleifenvariablen for (fVal = 0.0F; fVal < 10.0F; fVal += 0.01F) // not okay
K14 (Laufvariablen) verwendet werden.
for (iVal = 0; iVal < 1000; iVal++) // okay
Bei Fließkommazahlen gibt es Rundungsungenauigkeiten (IEEE754).
Fließkommavariablen (double und float) dürfen nie auf Gleichheit überprüft if (dVal == 3.5517) // not okay
werden.
const double cdTolerance = 0.0001;
Bei Fließkommazahlen gibt es Rundungsungenauigkeiten (IEEE754). double dDifference;
K15
dDifference = fabs(dVal - 3.5517);
Präprozessor
Jede Headerdatei (z.B. PWM.h) ist mittels include-Guard gegen ein mehrfaches #ifndef _PWM_H
Inkludieren zu schützen. Name der Headerdatei ist dabei wie rechts aufgezeigt zu #define _PWM_H
P1 verwenden. // all Code
Damit wird jede h-Datei bei der Übersetzung einer C-Datei nur einmal inkludiert.
Oft werden h-Dateien über andere h-Dateien sonst mehrfach inkludiert. #endif //_PWM_H
P2 Eine c-Datei darf nie inkludiert werden.
#defines sind groß zu schreiben. #define NUMBEROFFILES 5
P3
Damit lassen sich diese von Variablen besser unterscheiden.
Werden defines zur bedingten Compilierung eingesetzt, so ist im Namen SW_ als #define SW_LINUX
P4 #define SW_GLCD
Präfix vorzustellen.
Um Seiteneffekte zu minimieren sind Makros wie folgt zu implementieren //Swap the Bytes of a 16-Bit Value
Das gesamte Makro ist zu klammern
#define SWAPBYTES16(x) ((uint16_t)(((uint16_t)(x) << 8) | \
P5 Jeder Makroparameter ist zu klammern ((uint16_t)(x) >> 8)))
Makroparameter und das gesamte Makro sind auf den korrekten Typ zu
casten
Umfangreichere Makros sind lesbarer auf mehrere Zeilen aufzuteilen. Ein siehe P5
P6
Backslash trennt die Makrozeilen voneinander.
Um weitere Seiteneffekte auszuschließen, sollten inline-Funktionen Makros // MACRO: Sideeffect with char cval = 'Z' AND
vorgezogen werden. Diese bieten eine bessere Typsicherheit und vermeiden // usage with ISUPPER(cval++)
#define ISUPPER(c) (((c) >= 'A') && ((c) <= 'Z'))
unerwünschte Seiteneffekte.
Inline-Funktionen erscheinen zwar im Sourcecode als Funktion, jedoch wird der // Inline function: No Sideeffect with char cval = 'Z' AND
Funktionsrumpf meist vom Compiler direkt an die Stelle des Funktionsaufrufes // usage with isUpper(cval++)
kopiert. Der Umfang des Maschinencodes wird dadurch größer – die Performanz #include <stdbool.h>
nimmt zu. Um die inline-Funktionen auch außerhalb des Moduls aufrufen zu
inline bool isUpper(char cval)
P7 können, wird noch eine „richtige“ Implementierung zur Verfügung gestellt. Inline {
ist allerdings nur einen Hinweis an den Compiler. Umfangreiche Funktionen bool bret = false;
werden trotz inline Schlüsselwort nicht „geinlined“.
if ((cval >= 'A') && (cval <= 'Z'))
{
bret = true;
}
return bret;
}
Symbolische Konstanten sind – falls möglich – durch konstante Variablen zu #define PI 3.1415 //avoid
P8 ersetzen. const double cdPi = 3.1415; //better
Doxygen
D1 Von Anfang an ist doxygen zur Dokumentation einzusetzen für siehe [Link]
ES Dateiköpfe, Funktionen, Strukturen, Enumerationen, Unions und Bitfelder.
Jede Datei enthält einen Dateikopf, der von doxygen interpretiert werden kann. /**
@file Name der Datei * @file MyFirstCFile.c
* @brief File contains the main program
D2 @brief Kurzbeschreibung des Moduls * @author Daniel Fischer
ES @author Name des Modulanlegers * @date 01.08.2010
@date Erstellungsdatum *
* Additional Infos: This module is certified...
*/
Jede Funktion enthält einen Funktionskopf, der von doxygen interpretiert /**
werden kann. * @fn int CalcGreatest(int iA, int iB)
* @brief Calculates the greatest value
@fn Funktionsdeklaration * @param iA first value
@brief Kurzbeschreibung der Funktion * @param iB second value
@param Name und Beschreibung des formalen Paramters * @return int greatest value of iA and iB
D3 * @author Daniel Fischer
und nur falls vorhanden
ES * @date 01.08.2010
@return Datentyp und Beschreibung des Rückgabewerts,
*
und nur wenn ungleich void * The greatest value is calculated based on the
@author Name des Programmierers * given two parameters. If both are equal...
@date Erstellungsdatum */
Doxygen ist so zu konfigurieren, dass call graphs und include dependency graphs
D4 grafisch angezeigt werden.
ES Dazu ist das Tool GraphViz zu installieren. Siehe hierzu die entsprechende
Anleitung.
3 *(Multiplikation) / % L nach R
7 == != L nach R
PA1
8 &(bitweise Und) L nach R
9 ^ L nach R
10 | L nach R
11 && L nach R
12 || L nach R
13 ?: R nach L
15 , L nach R
Der Kommaoperator (Priorität 15) int i,j,k; // not allowed
PA2 for (i = 0; i < 10 , j = 0 ; i++) // not allowed
darf nicht verwendet werden.
Der ternäre Operator ?: (Priorität 13) Siehe auch Regel K7
PA3 sollte nicht verwendet werden. Ist
durch if-else zu ersetzen.
Es darf kein implizites Wissen über iA = ((iA + iB) / iC) * iB;
die Operatorpriorität und der if ((iA < 10) && (iB != iC))
PA4 Assoziativität vorausgesetzt werden.
Ausdrücke mit verschiedenen
Operatoren sind zu klammern.
ChangeLog:
21.08.18 3.0.0 Komplette Überarbeitung (Unterscheidung II/ES), weitere Regeln, weitere Kapitel (Präprozessor), Komplexe Datentypen DF
30.09.18 3.0.1 Bugfixes DF
03.10.18 3.0.2 Bugfixes, Codebeispiele hinzugefügt DF
04.10.18 3.0.3 Zwei Bugfixes DF
10.08.19 3.0.4 Diverse Bugfixes, Ergänzungen und Vereinheitlichung DF
15.02.20 3.0.5 Diverse Bugfixes (NULL) und Einführung von „_“ bei Funktionsnamen in Embedded Systems, EMI, CC ergänzt, Blanks DF
15.05.20 3.0.6 Redaktionelle Änderungen, CL6 zu PA verschoben, Kommentardichte anpasst, (A3) DF
18.07.20 3.0.7 Anpassungen bezüglich Skriptneuerstellung Ing.-Inf. / Programmieren 2, Sanity Checks DF
27.09.20 3.0.8 Neue Regeln KD7, P8 und redaktionelle Anpassungen DF
12.10.20 3.0.9 PA4 erweitert, KD8 neu DF
26.02.21 3.0.10 c% auf 10-30%, DV9 angepasst, KD9 ergänzt (ES) DF
ToDo/Under Consideration
Formale Spezifikation erweitern für KD2 und DV6 insbesondere hinsichtlich const mit Zeigern, verschiedene c berücksichtigen
Erweiterung auf C++ und Rollout in den entsprechenden LV (OOSWE, Programmieren 2/Teil C++)