Hilfe bei Pointern und Dynamischen Speicher
-
Schönen guten Tag
Bin gerade dabei für meine C++ Klausur zu büffeln und bin bis jetzt echt gut voran gekommen. Nur bin ich nun bei dem Thema Dynamischer Speicher und Pointern und habe nun echt Schwierigkeiten mir das zu merken.
Wäre eventuell jemanden so gütig mir ein wenig zu helfen ( über Skype zB ).
Würde den denjenigen auch bezahlen
Ihr wisst ja wie das ist, wenn man lernt und bleibt an einem Thema hängen und man verliert langsam die Lust daranLiebe Grüße
-
über skype? stell deine frage doch einfach!
-
Das Problem ist, dass ich viele Fragen habe und einfach ein Verständnis Problem vorliegt
Ich weiß das neuer Speicher per " new " allokiert wird und mit "delete" wieder freigegeben wird.
Ich besitze ein Skript in dem ein Navi Programm erläutert wird step by step...
Konstruktor / Destruktor und alles davor habe ich ohne Probleme verstanden. Aber
nun komme ich zu dem Thema Dynamischer Speicherverwaltung und das hier ist eine
Methode ( Konstruktor ) meiner Klasse und ich verstehe leider nichts mehrCWayPoint::CWayPoint(char* name, double latitude, double longitude) { // Bestimmung der Länge des Namen (ohne strlen) int len=0; if(name!=NULL) { char* pTmp=name; while(*pTmp!=0) { len++; pTmp++; } } if(len==0) m_maxlenName=10; // name ist NULL oder leer (Initialwert setzen) else m_maxlenName=len; // Array-Länge setzen // dynamische Allokation von Speicher (+1 Byte für abschließende 0) m_name=new char[m_maxlenName+1]; // Kopieren des Namens in den neuen Speicherbereich for(m_curlenName=0;m_curlenName<m_maxlenName;m_curlenName++) m_name[m_curlenName]=name[m_curlenName]; m_name[m_curlenName]=0; // String mit 0 abschließen // Initialisierung durch Aufruf der Getter-Methode if(setLatitude(latitude)==false) m_latitude=0.; // Initialisierung mit direkter Prüfung der Zusicherungen if(longitude < -180. || longitude > 180.) m_longitude=0.; else m_longitude=longitude; cout << "erzeuge Wegepunkt@" << this << " (" << m_name << ")" << endl; }
Vorher war es ein string name und nun ein char* ( Warum ? ) und was ist ein char*
ich verstehe irgendwie den ganzen Ablauf nun nicht mehr
mfg
-
Das ist schon echt übel.
-
*char ist ein zeiger auf einen 1B großen speicherbereich, bzw. in diesem fall ein "c-string".
der unterschied zum "string" ist der, dass du hier selbst speicher reservieren und freigeben musst.
-
Ahhh und char* ruft selbst den Desturktor auf ?
Weil habe vorhin mal aus Spaß den Destruktor heraus kommentiert und ich bekomm Fehlermeldungen
-
Ich verstehe diesen Bereich nicht so ganz:
m_name=new char[m_maxlenName+1]; for(m_curlenName=0;m_curlenName<m_maxlenName;m_curlenName++) m_name[m_curlenName]=name[m_curlenName]; m_name[m_curlenName]=0;
Warum wird char [Das in diesen Klammern geschrieben]
Und wie die Schleife an sich funktioniert
-
buu2188 schrieb:
Warum wird char [Das in diesen Klammern geschrieben]
Das ist die Indexschreibweise.
Solltest du von Arrays kennen.buu2188 schrieb:
Und wie die Schleife an sich funktioniert
Du kennst die for-Schleife noch nicht?
Mit der richtigen Einrückung kann man das besser erkennen:
m_name=new char[m_maxlenName+1]; //Neuen Speicher besorgen mit Platz für Terminator for(m_curlenName=0;m_curlenName<m_maxlenName;m_curlenName++) // über die Läge iterieren m_name[m_curlenName]=name[m_curlenName]; // Daten von name nach m_name kopieren //DAs gehört schon nicht mehr zur Schleife m_name[m_curlenName]=0; // C-String-Terminator anhängen
Bitte füge in deinen Code die richtige Einrückung ein. Das kannst du auch editieren.
-
der code ist auf vielen ebenen schlecht. und außerdem falsch. erinnert mich an einen anderen thread hier, in dem es um "schlechtes C++" geht.
ein char* ist ein zeiger auf einen char(-acter) (=ein zeichen, in C/C++-diktion synonym mit: "byte").
char* ist, wie ein std::string intern implementiert ist. CWayPoint baut also im Prinzip einen std::string nach. generell sollte man immer verwenden, was es gibt und nicht ständig das rad neu erfinden - vor allem, weil man dann, so wie hier, fehler machen kann, die zu abstürzen und speicherlecks führen können.aber der reihe nach. erst einmal brauche ich den code richtig eingerückt:
CWayPoint::CWayPoint(char* name, double latitude, double longitude) { // Bestimmung der Länge des Namen (ohne strlen) int len=0; if(name!=NULL) { char* pTmp=name; while(*pTmp!=0) { len++; pTmp++; } } if(len==0) m_maxlenName=10; // name ist NULL oder leer (Initialwert setzen) else m_maxlenName=len; // Array-Länge setzen // dynamische Allokation von Speicher (+1 Byte für abschließende 0) m_name=new char[m_maxlenName+1]; // Kopieren des Namens in den neuen Speicherbereich for(m_curlenName=0;m_curlenName<m_maxlenName;m_curlenName++) m_name[m_curlenName]=name[m_curlenName]; m_name[m_curlenName]=0; // String mit 0 abschließen // Initialisierung durch Aufruf der Getter-Methode if(setLatitude(latitude)==false) m_latitude=0.; // Initialisierung mit direkter Prüfung der Zusicherungen if(longitude < -180. || longitude > 180.) m_longitude=0.; else m_longitude=longitude; cout << "erzeuge Wegepunkt@" << this << " (" << m_name << ")" << endl; }
char* ist so derart low-level, dass es gar keinen destruktor/konstruktor hat. alles, was mit erzeugung und freigeben zu tun hat, musst *du* manuell machen.
schau dir an, wie man char* mal ohne new verwendet:
const char* zstring = "Hello, World!\n";
zstring ist ein zeiger auf char (auf const char, um genau zu sein - man kann den die zeichen nicht mehr ändern, daher const), und zwar ein zeiger auf genau *einen* char, in diesem fall ein zeiger auf 'H'.
const char* zstring = "Hello, World!\n"; const char* next_char = zstring + 1;
next_char ist ein zeiger auf den *nächsten* character, also auf e. zur kontrolle kannst du den zeiger dereferenzieren und dir den einzelnen char ausgeben lassen:
const char* zstring = "Hello, World!\n"; cout << *zstring << endl; //Ausgabe: H const char* next_char = zstring + 1; cout << *next_char << endl; //Ausgabe: e
ein derartiger (low-level) C-string hält sich an eine konvention, und zwar: der C-string ist mit dem speziellen character '\0' abgeschlossen. dieses zeichen wird - unsichtbar - am ende des strings vom compiler eingefügt, wenn du einen solchen c-string statisch (d.h. beim kompilieren) definierst. daher hat der string auch eine nicht-ganz-intuitive länge:
const char* zstring = "ABC"; //länge = 3 + die unsichtbar '\0' am ende = 4! const char* erster_char = zstring; cout << *erster_char << endl; //Ausgabe: A const char* zweiter_char = zstring+1; cout << *zweiter_char << endl; //Ausgabe: B const char* dritter_char = zstring+2; cout << *dritter_char << endl; //Ausgabe: C const char* vierter_char = zstring+3; cout << *vierter_char << endl; //Ausgabe nur '\n' von endl, da '\0' nicht angezeigt wird const char* fuenfter_char = zstring+4; cout << *fuenfter_char << endl; //BOOM, programm stürzt ab, da du auf einen char zugreifen willst, den es gar nicht mehr gibt!
dann gibt es auf ebene von zeigern noch eine abkürzung:
const char* zstring = "Hello, World!\n"; const char* dritter_char = zstring+2; cout << *dritter_char << endl; //gleichbedeutend mit: cout << *(zstring+2) << endl; //gleichbedeutend mit: cout << zstring[2] << endl;
so wie's aussieht musst du also erst einmal ein bisschen was mit zeigern lernen, das hat aber alles noch gar nichts mit new und delete zu tun.
new und delete verwendest du, um dynamisch, d.h. zur laufzeit speicher zu reservieren. das musst du z.b. dann machen, wenn du nicht weißt, wie lange ein string sein wird. z.b. wenn der benutzer deines programms einen beliebigen string eingeben darf. in C++ überlässt man diese arbeit normalerweise diverse standardklassen und verwendet new und delete am besten gar nicht mehr selbst, höchstens stark abstrahiert. wenn man allerdings lernt, ist man manchmal dazu gezwungen, sich intensiver mit bestimmten dingen auseinander zu setzen.
es gibt zwei formen von new (und delete). eine form, mit der man ein einzelnes objekt anlegt und eine form, mit der man ein array von objekten anlegt: new für ein einzelnes, new[] für mehrere. du verwendest new[].
zu jedem new[] gehört ein delete[] und zu jedem new ein delete. das ist einmal das wichtigste. dann: egal, wie dein programm zu ende geht, du musst sicherstellen, dass jeder speicher, den du mit new oder new[] anforderst auch wieder mit delete bzw delete[] freigegeben wird.m_name=new char[m_maxlenName+1];
mit dieser zeile erzeugst du ein array aus charactern, und zwar mit der länge m_maxlenName+1. stell es dir so vor, als würdest du einen string so definieren:
m_name = "ein string mit einer (variablen) länge von m_maxlenName... ... ..."; //+1 wegen der '\0' am schluss
ich hoffe, dass du diese schleife jetzt verstehst:
for(m_curlenName=0;m_curlenName<m_maxlenName;m_curlenName++) m_name[m_curlenName]=name[m_curlenName]; //heißt so viel wie: // dem (m_curlenName+1).en character in m_name wird der (m_curlenName+1).e character aus name zugewiesen. //siehe oben: erster_char, zweiter_char, dritter_char etc. m_name[m_curlenName]=0; //gleichbedeutend mit: *(m_name+m_curlenName) = 0; //gleichbedeutend mit = '\0'; //Das macht man, wie gesagt, aus konvention: '\0' zeigt an, dass der string hier aufhört - nicht unbedingt der speicher, den man dahinter angefordert hat.
und jetzt noch einmal der ganze code mit anmerkungen, warum und an welchen stellen er falsch ist:
CWayPoint::CWayPoint(char* name, double latitude, double longitude) { // Bestimmung der Länge des Namen (ohne strlen) int len=0; if(name!=NULL) { char* pTmp=name; while(*pTmp!=0) { len++; pTmp++; } } //MIT strlen wäre das natürlich sehr viel einfacher. if(len==0) m_maxlenName=10; // name ist NULL oder leer (Initialwert setzen) else m_maxlenName=len; // Array-Länge setzen //wenn der name sowieso null ist, dann könnte man auch //m_name auf NULL setzen. heutzutage heißt das übrigens nicht mehr // NULL, sondern nullptr. // dynamische Allokation von Speicher (+1 Byte für abschließende 0) m_name=new char[m_maxlenName+1]; // Kopieren des Namens in den neuen Speicherbereich for(m_curlenName=0;m_curlenName<m_maxlenName;m_curlenName++) m_name[m_curlenName]=name[m_curlenName]; // Hier macht es BOOM! m_maxlenName ist jedenfalls 10 (siehe oben), // auch wenn der zeiger name gar nirgendwohin zeigt - und oben wird // extra noch geprüft, ob name eh nicht null ist. //Erinnere dich: name[m_curlenName] ist dasselbe wie *(name+m_curlenName) // - bloß crasht alles, wenn name == NULL. m_name[m_curlenName]=0; // String mit 0 abschließen // Initialisierung durch Aufruf der Getter-Methode if(setLatitude(latitude)==false) m_latitude=0.; // Initialisierung mit direkter Prüfung der Zusicherungen if(longitude < -180. || longitude > 180.) m_longitude=0.; else m_longitude=longitude; cout << "erzeuge Wegepunkt@" << this << " (" << m_name << ")" << endl; }
der konstruktor ist außerdem zu lang. das selbstgebastelte strlen kann man durchaus zu einer eigenen funktion machen. die zusicherungen werden offenbar sowieso in gettern und settern überprüft - also den ganzen code gleich dorthin schieben. gibt es setName/getName? dann die ganze string logik auch dorthin schieben.
CWayPoint::CWayPoint(char const* name, double latitude, double longitude) : m_Name(nullptr) { this->setName(name); this->setLatitude(latitude); this->setLongitude(longitude); } CWayPoint::setName (char const* name) { if (m_Name == name) return; if (name == nullptr) { delete[] m_Name; m_Name = nullptr; m_NameLen = 0; return; } int len = my_strlen(name); //inkludiert '\0' if (len > m_NameLen) { delete[] m_Name; m_Name = new char[len]; } my_strncpy (m_Name, name, len); m_NameLen = len; } CWayPoint::~CWayPoint() { delete[] m_Name; }
-
WOW weiß gar nicht was ich sagen soll.
Vielen vielen Dank für diesen Post werde ihn mir nun in Ruhe zu Gemüte führen lassen
Wie kann ich mich dafür bedanken ?War ja auch eine Mühe das zu schreiben
-
Also sind character Pointer Zeiger auf die jeweiligen Blöcke eines Arrays?
Also zeigt der Pointer meines ersten Character's auf den "ersten Buchstaben" meines Arrays?
Deshalb muss ich einen neuen Character definieren und um +1 erhöhen für den zweiten Buchstaben des Arrys?
-
Noch eine kleine Frage rein aus Interesse.
Habe mal versucht ein Beispiel meines Prof's umzuschreiben und habe dabei folgendes Problem:Beispiel des Prof's:
int N; double* x; cout << "Anzahl zu speichernder Werte: " << endl; cin >> N; x=new double[N]; for (int i = 0 ; i < N ; i++) x[i]=0; delete[]x ;
Ich habe mal das hier zusammen geschnipselt :
int N; char* x = "ABC"; cout << "Anzahl der Buchstaben anzeigen lassen?: " << endl; cin >> N; x = new char[N]; for (int i = 0 ; i < N ; i++) x[i]=0; cout << *x << endl;
Bekomme das aber nur eine Adresse heraus
Warum?
Habe mir das Programm mal zusammen gebastelt damit ich mir das vorher genannte Thema mal an einem selbst gemachten Beispiel einfacher erkenntlich zu gestalten.
Versteht ihr meine Problematik? Würde gerne eine Schleife realisieren mit einem char der einen Namen etc gespeichert hat den ich per schleife auf die Console herausgebe.
Und nun noch eine ganz andere Frage:
int x = 5; int *pTr; pTr = &x;
pTr = Nur für Adressen?
*pTr = Nur für Werte?MFG
-
buu2188 schrieb:
Also sind character Pointer Zeiger auf die jeweiligen Blöcke eines Arrays?
im prinzip schon.
du schreibst also z.b. "char *a = new char[25];" und dann kannst du mit *a das erste element abrufen, mit *(a+1) das zweite, *(a+2) das dritte usw.was dein anderes problem angeht:
1. wirfst du dein array weg.
2. überschreibst du in dem programm dein array mit 0.
3. lautet die abbruchbedingung idr. *a==0.&pTr: adresse der zeigervariablen
pTr: in der zeigervariablen gespeicherte adresse
*pTr: an in der zeigervariablen gespeicherten adresse gespeicherter wert
-
buu2188 schrieb:
Also sind character Pointer Zeiger auf die jeweiligen Blöcke eines Arrays?
Jein; sie zeigen, genau genommen, auf ein einzelnes Objekt vom Typ "char". Wie viele danach noch kommen mögen, ist in einem char* nicht enthalten.
buu2188 schrieb:
Deshalb muss ich einen neuen Character definieren und um +1 erhöhen für den zweiten Buchstaben des Arrys?
Nein, es gibt verschiedene Möglichkeiten um auf nachfolgende chars zuzugreifen. Das wurde hier schon erklärt.
-
buu2188 schrieb:
Habe mal versucht ein Beispiel meines Prof's umzuschreiben
Als Lehrbeispiel ist das vielleicht geeignet, um Umgang mit Pointern zu lernen, aber in der Praxis wirst du diesen Code eher selten bis hoffentlich gar nicht sehen. Grund: keine Fehlerbehandlung, was passiert bei negativen Eingaben und außerdem wäre hier in C++ wohl ein
std::vector<double>
das Mittel der Wahl.buu2188 schrieb:
Ich habe mal das hier zusammen geschnipselt :
int N; char* x = "ABC";
Das ist schon falsch, dir fehlt const: du solltest eine Ausgabe wie "ISO C++11 does not allow conversion from string literal to 'char *'" bekommen. Wenn nicht, dann stell die Warnungen/Fehler richtig ein. Dann funktioniert natürlich auch die folgende Zuweisung auf
x[i]
nicht mehr. Nimm doch eine neue Variable!buu2188 schrieb:
for (int i = 0 ; i < N ; i++) x[i]=0; cout << *x << endl;
Bekomme das aber nur eine Adresse heraus
Warum?
Das wäre merkwürdig. Da sollte keine Adresse rauskommen, sondern nur das Zeichen mit dem Wert 0, das du vermutlich nicht am Bildschirm siehst, sowie der Zeilenumbruch.
Ich glaube, du solltest dich dringend mit ein paar Kommilitonen zusammensetzen und die Grundlagen (was ist ein Zeiger etc.) noch einmal angucken. Das geht vermutlich besser/schneller als hier. Das soll dich aber nicht davon abhalten, hier gern konkrete Dinge nachzufragen.
-
Ein Pointer ist eine Variable, die eine Adresse speichert.
Um an den Wert zu kommen, der an dieser Adresse liegt, muss man den Pointer dereferenzieren. Dafür ist der * Operator oder auch die [] da.
Eine Besonderheit bei Pointern ist, das bei Addition mit einem integralen Typ die Größe des Elements (auf das der Zeiger verweist) berücksichtigt wird.
x[i] ist identisch mit *(x+i)
Der Pointer enthält keine weitere Information darüber, wie groß der Speicherbereich ist, auf den er zeigt.
cout gibt bei einem Pointer dessen Inhalt (Adresse) aus, es sei denn, es ist ein char-Pointer.
Bei char* wird das wie ein C-String behandelt.Das *x ist bei dir aber vom Typ
char
und ist bei dir 0.char* x = "ABC"; // 4 Zeichen 'A', 'B', 'C', 0 for (int i = 0 ; x[i] != 0 ; i++) // als Array cout << x[i] << endl; for (char *p = x; *p; p++) // mit Pointer *p steht für *p != '\0' cout << *p << endl;
Der Zeiger p wird hier gebraucht, da x ja weiterhin auf den Anfang der Zeichenfolge zeigen soll.
-
Vielen vielen Dank für eure Hilfe
-
DirkB schrieb:
x[i] ist identisch mit *(x+i)
Rätselfrage: ist x oder i der Pointer?
-
Andromeda schrieb:
DirkB schrieb:
x[i] ist identisch mit *(x+i)
Rätselfrage: ist x oder i der Pointer?
Ja
-
Andromeda schrieb:
Rätselfrage: ist x oder i der Pointer?
i zeigt halt auf was anderes