0% fanden dieses Dokument nützlich (0 Abstimmungen)
132 Ansichten16 Seiten

C CodingStyleGuide 3.0.10

Hochgeladen von

Chedy Abd
Copyright
© © All Rights Reserved
Wir nehmen die Rechte an Inhalten ernst. Wenn Sie vermuten, dass dies Ihr Inhalt ist, beanspruchen Sie ihn hier.
Verfügbare Formate
Als PDF, TXT herunterladen oder online auf Scribd lesen
0% fanden dieses Dokument nützlich (0 Abstimmungen)
132 Ansichten16 Seiten

C CodingStyleGuide 3.0.10

Hochgeladen von

Chedy Abd
Copyright
© © All Rights Reserved
Wir nehmen die Rechte an Inhalten ernst. Wenn Sie vermuten, dass dies Ihr Inhalt ist, beanspruchen Sie ihn hier.
Verfügbare Formate
Als PDF, TXT herunterladen oder online auf Scribd lesen

C-Coding Styleguide Ingenieur-Informatik/Programmieren 2, Embedded Systems 1+2 und Embedded Echtzeitsysteme (EZS) 1

C-Coding Styleguide
-Programmierrichtlinien-

Hochschule Offenburg
Fakultät EMI

Lehrgebiete:
Ingenieur-Informatik/Programmieren 2
Embedded Systems 1+2
Embedded Echtzeitsysteme

Prof. Dr.-Ing. Daniel Fischer


Dipl.-Ing. (FH) Alexander Badura

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.

Hochschule Offenburg, Fakultät EMI V3.0.10, 26.02.2021


C-Coding Styleguide Ingenieur-Informatik/Programmieren 2, Embedded Systems 1+2 und Embedded Echtzeitsysteme (EZS) 2

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;

Hochschule Offenburg, Fakultät EMI V3.0.10, 26.02.2021


C-Coding Styleguide Ingenieur-Informatik/Programmieren 2, Embedded Systems 1+2 und Embedded Echtzeitsysteme (EZS) 3

enum Modes
{
continuous,
stop
};

4. Externe und globale Variablen (Ausnahmefall) extern int iSolutionsFound;

int SetNextQueen(struct nQueen* psNQueen);


5. Funktionsdeklarationen für alle Funktionen außer main void PrintBoard(struct nqueen* psNQueen);

6. Funktionsimplementierungen int main(void)


(Die main-Funktion wird in einem Modul immer als erste Funktion implementiert.) {
Damit ein Modul übersichtlich bleibt, werden teilweise die hier aufgelisteten
Elemente in Header-Dateien ausgelagert. }
Keine Verwendung von Variablen mit projekt- oder modulglobaler Gültigkeit. int main(void)
Stattdessen z.B. in main die verwendeten Variablen definieren und diese an andere {
A9 int iValue;
Funktionen mit „Call by Reference“ übergeben. vfunc1(iValue); // Call by value
II
vfunc2(&iValue), // Call by reference
}
Die Verwendung von Variablen mit projekt- oder modulglobaler Gültigkeit ist zu
A9 minimieren. Um ungewünschte Optimierungen zu verhindern ist das Schlüsselwort // w: SysTickHandler, main
ES volatile einzusetzen.
// r: main
volatile uint32_t ui32Clock1s;

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.

Hochschule Offenburg, Fakultät EMI V3.0.10, 26.02.2021


C-Coding Styleguide Ingenieur-Informatik/Programmieren 2, Embedded Systems 1+2 und Embedded Echtzeitsysteme (EZS) 4

Es ist der höchste Warninglevel einzustellen. Microsoft Visual Studio:


Warnungen weisen oft auf mögliche Programmierfehler hin. Projekt mit rechter Maustaste anwählen  Properties  C/C++ 
General  Warning Level auf Level4 (/W4) setzen.
A13 Keil uVision 5:
Project  Options for Target  C/C++  Warnings: auf All
Warnings setzen (default)
Warnungen müssen wie Fehler gehandhabt werden. Microsoft Visual Studio:
Somit ist man gezwungen, dass alle Warnungen behandelt werden. Dies führt Projekt mit rechter Maustaste anwählen  Properties  C/C++ 
A14 General  Treat Warnings As Errors auf Yes (/WX) setzen
allerdings dazu, dass man den Code entsprechend anpassen muss. Das return- Keil uVision 5:
Statement nach der Endlos-while-Schleife in main ist z.B. dann auszukommentieren. Project  Options for Target  C/C++  In Misc Controls den
folgenden Übergabeparameter --diag_error=warning eingeben.

Hochschule Offenburg, Fakultät EMI V3.0.10, 26.02.2021


C-Coding Styleguide Ingenieur-Informatik/Programmieren 2, Embedded Systems 1+2 und Embedded Echtzeitsysteme (EZS) 5

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;
}

Hochschule Offenburg, Fakultät EMI V3.0.10, 26.02.2021


