Embedded-Software-Development Lexikalische Grundlagen der C-Programmierung
C ist in der Embedded-Entwicklung eine überaus beliebte Programmiersprache. Ihre immense Flexibilität und Ausdrucksstärke birgt aber auch größte Gefahren für unerfahrene oder leichtfertige Programmierer. Daher ist es essentiell, sich ausgiebig mit den Grundlagen der Sprache vertraut zu machen.

C stellt eine sehr populäre, imperative Sprache dar, die sich durch zahlreiche Eigenschaften auszeichnet. Sie verfügt über einen relativ kleinen Sprachkern und eine kompakte Notation. Sie besitzt einen reichhaltigen Satz von Standarddatentypen und einen ebenso reichhaltigen Satz von Operatoren. Sie verfügt über Zeiger (pointer), Felder und Verbünde für komplexe Datenstrukturen; und sie kann diese sehr gut auf Maschinenebene abbilden, was eine hohe Effizienz bei vergleichsweise wenig Code ermöglicht.
Alles andere, wie z. B. E/A oder Speicherverwaltung, ist in Standard-Bibliotheken untergebracht. Das alles zusammengenommen macht C zu einer kompakten und relativ einfach zu erlernenden Sprache, die auch in der Embedded-Entwicklung weite Verbreitung gefunden hat, und die sich gut zwischen unterschiedlichen Systemen portieren lässt.
Die immense Flexibilität und Ausdrucksstärke von C birgt aber auch größte Gefahren in der Hand eines unerfahrenen oder leichtfertigen Programmierers. C ist per se keine sichere Sprache – wenn auch bei entsprechender Programmierdisziplin (und bei Befolgung entsprechender Coding-Standards) C durchaus auch für sichere Embedded-Software geeignet ist.
Lexikalische Elemente: Der "Wortschatz" von C
Wie bereits erwähnt handelt es sich bei C um eine Imperative Programmiersprache. Ein imperatives Programm beschreibt eine Berechnung durch eine Folge von Anweisungen, die den Status des Programms verändern. Im Gegensatz dazu wird in einer deklarativen Sprache bzw.Programm eine Berechnung beschrieben, in der codiert wird, was berechnet werden soll, aber nicht wie. Mit anderen Worten: In imperativen Sprachen wie C werden die Algorithmen bis ins letzte Detail so beschrieben, wie sie auch auszuführen sind.
Um das Prinzip der imperativen Programmierung besser verständlich zu machen gehen wir in diesem (und weiteren) Grundlagenartikeln zu C auf die essentiellen Elemente der Programmiersprache ein. Den Anfang stellen die sogenannten lexikalischen Elemente dar; auf Syntax, Präprozessor und die Standardbibliothek wird später in gesonderten Artikeln eingegangen werden.
Der Grundzeichensatz von C
Der Grundzeichensatz für C-Quelltexte umfasst folgende sichtbare Zeichen:
- Großbuchstaben: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
- Kleinbuchstaben: a b c d e f g h i j k l m n o p q r s t u v w x y z
- Dezimalziffern: 0 1 2 3 4 5 6 7 8 9
- Unterstrich: _
- Interpunktion: ! „ # % & ‚ ( ) * + , - . / : ; < = > ? [ \ ] ^ { | } ~
Zusätzlich können folgende Zeichen vorkommen:
- Space [Leerzeichen]
- BEL [Alarmglocke (bell)], mit der Ersatzdarstellung \a
- BS [Rückschritt (backspace)], mit der Ersatzdarstellung \b
- FF [Seitenvorschub (form feed)], mit der Ersatzdarstellung \f
- NL [Zeilenvorschub (newline)], mit der Ersatzdarstellung \n
- CR [Wagenrücklauf (carriage return)], mit der Ersatzdarstellung \r
- HT [Horizontaltabulator (horizontal tab)], mit der Ersatzdarstellung \t
- VT [Vertikaltabulator (vertical tab)], mit der Ersatzdarstellung \v
Es gibt auch Ersatzdarstellungen für die Anführungszeichen und zwei weitere Sonderzeichen zur Verwendung in Zeichen- und Zeichenkettenkonstanten. Hier dient der Rückschrägstrich dazu, die Sonderbedeutung des betreffenden Zeichens zu unterdrücken: \“, \‘, \?, \\. Um alle Zeichen des Zeichensatzes der Maschine darstellen zu können, gibt es ferner so genannte numerische Escape-Sequenzen (Ersatzdarstellungen):
- \d, oder \dd oder \ddd d (1...3) ist Oktalziffer (oft gebraucht: ‚\0‘, die Null)
- \xh oder \xhh oder . . . h (beliebige Anzahl) ist Hexadezimalziffer (0 bis 9, A bis F oder a bis f)
In Zeichen- und Zeichenkettenkonstanten (auch Literale genannt) können alle Zeichen des verwendeten Systems vorkommen.
White Space (Leerraum)
Als Leerraum (white space) gelten Leerzeichen, Zeilenvorschub, Wagenrücklauf, vertikaler und horizontaler Tabulator, sowie Seitenvorschub. Kommentare gelten auch als Leerraum. Leerraum wird syntaktisch ignoriert, außer in Zeichenketten- oder Zeichenkonstanten; er dient dazu, sonst aneinandergrenzende Wörter, Zeichen etc. zu trennen und den Quelltext für Menschen durch übersichtliche Gestaltung, z.B. Einrückungen nach Kontrollstruktur etc., gut lesbar zu machen.
Kommentare
Kommentare werden durch die Zeichenpaare /* und */ erzeugt. Alles, was dazwischen steht – auf einer Zeile oder mit beliebig vielen Zeilen dazwischen, gilt als Kommentar. Kommentare dürfen nicht geschachtelt werden.
/* Das ist zum Beispiel ein Kommentar
. . . und hier geht er immer noch weiter */
Schlüsselwörter in C
C hat die folgenden 32 Schlüsselwörter (reserved words, keywords):
auto, break, case, char, const, continue, default, do, double, else, enum, extern, float, for, goto, if, int, long, register, return, short, signed, sizeof, static, struct, switch, typedef, union, unsigned, void, volatile, while
Identifier (Bezeichner) in C
Bezeichner in C (identifier), sonst auch schlicht Namen genannt, werden folgen dermaßen gebildet (als regular expression (regulärer Ausdruck) in Unix-Notation): [A-Za-z_][A-Za-z_0-9]*. d.h. Buchstabe oder Unterstrich optional gefolgt von beliebiger (auch Null) Folge eben dieser, inklusive der Ziffern.
Bezeichner dürfen nicht mit einer Ziffer beginnen, Groß- und Kleinbuchstaben sind als verschieden zu werten. Bezeichner dürfen nicht aus der Menge der o.g. Schlüsselwörter sein (oder aus der Menge von Namen, die für die Standardbibliothek reserviert sind, sie müssen sich mindestens in den ersten 31 Zeichen unterscheiden. Mit Unterstrich beginnende Namen sind für das System reserviert und sollten nicht verwendet werden. Bezeichner mit externer Bindung (d.h. Weiterverarbeitung durch Linker etc.) können weiteren Beschränkungen unterliegen.
Konstanten
C kennt vier Hauptgruppen von Konstanten:
- Ganzzahlkonstanten Dezimal-, Oktal- oder Hex-Darstellung
- Gleitpunktzahlkonstanten mit Dezimalpunkt und/oder Exponentkennung
- Zeichenkonstanten eingeschlossen in ‚. . . ‚
- Zeichenkettenkonstanten eingeschlossen in „. . . „
Numerische Konstanten sind immer positiv, ein etwa vorhandenes Vorzeichen gilt als unärer Operator auf der Konstanten und gehört nicht dazu. Ganzzahlkonstanten sind vom Typ int, wenn das nicht ausreicht, vom Typ long, wenn auch das nicht ausreicht, vom Typ unsigned long. Man kann die größeren Typen auch durch Anfügen von Suffixen erzwingen, wie aus der folgenden Übersicht hervorgeht:
- Dezimalziffern 0 1 2 3 4 5 6 7 8 9
- Oktalziffern 0 1 2 3 4 5 6 7
- Hexziffern 0 1 2 3 4 5 6 7 8 9 A B C D E F a b c d e f
- 0 Die Konstante 0 (Null)
- l L Ganzzahlsuffix für long (Achtung, Verwechslungsgefahr l ['kleines L'] mit 1!)
- u U Ganzzahlsuffix für unsigned
- f F l L Gleitpunktzahlsuffix für float bzw. long double (s.o.)
- e E Gleitpunktzahlkennung für Exponent
Beginnt die Ganzzahlkonstante mit 0x oder 0X, so liegt Hexnotation vor und es folgen eine oder mehrere Hexziffern. Dabei stehen A-F bzw. a-f für die Werte 10...15. Beginnt andernfalls die Ganzzahlkonstante mit einer 0, so liegt Oktalnotation vor und es folgen eine oder mehrere Oktalziffern, andernfalls liegt Dezimalnotation vor.
Gleitpunktzahlkonstanten sind immer vom Typ double, falls nicht durch Suffix als float oder long double gekennzeichnet. Zur Erkennung müssen mindestens der Dezimalpunkt oder die Exponentkennung vorhanden sein.
Eine Zeichenkonstante (character constant) ist ein in einfache Hochkommata eingeschlossenes Zeichen aus dem Zeichensatz oder seine (auch mehrere Zeichen umfassende) Ersatzdarstellung. Die Betrachtung sog. wide character constants, sowie sog. multi byte character constants unterbleibt hier. Zeichenkonstanten sind vom Typ int, dürfen aber nicht wertmäßig größer als der entsprechende Typ char sein.
Eine Zeichenkettenkonstante (string constant) ist eine in sogenannte doppelte Anführungszeichen eingeschlossene Zeichenkette auf einer Zeile. Sie darf alle Zeichen des Zeichensatzes, inklusive etwaiger Ersatzdarstellungen, und (dann signifikanten) Leerraum enthalten. Nur durch Leerraum getrennte Zeichenketten werden vom Präprozessor zusammengefügt und gelten als eine Zeichenkette. Man kann eine Zeile auch umbrechen, indem man sie mit einem Rückschrägstrich terminiert. Die auf diese Weise fortgeführte Zeile gilt dann als eine logische Zeile.
Zeichenketten werden standardgemäß als array of char von niederen zu höheren Adressen mit terminierendem Nullwert im Speicher abgelegt. Ihre Speichergröße ist daher immer um 1 größer als die Größe, die der Anzahl der enthaltenen Zeichen entsprechen würde. Das sind also die allseits verbreiteten so genannten C-Strings. Der Nullwert dient als Terminierungsmarke für alle Routinen der Standardbibliothek und kann folglich im String selbst nicht vorkommen. Der terminierende Nullwert gehört somit nicht zu den Zeichen des Strings und wird folglich bei Ermittlung seiner Länge auch nicht mitgezählt.
Eine Zeichenkette als Typ array of char zu sehen, nimmt man aber nur bei der Initialisierung von Arrays oder der Anwendung des sizeof-Operators wahr. Bei den meisten Verwendungen treten jedoch sofort die üblichen syntaktischen Umwandlungen von C in Kraft, und man sieht nur noch einen Zeiger auf das erste Zeichen, also den Typ char *, über den man dann alle weitere Verarbeitung steuern kann.
Dies stellt einen Überblick über die lexikalischen Elemente der Programmiersprache C dar; also eine Zusammenfassung des von C aufgefassten und interpretierbaren Wortschatzes. Um hiermit Ausdrücke, Anweisungen oder Funktionen umsetzen zu können, kommt es auf den richtigen kontextuellen Zusammenhang dieser lexikalischen Elemente an. Dies wid demnach auch als die Syntax" von C bezeichnet. Auf diese syntaktischen Elemente von C gehen wir in einem anderen Beitrag näher ein.
Hinweis: Dieser Beitrag ist ein Auszug aus dem Handbuch „Embedded Systems Engineering“. Dieses ist auch als kostenlose PDF-Version in voller Länge auf ELEKTRONIKPRAXIS.de verfügbar.
Dieser Beitrag, mit Ausnahme der ersten beiden und des letzten Absatzes, ist Copyright © Bernd Rosenlechner 2007-2010. Dieser Text kann frei kopiert und weitergegeben werden unter der Lizenz Creative Commons – Namensnennung – Weitergabe unter gleichen Bedingungen (CC – BY – SA) Deutschland 2.0.
* Prof. Dr. Christian Siemers lehrt an der Technische Universität Clausthal und arbeitet dort am Institut für Elektrische Informationstechnik (IEI).
(ID:45389501)