Grundlagen für das Projekt (Teil 1)

Website: Lerne ELEKTROTECHNIK und PROGRAMMIEREN
Kurs: Kurs zum Buch "Lerne Programmieren mit Projekten: C++"
Buch: Grundlagen für das Projekt (Teil 1)
Gedruckt von: Gast
Datum: Thursday, 21. November 2024, 12:15

Beschreibung



1. Worum geht es?

Herzlich willkommen zu diesem Kapitel, das sich mit Zeigern und Referenzen, Funktionen und Templates beschäftigt. Du hast schon einiges über die grundlegenden Konzepte der Programmierung gelernt, aber jetzt ist es an der Zeit, einen tieferen Einblick in die mächtigen Werkzeuge zu bekommen, die dir in C++ zur Verfügung stehen.

In diesem Kapitel wirst du lernen, wie du mit Zeigern und Referenzen arbeiten kannst. Wir werden uns auf den Adress-Operator & und den Dereferenzierungs-Operator * konzentrieren und sehen, wie du damit Speicherplatz in deinem Programm verwalten kannst. Du wirst auch lernen, wie Zeiger in einer Liste gespeichert werden können.

Danach gehen wir auf Funktionen ein. Du wirst lernen, wie Daten an Funktionen übergeben werden können, entweder durch Call-By-Value oder Call-By-Reference, und wie Zeiger als Argumente übergeben werden können. Außerdem werden wir sehen, wie Funktionen Werte zurückgeben können.

Zum Abschluss werden wir uns mit flexiblen Datentypen beschäftigen. Du wirst lernen, wie Templates verwendet werden können, um Datentypen in Funktionen zu verwenden, ohne dass sie im Voraus definiert werden müssen.

2. Zeiger und Referenzen

In diesem Kapitel werden wir uns intensiv mit den Konzepten von Zeigern und Referenzen in C++ beschäftigen. Du wirst den Adressoperator kennenlernen und lernen, wie du ihn verwenden kannst, um einen Zeiger auf eine Variable zu erstellen. Außerdem werden wir den Dereferenzierungsoperator kennenlernen, mit dem du auf den Wert einer über einen Zeiger referenzierten Variable zugreifen kannst.

Ein weiterer wichtiger Teil dieses Kapitels wird die Verwendung von Zeigern in einer dynamischen Liste sein. Du wirst sehen, wie du Zeiger effizient in einer Liste speichern und verwalten kannst, um komplexe Datenstrukturen zu erstellen. Bist du bereit, tiefer in die Welt der Zeiger und Referenzen einzutauchen?

2.1. Der Adress-Operator &

Worum geht es? 

Dieses Code-Beispiel demonstriert die Verwendung des Adressoperators in C++. Der Adressoperator (&) gibt die Speicheradresse einer bestimmten Variablen aus.

Was kannst du danach?

  • Verwendung des Adressoperators (&) in C++
  • Ermittlung der Speicheradresse einer Variablen
  • Anwendbarkeit des Adressoperators in eigenen C++-Projekten.


#include <iostream>

using namespace std; 

int main() 
{
    // Ermittlung der Speicheradresse
    int x{5};
    cout << x << endl;  // Wert
    cout << &x << endl; // Adresse
    
    return 0;
}

2.2. Der Dereferenzierungs-Operator *

Worum geht es? 

Dieses Code-Beispiel demonstriert die Verwendung des Dereferenzierungs-Operators in C++. Der Dereferenzierungs-Operator (*) gibt den Wert einer über einen Zeiger referenzierten Variablen aus.

Was kannst du danach?

  • Verwendung des Dereferenzierungs-Operators (*) in C++
  • Zugriff auf den Wert einer über einen Zeiger referenzierten Variablen
  • Verwendung eines Zeigers, um die Adresse einer Variablen zu speichern
  • Veränderung des Wertes einer Variablen über einen Zeiger
#include <iostream>

