include “eingabe.h“

Werbung
Vorlesung Informatik I
Universität Augsburg
Wintersemester 2011/2012
Prof. Dr. Robert Lorenz
Lehrprofessur für Informatik
11. Programmieren in C – Der Compilierungsprozess
1
Aufbau eines C-Programms
1.
Direktiven für den Präprozessor
2.
Globale Vereinbarunen
a. Globale Variablen
b. Funktions-Prototypen
3.
main-Funktion
a. lokale Vereinbarungen
b. Anweisungen
4.
Funktions-Definitionen
a. lokale Vereinbarungen
b. Anweisungen
2
Aufbau eines C-Programms
C-Programme sind Sammlungen von Funktionen
Funktionen zerlegen Programme in kleinere, selbständige
Einheiten
erhöht Übersichtlichkeit und Wartbarkeit
Alle benutzten Funktionen müssen zu Beginn deklariert werden
3
Aufteilung großer C-Programme
Zur Modularisierung werden die Funktionen auf mehrere
Quelldateien verteilt und getrennt übersetzt
(genau eine der Dateien enthält die main-Funktion)
Jede solche Datei sollte eine sinnvoll zusammenhängende
Gruppe von Vereinbarungen enthalten.
Die Deklarationen der Funktionen, die in mehreren Quelldateien
benötigt werden, werden in Header-Dateien zusammengefasst
(werden mit #include eingebunden)
Mehrfach benötigten Vereinbarungen werden nur in wenigen
Dateien verwaltet
4
Compilierungsprozess - Überblick
Quellcode
Präprozessor
(erweiterter Quellcode)
Compiler
Assemblercode
Assembler
Bibliotheken
Objektcode
Linker
ausführbarer Code
5
Inhalt
Präprozessor
Compiler
Assembler
Linker
Bibliotheksfunktionen
Das Programm gcc
Große Programme
6
Präprozessor: Konstanten
Die Präprozessor-Direktive
#define <Name> <Zeichenkette>
veranlasst den Präprozessor, jedes Vorkommen von <Name> im
folgenden Quellcode durch <Zeichenkette> zu ersetzen
Ausnahme:
Keine Ersetzung in Namen und Zeichenketten
<Name> kann im Programm als Konstante benutzt werden,
deren Wert durch <Zeichenkette> gegeben ist
7
Präprozessor: Konstanten
Beispiele für sinnvolle eigene Deklarationen:
#define PI 3.14
#define TRUE 1
#define FALSE 0
#define MAX_VERSUCHE 3
Konvention:
Der Name der Konstante besteht aus Großbuchstaben und dem
'_' - Zeichen und wird an keiner anderen Stelle benutzt
8
Präprozessor: Konstanten
Vordefinierte Beispiele aus Header-Dateien:
Wertebereichgrenzen von Datentypen (in limits.h, float.h)
__LINE__
__FILE__
__DATE__
__TIME__
aktuelle Zeilennummer im Programmcode
(num.)
Dateiname (Zeichenkette)
aktuelles Datum (Zeichenkette)
aktuelle Zeit (Zeichenkette)
(u.v.m….)
9
Präprozessor: Konstanten
Vorteile:
- Konstanten muss man bei Bedarf nur einmal ändern
- Programm ist besser lesbar
Vergleich zu globalen konstanten Variablen:
- Keine zusätzliche Variable nötig
- Konfiguration mehrerer Programme durch einmalige Deklaration
in Header-Datei möglich
10
Präprozessor: Makros
Die Präprozessor-Direktive
#define <Name>(p1,...,pn) <Zeichenkette>
veranlasst den Präprozessor zu folgende Ersetzungen:
- <Name>(a1,...,an) durch <Zeichenkette>
- Vorkommen von pi in <Zeichenkette> durch ai
p1,...,pn: Parameternamen
a1,...,an: Ausdrücke
<Name>(a1,...,an) kann im Programm als Aufruf einer
durch <Zeichenkette> definierten Funktion benutzt werden
11
Präprozessor: Makros
Selbst definierte Beispiele:
#define inhalt(r) PI*(r)*(r);
„Aufruf“ im Code: double inhalt = inhalt(10);
#define max(a,b) ( ((a) > (b)) ? (a) : (b) );
„Aufruf“ im Code: int m=max(5*x,x*x);
12
Präprozessor: Makros
Vordefinierte Beispiele aus Headerdateien:
getchar()
EOF
Wert, der Dateiende oder Verbindungfehler signalisiert
Möglicher Rükgabewert von scanf (Wert oft -1)
EXIT_SUCCESS / EXIT_FAILURE
Signalisieren erfolgreichen/fehlerhaften Programmverlauf
Als Rückgabewerte von main benutzbar (Werte oft 0 / 1)
Systemabhängig definiert, systemunabhängig benutzbar
13
Präprozessor: Makros
Achtung:
In <Zeichenkette> benutzte Operationszeichen können stärker
binden als die Operatoren in einem für einen Parameternamen
eingesetzten Ausdruck!
Man muss dafür sorgen, dass der Ausdruck nach dem Einsetzen
unbedingt zuerst ausgewertet wird
Dazu unbedingt die in <Zeichenkette> vorkommenden
Parameternamen klammern (Beispiel vorige Folie)
#define inhalt(r) PI*(r)*(r);
14
Präprozessor: Makros
Vergleich zu Funktionen
Parameter sind Datentyp-unabhängig:
#define max(a,b) (((a)>(b))?(a):(b)));
Mögliche „Aufrufe“:
int m = max(5,7);
double m = max(5.0,7.5);
Es entsteht mehr Programmcode, da der „Funktionsrumpf“ für alle
Vorkommen des „Funktionsnamens“ eingesetzt wird
15
Präprozessor: Header-Dateien
Die Anweisung
#include <Header-Datei>
veranlasst den Präprozessor, die angegebene Header-Datei in
den Quellcode zu kopieren
Es entsteht ein erweiterter Quellcode
16
Präprozessor: Header-Dateien
Eine Header-Datei ist eine Textdatei um
- Funktions-Prototypen zu deklarieren (kennen wir schon)
- Konstanten zu deklarieren (wie eben)
- Makros zu definieren (wie eben)
Man kann (sollte) sich seine eigenen Header-Dateien schreiben
17
Präprozessor: Header-Dateien
Da die Header-Datei nur in den Quellcode kopiert wird, kann man
doch alles auch gleich direkt in den Quellcode schreiben!?
In einer Header-Dateien sammelt man Vereinbarungen und
Funktionalitäten, die man immer wieder für einen bestimmten
Anwendungsbereich braucht
Ist beliebig oft in wiederverwendbar, ohne dass man alles in
jedem Programm nochmal extra aufschreiben muss
18
Präprozessor: Header-Dateien
Beispiele:
stdio.h (Standard Input Output):
Unterstützung von Eingabe und Ausgabe
math.h:
Unterstützung mathematischer Funktionen und Konstanten
string.h:
Unterstützung bei der Verarbeitung von Zeichenketten
19
Präprozessor: Header-Dateien
Header-Dateien selbst schreiben – Vorgehen
Textdatei mit Präprozessor-Direktiven schreiben
Unter beliebigem Namen mit Endung .h abspeichern in:
- Programmverzeichnis
- include-Verzeichnis des Compilers
In Quellcode einbinden durch Anweisung:
- include-Verzeichnis: #include <Headerdatei.h>
(relative Pfadangaben erlaubt)
- Programmverzeichnis: #include “Headerdatei.h“
(relative und absolute Pfadangaben erlaubt)
20
Präprozessor: Header-Dateien
Header-Dateien selbst schreiben – Achtung:
Es sind verschachtelte include-Anweisungen möglich
Datei Header1.h enthält Direktive #include “Header2.h“
Dadurch kann es durch mehrfaches Einbinden von Direktiven
zu Syntaxfehlern wegen doppelten Definitionen kommen
Programm enthält Direktiven #include “Header1.h“ und
#include “Header2.h“
Lösung:
Bedingte Aktivierung von Direktiven
21
Präprozessor: Header-Dateien
Bedingte Aktivierung von Direktiven
Headerdateien sollten immer die folgende Form haben:
#ifndef DATEINAME_H_INCLUDED
#define DATEINAME_H_INCLUDED
<Deklarationen>
#endif
22
Präprozessor: Header-Dateien
Bedingte Aktivierung von Direktiven
#define
Fasst Deklarationen unter einem Namen zusammen
Name = Dateiname in Großbuchstaben + '_'
#ifndef
#ifdef
Text einbinden, falls Name noch nicht exisitiert
Text einbinden, falls Name schon exisitiert
...#endif
23
Präprozessor: Mehrteilige Programme
1. Zerlegung des Programms in funktionale allgemein
wiederverwendbare Module
2. Für jedes Modul:
- Headerdatei erstellen
- Code-Datei erstellen mit eingebundenem Header
3. Programmdatei mit main-Funktion erstellen
4. Übersetzungsprozess (später)
24
Präprozessor: Mehrteilige Programme
Beispiel: Datei format.h
#ifndef FORMAT_H_INCLUDED
#define FORMAT_H_INCLUDED
void vspace(int n);
void hspace(int n);
#endif
Analog für Sammlungen mathematischer Funktionen (Fakultät)
oder Eingabefunktionalitäten (Eingabestrom leeren,
Eingaben vom Benutzer verarbeiten,...)
25
Präprozessor: Mehrteilige Programme
Beispiel: Datei format.c
#include “format.h“
#include <stdio.h>
void vspace(int n){
int i;
for(i=1;i<=n;i++){printf("\n");}
}
void hspace(int n){
...
}
26
Präprozessor: Mehrteilige Programme
Beispiel: Datei programm.c
#include
#include
#include
#include
“format.h“
“eingabe.h“
“computations.h“
<stdio.h>
int main(){
...
}
27
Präprozessor: Mehrteilige Programme
Präprozessor-Konstanten und -Markos eignen sich zur
Definition von Fehlercodes von Funktionen in Modulen
Beispiel: Datei eingabe.h
#define EINGABE_FAILURE -1
double eingabe();
28
Präprozessor: Mehrteilige Programme
Präprozessor-Konstanten und -Markos eignen sich zur
Definition von Fehlercodes von Funktionen in Modulen
Beispiel: Datei eingabe.c
#include “eingabe.h“
#include <stdio.h>
double eingabe(){...
if((scanf("%lf",&zahl)==0)||(zahl < 0)){
leeren();
return EINGABE_FAILURE;
}...
}
29
Präprozessor: Mehrteilige Programme
Präprozessor-Konstanten und -Markos eignen sich zur
Definition von Fehlercodes von Funktionen in Modulen
Beispiel: Datei programm.c
#include “eingabe.h“
...
int main() {...
while(1) {...
zahl = eingabe();
if (zahl == EINGABE_FAILURE){
<Fehlerbehandlung>
}...
}
30
Präprozessor: Mehrteilige Programme
Benutze extern-Variablen, um Modulübergreifende Daten
zu speichern und zu manipulieren
Beispiel: Anzahl von Funktionsaufrufen zählen
eingabe.c
--------int versuche = 0;
double eingabe(){
versuche++;...}
programm.c
---------extern int versuche;
int main() {...
printf("\nAnzahl der Eingabeversuche = %i",versuche);...}
31
Compiler: Zusammenfassung
Überprüft den (erweiterten) Quellcode auf Syntaxfehler
(lässt sich das Programm in Maschinensprache übersetzen?)
Weist Variablen und Konstanten Speicherbereiche zu
Übersetzt den Quellcode in Assemblercode
(Assemblercode ist symbolischer=menschenlesbarer
Maschinencode)
32
Assembler
Überblick
Übersetzt Assemblercode in Maschinencode
Was jetzt noch fehlt?
- Zusammenführung von mehreren Quellcode-Dateien
- Integration von Bibliotheksfunktionen
33
Linker
Bindet andere Dateien mit dem Hauptprogramm:
- Führt mehreren Quellcode-Dateien in einer Datei
zusammen (man sagt, Quellcode-Dateien werden statisch
(ein)gebunden)
- Fügt Informationen für den Zugriff auf Bibliotheksfunktionen ein (man sagt, Bibliotheksfunktionen werden
dynamisch gebunden)
34
Linker
Bibliotheksfunktionen
- werden in Header-Dateien also nur deklariert (aber nicht
definiert)
- sind entweder vorübersetzte C-Programme oder direkt
ausführbare Assembler-Programme
- werden mit dem Compiler installiert
35
Das Programm gcc
Das Programm gcc umfasst die Ausführung von Präprozessor,
Compiler, Assembler und Linker
Mittels verschiedener Optionen (Kommandozeilenparametern)
lassen sich diese Programme auch einzeln ausführen
Mittels verschiedener Optionen lässt sich die Syntax-Kontrolle
unterschiedlich detailliert ausführen
36
Das Programm gcc
gcc – Optionen: Syntaxkontrolle
-ansi
-pedantic
-W
-Wall
-Wextra
-Wmain
weist den Compiler an, den C89-Standard zu verwenden.
bringt ihn weiterhin dazu, nichts außer dem C89-Standard
zuzulassen
schaltet zusätzliche Warnungen an.
schaltet alle zusätzlichen Warnungen an.
schaltet zusätzliche zusätzliche Warnungen an.
schaltet zusätzlich Warnungen über eine falsch definierte
main()-Funktion an.
Man sollte ALLE Warnungen zum verstummen bringen, selbst
wenn das Programm schon übersetzbar ist und läuft
37
Das Programm gcc
gcc – Optionenen: Teile ausführen
-E
Präprozessor wird ausgeführt (nicht Compiler, Assembler,
Linker)
Ausgabe in Datei <Programmname>.i
-S
Präprozessor und Compiler werden ausgeführt
(nicht Assembler, Linker)
Ausgabe in Datei <Programmname>.S
-c
Präprozessor, Compiler und Assembler werden ausgeführt
(nicht Linker)
Ausgabe in Datei <Programmname>.o
38
Das Programm gcc
gcc – Optionenen: weitere (Auswahl)
-save-temps
Zwischenergebnisse werden gespeichert
--help
Übersicht über Benutzung des Programms
-o <name>
Festlegung des Namens der Ausgabedatei
-WI -stack <Bytes>
Stackgröße erhöhen
39
Das Programm gcc
Übersetzung mehrteilger Programme
1. Dateien ohne main-Funktion übersetzen in Objektcode
gcc -ansi -pedantic -W -Wall -Wextra
-c modul1.c
2. Datei mit main-Funktion übersetzen + mit Objetdateien linken
gcc -ansi -pedantic -W -Wall -Wextra
-Wmain modul1.o modul2.o programm.c
-o Programm
40
Zugehörige Unterlagen
Herunterladen