1 Byte = 8 Bit Speicherplatz für genau ein Zeichen

Werbung
(3) Textzeichentyp
char
reserviert (i.a.) 1 Byte = 8 Bit Speicherplatz für genau ein Zeichen
(Erinnerung: z.B. ASCII­Code stellt Zeichen auf 8 Bit dar)
ein erster abgeleiteter Variablentyp:
Zeichenkette
Aufeinanderfolge von Zeichen (z.B. Wörter)
dargestellt und gespeichert als Feld des Typs char
anderer Name: string
Deklaration: char stringname[n];
dabei ist n die maximale Anzahl Zeichen, d.h. die Feldlänge
­ durch die Deklaration wird ein Block von n Byte Speicherplatz im
Arbeitspeicher reserviert. ­ Achtung! Der Name stringname enthält dabei die Speicheradresse, an der
die Zeichenkette im Arbeitspeicher beginnt (stringname ist ein sog. Zeiger)
­ Zugriff auf die Komponenten, also die einzelnen Zeichen durch
stringname[i]
wobei der Index i das i­1 . Zeichen der Zeichenkette bezeichnet, da in C die Numerierung bei 0 beginnt!
z.B. char str[5]="INFO";
str
➘
I
N
F
O
\0
1012 1013 1014 1015 1016 1017 ...


 
str[0] str[1] str[2] str[3] Endezeichen
str enthält die Adresse 1015
str[0] = 'I', str[1]='N' , ...
­ gespeichert wird natürlich jedes Zeichen in seiner Binärdarstellung!
­ es muß stets ein Byte Platz für das Endezeichen eingerechnet werden,
sonst kann es zu Problemen führen Erreichen des Endezeichens kann z.B. in einer Schleifenanweisung durch
i=0;
while (str[i]!=0)
{
tue etwas
i=i+1;
}
abgefragt werden.
­ Achtung! Zuweisung eines ganzen Wortes nur in der Deklaration erlaubt
danach nur noch Zugriff auf Einzelkomponenten möglich
Beispiel:
str = "text"; (FALSCH!)
str[0] = 't'; (RICHTIG!)
str[1] = 'e';
str[2] = 'x';
str[3] = 't';
(4) leerer Typ
void
sinnvoll z.B. Für Funktionen ohne Ergebniswert (sog. Prozeduren oder Subroutinen)
aus diesen Grundtypen lassen sich dann beliebig komplexere Typen selbstdefinieren
(dazu mehr in der 4. Vorlesung)
3.3 Ausdrücke und Operatoren
Ein Ausdruck ist eine Aneinanderreihung von Zahlen, Strings und Operatoren, die sich zu einem Wert auflösen lassen, sprich: eine mehr oder weniger komplexe Rechenvorschrift
⇨Ausdrücke können über Operatoren miteinander verknüpft werden ⇨Ausdrücke können Variablen zugewiesen werden
Operatoren: es gibt grundsätzlich unäre, binäre und terniäre Operatoren
z.B.:
(1) unäre Operatoren: (in C) Inkrementoperator ++ausdruck
ausdruck++ Dekrementoperator ­­ausdruck
ausdruck­­
(2) binäre Operatoren: arithmetische Grundoperationen + , ­ , * , / (in allen Programmiersprachen)
(3) terniäre Operatoren: eher selten
in C z.B. Ausdruck1 ? Ausdruck2 : Ausdruck3
ist Ausdruck 1 wahr so berechne Audruck2 sonst berechne Ausdruck3
Zuweisung: (binärer Operator)
Variablenname = Ausdruck
­ eine Zuweisung ist selbst wieder ein Ausdruck ­ die Zuweisung hat den Wert, der der Variablen zugewiesen wird
Vergleiche und logische Operatoren
Vergleiche
Symbolik fürVergleiche in Programmiersprachen ähnlich der aus
der Mathematik bekannten: < , > ≤ ... <=
≥ ...
>=
= ... ==
doppeltes Gleichheitszeichen, da '=' der Zuweisung entspricht
allgemein ist die Syntax:
Ausdruck Vergleichsoperator Ausdruck
­ liefert wieder einen Ausdruck
­ dieser Ausdruck hat einen int­Wert, und zwar 0 , falls der Vergleich nicht zutrifft
1, falls der Vergleich zutrifft
Logische Operatoren
Aussagenlogisches UND
Symbolik:
&&
.and. &
ODER
||
.or.
|
VERNEINUNG
!
.not.
~
(in C)
(in FORTRAN)
(in MATLAB)
Ausdruck logischer Operator Ausdruck
­ liefert wieder einen Ausdruck
­ dieser Ausdruck hat einen int­Wert, und zwar 0 , falls der Gesamtausdruck wahr
1, falls der Gesamtausdruck falsch ist Anweisungen ­ sind in der jeweiligen Programmiersprache erlaubte Ausdrücke, die zeilenweise in den
Programmen geschrieben werden (oder durch entsprechende Zeichen voneinander
abgegrenzt, z.B. ein Komma in FORTRAN oder in MATLAB)
­ möglicherweise prinzipiell durch ein Zeichen abgeschlossen werden in C: Ausdruck ;
Priorität von Operatoren
(von höchster zu niedrigster Priorität)
Symbol Name/Bedeutung Assoziativität
----------------------------------------------------------------++
Erhöhung nach Auswertung
von links nach rechts
-Erniedrigung nach Auswertung
( )
Funktionsaufruf
[ ]
Arrayelement
->
Zeiger auf Strukturfeld
.
Felder einer Struktur oder Union
----------------------------------------------------------------++
Erhöhung vor Auswertung
von rechts nach links
-Erniedrigung vor Auswertung
!
logisches NOT
unäres Minus
+
unäres Plus
&
Adresse von
*
Dereferenzierung
sizeof Größe in Bytes
(type) Typumwandlung (cast)
-----------------------------------------------------------------
*
Multiplikation
von links nach rechts
/
Division
%
Divisionsrest (modulo)
----------------------------------------------------------------+
Addition
von links nach rechts
Subtraktion
----------------------------------------------------------------<
kleiner als
von links nach rechts
<=
kleiner oder gleich
>
größer als
>=
größer oder gleich
----------------------------------------------------------------==
gleich
von links nach rechts
!=
ungleich
----------------------------------------------------------------&&
logisches AND
von links nach rechts
----------------------------------------------------------------||
logisches OR
von links nach rechts
----------------------------------------------------------------? :
Bedingung
von rechts nach links
----------------------------------------------------------------=
Zuweisung
von rechts nach links
*=
zusammengesetzte Zuweisung
/=
%=
+=
-=
<<=
>>=
&=
^=
|=
----------------------------------------------------------------,
Komma-Operator
von links nach rechts
-----------------------------------------------------------------
Typumwandlung
nötig, falls in einer Operation Operanden verschiedenen Typs beteiligt sind
● implizite Typumwandlung: durch den Compiler
z.B. int i=1, k=3;
float x;
x = i*k; int*int liefert int, in der Zuweisung Umwandlung intfloat
●
explizite Typumwandlung: durch einen cast­Operator z.B. float y = 6.78;
i = 3 + (int) y;
4. Entwurf von Algorithmen
Entwurfbeispiel: Faktorisierung einer natürlichen Zahl n
Eingabedaten:
natürliche Zahl n Typ integer
Primzahlen mit ihren Vielfachheiten
Ausgabedaten:
⇨Matrix mit max. (n­1) Spalten (max. n­1 Primzahlen) und 2 Zeilen zweidimensionales Feld für Variable von integer­Typ
Fälle:
n=1 und n>1
Teilaufgaben:
­ Rahmenprogramm (Ein­und Ausgaben)
­ Auswahl eines Algorithmus zur Faktorisierung (Menue)
­ Methoden 1. Iterative Methode 2. Zahlkörpersieb
3. quadratisches Sieb ...
⇨entsprechend drei Teilaufgaben für die Methoden
zu implementieren, weitere Gliederung in Teilaufgaben innerhalb jeder der Methoden
Prinzipien zum Entwurf:
● Zerlegung des Problems in Teilprobleme (Modularisierung)
 durch schrittweise Verfeinerung (TOP­DOWN­Entwurf)
 ausgehend von Lösungen für Teilaufgaben Zusammensetzen zu einem Programm