using namespace std; 

int main() 
{
    // Speicherzugriff mit Zeigern
    char c{'a'};
    char *p_c = &c; // Adresse von c speichern
    *p_c = 'b';
    cout << c << endl;
    
    return 0;
}

2.3. Speicher von Zeigern in einer Liste

Worum geht es? 

Dieses Code-Beispiel zeigt, wie man Zeiger in einem Vektor speichert. Wenn man nur die Adresse einer Variablen in einem Vektor speichert, kann man darauf zugreifen und deren Wert ändern, ohne die tatsächliche Variable direkt zu beeinflussen. Dies kann besonders nützlich sein, wenn man mehrere Variablen verwalten möchte, ohne sie direkt in den Vektor zu kopieren.

Was kannst du danach?

  • Zeiger in einem Vektor speichern
  • Zugriff auf den Wert an der Adresse eines Vektor-Elements


#include <iostream>
#include <vector>

using namespace std; 

int main() 
{
    // Variablen in Vektor speichern
    int l{1}, m{2}, n{3};
    vector<int> v1{l, m};
    v1.push_back(n);

    // Achtung: Vektor erzeugt Kopie
    v1.at(0) = 4;
    cout << v1.at(0) << endl;
    cout << l << endl;
    
    return 0;
}

#include <iostream>
#include <vector>

using namespace std; 

int main() 
{
    // Zeiger in Vektor speichern
    int o{1}, p{2}, q{3};    
    vector<int *> v2{&o, &p, &q};
    *v2.at(0) = 4;
    
    cout << "Adresse: " << v2.at(0);
    cout << " Wert: " << *v2.at(0) << endl;
    cout << "Adresse: " << &o;
    cout << " Wert: " << o << endl;
    
    return 0;
}

3. Funktionen

In diesem Kapitel werden wir uns genauer mit Funktionen beschäftigen. Du wirst lernen, wie du Daten an Funktionen übergeben kannst, indem du Call-By-ValueCall-By-Reference oder Zeiger nutzt. 

Außerdem werden wir uns mit den Möglichkeiten befassen, wie Funktionen Werte zurückgeben können. Zum Abschluss werden wir sehen, wie du mit Templates flexibel Datentypen in deinen Funktionen verwenden kannst.


#include <iostream>
#include <cmath>
#include <vector>
#include <algorithm>

using namespace std; 

int main() 
{
    // Fremde Funktionen aufrufen
    vector<int> numbers{3, 7, 8, 2, 4, 4, 10};
    sort(numbers.begin(), numbers.end());
    for(int elem : numbers)
    {
        cout << elem << ", ";
    }
    cout << endl;

    cout << "Sinus Pi/2 = " << sin(M_PI_2) << endl;
    
    return 0;
}

#include <iostream>
using namespace std;

// Funktion ohne Input und Output
void SayHi()
{
    // Aufruf mehrerer Befehle
    cout << "Hallo ";
    cout << "Welt!";
    cout << endl;
}

int main() 
{
    // Funktion ohne Input und Output
    SayHi();
    
    return 0;
}

3.1. Datenübergabe mit Call-By-Value

Worum geht es? 

Dieses Code-Beispiel zeigt die Übergabe von Variablen an eine Funktion mithilfe der Methode "Call-by-Value". In C++ kann man Werte entweder direkt übergeben (Call-by-Value) oder per Referenz (Call-by-Reference). Beim Call-by-Value wird eine Kopie des Wertes an die Funktion übergeben, wodurch das Original nicht verändert werden kann.

Was kannst du danach?

  • Werte an eine Funktion als Kopie übergeben


#include <iostream>
using namespace std; 

// Übergabe von Werten (Call-by-Value)
void Increment1(int a, int b)
{
    a = a + b;
    cout << "In der Funktion: a = " << a << endl;
}

