C++ Einführung
C++ Variablen im Speicher, Zeiger
C++ Texte etc.
C++ Dateien, Formatierte Ausgabe, ...
C++ OOP
Hilfe / Anhang
Downloads / Links
Partner
Fehler melden / Kontakt
Impressum
|
| Seite: [1] - Drucken |
Inhalt:
Durchatmen - Zusammenfassung des zweiten Teils
| [drucken] |
C++ Speicher und Zeiger
- "Normale" Variablen werden auf dem Stack abgelegt, einem Speicherbereich, der zwar schnell ist, aber nur eine begrenzte Größe besitzt!
- Daher kann man auch Speicher auf dem wesentlich größeren Heap reservieren, nur begrenzt durch physikalisch verfügbaren Speicherplatz... (Auf 32 Bit-Systemen knapp 4 GB Gesamtspeicher)
- Jeder Wert im Speicher hat eine Addresse, die angibt, wo man den Wert im Speicher findet. Dabei handelt es sich um einen hexadezimalen Wert, den man in speziellen Variablen, sog. "Pointern" speichern kann.
//Variable: int test=3; //Pointer für "int"s: int* ptest; //Addresse herausfinden und speichern: ptest=&test; //Nun die Addresse und den Inhalt ausgeben: std::cout << "ptest = " << ptest << std::endl << "*ptest = " << *ptest << std::endl;
Anwendungsgebiete
- "Call-by-reference" beschreibt die Übergabe von Addressen an Funktionen, damit man anschließend den dahinterliegenden Wert verändern kann. Dabei handelt es sich um eine Möglichkeit, Werte aus einer Funktion zurückzugeben!
//Tauschen: (Funktion ohne Fehler) void tausche(int* zahl1, int* zahl2) { int temp=*zahl1; //Die Werte werden getauscht... *zahl1=*zahl2; *zahl2=temp; } //Beispielaufruf: int a=3; int b=5; tausche(&a,&b); //Addressen übergeben!
- Dynamische Reservierung von Speicher:
- Durch "new" kann man Speicher reservieren. Diesen sollte man mit "delete" wieder freigeben!
- Auf diesem Speicher kann man ganz normal Werte ablegen oder damit arbeiten
#include <iostream> int main() { int* pint=NULL; //Pointer definieren pint=new int; //Freien Speicher anfordern... if (pint==NULL) { std::cerr << "err: Kein Speicher!\n"; return -1; } std::cout << "Vorher: " << *pint << std::endl; *pint=5; std::cout << "Nacher: " << *pint << std::endl; delete pint; //Wenn das Programm weiter laufen sollte: // pint=NULL; //Ist hier etwas schwachsinnig, //da das Programm hier endet... ;) return 0; }
Referenzen
- Referenzen sind Aliasnamen für Variablen
- Eine Referenz zeigt quasi auf den gleichen Speicherbereich, wie die Ur-Variable
- Man kann Referenzen nicht wieder neu zuweisen!
- Auch mit Referenzen kann man Call-by-reference implementieren:
//referenzen.cpp #include <iostream> void tausche(int &eins, int &zwei) { int temp = eins; eins = zwei; zwei = temp; } int main() { int a=5; int b=3; std::cout << "Es steht " << a << ":" << b << std::endl; tausche(a,b); //Halbzeit: Schiedsrichter best*****! std::cout << "Es steht " << a << ":" << b << std::endl; return 0; }
Arrays
- Arrays sind Listen von Variablen eines Typs
- Arrays haben eine feste Gröe, können aber ebenfalls dynamisch angelegt werden!
- Du kannst Arrays so anlegen
- Füllen kannst du solch ein Array mit:
array[/*index*/]=/*wert*/; //z.B.: kopfweh[0]=3; //Alternativ: int kopfweh[7]={3,1,1,2,1,31,7};
Auslesen kann man ein Array ebenfalls über den Index in eckigen Klammern! (Beginnt mit "0") Meist wirst du dabei eine Schleife benutzen, die alle Indizes durchiteriert (hier z.B. von 0 bis 6):
for (int i=0; i<=6; i++) { std::cout << kopfweh[i] << std::endl; }
 | Ein Array mit der Größe "[7]" kann genau sieben Elemente enthalten, wobei das erste den Index "[0]" und das letzte den Index "[6]" besitzt! Zugriffe auf zu große oder zu kleine Werte lassen das Programm zum Abstürzen! |