(BOTTOM­UP­Entwurf)
● Zerlegen in Teilaufgaben durch Teilen der Eingangsdaten und Anwendung des selben Algorithmus auf die kleineren Datenmengen
⇨ Prinzip „TEILE UND HERRSCHE“ (DIVIDE & CONQUER)
●
exakte Analyse der Daten ⇨ entsprechende Definition von passenden Datentypen
●
⇨ abstrakte Datentypen (Datentyp + zugehör. Operationen)
Unterprogrammtechnik
Teilprobleme in (für sich abgeschlossenen) Funktionen/Routinen implementieren
⇨Mehrfachnutzung von Teilalgorithmen möglich
⇨ unabhängig voneinander programmierbar (nur Schnittstellen müssen klar sein)
Ziel ist ein effizienter Algorithmus zur Problemlösung, d.h. möglichst geringer Ressourcenaufwand (Speicher & Laufzeit)
Grundsätzliche Schritte:
gegeben sei eine Problemstellung, deren Lösung die auf dem Computer umsetzbar ist
1.
2.
3.
4.
Präzise Problembeschreibung (Spezifikation)
Eingabedaten? Welche Ergebnisse (Ausgabedaten) werden erwartet?
Welche Fälle sind zu unterscheiden?
Zerlegung in Teilprobleme, schrittweise Verfeinerung
5. Lösen der Teilprobleme ⇨Teilalgorithmen
Formulierung unter alleiniger Verwendung der Grundstrukturen (Sequenz, Schleife, Selektion)
dabei Definition passender Datentypen
graphische Veranschaulichung von Algorithmen: Flußdiagramme
Struktogramme
(s. Wikipedia für die Elemente)
oder Formulierung als sogenannten Pseudocode (halbformale Form)
Flußdiagramm: grün: grau:
braun:
rot:
Start und Ende des Programms
Anweisungen, in denen Operationen ausgeführt werden
Datenein­ bzw. ausgabe
Selektion
Struktogramm
Sequenzen
Selektion (if­Anweisung)
Sequenzen im else­Zweig
Schleife (do­while)
Selektion (if­Anweisung)
Ja
Nein
Sequenzen in if– bzw. Else­Zweig
Rekursiver Aufruf
Oder:
Algorithmus für welches mathematische Problem?
Beispielalgorithmus:
Auszugeben ist die Summe derjenigen von N einzulesenden Zahlen, die zwischen den vorzugebenden Werten L und R liegen. Flußdiagramm:
Struktogramm:
Pseudocode­Darstellung:
Beispiel
Eingabe: N, L, R Zähler = 1
Summe = 0 solange Zähler <= N führe aus:
Eingabe: Zahl falls L < Zahl < R dann Summe = Summe + Zahl;
Zähler = Zähler + 1
Ausgabe: Summe Beispielende
5. Unterprogrammtechnik/Module
Unterprogramm/Modul: ● separate Programmeinheit, die Anweisungen umfaßt, um eine bestimmte Aufgabe zu erfüllen
● bekommt i.a. Argumente (Werte, Informationen) vom aufrufenden Programm ● gibt i.a. ein Ergebnis an das aufrufende Programm zurück
● Bezeichnung und Funktionalität kann je nach Programmiersprache variieren:
Bezeichnung: in C:
function
in FORTRAN: function (mit Rückgabewert), subroutine (ohne Rückgabewert)
in PASCAL: function (mit Rückgabewert), procedure (ohne Rückgabewert)
Funktionen in Funktionen definieren (Verschachtelung von Unterprogrammen)?
in C nein
alle Funktionen existieren hierarchisch auf der gleichen Ebene
in FORTRAN nein
alle Funktionen existieren hierarchisch auf der gleichen Ebene
in PASCAL ja
Unterprogramme von Unterprogrammen sind erlaubt
Hauptprogramm: in C ebenfalls eine Funktion
trägt immer den Namen main , kann nicht von anderen Funktionen aufgerufen werden in FORTRAN hat den Rahmen PROGRAM programmname ...
END PROGRAM
in PASCAL hat den Rahmen program programmname
...
end.
Bibliotheksfunktionen:
­ vordefinierte Standardfunktionen, die mit der Entwicklungsumgebung/ mit dem
Compiler geliefert werden in C in Funktionsdateien mit einer zugehörigen Headerdatei name.h
z.B.:
Standardmathebibliothek Header:
math.h
Standard­I/O­Bibliothek
Header:
stdio.h
Bibliothek für string­Funktionen Header:
string.h
in FORTRAN in Modulen
in PASCAL in units
­ Sammlungen von bereits vorhandenen Funktionen zu bestimmten Themen,
die frei oder mit Bezahlung erhältlich sind (z.B. im Netz)
in FORTRAN z.B.: lapack, blas, daspk, ...
in C z.B.:
cblas, cvode, ...
5.1 Formaler syntaktischer Aufbau einer Funktion
Definition einer Funktion (Syntax in C)
Rückgabetyp Funktionsname(Liste von Argumenten) Funktionskopf
{
Deklarationen lokaler Variabler Anweisungen
Funktionsblock
return Ergebniswert
}
besteht aus: ­ Funktionskopf Schnittstellen zum aufrufenden Programm
d.h. Struktur der Funktion (Name, Argumente, Ergebnis)
­ Funktionsblock/rumpf
Algorithmus, den die Funktion umfaßt, d.h. eigentliche Umsetzung der Aufgabe der Funktion
Dateneingabe (Argumente)
Funktion
Datenrückgabe
(Ergebnis)
Komponenten:
● Funktionsname ... Bezeichung der Funktion, unter der sie aufgerufen wird
● Argumentliste ... ­ Liste der Eingabeparameter mit ihren Datentypen, die vom
aufrufenden Programm an die Funktion gegeben werden
­ für die dort aufgelisteten Variablen wird mit Aufruf der Funktion Speicherplatz bereitgestellt
● lokale Variable ... ­ sind nur im Funktionsblock gültig, ab ihrer Deklaration
reservieren sie Speicher bis zum Abschluß der Funktion
­ dienen der Zwischenspeicherung von Werten während der
Funktionsausführung
● return ... Anweisung ist dann erforderlich, wenn ein Ergebnis zurückgegeben
wird
● Rückgabetyp ... Datentyp des Ergebnisses (Funktionswertes), das an das aufrufende
Programm zurückgegeben wird
bei Funktionen ohne Rückgabe: void­Typ und return; (ohne Wert) ist optional
Erinnerung: in anderen Programmiersprachen tragen solche Funktionen auch einen anderen Namen (subroutine, procedure)
Beispiel:
Funktion zur Berechnung des Mittelwertes dreier Zahlen
Aufruf im Programm/in einer anderen Funktion:
● in Zuweisung:
m = mittelwert(x1, x2, x3);
● in einem Ausdruck:
y = sin(3.5*mittelwert(x1, x2, x3));
printf(''Der Mittelwert ist %7.3f. \n'',mittelwert(x1,x2,x3));
Wo wird die Funktion definiert?
Alle im Programm benutzten Funktionen müssen dem Compiler vor Beginn der main­Funktion (Hauptprogramm) bekannt sein.
Funktionen, die durch andere Funktionen aufgerufen werden, müssen vor letzteren dem Compiler bekannt sein.
Varianten zum „Bekanntmachen“:
1. Definition der Funktion innerhalb des Programms
a) vor dem Hauptprogramm
Definition der Funktion
main­ Funktion
b) nach dem Hauptprogramm und Deklaration vor dem Hauptprogramm
Deklaration der Funktion
main­ Funktion
Definition der Funktion
Deklaration einer Funktion:
Rückgabetyp Funktionsname(Liste der Argumenttypen);
Bekannmachen des Namens zusammen mit allen Schnittstellen zum aufrufenden Programm, d.h. mit den Argumenttypen und dem Rückgabetyp
= Prototyp der Funktion
Beispiel zu 1. : Mittelwert dreier Zahlen, nun im Programm eingebunden
2. Definition der Funktion in anderer Quelldatei und Deklaration im Programm
Quelldatei func.c
Definition der Funktion
Headerdatei func.h
Deklaration der Funktion
(beide in demselben Verzeichnis)
Hauptprogramm
Deklaration der Funktion
durch Einbinden der Headerdatei mit
#include ''func.h''
main­ Funktion
­ Deklaration(Bekanntmachen) der Funktion im Hauptprogramm vor der main­Funktion durch Einbinden (#include) der zugehörigen Headerdatei
Headerdatei (header = Funktionskopf) beinhaltet nur eine Liste der Funktionen,
die in der Funktionsdatei definiert sind
­ die Präprozessor­Direktive #include ''func.h''
fügt den Text, so wie er in der Headerdatei steht, direkt ins Programm ein
d.h. durch Verwenden der Headerdatei muß man nicht im eigenen Programm alle Funktionsdeklarationen wieder selbst schreiben
­ Standardbibliotheks­Header werden durch #include < name.h >
eingebunden. Das sagt dem Präprozessor, daß diese im Standardbibliotheks­ verzeichnis der Entwicklungsumgebung zu finden sind
­ steht die Funktionsquelldatei in einem anderen Verzeichnis als das Hauptprogramm
muß in der #include­Direktive der volle Pfad zu dem Verzeichnis angegeben werden
­ in PellesC: Funktionsdateien und Hauptprogramm zu einem Projekt
zusammenfassen
Arbeit des Linkers ist dann im Hintergrund automatisch aktiv
­ Verwendung von Headerdateien ist C­typisch, in anderen Programmiersprachen nicht üblich
sinnvoll, wenn
● die Funktionen für mehrere Programme genutzt werden sollen (Module)
● das Problem ein größeres Programmpaket erfordert, das sonst nicht mehr übersichtlich ist (Zerlegung eines Programms in Teilprogramme)
● häufig werden solche Funktionen in Bibliotheken zusammengefaßt, die dann
nur einmal compiliert werden müssen und bei Bedarf nur durch ihre
Deklarationen dem Programm bekanntgemacht (nur Funktionsköpfe! Nur Schnittstellen der Funktion nach außen)
und durch den Linker in Maschinencode an das übersetzte Programm angebunden werden
z.B. Standardfunktionen in C, wie printf, scanf
dazu Einbinden der Headerdatei stdio.h der Standard­I/O­Bibliothek von C 5.2 Argumentübergabe
Wenn eine Funktion mit einer nichtleeren Argumentliste durch ein Programm aufgerufen wird, bedeutet das einen Transfer der Daten aus den aktuellen Variablen der Liste in die formalen Variablen des Funktionskopfes.
Was geschieht genau beim Aufruf?
● Programmabarbeitung springt zum Maschinencode der Funktion
● für die formalen Parameter (Argumente) der Funktion wird Speicherplatz reserviert
● die Werte (der aktuellen Variablen), die beim Aufruf der Funktion in der Liste stehen, werden an die formalen Parameter in der Reihenfolge der Liste übergeben,
d.h. an die entsprechenden Speicherplätze geschrieben
● die Funktion arbeitet nur auf den formalen nicht auf den aktuellen Parametern Beispiel:
AS
//Funktionsdeklaration
double func(int i, double x)
2
{ double y;
y= i*x;
return y;
}
int main
{ int j=1; double z = 0.2;
z = func(j,z);
return 0;
} 1
Selbst, wenn die selben Argumentnamen beim Aufruf der Funktion verwendet werden wie in der Definition der Funktion, sind aktuelle und formale Variablen voneinander
verschieden! Für die Zeit des Funktionsaufrufs werden über die Variablennamen stets die „nächsten“
(die „innersten“) Variablen angesprochen und die formalen Parameter (Variablen) des Funktionskopfes sind der Funktion „näher“ als die im Programm deklarierten „aktuellen“
Variablen der Argumente.
Unterschiedliche Arten des Datentransfers:
● Argumentübergabe „call­by­value“ (Wertübergabe)
● Argumentübergabe „call­by­reference“ (Speicheradressübergabe)
in vielen Programmiersprachen gibt es diese zwei Arten (z.B. C, FORTAN, PASCAL)
in anderen nicht (z.B. LOGO, MATLAB nur Wertübergaben)
(A) Argumentübergabe „call­by­value“ (Wertübergabe)
Es wird bei Aufruf der Funktion von der als Argument auftretenden Variablen nur der
gespeicherte Wert an den entsprechenden formalen Parameter der Funktion übergeben ⇨d.h. das was auf dem Speicherplatz der „aktuellen“ Variablen steht, wird in den mit Funktionsaufruf reservierten Speicherplatz der entsprechenden „formalen“
Variablen geschrieben, s. obiges Beispiel)
⇨Funktion kann nur lesend auf die aktuellen Variablen zugreifen aber nicht schreibend. (B) Argumentübergabe „call­by­reference“ (Speicheradressübergabe)
Es wird bei Aufruf der Funktion als Argument die Speicheradresse einer Variablen im Arbeitsspeicher an den entsprechenden formalen Parameter der Funktion übergeben ⇨ da die Funktion damit direkt auf den Speicherplatz einer „aktuellen“ Variablen des
aufrufenden Programms zugreifen kann, kann sie dort sowohl den aktuellen Wert
herauslesen als auch einen neuen Wert hineinschreiben
⇨Funktion kann lesend und schreibend auf die aktuellen Variablen zugreifen. „call­by­reference“:
Referenzparameter = Speicheradresse einer Variablen
Wie erhält man die Speicheradresse?
In C:
Adressoperator(Referenzierungsoperator) &
Beispiel:
int i; Variable i wird deklariert, d.h. 4 Byte Speicherplatz
werden reserviert
printf(''Speicheradresse von i: %d'',&i);
gibt die reservierte Speicheradresse (erstes Byte) auf dem Bildschirm aus
i
2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113
Bildschirmausgabe: Speicheradresse von i: 2105
Objekte, die die Speicheradresse von Variablen speichern, heißen Zeigervariable (Pointer)
Deklaration solcher Zeigervariablen:
Typ * zeigervariablenname;
Typ ist entscheidend, da damit auch die Information gegeben ist, wieviel Byte
die zu der gespeicherten Anfangsadresse gehörige Variable insgesamt einnimmt
Beispiel:
int * p ; p „zeigt“ auf eine Variable vom Typ int, d.h. p reserviert Speicherplatz für eine Speicheradresse einer
int­Variablen int i = 1; Variable i reserviert Speicherplatz von 4 Byte und dieser wird mit dem Wert '1' initialisiert, d.h. dieser Wert wird
dort als erster Wert gespeichert p = &i; auf den Speicherplatz von p wird die Anfangsspeicheradresse
von i geschrieben
p
i
2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113
Wechselspiel: Adressoperator &: Variable  Speicheradresse (Referenzierungsoperator)
Dereferenzierungsoperator *: Speicheradresse  Variable (Zugriff auf gespeicherten Wert)
Beispiel: Fortsetzung
int j;
j=*p; in den Speicherplatz, den j reserviert, wird der Wert eingeschrieben,
der an der Speicheradresse steht, die p speichert („auf die p zeigt“),
hier speichert p die Adresse von i ⇨ j bekommt den Wert '1'
Standardbeispiel:
Zur Illustration der Unterschiede zwischen den zwei Argumentübergaben
Tausch zweier Variablenwerte.
void tausch1 (int a, int b)
{ int m; m = a; a = b; b = m;
}
void tausch2 (int *a, int *b)
{ int m;
m = *a; *a = *b; * b= m;
}
Korrekter Aufruf der beiden Funktionen mit unterschiedlichen Ergebnissen:
int x=22, y=44;
tausch1 (x ,y); tausch2(&x, &y); Häufig ist die Argumentliste von Funktionen eine Mischform, d.h. umfaßt sowohl
Werte­ als auch Referenzparameter
Herunterladen