int main() 
{
    // Übergabe von Werten (Call-by-Value)
    int a{1};
    Increment1(a, 2);
    cout << "In main: a = " << a << endl;
    
    return 0;
}

3.2. Datenübergabe mit Call-By-Reference

Worum geht es? 

Dieses Beispiel zeigt die Datenübergabe mittels Call-By-Reference in C++. Im Gegensatz zur Datenübergabe mit Call-By-Value (bei welcher nur eine Kopie des Wertes übergeben wird), wird bei Call-By-Reference ein Zeiger auf den ursprünglichen Wert übergeben. Hierdurch kann die Funktion direkt auf den ursprünglichen Wert zugreifen und ihn verändern.

Was kannst du danach?

  • Übergabe von Werten an eine Funktion im Original
  • Verstehen des Unterschieds zwischen Call-By-Value und Call-By-Reference


#include <iostream>
using namespace std; 

// Übergabe von Werten (Call-by-Reference)
void Increment2(int &c, int d)
{
    c = c + d;
    cout << "In der Funktion: c = " << c << endl;
}

int main() 
{
    int c{1};
    Increment2(c, 2);
    cout << "In main: c = " << c << endl;
    
    return 0;
}

3.3. Übergabe von Zeigern

Worum geht es? 

Schließlich kann man auch Zeiger als Übergabeparameter an Funktionen übergeben. Hierbei wird ein Zeiger auf die Originaldaten übergeben, anstatt die Daten selbst. Die Funktion kann nun auf die Daten über den Zeiger zugreifen und sie ändern.

Was kannst du danach?

  • Übergabe von Daten an Funktionen mit Hilfe von Zeigern


#include <iostream>
using namespace std; 

// Übergabe von Zeigern
void Increment3(int *e, int f)
{
    *e = *e + f;
    cout << "In der Funktion: e = " << *e << endl;
}

int main() 
{
    int e{1};
    int *p_e = &e;
    Increment3(p_e, 2);
    cout << "In main: e = " << *p_e << endl;
    
    return 0;
}

3.4. Rückgabewerte

Worum geht es? 

Dieses Kapitel beschäftigt sich mit dem Thema der Rückgabe von Daten aus Funktionen in C++. Hierbei geht es darum, wie man Funktionen so schreibt, dass sie Daten an den Aufrufer zurückgeben. 

Was kannst du danach?

  • Rückgabe von Daten an die aufrufende Seite


#include <iostream>
using namespace std; 

// Rückgabe von Werten
int AddiereInt(int a, int b)
{
    int result = a + b;
    return result;
}

double AddiereDouble(double a, double b)
{
    int result = a + b;
    return result;
}

int main() 
{
    int res1 = AddiereInt(1, 2);
    double res2 = AddiereDouble(1.11, 2.22);
    
    cout << "res1=" << res1 << endl;
    cout << "res2=" << res2 << endl;

    return 0;
}

3.5. Flexible Datentypen mit Templates

Worum geht es? 

Dieses Beispiel zeigt, wie Templates in C++ verwendet werden können, um Funktionen mit flexibler Datentyp-Unterstützung zu schreiben. Die Funktion "Addiere" ist als Template definiert und nimmt zwei Argumente vom Typ T entgegen. Dieser Datentyp wird bei der Verwendung der Funktion explizit angegeben. In diesem Beispiel wird die Funktion sowohl für die Datentypen int und double verwendet.

Was kannst du danach?

  • Verstehen, wie Templates funktionieren
  • Funktionen mit Templates wiederverwendbar und flexibel gestalten


#include <iostream>
using namespace std; 

// Flexible Datentypen
template <typename T>
T Addiere(T a, T b)
{
    T result = a + b;
    return result;
}

int main() 
{
    int res1 = Addiere<int>(1, 2);
    double res2 = Addiere<double>(1.11, 2.22);
    
    cout << "res1=" << res1 << endl;
    cout << "res2=" << res2 << endl;
    
    return 0;
}