1. Semester
Programmieren mit C
08.12.2016
Begleitmaterial zur Lehrveranstaltung - Steven Müller
Dieses Material stellt eine stark verkürzte und unvollständige Übersicht der besprochenen Lehrinhalte
bereit. Es ist teilweise nur in Verbindung mit den Vorträgen, Übungen und Erläuterungen zur
Lehrveranstaltung sinnvoll und sollte durch eigene Notizen ergänzt und korrigiert werden.
Zwar wurde das Begleitmaterial hinreichend geprüft, ein Korrektheitsanspruch ist hiermit jedoch
ausdrücklich nicht verbunden. Fehler in Text, Abbildungen, Tabellen oder Quellcodebeispielen sind
nicht ausgeschlossen. Betrachten Sie dieses Material daher lediglich als Ergänzung zur Vorlesung.
Inhalt
1 Softwareentwicklung vs. Programmierung
1.1 Kleiner Einblick in die Softwareentwicklung
1.1.1 Entstehung von Software
1.1.2 Begriff der Softwareentwicklung
1.1.3 Softwarelebenszyklus
1.2 Programmieren im Großen und im Kleinen
1.2.1 Programmieren im Großen
1.2.2 Programmieren im Kleinen
1.3 Übersicht Programmierkonzepte und -paradigmen
1.3.1 Hauptparadigmen
1.3.2 Weiterführende Paradigmen
1.4 Einordnung in den Kontext der Lehrveranstaltung
2 Prozedurale Algorithmierung
2.1 Begriffe und Eigenschaften eines Algorithmus
2.1.1 Einführung zentraler Begriffe
2.1.2 Eigenschaften eines wohlstrukturierten Algorithmus
2.2 Darstellungsmittel für Algorithmen
2.2.1 Pseudocode
2.2.2 Programmablaufplan (PAP)
2.2.3 Nassi-Shneiderman-Diagramm - DIN 66261
2.3 Grundelemente und Strukturen
2.3.1 Allgemeine Elementkategorien
2.3.2 Unstrukturierte und strukturierte Datentypen
2.3.3 Darstellung von Datentypen
2.3.4 Strukturen zur Ablaufsteuerung eines Algorithmus
2.3.5 Dialoge zur Mensch-Maschine-Interaktion
2.3.6 Fehlertoleranz der Benutzerschnittstelle
2.4 Vorgehensweise Analyse und Entwurf eines Algorithmus
2.4.1 Analyse und Spezifikation
2.4.2 Prinzipen der Konstruktion von Algorithmen
4 Die Programmiersprache C
4.1 Überblick und Hintergrund C
4.1.1 Ursprung, praktische Bedeutung und Nutzungsfeld von C
4.1.2 Einordnung weiterer C Sprachen
4.2 Funktionsweise, Struktur und grundlegende Syntax
4.2.1 Programmiertechnische Charakteristik
4.2.2 Grundstruktur
4.3 Integrierte Entwicklungsumgebung
4.3.1 Bestandteile und Programmierwerkzeug einer IDE
4.3.2 Hintergrund und Installation Code Blocks::
• [3] Horn, C.; Kerner, I. O.: „Lehr- und Übungsbuch Informatik - Grundlagen und Überblick“ Band
1 Fachbuchverlag Leipzig
• [4] Horn, C.; Kerner, I. O.: „Lehr- und Übungsbuch Informatik - Praktische Informatik“ Band 3
Fachbuchverlag Leipzig
Softwaresysteme sind Produkte außerordentlicher hoher Komplexität. Bei der Erstellung komplexer
Softwaresysteme, an denen mehrere Entwickler beteiligt sind und die hunderte Einzelfunktionen
aufweisen, ist diese Vorgehensweise nicht zielführend. Vielmehr muss der Herstellungsprozess
organisiert werden. (vgl. [5], S. 30).
Softwareentwicklung bezeichnet allgemein Methoden und Techniken zur Herstellung von Software.
Diese Methoden und Werkzeuge sind oftmals noch unzureichend. Dies resultiert letztlich daraus, dass
Softwareentwicklung eine relativ junge Ingenieursdisziplin ist.
Das Ziel der Softwareentwicklung besteht darin, ein korrektes, effizientes und verständliches System
zu entwickeln, das als Basis für spätere Weiterentwicklung brauchbar ist (vgl. [7], S.5).
Anfangs war Programmieren Handwerk. Das heißt talentierte Amateure (Physiker, Mathematiker,
Maschinenbauer) erstellen Programme als spezielle Werkzeuge für eine einzelne Aufgabe.
Mitte der 1960er bis 70er war der Begriff Softwarekrise gegenwärtig, der sich auf die nicht ändernde
schlechte Qualität der erzeugten Software-Systeme bezog.
1967 wurde von einer Forschungsgruppe der NATO der Begriff Software Engineering gebildet (vgl. [5],
S. 22). Software Engineering beschreibt den Prozess einer professionellen ingenieurtechnischen
Softwareentwicklung zur Herstellung von Software-Systemen als Industrieprodukt.
Die Wissenschaft Software Engineering (SE) thematisiert die Entwicklung geeigneter Methoden und
Vorgehensmodelle für die Projektarbeit im Rahmen der Softwareentwicklung (vgl. [5], S. 23).
Software Engineering ist gekennzeichnet durch geplantes, systematisches Vorgehen bei den Prozessen
der Softwareentwicklung. Primäres Ziel ist die wirtschaftliche Produktion qualitativ hochwertiger
Software im Rahmen eines Projektes (vgl. [4], S. 45).
Darüber hinaus ermöglicht dies die Lösung der Problemlage: Trennung zwischen Auftraggeber und
Entwickler (detailliert [4], S. 43ff.).
Ein weiteres Merkmal in Softwareprojekten ist die Heterogenität des erforderlichen Personals. Daraus
resultiert ein hoher Kommunikations- und Koordinationsaufwand, worunter die Produktivität leidet.
SE bietet Lösungsansätze für eine arbeitsteilige Entwicklung in Softwareprojekten. Dementsprechend
entwickeln Systemanalytiker (genaue Anforderungen an die Software), Entwickler (Entwurf der
Softwarearchitektur), GUI-Designer (Usability), Programmierer etc. Darüber hinaus werden auch
Fachleute benötigt, die für die inhaltlichen Anforderungen verantwortlich sind. Häufig sind bereits in
der Entwicklungsphase spätere Benutzer und Benutzerinnen des Produktes involviert, um deren
künftige Akzeptanz zu gewährleisten.
Softwareentwicklungsprozesse sind iterativ, das heißt sie basieren auf einem fortlaufenden
evolutionären Prozess (vgl. später Vorgehensmodelle).
• Analyse (Anforderungsspezifikation),
• Entwurf (Modellierung)
• Implementierung (Programmierung und Test)
• Wartung (Anpassungen, Fehlerbeseitigung)
Das entstehende Softwareprodukt ist kein statisches Endprodukt sondern unterliegt ebenfalls einem
evolutionären Prozess, der Softwarelebenszyklus genannt wird.
Der Softwarelebenszyklus ist ein Modell für alle Aktivitäten während der Existenz einer Software.
Die Evolution einer Software beginnt mit der völligen Neuentwicklung (Forward Engineering) und setzt
sich mit der Anpassung und Übernahme vorhandener Komponenten einer Lösung im Reengineering
fort (ausführlich hierzu [3], S. 46 ff. und [4], S. 248 ff.).
Entsprechend der Komplexität der zu lösenden Problemstellung sind unterschiedliche Methoden für
die Entwicklung von Software einzusetzen. Die Softwaretechnik teilt diesbezügliche Methoden in zwei
Bereiche: in die Programmierung im Großen und in Programmierung im Kleinen.
Programmieren im Großen bezieht sich auf die Entwicklung von Programmen zu Problemen sehr hoher
Komplexität. Grundprinzip ist die Zerlegung des Gesamtproblems in überschaubare Teile.
Diese Zerlegung orientiert sich beim Programmieren im Großen an den Elementen (Daten, Objekte)
des Problembereichs. Die Datenstrukturen sowie Prozeduren zum Zugriff und zur Manipulation der
Daten werden in Komponenten (Module, Klassen) zusammengefasst. Diese werden strukturiert
zusammengesetzt und bilden die sogenannte Architektur des Softwaresystems.
Unter Programmierung im Kleinen sind Methoden zu verstehen, die Erstellen von Programmen
geringerer Komplexität nötig sind. Ziel ist die Entwicklung korrekter und effizienter
Softwarefunktionen. Bei der Programmierung im Kleinen richtet sich die Zerlegung daher an den
einzelnen Funktionalitäten des Programms aus.
Diese sind für das Gelingen großer Projekte nicht von zentraler Bedeutung wie etwa die Aspekte der
Programmierung im Großen. Dennoch ist die Bedeutung der Programmierung im Kleinen nicht
unerheblich. Programmierfehler werden im Kleinen gemacht! Viele Systemüberlegungen lassen sich
nicht verstehen, wenn man nicht vorher eine solide Grundlage erworben hat.
Die zentrale Aufgabe der Programmierung im Kleinen ist der Entwurf eines Lösungsalgorithmus. Das
Problem wird sukzessive in Teilprobleme zerlegt, und die dazu jeweils gefundenen Teillösungen
werden schließlich zur Gesamtlösung zusammengesetzt. Bausteine des Programmierens im Kleinen
sind Prozeduren. Dementsprechend wird auch von prozeduraler Zerlegung Gesprochen.
1.3.1 Hauptparadigmen
Die Eigenschaften jeder Programmiersprache und die Methoden Programme zu entwickeln, werden
durch die zu Grunde liegenden Paradigmen entscheidend geprägt. Diese unterscheiden sich
hinsichtlich ihrer Konzepte zur Repräsentation dynamischer und statischer Elemente und in der
methodischen Herangehensweise zur Strukturierung des Quellcodes.
• Imperative Programmierung
Programme werden als verzweigte Folgen von Anweisungen (Befehlen) formuliert. Diese
enthalten Zuweisungen, die bei ihrer Ausführung Werte von Variablen und damit den
Programmzustand verändern (Berechnungen). Verzweigungen im Programm werden durch
Bedingungen über Variable entschieden. Parametrisierte Funktionen werden verwendet, um
Berechnungen zu abstrahieren und an verschiedenen Stellen im Programm aufzurufen.
• Deklarative Programmierung
Programmier-
paradigmen
Imperative Deklarative
Konzepte Konzepte
Bei der prozeduralen Programmierung wird die Gesamtaufgabe, die eine Software lösen soll, in
kleinere Teilprobleme zerlegt. Die bei der Aufgabenlösung entstehenden Programm-Module werden
Prozeduren bzw. Funktionen genannt.
Diese Teilaufgaben sind einfacher zu beschreiben, programmieren und testen. Der Quellcode
wiederverwendbar und universell implementiert werden. Prozeduren und Funktionen lassen sich zu
Bibliotheken zusammenfassen, die dann verteilt und in beliebig viele andere Programme eingebunden
werden können.
Objektorientierte Programmierung
Bei der objektorientierten Methodik erfolgt die Modularisierung dadurch, dass die charakteristischen
Elemente des Anwendungsgebietes sowie ihre Eigenschaften, Verhaltensweisen und Beziehungen
modelliert werden.
Daten und Funktionen werden zu Objekten zusammengefasst. Diese Objekte können auf vielfältige
Weise miteinander in Verbindung stehen, indem sie gegenseitig ihre Methoden aufrufen oder ein
Objekt andere Objekte enthält. So bilden die Objekte einer Software ein sehr flexibles Gesamtsystem.
Funktionale Programmierung
In einem funktionalen Programm wird die Reihenfolge der Berechnungsschritte in der Regel nicht
festgelegt. Die Grundelemente der funktionalen Programmierung sind rekursive Funktionen und deren
Aufrufe. Berechnungen werden als Auswertung mathematischer Funktionen verstanden.
Die logische Programmierung basiert auf der Prädikatenlogik. Ein logisches Programm kann als Folge
von prädikatenlogischen Formeln verstanden werden. Um eine Aufgabe zu lösen, beschreibt man
Eigenschaften, die eine Lösung erfüllen muss, statt einen Rechenweg, der eine Lösung findet.
Das heißt die Entwicklung und Implementierung prozeduraler Algorithmen steht im Vordergrund.
Diesbezüglich werden Vorgehensweisen zur prozeduraler Lösungsalgorithmen und entsprechender
prozeduraler Zerlegung in Teillösungen besprochen.
Basierend auf dem Paradigma der prozedurale Programmierung werden elementare Sprachkonstrukte
wie Syntaxbeschreibung, Typen, Operatoren, Ablaufsteuerung am Beispiel der Sprache C vorgestellt
und mit Programmierübungen gefestigt.
Algorithmierung
Die Entwicklung eines allgemeinen Lösungsplans, der festlegt wie man schrittweise von der
Aufgabenstellung zur Lösung kommt, heißt Algorithmierung. Der Lösungsplan soll generisch sein und
heißt Algorithmus (vgl. [3], S.143).
Algorithmus
Ein Algorithmus ist folglich eine vollständige Vorschrift zur Durchführung von Operationen in endlich
vielen Schritten.
Programmierung
Formulierung des Algorithmus in einer für eine Maschine interpretierbaren Sprache. Programmierung
ist der Prozess der Umsetzung einer algorithmischen Idee in eine formale Sprache (vgl. [3], S.143).
Prozeduraler Algorithmus
Ansatz der prozeduralen Programmierung ist es, einen Algorithmus in überschaubare generische Teile
(Module) zu zerlegen. Im Wesentlichen werden hierbei Eingabeinformationen in einem System von
Regeln in Ausgabeinformationen übertragen (vgl. [3], S.143).
Module
Module sind schließlich Programmbausteine, die separat entwickelt und getestet werden können. Ein
Modul stellt ein Algorithmus als verfügbare Handlung für ein Teilprogramm zur Verfügung.
Terminierung Determiniertheit
Korrecktheit
Finitheit
Terminierung
Determiniertheit
Korrektheit:
Komplexität
Zeitkomplexität: Die Bewertung von Algorithmen erfolgt durch die so genannten Zeitkomplexität T(n),
d.h. die Rechenzeit T(n) in Abhängigkeit von der Zahl n der bearbeitenden Datenelemente.
Die Entwicklung des Algorithmus beginnt mit einer Strukturierung oder Modularisierung der
Lösungsidee in abgegrenzte Blöcke. Die Darstellung des Algorithmus kann im Wesentlichen mittels
Pseudocode, PAP und Struktogramm erfolgen.
2.2.1 Pseudocode
Im Pseudocode wird die logische Folge der einzelnen Schritte durch Verwendung formalisierter Worte
deutlich gemacht.
Mittels grafischer Symbole wird die logische Folge der einzelnen Arbeitsschritte des Algorithmus
dargestellt.
2.2.3 Nassi-Shneiderman-Diagramm
• Beziehungen zwischen Strukturblöcken sind entweder völlig unabhängig oder völlig abhängig.
Eine teilweise Überlappung von Blöcken ist nicht möglich.
• Jeder Block kann benutzt werden ohne genaue Kenntnis seiner internen Realisierung, nur mit
Kenntnis der Funktion und Bereitstellung der benötigten Eingangsinformationen.
• Datentypen
• Operatoren
• Kontrollstrukturen
• Variablen
Informationen und Datenobjekte mit denen ein prozedurales Programm arbeiten kann lassen sich in
elementare (unstrukturierte) Typen und in komplexe (strukturierte) Typen unterteilen.
Unstrukturierte Datentypen
Zahl:
Zeichenkette:
Logische Werte:
Konstante:
Adresse:
Index:
Feld:
Matrix:
Liste:
Datensatz:
Baum:
Datei:
In Abhängigkeit der Bitbreite und der Kodierung ergibt sich für jeden Typ ein begrenzter Wertebereich.
Die Repräsentation reeller Zahlen ist aufwändiger und unterliegt Beschränkungen. Es existieren
verschiedene Lösungsansätze um reelle Zahlen auf Rechenmaschinen (seit Abakus) abzubilden. Für
diese gilt, dass deren Wertebereiche im Gegensatz zur reellen Zahl praktisch endlich sind.
Eine weitere Möglichkeit sind Fließkommazahlen, die eine approximative Darstellung einer reellen
Zahl realisieren.
Die Berechnung mit Fließkommazahlen ist aufwendiger als mit Festkommazahlen, dafür ist der
Wertebereich von Fließkommazahlen bei gleichem Speicheraufwand größer (auch in [3] S. 202).
Darstellung Fließkommazahl
Bei einer Fließkommazahl muss eine Information zur Position des Kommas mitgespeichert werden.
±
ℎ =± (z.B. 13.000.000 = 1,3 ∙ 10 )
Bsp. Festlegung beim IEEE 754 (Standard for Floating-Point Arithmetic) existiert für den Typ Single
folgender Wertebereich:
Es sind verschiedene Zeichensätze gebräuchlich, wie bspw. ASCII oder Unicode. Die Zeichen selbst
werden über einen Ganzzahlwert repräsentiert, der entsprechend des zugeordneten Zeichensatzes ein
bestimmtes Zeichen darstellt.
ISO 8859-2 umfasst ergänzend zum ASCII-Zeichensatz eine Erweiterung um bestimmte Zeichen
mitteleuropäischer Sprachen, wie Deutsch.
Wie in 2.2.3 dargelegt wurde, liegt der strukturierten Programmierung das Blockkonzept auf Basis der
drei Kontrollstrukturen Sequenz, Iteration und Alternative zu Grunde. Kontrollstrukturen bestimmen
die Reihenfolge und Häufigkeit der Ausführung von Aktionen in einem Algorithmus (ausführlich in [3]
S. 148 ff.). Diese werden im Folgenden betrachtet.
Sequenz
Eine Sequenz besteht aus einer Anzahl von Verarbeitungen, die nacheinander durchlaufen werden. Die
Sequenz repräsentiert eine einfache Reihung von Anweisungen oder Blöcken, die als
Verarbeitungsblock zusammengefasst ist. Damit stellt die Sequenz ein wesentliches Hilfsmittel für die
hierarchische Strukturierung von Abläufen dar (siehe [3] S. 149).
Auswahl / Alternative
Die einfache Auswahl oder Selektion beschreibt die bedingte Durchführung von Strukturblöcken in
Abhängigkeit von Bedingungen. Unterscheidbar sind bedingte Verarbeitung und einfache Alternative.
Fallauswahl
Die Fallauswahl ist eine Mehrfachalternative und erlaubt in einem einzigen Konstrukt bei mehreren
Bedingungen genau die Durchführung des zu dieser Bedingung gehörenden Strukturblocks.
Iteration (Wiederholung)
Bei einer Iteration wird in Abhängigkeit von einer bestimmten Bedingung ein gewisser Strukturblock
mehrfach ausgeführt, wobei die steuernden Parameter sich ändern müssen.
Wird auch als abweisende oder kopfgesteuerte Iteration oder Schleife bezeichnet. Wenn die
Eintrittsbedingung bereits bei der ersten Überprüfung nicht erfüllt ist, wird der Strukturblock niemals
ausgeführt.
Hier wird zunächst der Strukturblock ausgeführt und erst danach eine Bedingung abgeprüft. Im
Gegensatz zu oben muss die Schleife mindestens einmal durchlaufen werden. Sie wird beendet, wenn
die Abbruchbedingung das erste Mal den Wert „wahr“ annimmt oder die Wiederholungsbedienung
nicht erfüllt ist (Wert „falsch“).
Hier wird die Bedingung ohne veränderliche Parameter formuliert und es entsteht faktisch eine
Endlosschleife. Die meisten Programmiersprachen verfügen darüber hinaus über Befehle, dass sich die
Möglichkeit ergibt im Strukturblock die Schleife abzubrechen (exit) oder an den Beginn der Schleife
zurückzukehren (loop).
Anwender kommunizieren mit den von ihnen genutzten Computersystemen. Dazu existieren
heutzutage vielfältige Möglichkeiten, die sehr stark vom System und Anwendungskontext abhängen.
In ISO 9241 werden Richtlinien der Mensch-Computer-Interaktion beschrieben. Die Normenreihe trägt
Titel Ergonomie der Mensch-System-Interaktion.
Der Teil 110 der DIN EN ISO 9241 beschreibt folgende Grundsätze für die Gestaltung und Bewertung
einer Schnittstelle zwischen Benutzer und System (Dialoggestaltung):
• Aufgabenangemessenheit
• Selbstbeschreibungsfähigkeit
• Lernförderlichkeit
• Steuerbarkeit
• Erwartungskonformität
• Individualisierbarkeit
• Fehlertoleranz
Tabelle 4: Dialogarten
Bei der Entwicklung der Benutzerschnittstelle ist der Lösungsalgorithmus so zu gestalten, dass die
Funktionsweise des Programms auch bei unvorhergesehene Eingaben des Nutzers aufrechterhalten
wird. Es existieren verschiedene Stufen der Fehlertoleranz, die im Rahmen dieser LV jedoch nicht
weiter betrachtet werden.
Entsprechend der Grundsätze der Dialoggestaltung nach ISO 9241-110 sind u.a. anzustreben:
Dieser Abschnitt beschreibt rudimentär elementare Vorgehensweisen die zur Entwicklung eines
Algorithmus erforderlich sind. Eine ausführliche Bearbeitung der Themen Analyse und Entwurf erfolgt
im Rahmen des Moduls Software Engineering.
Analyse
• Datenanalyse
• Prozessanalyse
Spezifikation
Die Spezifikation ist eine Präzisierung der Analyseergebnisse und führt schließlich zu einer
verbindlichen Vereinbarung von Anforderungen zwischen Auftraggeber und Entwickler.
Folgende Prinzipien gemeinsam führen zur strukturierten Programmierung und sind neben anderem
Ausdruck des „defensiven Ansatzes“ der Softwaretechnologie. Dieser Ansatz soll die Verständlichkeit
des Quelltextes für Dritte erleichtern und so die Wartbarkeit verbessern. So gilt nicht der kompakteste
und so am schwersten lesbare Programmcode als der Beste, sondern derjenige mit der größten
Übersichtlichkeit
Schrittweise Verfeinerung
Top-Down-Entwicklung
Abbildung 9: Top-Down-Entwicklung