Prof. Dr. Malte Schilling
Autonomous Intelligent Systems Group
char, short, int, long, long long
, Bsp.: int i = 42;
float, double, long double
, Beispiel: double d = 42.23;
char
, Beispiel: char c = ’a’;
bool
, Beispiel: bool b = true;
<string>
an. int i;
) oder direkt mit einem Wert initialisiert werden (int i = 42; float f = randomFloat();
).auto
verwendet werden:
auto i = 42;
auto f = randomFloat();
Ein Datentyp ist eine Menge von Werten zusammen mit einer Menge von Operationen, die auf diesen Werten definiert sind.
Alles mit einem Namen (Variablen, Funktionen, etc.) erhält einen Typ vor der Laufzeit.
Allem, was einen Namen hat (Variablen, Funktionen, etc.), wird zur Laufzeit einen Typ zugewiesen basierend auf dem aktuellen Zustand und Wert.
Quellcode: Der ursprüngliche Code, der normalerweise von einem Menschen in einen Computer eingegeben wird.
Übersetzung: Die Umwandlung von Quellcode in etwas, das ein Computer verstehen kann (d.h. Maschinencode).
Hauptunterschied: Wann wird der Quellcode übersetzt?
Zeitraum, in dem das Programm Befehle ausführt (gegebenenfalls nach Kompilierung).
Beim Übersetzen in ein ausführbares Programm werden folgende Schritte durchgeführt:
#include
).Crash während der Laufzeit – kann eine Zeichenkette nicht dividieren.
Kompilierfehler: Dieser Code wird nie ausgeführt.
returns "101010"
Kompilierfehler: “10” ist ein String! Dieser Code würde nicht laufen können.
returns "103"
Kompilierfehler: “10” ist ein String!
… hilft uns dabei Fehler zu verhindern, bevor unsere Code läuft.
int i = 5.6;
In C++ gibt es verschiedene Cast-Operatoren mit abgestufter Wirkungsweise:
static_cast
= Operator erlaubt Konvertierungen zwischen Basisdatentypen sowie zwischen Klassen, die durch öffentliche, nicht-virtuelle Ableitung auseinander hervorgehen.dynamic_cast
= Hiermit werden Zeiger oder Referenzen auf miteinander verwandte polymorphe Klassen – und nur solche – konvertiert.const_cast
= Dient ausschließlich dazu, die Konstantheit eines Typs zu entfernen. Dies ist ein schwerwiegender und potentiell fehlerträchtiger Vorgang.reinterpret_cast
= Dieser Operator konvertiert beliebige Zeigertypen ineinander, unabhängig vom Verwandtschaftsgrad, und konvertiert alle Zeiger in ganzzahlige Typen und umgekehrt.const_cast
reinterpret_cast
auto
Schlüsselwort verwendet anstelle eines Typen, wenn eine Variable deklariert wird. Dies teilt dem Compiler mit, dass er den Typ selbst bestimmen muss.
auto
bedeutet natürlich nicht, dass die Variable keinen Typ hat. Es drückt nur aus, dass der Typ vom Compiler hergeleitet wird.
Faustregel: AAA-Style (Almost Always Auto) nutzen.
Kontrollstrukturen erlauben …
if-else
Programm gibt x false
aus. Zu welchem if
gehört das else
?
Besser: Explizite Blöcke benutzen!
while
-SchleifenAnweisung wird wiederholt ausgeführt, solange Bedingung zutrifft (also zu true
ausgewertet wird).
while
while
true
istwhile
wird hier Anweisung mindestens einmal ausgeführtdo while
do-while
Der große Bruder der while
-Schleife …
for
bool
konvertierbar seinfor
-Schleifenoder besser …
break
-AnweisungDie Anweisung break
verursacht, dass der aktuelle (innerste) for
, while
oder case
-Block verlassen wird.
continue
-Anweisungfor
, while
und do-while
-Schleifenswitch-case
bool, char, wchar_t,(un)signed int,short
)C1
, C2
,… müssen konstante Ganzzahl-Ausdrücke sein.case
Marke Variablen deklariert werden, so muss die Anweisungsliste in einem Block zusammengefasst werden (Grund: nicht klar definierbare Lebenszeit des Variablenbezeichners)switch-case
Beispielswitch-case
-Anmerkungenbreak
optional ist kann es hierzu zu Überspringen von Variablendeklarationen kommen!Lösung: lokale Blöcke benutzen
goto
-AnweisungExceptions
(später)goto
MARKE
und goto MARKE
müssen in der gleichen Funktion stehensetjmp
und longjmp
(verfügbar durch den Header <csetjmp>
)try-catch
-Blöcken und dem throw
-Operator (später)Soll eine Funktion keinen Wert zurückgeben, muss der Typ void
vereinbart werden (return
darf dann entfallen).
Beim Aufrufen einer Funktion werden die Werte der Parameter als Kopie übergeben (call by value).
Ausnahme: Arrays (und damit auch „Strings“) – mehr dazu später.
int max( int , int );
– hier dürfen die Namen der Parameter entfallen.Warum gibt es die Unterscheidung zwischen Definition und Deklaration?
Eine Besonderheit in C im Vergleich zu anderen C-ähnlichen Sprachen:
void
als Parameterliste anzugeben.Funktionsparameter werden in C und C++ immer by value übergeben, d. h.:
Was ist, wenn wir zwei Versionen einer Funktion für zwei verschiedene Typen haben wollen?
Beispiel: int-Division und double-Division
Definiere zwei Funktionen mit demselben Namen, aber unterschiedlichen Typen.
Im jupyter-book sind verschiedene Aufgaben angegeben:
Hierüber kann dann direkt auf dem Hub eine Umgebung gestartet werden, in der C++ interpretiert wird (wird die ersten Termine genutzt):
Struct
– Repräsentationen in C und C++Vorgehensweise:
Ziel: Formales Modell, das von einer Maschine bearbeitbar ist.
Ziel: Maschinelle Verarbeitung von Informationen. Verarbeitung nur möglich auf Basis von Repräsentationen (Schwerpunkt Woche 2).
Ziel der Modellbildung: Lösung eines Problems.
Festlegen (durch Spezifikation), welches Ziel gelöst werden soll.
Logische Strukturen, Graphen, Grammatiken, Automatenmodelle, Petri-Netze, UML-Diagramme …
In fünf Schritten
Durchführen von 1. Datenanalyse und Datendefinition: Überprüfen, ob die Aufgabenstellung die Verwendung von Strukturen nahe legt.
Struct
sstruct
können mehrere Datentypen zu einem neuen Datentypen zusammengefasst werden.struct
gehört immer zum Namen der Struktur!
typedef
kann direkt bei der Deklaration einer Struktur ein „einfacherer“ Name vereinbart werden:Structs können innerhalb von Structs verwendet werden (wie Klassen in z.B. Java).
Die Größe eines Structs kann wie für primitive Datentypen abgefragt werden:
Aber: sizeof ( struct )
\(\neq\) Summe der Größe der einzelnen Bestandteile/ Member:
Im jupyter-book sind verschiedene Aufgaben angegeben:
Hierüber kann dann direkt auf dem Hub eine Umgebung gestartet werden, in der C++ interpretiert wird (wird die ersten Termine genutzt):
Erinnerung: Eine Variable ist ein Name für eine Stelle im Speicher, an der ein Datum abgespeichert wird.
&
erfahren:Eine Variable, die die Adresse einer anderen Variable speichert (auf sie zeigt), nennt man einen Zeiger (engl. Pointer).
int
-Variable hat den Typ int*
.Allgemein: Ein Pointer auf eine Variable vom Typ T
hat den Typ T*
.
*
kann auf das gespeicherte Datum zugegriffen werden.&
.NULL
– „Zeiger ins Nichts“.*
.T
ist T*
.ptr[i] == *(ptr+i)
T[]
als Parameter an eine Funktion, „verfällt“ der Typ zum entsprechenden Pointer T*
(Array Decaying).T
ist T&
.Im jupyter-book sind verschiedene Aufgaben angegeben:
Hierüber kann dann direkt auf dem Hub eine Umgebung gestartet werden, in der C++ interpretiert wird (wird die ersten Termine genutzt):
NULL
-PointerDer spezielle Wert NULL
kann einem Pointer zugewiesen werden, wenn dieser ins „Nichts“ zeigen soll:
nullptr
ist ein spezieller Zeigerwert, der seit C++11 eingeführt wurde und explizit auf keine gültige Speicheradresse zeigt (vorher einfach 0
verwendet).
NULL
ist definiert in stdio.h
).
*
vor jedem definierten Pointer stehen.const
const
mehrere Bedeutungen:
int const* ip;oder const int* ip;
int* const ip;
const int* const ip;
Der falsche Umgang mit Pointern ist eine der Hauptfehlerquellen in C!
Pointer erlauben unkontrollierten Zugriff auf beliebige Speicherstellen.
Addition einer Konstanten erhöht einen Pointer um ein Vielfaches der Größe des Basisdatentyps (Pointerarithmetik).
→ Pointerarithmetik: Auf diesem System hat char
die Größe 1 und int
die Größe 4.
T
ist T&
.Pointer | Referenzen | |
---|---|---|
Initialisierung | int* ip = &i; |
int& ir = i; |
Zugriff lesend | *ip |
ir |
Zugriff schreibend | *ip = 5; |
ir = 5; |
Kann ins „Nichts“ zeigen | Ja | Nein |
Kann neu zugewiesen werden | Ja | Nein |
Beispiel:
Es gibt dabei zwei wesentliche Dinge zu beachten:
[]
-Operator zugegriffen werden.ptr[i] == *(ptr+i)
Im jupyter-book sind verschiedene Aufgaben angegeben:
Hierüber kann dann direkt auf dem Hub eine Umgebung gestartet werden, in der C++ interpretiert wird (wird die ersten Termine genutzt):
&
.NULL
– „Zeiger ins Nichts“.*
.T
ist T*
.ptr[i] == *(ptr+i)
T[]
als Parameter an eine Funktion, „verfällt“ der Typ zum entsprechenden Pointer T*
(Array Decaying).T
ist T&
.Pointer | Referenzen | |
---|---|---|
Initialisierung | int* ip = &i; |
int& ir = i; |
Zugriff lesend | *ip |
ir |
Zugriff schreibend | *ip = 5; |
ir = 5; |
Kann ins „Nichts“ zeigen | Ja | Nein |
Kann neu zugewiesen werden | Ja | Nein |
const
bei Pointern und Referenzenconst
sollte bei Pointern und Referenzen verwendet werden, wenn auf ein Argument nur lesend zugegriffen wird:Erinnerung: Bei Pointern hat das Schlüsselwort const
mehrere Bedeutungen:
int const* ip; oder const int* ip;
int* const ip;
const int* const ip;
new
und delete
new
und delete
.new
erwartet einen Typ sowie (optional) Argumente für den Konstruktor und gibt einen Pointer des angegebenen Typs zurück: Widget* w = new Widget{args};
delete
ruft den Destruktor des angegeben Pointers auf und gibt den entsprechenden Speicherbereich frei: delete w;
new[]
.delete[]
freigegeben werden: Widget* widgets = new Widget[10]; ... delete[] widgets;
Das Vermischen von new
und delete[]
sowie new[]
und delete
ist verboten!