C-Coding Styleguide Ingenieur-Informatik/Programmieren 2, Embedded Systems 1+2 und Embedded Echtzeitsysteme (EZS) 6

Datentypen, Variablen, Konstanten und Ausdrücke


Variablen und Konstanten haben selbstsprechende Namen in Englisch und int iTrials = 1; //prefix is i
DV1 beginnen mit einem Großbuchstaben. Der Datentyp soll dabei als Präfix const float cfPI = 3.1415f; // prefix is cf
(Kurzform) dem Namen hinzugefügt werden.
Es werden nicht mehr die nativen Datentypen, sondern die typedefs aus stdint.h #include <stdint.h>
(C99) verwendet. Falls stdint.h nicht vorhanden ist, ist dies in einer eigenen
//if stdint.h not available
Headerdatei abzubilden. typedef unsigned int uint32_t;
uint8_t, uint16_t, uint32_t, uint64_t für unsigned Variablen
DV2 int8_t, int16_t, int32_t, int64_t für signed Variablen
ES
Für float, double und long double sind eigene typedefs zu verwenden: typedef float f32_t;
typedef double f64_t;
f32_t, f64_t, f96_t, f124_t typedef long double f96_t;
long double ist plattformabhängig. Fließkommazahlen sollten aus
Performanzgründen auf einem Mikrocontroller ohne FPU vermieden werden.
Bei Variablennamen ist die Ungarische Notation als Präfix zu verwenden.
short int si signed char c short int siCounter; // counter 1
unsigned short int usiCounter; // counter 2
unsigned short int usi unsigned char uc int iCounter; // counter 3
int i float f unsiged int uiCounter; // counter 4
unsigned int ui double d long int liCounter; // counter 5
long int li long double ld unsigned long int uliCounter; // counter 6
DV3 unsigned long int uli _Bool (C99 native) b long long int lliCount; // counter 7
unsigned long long int ulliCounter; // counter 8
II long long int lli signed char cByte; // a signed byte
unsigned long long int ulli unsigned char ucByte; // an unsigned byte
In <stdbool.h> finden sich die Makros für bool, true und false, um die float fAverage; // Average 1
Kompatibilität mit C++ zu gewährleisten. Der C-Standard gibt bei char nicht vor, double dAverage; // Average 2
ob es sich um ein signed char oder ein unsigned char handelt. Daher sollte man long double ldAverage; // Average 3
_Bool bErrorflag1; // Errorfl.1 (C99 native)
immer signed oder unsigned als Modifizierer bei char verwenden. Bei short int, int, bool bErrorflag2; // Errorfl.2 (stdbool.h)
long int und long long int handelt es sich immer um signed Variablen.
Bei Variablennamen ist die Kurzkennung als Präfix zu verwenden.
int16_t s16 int8_t s8 int16_t s16Counter; int8_t s8Byte;
uint16_t u16Counter; uint8_t u8Byte;
uint16_t u16 uint8_t u8 int32_t s32Counter; f32_t f32Average;
int32_t s32 f32_t f32 uint32_t u32Counter; f64_t f64Average;
DV3 uint32_t u32 f64_t f64 int64_t s64Counter; f96_t f96Average;
ES int64_t s64 f96_t f96 uint64_t u64Counter; // C99 native boolean
uint64_t u64 _Bool (C99 native) b _Bool bErrorflag1;
// stdbool.h
In <stdbool.h> finden sich die Makros für bool, true und false, um die bool bErrorflag2;
Kompatibilität mit C++ zu gewährleisten.

Hochschule Offenburg, Fakultät EMI V3.0.10, 26.02.2021


C-Coding Styleguide Ingenieur-Informatik/Programmieren 2, Embedded Systems 1+2 und Embedded Echtzeitsysteme (EZS) 7

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.

Hochschule Offenburg, Fakultät EMI V3.0.10, 26.02.2021


C-Coding Styleguide Ingenieur-Informatik/Programmieren 2, Embedded Systems 1+2 und Embedded Echtzeitsysteme (EZS) 8

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.

Hochschule Offenburg, Fakultät EMI V3.0.10, 26.02.2021


C-Coding Styleguide Ingenieur-Informatik/Programmieren 2, Embedded Systems 1+2 und Embedded Echtzeitsysteme (EZS) 9

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;

// here the implementation starts

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.

Hochschule Offenburg, Fakultät EMI V3.0.10, 26.02.2021


C-Coding Styleguide Ingenieur-Informatik/Programmieren 2, Embedded Systems 1+2 und Embedded Echtzeitsysteme (EZS) 10

Es sind selbstsprechende Funktionsnamen zu verwenden. Der Funktionsname