Offsets (Arrays)
Legst du ein Array "int test[]" an, kannst du mittels "std::cout << test;" die Anfangsadresse ausgeben lassen!
Mithilfe der eckigen Klammern kann auf Elemente des Arrays zugegriffen werden. Dabei wird im Speicher um ein Offset weitergezählt (Startaddresse+Offset): OFFSET=sizeof(/*typ*/)*index
Die Ausgabe des Arraynamens ohne eckige Klammern gibt die Startaddresse aus, also die Addresse, an der das Feld im Speicher beginnt! Erhöhst oder veringerst du einen solchen Zeiger, musst du ihn vor dem Aufruf von "delete []" unbedingt wieder auf den Ausgangswert zurücksetzen, da ab der Addresse einfach Größe * sizeof(/*typ*/) Bytes im Speicher freigegeben werden. Zeigt der Zeiger nicht mehr auf die ursprüngliche Addresse, so kann das Programm den Speicher nicht mehr korrekt abgeben und stürzt vermutlich ab...
Mehrdimensionale Arrays
Man kann auch "Arrays in Arrays" speichern, also eine Tabelle erstellen:
//feld2d.cpp #include <iostream> int main() { int feld[2][3]={{1,2,3},{4,5,6}}; for (int i=0; i<2; i++) { for (int j=0; j<3; j++) { std::cout << feld[i][j] << " "; } std::cout << std::endl; } return 0; }

