Verlässliches Programmieren in C/C++
Teil A: Grundlagen zu C
(übernommen aus dem Manuskript der Vorlesung Datenverarbeitung)
Prof. Dr. F. Belli Universität Paderborn,
Fakultät EIM, Fachbereich Elektrotechnik und Informationstechnik
Angewandte Datentechnik (Softwaretechnik)
I. Einfache Konstrukte von Programmiersprachen
1. Einleitung
2. Algorithmus und Programm
3. Syntax, Semantik und Pragmatik
4. Einstieg: Einfache Sprachkonstrukte und allgemeiner Programmaufbau 5. Programmierumgebung
6. Weitere Steuerungskonstrukte
7. Exkurs: Einheitliche Darstellung von Daten und Operationen 8. Elementare Datentypen und Operationen in C
9. Strukturierte Datentypen 10. Zeiger-Datentyp
II. Fortgeschrittene Elemente der Programmierung
12. Unterprogrammtechniken 13. Rekursion
14. Dynamische Datenstrukturen 15. Dateiverarbeitung
16. Komplexität von Algorithmen und ihre Bestimmung 17. Programmprüfung
4. Einstieg: Sprachkonstrukte und allgemeiner Programmaufbau
Ziele dieser Einheit:
- Kennen lernen elementarer Bestandteile konventioneller Programmiersprachen,
- Zusammensetzung dieser Bestandteile zur Bildung einfacher Programme,
- Berücksichtigung potentieller Fehlerquellen in
Programmen.
Datenverarbeitung 4. Sprachkonstrukte und allg. Programmaufbau
4.1 Programmstruktur
Programm
Programm
include-Anweisung Variablendefinition
Main-Funktion
4.2 Include-Anweisung, Variablendefinition (Deklaration)
Datentyp (DT)
int float
Bezeichner Datentyp
; ,
Variablendefinition
Include-Anweisung (Preprocessor-Anweisung)
#include <stdio.h>
Bemerkung: Die eckigen Klammern werden üblicherweise zur
Kennzeichnung von Nicht-Terminalen benutzt; hier allerdings nicht!
Datenverarbeitung 4. Sprachkonstrukte und allg. Programmaufbau
- Variablen identifizieren Speicherstellen.
- bestehen aus einem Namen (bzw. Bezeichner), Stelle/Adresse im Speicher und Inhalt
- Sie treten definierend bzw. angewandt im Programm auf:
definierend: Typ VarName;
angewandt: VarName = ...
Beispiele: int zahl;
int zahl1, zahl2;
double geldbetrag;
zahl1=10; zahl2=20;
…
zahl=0;
…
Unser erstes C-Programm
#include <stdio.h> /*Standard Ein-/Ausgabe*/
/* Hauptprogramm */
void main() /* void: kein Ergebnis-DT*/
{ /* Beginn */
/* Ausgabe von Hallo, Welt! */
printf("Hallo, Welt! \n");
/* „printf“ : Funktion „print
formatted“ */
Datenverarbeitung 4. Sprachkonstrukte und allg. Programmaufbau
Beispiel: void main ( int argc, char* argv[] ) {
int zhl;
int quadrat = zhl*zhl;
printf ("%d",quadrat);
}
Main-Funktion
Parameterliste
( )
Zusammengesetzte Anweisung int main
void
Anweisung ;
Zusammengesetzte Anweisung (compound statement)
{ }
4.3 Blockstruktur der Programme
void main () {
{ /* Hier rechne ich sinus */
. }
.. } .
.
{ /*... cosinus ...*/
.. } .
{
{ ...
} { ...
} } .
Datenverarbeitung 4. Sprachkonstrukte und allg. Programmaufbau
4.4 Allgemeine Darstellung der Anweisungen
Anweisung
Schreibanweisung
Vergleich (Bedingte Anweisung) Leseanweisung
Zuweisung
Zusammengesetzte Anweisung
...
;
Beispiele:
Schreibanweisung:
Leseanweisung:
Zuweisung:
Zusammengesetze Anweisung:
Vergleich (Bedingte
printf("Hallo, Welt! \n");
scanf("%d", &zhl1);
zhl = 100;
{
int zhl;
int quadrat = zhl*zhl;
printf ("%d",quadrat);
}
if ( ... )
Datenverarbeitung 4. Sprachkonstrukte und allg. Programmaufbau
Schreib- und Leseanweisung näher betrachtet
Schreibanweisungen
printf("Zahl eingeben \n");
/* druckt die konstante Zeichenkette (Literal) „Zahl …“ aus */
printf("%s", "hey");
/* druckt die Zeichenkette (string) „hey“ aus */
printf("%c%c%c",‘h‘,‘e‘,‘y‘);
/* druckt sukzessiv die Einzelzeichen (character) „h“,„e“,„y“ aus */
int zhl;
printf(“Die Zahl \n%d ist positiv“, zhl);
/* Ausgb: „Die Zahl“, neue Zeile, Ausgb: der aktuelle Wert von zhl */
/* (decimal integer), letzte Ausgb: „ ist positiv.“ */
Leseanweisungen
int zhl, zhl1,zhl2; char ch;
scanf("%d", &zhl);
/* liest die ganze Zahl (decimal integer) „zhl“ ein */
scanf(" %c%d%d", &ch, &zhl1, &zhl2);
/* liest das Zeichen ch und ganze Zahlen „zhl1“ und „zhl2“ ein */
Ausdrücke
(nach George Boole) Boolescher Ausdruck
Einfacher Ausdruck
Einfacher Ausdruck
>
==
<=
<
Datenverarbeitung 4. Sprachkonstrukte und allg. Programmaufbau
einfacher (arithmetischer) Ausdruck
Präfix-Operation
Term +
- + -
Infix-Operation
Kontextsensitive (umgebungsabhängige) Interpretation der Operationen + und -.
Term
Faktor
% / Faktor *
Datenverarbeitung 4. Sprachkonstrukte und allg. Programmaufbau
Faktor
vorzeichenlose Konstante
Bezeichner einfacher Ausdruck
( )
indirekt rekursive Definition „einfacher Ausdruck“
Bezeichner für eine Konstante
INTEGER-Zahl
REAL-Zahl
Vorzeichenlose Konstante
Datenverarbeitung 4. Sprachkonstrukte und allg. Programmaufbau
Beispiele für Ausdrücke:
2, 2 * 3, 2 * 1, 50 % 2 a, 2 + a, 7 * b
(a + b) * (c / d) % (e * 2 + f) arithmetische
Ausdrücke
a<5, a+b >= -10,
!(((a > b) || (c >= 10)) && ((a + 2) > 5)) boolescher
Ausdruck
4.5 Vergleich
Bedingung
Anweisung 1 if
;
( )
Bedingte Anweisung (Binäre Entscheidung)
Datenverarbeitung 4. Sprachkonstrukte und allg. Programmaufbau
Bedingung
Ausdruck < Ausdruck
!=
>=
>
==
<=
Zusammengesetzte Bedingung
Bedingung
||
(
!
&&
)
4.6 (Wert-)Zuweisung („Ergibt-sich-Anweisung“)
Variable = Ausdruck
(Bezeichner)
ergibt sich aus
guthaben = 50; /* Geschenk G´mutter */
gesch_tante = 100;
Beispiele:
guthaben + gesch_tante;
/*altes*/
guthaben = /*neues*/
zhl1 = 3;
zhl2 = 4;
zhl1 = zhl1 + zhl2;
...
zhl2 = zhl1 + zhl2;
Datenverarbeitung 4. Sprachkonstrukte und allg. Programmaufbau
C-spezifische Syntax gängiger Zuweisungsoperatoren
- viele binäre Operatoren haben entsprechenden Zuweisungsoperator:
+=, -=, *=, /= usw.
- <Ausdruck1> <Operator>= <Ausdruck2>
- ist äquivalent zu
- <Ausdruck1> = (<Ausdruck1>) <Operator> (<Ausdruck2>)
- Beispiele:
• x += 2; => x = x + 2;
• x *= y + 1; => x = x * (y + 1);
/* Klammerung beachten! */
4.7 Selektion der nächsten Anweisung durch Vergleich
...
int zhl;
scanf(“%d“, &zhl);
Aufgabe: Schreibe ein C-Programm, das eine Zahl zhl einliest und ausgibt, ob sie positiv oder negativ ist.
else printf(“negative Zahl“);
if (zhl > 0)
printf(“positive Zahl“);
Datenverarbeitung 4. Sprachkonstrukte und allg. Programmaufbau
Programmablaufplan
: BEGINzhl
zhl > 0
?
zhl positiv
zhl negativ
END
lies zhl ein
Ausgabe
J N
Selektion
Der korrigierte Programmablaufplan:
J N
N
zhl = 0?
zhl negativ J
lies zhl ein BEGIN
zhl
zhl > 0?
zhl
positiv zhl = 0
Vergleiche
Datenverarbeitung 4. Sprachkonstrukte und allg. Programmaufbau
Das korrigierte Programm:
#include <stdio.h>
void main() {
int zhl;
scanf("%d", &zhl);
if (zhl > 0)
printf("positive Zahl");
else {
if (zhl == 0)
printf("Zahl 0");
else printf("negative Zahl");
}
5. Programmierumgebung
Ziele dieser Einheit:
- Schritte von der Erstellung des Programms zu seiner Ausführung.
- Debuggen bzw. Verifikation des Programms
und Validation der Ergebnisse.
Datenverarbeitung 5. Programmierumgebung Editor
(„edieren“)
meinprogramm.c
N
(„verifizieren/validieren“) N
(„debuggen“) Korrigieren
meinprogramm.exe („compilieren“)
C-Compiler Quell-
Code Fehler-
frei?
J
J
ausführen Erfolg-
reich
?
Ergebnisse
Binder/Lader
Ausführung unseres ersten C-Programms
#include <stdio.h> /*Standard Ein-/Ausgabe*/
/* Hauptprogramm */
void main() /* void: kein Ergebnis-DT*/
{ /* Beginn */
/* Ausgabe von Hallo, Welt! */
printf("Hallo, Welt! \n");
/* „printf“ : Funktion „formatted print“ */
Datenverarbeitung 5. Programmierumgebung
Pre- processor
Modifizierter Quell-Code Compiler
(Übersetzer) Verschieb-
barer Ob- jekt-Code
Lader
Ein- gaben
Quell- Code (Source)
Ergeb- nisse Binder
(Linker)
Ausführba- rer (Absolut-)
Objekt-Code andere
Objekt-Codes;
Bibl.-funkt- ionen;
Startcode
Der Präprozessor
kann wie folgt benutzt werden:1. Einfügen von Quelltextdateien und Bibliotheken. Zweierlei Bedeutung:
#include <<Dateiname>> , z.B. #include <myFile.h>, bedeutet Ansprechen von Standardfunktionen, welche meist in den Verzeichnissen
/usr/include oder /usr/local/include/ stehen.
oder
#include "<Dateiname>", z.B. #include "myFile.h",
bedeutet Einfügen von benutzerdefinierten Dateien. Die Suche erfolgt in der lokalen Umgebung (aktuelles Verzeichnis).
Bemerkung: Achten Sie auf die Benutzung von „<...>“ bei Nicht-Terminalen.
2. Makrodefinition und -ersetzung.
#define <Makroname> <Ersatztext>
Abkürzung häufig benutzter Zeichenfolgen.
Datenverarbeitung 5. Programmierumgebung
Vorsicht:
Makrodefinition: #define SQUARE(X) X*X Makroaufruf (Bsp.): b=SQUARE(a+1)
erzeugter Code: b=a+1*a+1 Richtig: #define SQUARE(X) ((X)*(X))
3. Zur Steuerung der bedingten Übersetzung.
Präprozessor-Direktiven zur Festlegung zu übersetzender Programmteile:
#if def_1
Programm_A
#elif def_2
Programm_B
…
#else def_9
Programm_Z
Übersetzung, wenn der Makroname bekannt ist:
#ifdef Makroname
Gehen Sie bitte diese Schritte am folgenden Programm durch:
#include <stdio.h>
void main() {
int zahl;
scanf("%d", &zahl);
if (zahl > 0)
printf("positive Zahl");
else {
if (zahl == 0)
Datenverarbeitung 6. Steuerkonstrukte
6. Weitere Steuerungskonstrukte
Ziele dieser Einheit:
- Konstrukte, welche eine nicht-lineare Ausführung von Programmen ermöglichen. Dazu gehören:
Wiederholte Ausführung logisch/strukturell zusammenhängender Programmteile
(Schleifen/Iterationen/Loops).
Erweiterung einfacher (einseitiger/zweiseitiger) Entscheidungen auf mehrseitige Entscheidungen (Mehrfachauswahl/Schalter/Switch).
- Erkennung/Berücksichtigung der Fehlerquellen bei
Verwendung dieser Konstrukte.
6.1 Abweisende Schleife (while-Anweisung)
int idx = 1;
int sum = 0;
while (idx <= 100)
{/*idx: Laufvariable*/
...
. . .
Beispiel:
Bed.?
Anweisung
N J
(Terminierungs)Bedingung
Anweisung
while ( )
Ausführung des Schleifenrumpfs so lange eine Bedingung erfüllt ist.
Schleifen- kopf
„kopfgesteuert“
/* initialisieren */
Datenverarbeitung 6. Steuerkonstrukte
6.2 Nichtabweisende Schleife (do-while-Anweisung)
Ausführung des Schleifenrumpfs bis eine Bedingung erfüllt ist.
Beispiel: ...
...
...
Anweisung
N J
...
int zahl;
zahl = 30000;
do {
printf ("Zahl: %d", zahl);
zahl = zahl - 1;
}
while (zahl > 0);
...
Nichtabweisende Schleife (do-while-Anweisung)
Anweisung
(Terminierungs)Bedingung do
while ( ) ;
Datenverarbeitung 6. Steuerkonstrukte
int sum = 0;
int idx;
for (idx=1; idx<=100; idx++) sum = sum + idx;
_____________
int sum = 0;
int idx;
for (idx=100; idx>=1; idx--) sum = sum + idx;
...
6.3 Zählschleifen (for-Anweisung)
Ausführung des Schleifenrumpfs von einem Anfangs- bis zu einem Endwert der Laufvariable
/* inkrementieren */
/* dekrementieren */
Beispiel:
Bed.?
Anweisung
Anweisung N J
Zählschleifen (for-Anweisung)
for ( Ausdruck1 ;
(Terminierungs) Bedingung
;
Ausdruck2 )
Anweisung Ausdruck1: wird einmal vor
Beginn der Schleife ausgeführt/ausgewertet
Bedingung: wird vor jedem Schleifendurchlauf geprüft Ausdruck2: wird nach
Datenverarbeitung 6. Steuerkonstrukte
6.4 Auswahlkriterien für verschiedene Schleifenformen und ihre Darstellung
Eckwerte
– Anfangs- und Endwerte der Laufvariable von vorne bekannt:
Zählschleife (for (idx=0;... ))
– Schleife bei Nicht-Erfüllung einer Bedingung abbrechen:
• while ... do ... : sofortiger Abbruch möglich
• do ... while ... : mindestens eine Durchführung vor dem Abbruch.
– Bemerkung: Die Anweisungen
break
bzw.continue
bewirken sofortigen Abbruch der Schleife bzw. Übergehen eines Schleifenschrittes.Abweisende
(kopfgesteuerte) Schleife
SOLANGE Dünstzeit unter 30 Min.
wiederhole
Dünste um eine Minute weiter!
Nicht-abweisende
(fußgesteuerte) Schleife
BIS Dünstzeit über 30 min.
Dünste um eine min weiter!
wiederhole
Zählschleife
VON Dünstzeit = 0 Min.
wiederhole
Dünste um eine Minute weiter!
Datenverarbeitung 6. Steuerkonstrukte
6.5 Tücken der Schleifen und anderer Sprachkonstrukte
N
J Anfangswerte idx ← 1;sum ← 0
idx<=100
sum ← sum+idx idx ← idx+1
...
idx = 1;
sum = 0;
while (idx <= 100) {
sum = sum + idx;
idx = idx + 1;
}...
...
idx = 0;
sum = 0;
while (idx < 100) idx = idx + 1;
sum = sum + idx;
...
Fehler 1:
N
J
Anfangswerte idx ← 0;sum ← 0
idx<100
idx ← idx+1 sum ← sum+idx
Schleifenrumpf nicht als Verbundanweisung
Datenverarbeitung 6. Steuerkonstrukte
...
idx = 0;
sum = 0;
while (idx < 101) {
idx = idx + 1;
sum = sum + idx;
}
...
Fehler 2:
N
J Anfangswerte idx ← 0;sum ← 0
idx<101
idx ← idx+1 sum ← sum+idx Fehlerhafte Bedingung
⇒ Ein Schleifendurchlauf zuviel
⇒ sum ergibt den Wert für Summe 1 bis 101
Fehler 3:
...
idx = 0;
while (idx < 100) {
sum = 0;
idx = idx + 1;
sum = sum + idx;
}...
N
J Anfangswerte
idx ← 0
idx<100
sum ← 0 idx ← idx+1 sum ← sum+idx sum wird im Schleifenrumpf initialisiert
Datenverarbeitung 6. Steuerkonstrukte
...
idx = 1;
sum = 0;
while (idx <= 100) {
sum = sum + idx;
idx = idx + 1;
}...
Fehler 4:
N
J Anfangswerte idx ← 1;sum ← 0
idx<=100
sum ← sum+idx idx ← idx+1
Anfangswerte nicht definiert!
→ sum <- irgendein Wert, bloß nicht die Summe, die erwünscht wird!
/* vergessen */
Noch mehr Fehler, Fortsetzung (Zählschleifen)
idx = 1;
sum = 0;
for (idx=1; idx<=100; idx++) {
sum = sum + idx;
idx = idx + 1;
}
.. . ..
. ...
J
N Anfangswert
sum ← 0, idx ← 1
i > 100
sum ← sum + idx
Datenverarbeitung 6. Steuerkonstrukte
Zusammenfassung:
„Fehler in do-while und while-do-Schleifen“:
– Die Bedingung muss auswertbar sein. Die Variablen müssen initialisiert (auf Anfangswerte gesetzt) sein.
– Schleifenrumpf muss eine Aktion enthalten, die die Schleifenbedingung berührt und irgendwann falsch werden lässt.
– Keine „Blindgänger“ im Schleifenrumpf (Aktionen für „draußen“).
6.6 Allgemeine Fallunterscheidung (Mehrfach-auswahl /Schalter) durch switch
falls heute
mo di mi don fr sonst
Kino
Disco
Pizza saure
Bingo Bohnen Gurken
Datenverarbeitung 6. Steuerkonstrukte
Schalter bei C:
Ausdruck
Anweisung
switch ( ) {
case default
Konstante : }
int x = ...
switch (x) {
case 1: printf(“eins\n“); break;
case 2: printf(“zwei\n“); break;
...
case 12: printf(“zwo“); /* kein break */
case 11: printf(“elf\n“); break;
case ‘F‘: printf(“siebzig\n“); break;
default : printf(“nanu?\n“); break;
Beispiel:
Bemerkungen zum switch:
- Konstante (case-Marken) und break:
müssen konstanter Integer-Ausdruck sein
Reihenfolge der Marken ist beliebig
Ausführung startet ab einer Marke, falls der Ausdruck der Konstante entspricht
Abbruch mit break ! - default
wird ausgeführt, falls keine Marke vorhanden
ist optional, d.h kann weggelassen werden - Beispiele:
: 1, 2, 3,..., ´J´,´F´,
nicht A + B, x + monat > 12 , ...
Datenverarbeitung 6. Steuerkonstrukte
if (monat == 1) tage = 31;
else if (monat == 2) {
if (jahr % 4 != 0) tage = 28;
else if ((jahr % 100 == 0)
&& (jahr % 400 != 0)) tage = 28;
else
tage = 29;
}
else if (monat == 3) tage = 31;
else if (monat == 4) .
. .
/* bis man schwarz wird! */
Beispiel
: Schreibe ein Programm, das für ein gegebenes Jahr für die verschiedenen Monate die Anzahl der Monatstage solange ausgibt, bis als Monat eine 0 eingeben wird.switch (monat) {
case 1: tage=31;break; case 3: tage=31;break;
case 5: tage=31;break; case 7: tage=31;break;
case 8: tage=31;break; case 10: tage=31;break;
case 12: tage=31;break;
case 4: tage=30; break; case 6: tage=30;break;
case 9: tage=30; break; case 11: tage=30;break;
case 2:
if (jahr % 4 != 0) tage = 28;
... /* wie vorhin */
break;
/* optional */
default: printf("Fehler"); break;
}
besser
switch-Anweisung:
noch besser:
switch (monat) {
case 1: case 3: case 5:
case 7: case 8: case 10:
case 12: tage = 31; break;
case 4: case 6: case 9:
case 11: tage = 30; break;
case 2:
if (jahr % 4 != 0) tage = 28;
... /* wie vorhin */
break;
/* optional */
default: printf("Fehler"); break;
Datenverarbeitung 8. Datentypen und Operationen
8. Elementare Datentypen und Operationen
Ziele dieser Einheit:
- Sinn und Zweck der Typisierung von Variablen - Eigenschaften einfacher, skalarer Datentypen,
Operationen
- Typumwandlung
- Präzedenzrelation von Operatoren
- C-spezifische Syntax von Operatoren
8.1.1 Numerische, ganzzahlige Datentypen
-short int (short): kurzer Ganzzahl-Typ (Minimum: 16 Bit) Wertebereich: -32768 .. 32767
Deklaration, Beispiele: short zhl1, zhl2; short int zhl1=1,zhl2=3;
Operationen: Ein-/Ausgabe, arithmetische Operationen, Verwendung in arithmetischen/booleschen Ausdrücken
Arithmetische + (Addition), - (Subtraktion), * (Multiplikation), Operationen: / (ganzzahlige Division),
8.1 Einfache, skalare Datentypen
Bemerkung: Wertebereiche der behandelten Datentypen können je nach Compiler/Prozessor von den hier angegebenen Werten abweichen!
Datenverarbeitung 8. Datentypen und Operationen
-int: typische Ganzzahl (Minimum: 16 Bit, hier: 32 Bit);
Wertebereich: -2147483648 .. 2147483647
Deklaration, Beispiele: int zhl1, zhl2; int zhl1=1, zhl2=3;
Operationen: analog zu short Arithm. Operationen: analog zu short
-long int (long): langer Ganzzahl-Typ (Minimum: 32 Bit, hier: 32 Bit) Wertebereich: -2147483648 .. 2147483647
Deklaration, Beispiele: long zahl; long zahl2 = 1213;
Operationen: analog zu short/int Arithm. Operationen: analog zu short/int
- Zahlen können oktal, dezimal und hexadezimal dargestellt werden:
37 dezimal
037 oktal (führende 0), entspricht 31 (dezimal)
0x37 hexadezimal (führende 0x), entspricht 55 (dezimal)
#include <stdio.h>
void main( ) {
/* Umwandlung von zahl in seine Binärdarstellung */
/* Ausgabe fängt mit der letzten Stelle an */
int zahl = 253366;
short rest;
while (zahl > 0) {
rest = zahl % 2; /* Modulo */
printf("%d\n", rest);
zahl = zahl / 2; /* ganzzahlige Division */
}
Beispiel:
Datenverarbeitung 8. Datentypen und Operationen
- float Gleitpunktzahl einfacher Genauigkeit (32 Bit)
Wertebereich: -3.4*10^38 .. 3.4*10^38, 6 Stellen Genauigkeit Deklaration, Beispiele: float zhl1, zhl2;
Operationen: Ein-/Ausgabe, arithmetische Operationen, Verwendung in arithmetischen/booleschen Ausdrücken
Arithmetische
Operationen: + (Addition), - (Subtraktion), * (Multiplikation), / (Division)
- double Gleitpunktzahl mittlerer Genauigkeit (64 Bit)
Wertebereich: -1.7*10^308 .. 1.7*10^308, 15 Stellen Genauigkeit Deklaration, Beispiele: double pi = 3.1415; double zahl;
8.1.2 Gleitpunktzahlen
- long double Gleitpunktzahl hoher Genauigkeit (80 Bit)
Wertebereich: -3.4*10^4932 .. 3.4*10^4932, 17 Stellen Genauigkeit Deklaration, Beispiele: long double zahl; long double e = 2.718;
- Schreibweisen für Gleitpunktzahlen:
Dezimalpunkt 1.234 .5 -16.
-- Suffix f/F für float, double 234.f (nicht 234f) -- Suffix l/L für long double 13.L 14.l
mit Exponent e/E zur Basis 10 .314e2 ( = 0.314*10^2 = 31.4)
.314e-2 ( = 0.314*10^-2 = 0.00314)
Datenverarbeitung 8. Datentypen und Operationen
#include <stdio.h>
int main( ) {
float flaeche, radius;
float pi = 3.14159;
scanf("%f", &radius);
flaeche = pi * radius * radius;
printf("Radius %f, Flaeche: %f \n", radius, flaeche);
}
Beispiel:
- char
- 1 Zeichen – Größe: 1 Byte (nicht zwingend acht Bit)
- numerische Interpretation möglich, Wertebereich -128 .. 127 - Beispiele:
char ch1=‘Q‘, ch2=‘\n‘;
char char3=65; /* (hexadez.: 41) entspricht ‘A‘ – s. ASCII-Code */
- signed/unsigned
- Typen mit/ohne Vorzeichen (anwendbar auf Integer- und Character-Typen) - unsigned short: 0 .. 65535 (16 Bit)
- unsigned int: 0 .. 4294967295 (32 Bit) - unsigned long: 0 .. 4294967295 (32 Bit)
8.1.3 Alphanumerische und sonstige Datentypen
Datenverarbeitung 8. Datentypen und Operationen
- Problem: Anwendung eines Operators auf verschiedene Typen.
Beispiel: int anzahl = 4;
float preis = 10.34;
float gesamtPreis = anzahl * preis;
- Bei einer Berechnung (rechte Seite der Zuweisung) mit verschiedenen Datentypen findet eine automatische Typumwandlung an den stärkeren Typen statt.
- Dabei gilt folgende Hierarchie (a<b : b dominiert a):
char < int < long < float < double < long double
- Bei einer Wertzuweisung erfolgt automatisch eine Anpassung an den Typ der linken Seite der Zuweisung.
Beispiel: float fl1 = 2.34; float fl2 = 4.56;
int zahl;
zahl = fl1 * fl2; /* zahl enthält den Wert 10 */
/* und nicht 10.6704. Das bedeutet: */
8.2 Typumwandlung
- Mit einem cast-Ausdruck ist es möglich, den Typ eines Operanden zu ändern.
- Beispiel:
/* Deklaration und Initialisierung */
int zhl1=1, zhl2=4;
double dbl;
/* Anweisung1: */
dbl = (double)zhl1 /zhl2; /* dbl erhaelt den Wert 0.25 */
/* zhl1 explizit, zhl2 automatisch nach double umgewandelt und */
/* eine Fließkomma-Division durchgeführt. */
Explizite Typumwandlung (cast)
Datenverarbeitung 8. Datentypen und Operationen
#include <stdio.h>
void main() {
float radius, inhalt, umfang;
scanf("%f", &radius);
inhalt = radius * radius * 3.14159;
umfang = 2 * radius * 3.14159;
printf("Radius: %f \n", radius);
printf("Kreisinhalt: %f \n", inhalt);
printf("Kreisumfang: %f \n", umfang);
}
8.3 Konstanten ( const )
float pi = 3.14159;
Pi
pi const float pi = 3.14159;
- In C ist kein spezieller Datentyp für Wahrheitswerte (true/false) verfügbar, wie in anderen Sprachen: z.B. bool (C++) oder boolean (Pascal, Java).
- Jeder der Grundtypen kann dafür (als boolean) verwendet werden.
- Jeder Ausdruck ist logisch interpretierbar.
- Interpretation:
Falsch: Ausdruck hat den Wert 0.
Wahr: Ausdruck hat anderen Wert als 0.
- Logische Operatoren liefern immer 0 oder 1.
- n sei ganzzahlige Variable: Dann kann !(!(n))sich von (n) unterscheiden!
- Short-circuit Auswertung logischer Ausdrücke, d.h. Auswertung nur solange bis Ergebnis des Ausdrucks feststeht:
8.4 Boolesche Ausdrücke
Datenverarbeitung 8. Datentypen und Operationen
#include <stdio.h>
void main( ) {
int zahl1, zahl2;
int istKleiner;
scanf("%d %d", &zahl1, &zahl2);
istKleiner = (zahl1 < zahl2);
if (istKleiner)
printf("Zahl1 ist kleiner als Zahl2\n");
else
printf("Zahl1 ist nicht kleiner als Zahl2\n");
}
Beispiel:
-enum
Aufzählungstyp, der es ermöglicht, Konstanten einen Integer-Wert zuzuweisen und diese im Programm über Bezeichner anzusprechen.
8.5 Selbstdefinierter Aufzählungs-Datentyp
/* Deklaration der Variablen stuermig mit dem Typ Jahreszeiten */
enum Jahreszeiten stuermig; Beispiele:
/* Definition des Aufzaehlungstyps Jahreszeiten */
enum Jahreszeiten {Fruehjahr, Sommer, Herbst, Winter, Karneval};
/* Zuweisung eines Wertes an die Variable */
stuermig = Herbst;
Datenverarbeitung 8. Datentypen und Operationen
#include <stdio.h>
int main( ) {
enum ampel {rot, gelb, gruen, rotgelb};
enum ampel ampelPhase = rot;
...
switch (ampelPhase) {
case rot:
case gelb:
printf("Anhalten\n");
break;
case rotgelb:
case gruen:
printf("Gas geben\n");
}
Beispiel:
- Erstmaliger Zugriff im Programm auf Variablen sollte schreibend sein.
- Lebensdauer: Variablen existieren nur eine bestimmte Zeit im Speicher.
- Sichtbarkeit: innerhalb desselben Programms unterschiedliche Verfügbarkeit.
8.6 Variablen und ihre Sichtbarkeit
Sichtbarkeitsregeln:
- Globale Variablen
außerhalb aller Blöcke definiert
in allen Blöcken sichtbar - Lokale Variablen
innerhalb eines Blocks definiert, gültig ab Definitionsstelle
in tieferliegenden Blöcken sichtbar
Verdeckung durch Definitionen gleichnamiger Variablen in
Datenverarbeitung 8. Datentypen und Operationen
Beispiel: ...
int zhl = 10;
if (zhl != 0)
{ /* neuer Block */
printf("%d \n", zhl); /* Ausgabe: 10 */
int zhl = 20;
printf("%d \n", zhl); /* Ausgabe: 20 */
}
printf("%d \n", zhl);
...
} {
{...
} {...
} {...
}
Verdeckung der äußeren zhl Sichtbarkeit/Gültigkeit
/* Ausgabe: 10 */
- +=, -=, *=, /= usw.
Zuweisungsoperatoren - ++, --
Inkrement- und Dekrementoperatoren (als Prefix- und Postfix- Operatoren möglich)
- &&, ||, !
Logische Verknüpfungen AND, OR, NOT - ?
Fragezeichen-Operator - &, ^, |, ~
Bit-Operatoren AND, XOR, OR, Komplement
8.7 C- spezifische Syntax gängiger Operatoren ( Übersicht )
Datenverarbeitung 8. Datentypen und Operationen
- Vergleich: >, <, >=, <=
- Un-/Gleichheit: !=, ==
- logisches AND, OR, NOT: &&, ||, !
- Fragezeichen-Operator: abkürzende Schreibweise - Beispiel:
if (m > n) res = m;
else
res = n;
8.7.1 Logische Operatoren und Fragezeichen-Operator
ist gleichwertig zu
res = (m > n) ? m : n;
- Operatoren zum In-/Dekrementieren von Variablen:
++ : Inkrementieren, d.h. Addition von 1 zum Operanden
-- : Dekrementieren, d.h. Subtraktion von 1 vom Operanden - als Präfix- und Postfix-Operatoren möglich.
- Beispiel:
++idx inkrementiert idx bevor der Wert genutzt wird. Die Wirkung ist:
idx = idx+1; res = idx; ...
idx++ inkrementiert idx nachdem der Wert genutzt wurde. Die Wirkung ist:
res = idx; idx = idx+1; ...
8.7.2 Verkürztes In- und Dekrementieren, Zuweisen
- Beispiel: int res;
int idx = 5;
int res;
int idx = 5;
Datenverarbeitung 8. Datentypen und Operationen
8.8 Exkurs: Bitweise Operatoren
& Bitweises AND
| Bitweises OR
^ Bitweises XOR
<< Links-Shift
>> Rechts-Shift
~ Bitweises Komplement
a b a AND b a OR b a XOR b NOT a
false false false false false true
false true false true true true
true false false true true false
true true true true false false
- <<, >> Bitweise Shiftoperatoren (Verdoppeln/Halbieren) dienen zur bitweisen Manipulation auf den Typen char, short, int , long
- Beispiel 2:
int idx = 57;
idx = idx << 3; /* Shift um 3 Stellen nach links */
/* idx hat Wert 456 = 57*2^3 bzw. binär 111001000 */
- Beispiel 1:
int idx = 12345;
idx = idx & 127;
/* idx ist 57 */
Datenverarbeitung 8. Datentypen und Operationen
( ) [ ] -> . Linksassoziativ
! ~ ++ + - * & (type) sizeof Rechtsassoziativ
* / % Linksassoziativ
+ - Linksassoziativ
<< >> Linksassoziativ
< <= > => Linksassoziativ
== != Linksassoziativ
& Linksassoziativ
^ Linksassoziativ
| Linksassoziativ
&& Linksassoziativ
|| Linksassoziativ
? : Rechtsassoziativ
= += -= *= /= %= usw. Rechtsassoziativ
, Linksassoziativ
… oder kurz:
8.9 Vorrang-Regeln (Präzedenz) der Operatoren (Übersicht)
abnehmend
9. Strukturierte Datentypen
Ziele dieser Einheit:
- Realisierung strukturierter Datentypen array (Feld), structure , union.
- Problemangepasste, selbst definierte Datenstrukturen.
- Schaffung geeigneter Operationen darauf.
- Damit: Erster Schritt in Richtung Datenabstraktion bzw.
Abstrakte Datentypen.
Datenverarbeitung 9. Strukturierte Datentypen
9.1 Felder (arrays)
Beispiel: Identifizierung der Kleiderhaken in einer Garderobe int haken0,haken1,....,haken12345;
...schreiben, schreiben!
oder durch den Aufzählungstyp
enum garderobe {haken1, haken2, ..., haken12345};
zu umständlich!
besser:
int garderobe [12346];
Syntaxdiagramm (nicht vollständig)
]
[ Größe
Bezeichner Datentyp
= Initialisierung ;
Beispiele:
int zahl[10];
Datenverarbeitung 9. Strukturierte Datentypen
- Felder aller Datentypen sind möglich.
- Bei der Definition genügt die Größenangabe (Inhalt der Einträge ist dann aber undefiniert).
float feld[10];
int vector[4];
- Schreib-/Lesezugriffe auf die Einträge erfolgen über einen Index.
Beispiel: for (idx=0; idx<4; idx++) vektor[idx] = 0;
vektor[2] = 3;
printf(“%d \n“, vektor[0]);
- Der Index läuft von 0 bis (Größe -1).
Bemerkungen (1)
Bemerkungen (2)
- Zugriffe auf Indizes, die nicht zwischen 0 bis (Größe -1) liegen, sind undefiniert!
Beispiel: int a [10];
a[10] = 3; /* falsch! */
a[10]
Überlauf (overflow)!
a[9]
a[1] ...
a:
a[0] ...
- Fehler dieser Art können zu undefinierten Verhalten/Abstürzen des
Datenverarbeitung 9. Strukturierte Datentypen
- Mehrdimensionale Felder durch Hinzunahmen weiterer Indizes.
matrix:
[0][0] [0][1]
[1][0] [1][1]
[2][0] [2][1]
[3][0] [3][1]
2 3 9 2 3 5 6 8
Beispiel:
int matrix [4][2] = {{2,3},{9,2},{3,5},{6,8}};
int zl, sp;
for (zl=0; zl<4; zl++) {
for (sp=0; sp<2; sp++)
printf("%d ", matrix[zl][sp]);
printf("\n");
}
Ausgabe:
2 3 9 2 3 5 6 8
Beispiel: Lies eine Serie von Roulette-Ergebnissen und schreib die Häufigkeit der einzelnen Zahlen.
#include <stdio.h>
const int kleinstzahl = 0;
const int groesstzahl = 36;
void main() {
int gruentisch [37];
int zahl, idx; /*Als Nächstes: Initialisierung */
for (idx=kleinstzahl; idx <= groesstzahl; idx++) { gruentisch[idx] = 0; } do /* Eingabe bis ...(s. while ...) */
{
scanf("%d", &zahl);
if ((kleinstzahl <= zahl) && (zahl <= groesstzahl)) gruentisch[zahl] = gruentisch[zahl] + 1;
}
while (zahl >= 0); /* Abbruch durch Eingabe einer negativen Zahl*/
Datenverarbeitung 9. Strukturierte Datentypen
9.2 Zeichenketten (strings) als Felder von Zeichen
H a l l o \0
-char-Felder sind als Zeichenketten (strings) interpretierbar.
- Strings werden durch \0 terminiert:
Beispiel: “Hallo“ entspricht:
- Spezielle Bibliotheken in C zur Stringverarbeitung stehen zur Verfügung.
Beispiel:
int idx;
char gruss[6] = “Hallo“; /* mindest. 6 Einträge nötig */
char fluch[6] = “Mist“; /* mindest. 5 Einträge nötig */
for(idx=0; idx<6; idx++)
fluch[idx]=gruss[idx]; /* Kopie */
printf(“%s %s \n“, gruss, fluch);
/*Ausgabe: Hallo Hallo */
9.3 Strukturen mit struct
- Zusammenfassen/Gruppieren unterschiedlicher Datentypen
/* oder auch */
struct complex Beispiel: struct complex
struct Strukturname { Datentyp Bezeichner ,
}
Bezeichner ,
;
;
Datenverarbeitung 9. Strukturierte Datentypen
Definition von Strukturen, Variablen
Vier (4) Möglichkeiten:
- Nur Angabe des Strukturnamens:
struct complex {float re, im;};
Variablendefinition später: struct complex cmpl_zhl;
- Angabe von Strukturname + Variable(n)
struct complex {float re, im;} cmpl_zhl;
- Angabe keines Strukturnamens, nur Variable(n) (es können dann keine weiteren Variablen des Typs definiert werden)
struct {float re, im;} cmpl_zhl;
- Angabe keines Strukturnamens, keiner Variable (sinnlos, aber möglich)
struct {float re, im;};
- Die Komponente einer Struktur wird mit dem Punktoperator „
.
“ angesprochen.- Dabei ist der linke Operator die Strukturvariable und der rechte Operator der Komponentenname.
Beispiel: cmpl_zhl.re cmpl_zhl.im
- Wird der Name des Strukturtyps im weiteren Programm nicht benötigt, kann er auch entfallen.
Beispiel: struct {
double re, im;
} cmpl_zhl1, cmpl_zhl2;
- Verschachtelung von Strukturen möglich
Bemerkungen zu struct
Datenverarbeitung 9. Strukturierte Datentypen
Beispiel:
#include <stdio.h>
void main( ) {
struct {
unsigned short tag;
unsigned short monat;
unsigned int jahr;
} datum;
datum.tag = datum.monat = datum.jahr = 0;
scanf(" %d %d %d", &datum.tag, &datum.monat, &datum.jahr);
printf("Tag: %d Monat: %d Jahr: %d \n",datum.tag, datum.monat, datum.jahr);
Beispiel:
Eine Kunden-Kartei, bestehend aus Kunden-Daten mit folgender Struktur
KUNDE_n KUNDE_1
NAME VORNAME KONTO-
NR. KONTO- STAND
KUNDE_2
NAME VORNAME KONTO-
NR. KONTO- STAND
KUNDE_3
NAME VORNAME KONTO-
NR. KONTO-
STAND ...
Man kann sich die Kartei auch wie einen Karteikasten vorstellen, der mit Informationen gleicher Struktur (Karteikarten) gefüllt ist.
KUNDE n
KUNDE_3
struct Kunde {
char Vorname [20];
char Name [20];
Datenverarbeitung 9. Strukturierte Datentypen
9.4 Strukturen mit union
- Vereinigungstyp, in dem zur Laufzeit nur eine Komponente sinnvolle Werte besitzt.
union Strukturname { Datentyp Bezeichner ,
}
Bezeichner ,
;
;
Beispiel: union wert {
float gleitpunkt;
int ganzZahl;
char zeichen;
Bemerkungen zu union:
- Benutzung entspricht syntaktisch dem Konstrukt struct.
- Die Größe (=Speicherplatzverbrauch/-aufwand) eines Vereinigungstyps entspricht der Größe der aufwendigsten Komponente.
- Der Programmierer trägt die Verantwortung, dass jeweils der „richtige Typ“ benutzt wird.
Beispiel: union wert zahl;
zahl.ganzZahl = 7;
...
float zahl1 = zahl.gleitPunkt;
/* nicht gut… */
Datenverarbeitung 9. Strukturierte Datentypen
9.5 Einfache selbstdefinierte Datentypen
Definition von Variablen:
bisher:
Bezeichner Datentyp
; ,
Alias auch möglich:
Datentyp
typedef ;
typedef int Length;
Length laenge;
Beispiel:
Bemerkungen zu typedef:
- typedef erlaubt die Erzeugung eines Alias für existierende Typen.
Beispiel: typedef int ganzzahl;
ganzzahl zahl = 4;
- mittels typedef werden keine neuen Datentypen erzeugt.
- Sinn:
Portabilität: Maschinenabhängige Datentypen müssen unter
Benutzung von typedef bei einer Portierung nur einmal geändert werden.
Erhöhung der Verständlichkeit des Quellcodes.
Datenverarbeitung 10. Zeiger-Datentyp