F11 setzt sich aus einem Verb und weiteren spezifizierenden Wörtern in englischer char* CopyStringWithoutBlanks(char* pcDest, char* pcSource)
II Sprache zusammen. Verb und spezifizierende Wörter beginnen jeweils mit
einem Großbuchstaben (Kein Camelcase). Typische Verben sind hierbei „Get“,
„Set“, „Init“, „Delete“, „Getnext“, „Getlast“, „Print“, „Is“, … .
Es sind selbstsprechende Funktionsnamen zu verwenden. Der Funktionsname
setzt sich wie folgt zusammen: // File PWM.c
<Kurzkennung Rückgabewert><Modulname><_><Verb><weitere uint32_t u32PWM_GetSignalGPIO (uint8_t u8SignalSource);
spezifizierende Wörter>. Der Modulname ist der Name der C-Datei, in welcher
die Funktion implementiert ist. Darauf folgen ein Unterscore, ein Verb und
weitere spezifizierende Wörter in englischer Sprache. Verb und spezifizierende psPWMConfig pxPWM_GetConfig (uint8_t u8SignalSource);
Wörter beginnen jeweils mit einem Großbuchstaben. Typische Verben sind
hierbei „Get“, „Set“, „Init“, „Delete“, „Copy“, „GetNext“, „GetLast“, „Print“,
„Is“, … .

Für die Kurzkennung der Rückgabewerte sind zu verwenden:


int16_t s16 int8_t s8
uint16_t u16 uint8_t u8
F11 int32_t s32 f32_t f32
ES uint32_t u32 f64_t f64
int64_t s64 f96_t f96
uint64_t u64 _Bool (C99 native) b
void v bool (stdbool.h) b

Falls Pointer zurückgegeben werden, ist der Kurzkennung noch ein p


voranzustellen.
Werden eigene signed Datentypen (typedefs) zurückgegeben, so ist ein x als
Präfix zu verwenden. Falls es sich um einen eigenen unsigned Datentyp handelt,
so ist ein ux als Präfix zu verwenden.
Entsprechend sind Funktionsnamen in FreeRTOS aufgebaut, welches in
Embedded Systems 2 zur Anwendung kommt.
Ausnahme: Namen der ISR (Interrupt Service Routinen). Deren Namen sind in
der startup.s in der Interruptvektortabelle schon vorgegeben. void SysTick_Handler(void)
F12 Die main-Funktion ist immer die erste Funktion in der C-Datei Main.c.
Werden an eine Funktion Zeiger übergeben, so sind diese am Anfang der void DoCallByReference(int* piA)
F13 Funktion auf != NULL zu überprüfen (Sanity Check) und es ist ein korrektes {
if (piA != NULL)
Fehlerhandling zu implementieren.

Hochschule Offenburg, Fakultät EMI V3.0.10, 26.02.2021


C-Coding Styleguide Ingenieur-Informatik/Programmieren 2, Embedded Systems 1+2 und Embedded Echtzeitsysteme (EZS) 11

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;

Hochschule Offenburg, Fakultät EMI V3.0.10, 26.02.2021


C-Coding Styleguide Ingenieur-Informatik/Programmieren 2, Embedded Systems 1+2 und Embedded Echtzeitsysteme (EZS) 12

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);

if (cdTolerance > dDifference) // okay

Hochschule Offenburg, Fakultät EMI V3.0.10, 26.02.2021


C-Coding Styleguide Ingenieur-Informatik/Programmieren 2, Embedded Systems 1+2 und Embedded Echtzeitsysteme (EZS) 13

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

Hochschule Offenburg, Fakultät EMI V3.0.10, 26.02.2021


C-Coding Styleguide Ingenieur-Informatik/Programmieren 2, Embedded Systems 1+2 und Embedded Echtzeitsysteme (EZS) 14

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.

Hochschule Offenburg, Fakultät EMI V3.0.10, 26.02.2021


C-Coding Styleguide Ingenieur-Informatik/Programmieren 2, Embedded Systems 1+2 und Embedded Echtzeitsysteme (EZS) 15

Priorität und Assoziativität


Die Prioritäten der Operatoren sowie Priorität: (niedrigerer Wert entspricht höherer Priorität) Assoziativität
deren Assoziativität sind zu
1 ++(Postfix) --(Postfix) () [] -> . L nach R
berücksichtigen.
2 ++(Präfix) --(Präfix) ! ~ -(unär) +(unär) R nach L
&(Adresse) *(Indirektion) sizof type(Typumwandung/cast)

3 *(Multiplikation) / % L nach R

4 +(Addition) -(Subtraktion) L nach R

5 << >> L nach R

6 < <= > >= 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

14 = *= /= %= += -= <<= >>= &= ^= |= 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.

Hochschule Offenburg, Fakultät EMI V3.0.10, 26.02.2021


C-Coding Styleguide Ingenieur-Informatik/Programmieren 2, Embedded Systems 1+2 und Embedded Echtzeitsysteme (EZS) 16

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++)

Hochschule Offenburg, Fakultät EMI V3.0.10, 26.02.2021

Das könnte Ihnen auch gefallen