|
| [drucken] |
Ein paar Beispielprogramme
Hier stehen ein paar Programme, die zeigen, was wir schon gelernt haben:
Beliebige Anzahl von Werten einlesen und ausgeben
Um die Werte einlesen zu können, müssen wir zuerst die Anzahl der Werte einlesen! Danach werden alle Werte eingelesen und dann in einer Tabelle ausgegeben, die x Spalten und dementsprechend y Zeilen hat. Dazu werden wir das Programm wie folgt strukturieren:
| einlesen() |
Hier werden die Werte eingelesen und in einem Array abgelegt! |
| ausgeben() |
Hier werden die Werte ausgegeben... |
Die "einlesen()"-Funktion erwartet zwei Parameter:
- Eine Referenz auf einen Pointer auf einen "int" (Pointer wird in "main()" definiert)
- Eine Addresse auf einen Integer, die Größe des Feldes wird hier gespeichert (hätte auch mithilfe von Referenzen gemacht werden können, hier aber zu Demonstrationszwecken mit Zeigern!)
Die "ausgeben()"-Funktion erwartet ebenfalls zwei Parameter:
- Zeiger auf den Beginn des Arrays
- Größe des Feldes
Die main()-Funktion sollte dann so aussehen:
//tabelle.cpp //Ein Programm, das Werte einliest //und als Tabelle ausgibt... #include <iostream> //Prototypen: int einlesen(int* &pf, int* fg); void ausgeben(int* pf, int fg); //main() int main() { int* pfeld=NULL; int feldgroesse; if (einlesen(pfeld, &feldgroesse)==-1) return -1; //Kein Speicher! ausgeben(pfeld, feldgroesse); delete[] pfeld; }
Man sieht, dass ein "int*" angelegt wird und auf "NULL" gesetzt wird. Hier sollen später die Werte liegen. (dynamisches Array)
Vielleicht ist dir aufgefallen, dass die Funktion nichts zurückgibt! Laut C++-Standard ist es erlaubt, dass "main()" nichts zurückgibt!
Auch die Strukturierung ist klar: Alle Operationen liegen in separaten Funktionen! Nun zum Einlesen:
//einlesen() int einlesen(int* &pf, int* fg) { std::cout << "Wie viele Werte willst du einlesen? "; std::cin >> *fg; //Anzahl einlesen! pf=new int[*fg]; //Feld anlegen mit [Anzahl]! if (pf==NULL) { std::cerr << "err: Kein Speicher!\n"; return -1; //Kein Speicher! } std::cout << "Bitte gebe nun " << *fg << " Werte ein:\n"; for (int i=1; i<=*fg; i++) { std::cout << i << "/" << *fg << ": "; std::cin >> pf[i-1]; //Hier werden die Werte im //Array abgespeichert! // (0 bis *fg-1) } std::cout << "Werte wurden eingelesen!" << std::endl; }
Speicher wird reserviert, mit Werten belegt und bei Fehlern wird mir "return -1;" abgebrochen...
Somit noch zum Ausgeben:
void ausgeben(int* pf, int fg) { int spalten,zeilen; std::cout << "Wieviele Spalten soll die Tabelle haben? (1-" << fg << "): "; std::cin >> spalten; zeilen=fg/spalten+fg%spalten; //Aufrunden... for (int i=0; i<zeilen; i++) { for (int j=0; j<spalten; j++) { int index=i*spalten+j; if (index<fg) //Nur vorhandene Werte ausgeben! std::cout << pf[index] << " "; else //den Rest leer lassen... std::cout << " "; } std::cout << std::endl; } }
Auch hier nur simple Schleifen...
Mit ergänzter Fehlerbehandlung bei der EIngabe sieht das ganze Programm dann wie folgt aus:
//tabelle.cpp //Ein Programm, das Werte einliest //und als Tabelle ausgibt... #include <iostream> //Prototypen: int einlesen(int* &pf, int* fg); void ausgeben(int* pf, int fg); std::istream& puffer_weg(std::istream& in); //main() int main() { int* pfeld=NULL; int feldgroesse; if (einlesen(pfeld, &feldgroesse)==-1) return -1; //Kein Speicher! ausgeben(pfeld, feldgroesse); delete[] pfeld; } //einlesen() int einlesen(int* &pf, int* fg) { std::cout << "Wie viele Werte willst du einlesen? "; *fg=0; while (!(std::cin >> *fg;)) //Anzahl einlesen! { std::cerr << "err: Falsche Eingabe!" << std::endl << ">"; puffer_weg(std::cin); } pf=new int[*fg]; //Feld anlegen mit [Anzahl]! if (pf==NULL) { std::cerr << "err: Kein Speicher!\n"; return -1; //Kein Speicher! } std::cout << "Bitte gebe nun " << *fg << " Werte ein:\n"; for (int i=1; i<=*fg; i++) { std::cout << i << "/" << *fg << ": "; while (!(std::cin >> pf[i-1];)) { std::cerr << "err: Falsche Eingabe!" << std::endl << ">"; puffer_weg(std::cin); } //Hier werden die Werte im //Array abgespeichert! // (0 bis *fg-1) } std::cout << "Werte wurden eingelesen!" << std::endl; } void ausgeben(int* pf, int fg) { int spalten,zeilen; std::cout << "Wieviele Spalten soll die Tabelle haben? (1-" << fg << "): "; while (!(std::cin >> spalten;)) { std::cerr << "err: Falsche Eingabe!" << std::endl << ">"; puffer_weg(std::cin); } zeilen=fg/spalten+fg%spalten; //Aufrunden... for (int i=0; i<zeilen; i++) { for (int j=0; j<spalten; j++) { int index=i*spalten+j; if (index<fg) //Nur vorhandene Werte ausgeben! std::cout << pf[index] << " "; else //den Rest leer lassen... std::cout << " "; } std::cout << std::endl; } } std::istream& puffer_weg(std::istream& in) { return in.clear(), in.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); }
Ein paar "Aufgaben" (nicht viel)
Wenn du die Aufgaben lösen kannst, solltest du dieses Kapitel verstanden haben...
- Schreibe ein kleines Programm, welches 3 Zahlen tauscht!
- Füge dem Tabellenprogramm die Option hinzu, die Spalten und Zeilen zu tauschen!
- Schreibe dieses Programm fertig:
//zeigera3.cpp #include <iostream> int main() { int dertolleint; //Hierum dreht sich das Programm! int_einlesen(); int_quadrieren(); int_ausgeben(); int_vorzeichen_aendern(); int_ausgeben(); }
Benutze dazu Zeiger und/oder Referenzen!!!
Wie geht's weiter?
Glückwunsch! Du hast Kapitel 2 absolviert! Jetzt wollen wir uns mit Texteingaben beschäftigen, damit du Namen etc. einlesen kannst! (Ich schwöre das es endlich soweit ist!!!)
|
| Seite: [1] - Drucken |
|