Überschreibung meines Pointers



  • Hey,
    ich mache für mein Uni Praktikum einen Binären Suchbaum und an einer stelle im code (Zeile 84) Überschreibt ein std::cout << (Message) meinen pointer start ... im Debugger steht aufeinmal was total anderes

    binärer_suchbaum.h (oder so ähnlich richtiger name in der .cpp)

    #pragma once
    #include <iostream>
    
    
    #define Message std::cout <<
    #define endl <<std::endl
    #define read std::cin >>
    
    
    
    namespace suchbaum {
    	template <typename T>
    	struct BaumKnoten
    	{
    		typedef T type ;
    		T var;
    		BaumKnoten<T>* lower;
    		BaumKnoten<T>* higher;
    	};
    
    	template<typename T>
    	void ausgeben(BaumKnoten<T>* start);
    	template <typename T>
    	void knoten_ausgeben(BaumKnoten<T>* knoten, int tiefe);
    	template <typename T>
    	BaumKnoten<T>* einfuegen(T var, BaumKnoten<T>* start);
    	template <typename T>
    	BaumKnoten<T> create(T var);
    
    }
    

    suchbaum_main.cpp

    #include "binaerer_suchbaum.h"
    
    //Funktioniert bei einem Baum der DAUERHAFT 1 type (int, char, string, ...) speichert || keine Mix möglichkeit
    
    template<typename T>
    void suchbaum::knoten_ausgeben(BaumKnoten<T>* knoten, int tiefe)
    {
    	if (knoten->higher)
    		knoten_ausgeben(knoten->higher, tiefe + 1);
    
    	for (int i = tiefe; i > 0; i--) { //for (int i = tiefe * 2; i > 0; i--) {
    		Message "++";
    	}
    	Message knoten->var endl;
    
    	if (knoten->lower != nullptr)
    		knoten_ausgeben(knoten->lower, tiefe + 1);
    }
    
    template<typename T>
    void suchbaum::ausgeben(BaumKnoten<T>* start)
    {
    	if (start == nullptr) {
    		Message "Leerer Baum." endl;
    		return;
    	}
    	knoten_ausgeben(start, 0);
    }
    
    template<typename T>
    suchbaum::BaumKnoten<T>* suchbaum::einfuegen(T var, BaumKnoten<T>* start)
    {
    
    	if (start == nullptr) {
    		start = &create<T>(var);
    		return start;
    	}
    
    	BaumKnoten<T> temp = *(start);
    	while (1) {
    		if (temp.var > var) {
    			if (temp.lower == nullptr) {
    				temp.lower = &create<T>(var);
    				return start;
    			}
    			else
    				temp = *(temp.lower);
    		}
    		else if(temp.var < var) {
    			if (temp.higher == nullptr) {
    				temp.higher = &create<T>(var);
    				return start;
    			}
    			else
    				temp = *(temp.higher);
    		}
    	}
    
    }
    
    template<typename T>
    suchbaum::BaumKnoten<T> suchbaum::create(T var)
    {
    	BaumKnoten<T> temp;
    	temp.var = var;
    	temp.higher = nullptr;
    	temp.lower = nullptr;
    	return temp;
    }
    
    
    
    int main() {
    	typedef int standart_type;
    	int end = 99;
    	int input;
    	suchbaum::BaumKnoten<standart_type>* start = nullptr;			//könnte man umgestalten das nicht nur Integer gespeichert werden können
    
    	suchbaum::ausgeben<standart_type>(start);
    	while (1) {
    //mit nullpointer funktioniert es
    //nach der ersten eingabe wird hier aus start  was anderes. (die Pointer speicheradresse ist gleich)
    //aber die inahlte bei var bzw lower und higher sind anders (lower/higher = 0xccccccc)
    		Message "Naechster Wert(99 beendet) : ";
    		read input;
    		Message input endl;
    		if (input == end)
    			break;
    		else
    			start = suchbaum::einfuegen<standart_type>(input, start);
    	}
    	suchbaum::ausgeben<standart_type>(start);
    
    	system("Pause");
    	return 0;
    }
    


  • @Darktutnix sagte in Überschreibung meines Pointers:

    #define Message std::cout <<
    #define endl <<std::endl
    #define read std::cin >>
    

    Bitte lass solchen Blödsinn bleiben.

    PS: Standart schreibt man mit d: Standardt


  • Mod



  • @Columbo *scnr*



  • dürfte ich fragen warum das Blödsinn ist?

    Ist doch einfach nur eine Textersetzung des Präprozessor oder verändert das in irgendeiner art und weiße die Funktion von cout bzw cin ?

    Desweiteren ist mir Rechtschreibung ziemlich egal.



  • @Darktutnix sagte in Überschreibung meines Pointers:

    Ist doch einfach nur eine Textersetzung des Präprozessor oder verändert das in irgendeiner art und weiße die Funktion von cout bzw cin ?

    Das ist bei Makros schwer zu sagen. Ich werde meine Zeit jedenfalls nicht mit solchem Code verschwenden.

    Desweiteren ist mir Rechtschreibung ziemlich egal.

    Merkt man.



  • @Darktutnix sagte in Überschreibung meines Pointers:

    dürfte ich fragen warum das Blödsinn ist?

    Noch dazu in einem Header. Wenn dessen benutzer *danach* <iostream> einbindet wird es sehr spaßig. Allgemein? Weil es Fehleranfällig und für andere schlecht zu lesen ist.

    @Darktutnix sagte in Überschreibung meines Pointers:

    temp.higher = &create<T>(var);
    

    Wie lange glaubst du ist das eine gültige adresse die du da nimmst?

    Du könntest auch einfach mal die Warnungen deines Lieblingscompilers einschalten:

    warning C4172: returning address of local variable or temporary
    warning C26496: The variable 'end' is assigned only once, mark it as const (con.4).
    warning C26494: Variable 'input' is uninitialized. Always initialize an object (type.5).
    warning C26489: Don't dereference a pointer that may be invalid: '*temp.lower'. 'temp.suchbaum::BaumKnoten<int>::lower' may have been invalidated at line 62 (lifetime.1).
    warning C26489: Don't dereference a pointer that may be invalid: '*temp.higher'. 'temp.suchbaum::BaumKnoten<int>::higher' may have been invalidated at line 62 (lifetime.1).
    warning C26440: Function 'suchbaum::create<int>' can be declared 'noexcept' (f.6).
    


  • @manni66 sagte in Überschreibung meines Pointers:

    @Darktutnix sagte in Überschreibung meines Pointers:

    Ist doch einfach nur eine Textersetzung des Präprozessor oder verändert das in irgendeiner art und weiße die Funktion von cout bzw cin ?

    Das ist bei Makros schwer zu sagen.

    https://en.cppreference.com/w/cpp/preprocessor/replace

    ich verstehe es so das der Präprozessor einfach ( #define "name" "code" ) alle "name" durch "code" ersetzt. Also zb aus for(x) macht er dann for(int i = 0;i <x;i++) als Beispiel. Ergo dürfte doch rein garnichts passieren.

    Wurde ebend sogar auf dem Discord gefragt ob das Kompiliert ... Ja tut es x.x.

    Nichts destotroz werde ich das mal ausprobieren.

    Ich werde meine Zeit jedenfalls nicht mit solchem Code verschwenden.

    Sagen wir es so. Ich hab die irgendwann mal als #define bei Notepad++ abgespeichert und kopiere das einfach immer wieder rein. im normalfall würde ich es auch nicht machen aber bei den Praktika aus dem ersten semester wird man irgendwann dusselig bei dem ganzen std::cout << schreiben. Ist aber Grundlegend egal.

    @Swordfish sagte in Überschreibung meines Pointers:

    @Darktutnix sagte in Überschreibung meines Pointers:

    dürfte ich fragen warum das Blödsinn ist?

    Noch dazu in einem Header. Wenn dessen benutzer *danach* <iostream> einbindet wird es sehr spaßig. Allgemein? Weil es Fehleranfällig und für andere schlecht zu lesen ist.

    <iostream> ist schon Eingebunden und durch #program once in iostream passiert genau garnichts. (auser das ist dort nicht vorhanden was ich mir nicht vorstellen kann)

    Allgemein? Weil es Fehleranfällig und für andere schlecht zu lesen ist.

    Hmm. Okay ist ein Argument, dass ich zwar nicht nachvollziehen kann, aber egal. In einer Team Arbeit sollte das wegfallen. Danke für den Tipp.

    @Darktutnix sagte in Überschreibung meines Pointers:

    temp.higher = &create<T>(var);
    

    Wie lange glaubst du ist das eine gültige adresse die du da nimmst?

    oO Ich erzeuge ein Objekt in Create das ich zurück gebe und davon nehme ich den Pointer. Also solange das neue Objekt existiert (also das zurückgegebene) sollte sich die Addresse nicht verändern.
    Ein Logik fehler hier?

    Oder bekomme ich gar den pointer von der Funktion create wenn ich das &create<T>(var); ausführe?

    Du könntest auch einfach mal die Warnungen deines Lieblingscompilers einschalten:

    warning C4172: returning address of local variable or temporary
    warning C26496: The variable 'end' is assigned only once, mark it as const (con.4).
    warning C26494: Variable 'input' is uninitialized. Always initialize an object (type.5).
    warning C26489: Don't dereference a pointer that may be invalid: '*temp.lower'. 'temp.suchbaum::BaumKnoten<int>::lower' may have been invalidated at line 62 (lifetime.1).
    warning C26489: Don't dereference a pointer that may be invalid: '*temp.higher'. 'temp.suchbaum::BaumKnoten<int>::higher' may have been invalidated at line 62 (lifetime.1).
    warning C26440: Function 'suchbaum::create<int>' can be declared 'noexcept' (f.6).
    
    > warning C26489: Don't dereference a pointer that may be invalid: '*temp.lower'.'temp.suchbaum::BaumKnoten<int>::lower' may have been invalidated at line 62 (lifetime.1).
    > warning C26489: Don't dereference a pointer that may be invalid: '*temp.higher'. 'temp.suchbaum::BaumKnoten<int>::higher' may have been invalidated at line 62 (lifetime.1).
    

    hmm ... ist für mich nicht nachvollziehbar, warum die nur eine Lebensperiode haben sollen. Ist es wirklich das was ich meinte mit dem Pointer der Funktion anstelle des rückgabe Objekts?



  • @Darktutnix sagte in Überschreibung meines Pointers:

    <iostream> ist schon Eingebunden und durch #program once in iostream passiert genau garnichts. (auser das ist dort nicht vorhanden was ich mir nicht vorstellen kann)

    Ja. Bei dir. Jetzt. Aber Headerdateien werden oft auch von anderem Code verwendet dessen Programmierer nicht damit rechnet, daß du darin solche #defines hast. Auch ist das Einbinden von <iostream> in deiner binärer_suchbaum.h ziemlich sinnfrei, weil der Header <iostream> garnicht braucht.

    @Darktutnix sagte in Überschreibung meines Pointers:

    hmm ... ist für mich nicht nachvollziehbar, warum die nur eine Lebensperiode haben sollen. Ist es wirklich das was ich meinte mit dem Pointer der Funktion anstelle des rückgabe Objekts?

    Das Objekt, das zurückgegeben wird ist ein Temporary davon nimmst du die Adresse. Sobald dieses Temporary das Zeitliche segnet ist diese Adresse ein invalid-pointer-value und du kannst dir von mir aus ausdrucken und and die Wand hängen aber sonst nichts sinnvolles mehr damit anstellen - vor allem nicht dereferenzieren.

    Temporary objects are destroyed as the last step in evaluating the full-expression (1.9) that (lexically) contains the point where they were created.

    Was auch seltsam ist: Warum sind die Funktionen die mit deinem Baum arbeiten freie Funktionen denen ein Baum übergeben wird (wie man es in C machen würde) und keine Member?


  • Mod

    template <typename T>
    BaumKnoten<T> create(T var);
    

    Dieses Funktionstemplate wird in einem Aufruf immer einen prvalue Ausdruck ergeben, d.h. einen Ausdruck der für sich genommen gar kein Objekt benennt, sondern erst einmal nur einen Wert darstellt.
    Wenn wir auf diesen Ausdruck nun den Adress-Operator anwenden, wird die Erzeugung eines tatsächlichen Objekts veranlasst, welches mit diesem Wert initialisiert wird und dessen Adresse ergeben wird. Aber dieses Objekt existiert nur über eine begrenzte Zeitspanne, i.A. bis zum Ende des Statements in dem es erzeugt worden ist. In der Tat ist es ziemlich unsinnig, dass eine Funktion die rohe Adresse eines neuen Objektes zurückgibt, da der Speicher dafür irgendwo auf dem Heap alloziert worden sein muss.

    Du könntest statt einem Zeiger start einfach einen leeren suchbaum::BaumKnoten<standart_type> start = {} definieren, der ausgenullt ist, und dann an einfuegen die Adresse von start übergeben. (Um Nullzeiger als Argumente brauchst du dich dann nicht mehr zu sorgen, das wäre nicht länger intendiert).



  • @Swordfish sagte in Überschreibung meines Pointers:

    @Darktutnix sagte in Überschreibung meines Pointers:

    <iostream> ist schon Eingebunden und durch #program once in iostream passiert genau garnichts. (auser das ist dort nicht vorhanden was ich mir nicht vorstellen kann)

    Ja. Bei dir. Jetzt. Aber Headerdateien werden oft auch von anderem Code verwendet dessen Programmierer nicht damit rechnet, daß du darin solche #defines hast. Auch ist das einbinden von <iostream> in deiner binärer_suchbaum.h ziemlich sinnfrei, weil der Header <iostream> garnicht braucht.

    Hmm ... ganz erlich xD ich habe noch nie mit anderen Coder gearbeitet und denke auch bei diesen "Programmen" nicht darüber nach ob die jemals nochjemand sieht. Aber es ist aufjedenfall Wichtig sowas später zu beachten.

    Was auch seltsam ist: Warum sind die Funktionen die mit deinem Baum arbeiten freie Funktionen denen ein Baum übergeben wird (wie man es in C machen würde) und keine Member?

    Weil das die Aufgabenstellung ist. Wir haben extrem enge Rahmenbedingugnen im ersten Semester (ich selbst Programmiere schon länger, das meiste selbst beigebracht, weswegen ich mir selbst die Template Aufgabe gestellt habe), aber eigentlich ist es nicht möglich (bzw nicht erlaubt was keine Klausurzulassung bedeutet), wobei die Dozenten bei der Abgabe bei solchen kleinen änderungen nix sagen (Grundlegend ändere ich ja nichts an der Funktionsweise durch das Template). Würde ich jetzt allerdings auf Klassen vorgreifen, dürfte ich das ganze nochmal machen. Auserdem sind Globale/static vars explizit untersagt.

    Andere möglichkeit würde mir jetzt nicht einfallen. Oder was meinst du mit "Member"? Klassen oder?

    @Columbo sagte in Überschreibung meines Pointers:

    template <typename T>
    BaumKnoten<T> create(T var);
    

    Dieses Funktionstemplate wird in einem Aufruf immer einen prvalue Ausdruck ergeben, d.h. einen Ausdruck der für sich genommen gar kein Objekt benennt, sondern erst einmal nur einen Wert darstellt.
    Wenn wir auf diesen Ausdruck nun den Adress-Operator anwenden, wird die Erzeugung eines tatsächlichen Objekts veranlasst, welches mit diesem Wert initialisiert wird und dessen Adresse ergeben wird. Aber dieses Objekt existiert nur über eine begrenzte Zeitspanne, i.A. bis zum Ende des Statements in dem es erzeugt worden ist. In der Tat ist es ziemlich unsinnig, dass eine Funktion die rohe Adresse eines neuen Objektes zurückgibt, da der Speicher dafür irgendwo auf dem Heap alloziert worden sein muss.

    Du könntest statt einem Zeiger start einfach einen leeren suchbaum::BaumKnoten<standart_type> start = {} definieren, der ausgenullt ist, und dann an einfuegen die Adresse von start übergeben. (Um Nullzeiger als Argumente brauchst du dich dann nicht mehr zu sorgen, das wäre nicht länger intendiert).

    Also Grundlegend muss ich eine template<type> immer inizalisieren (also zb suchbaum::BaumKnoten<standart_type> start = {} so) damit es nicht temporär erzeugt wird? Wenn ich einfach nur sage ich habe ein template<type> und fülle da s jetzt wie ich es in create mache erzeuge ich nur temporäre daten?

    Aber pointer auf so suchbaum::BaumKnoten<standart_type> start = {} initalisierte sachen verschieben sich nicht nach lust und laune? (also ich mache start und gebe jetzt *start weiter)

    Danke für die Hilfe 😃



  • @Darktutnix sagte in Überschreibung meines Pointers:

    Andere möglichkeit würde mir jetzt nicht einfallen. Oder was meinst du mit "Member"? Klassen oder?

    struct und class ist dasselbe. Bei einer struct sind die Member standardtmäßig public, bei einer class sind die Member standardtmäßig private. Sonst gibt es keinen Unterschied.

    struct foo { // alles folgende ist public ...
        int qux;  // Member
        foo() : qux{ 42 } {}  // Konstruktor
        int bar() { return return qux; }  // Memberfunktion *)
    private: // ... jetzt nimmer
        int haensel;
    };
    

    1:1 dasselbe:

    class foo { // alles folgende ist private ...
        int haensel;
    public: // ... jetzt nimmer
        int qux;  // Member
        foo() : qux{ 42 } {}  // Konstruktor
        int bar() { return return qux; }  // Memberfunktion *)
    };
    

    * ) Für die Klugscheißer, ja, es ist eine Funktion weil es "Memberfunktionen" im C++ Sprachgebrauch eigentlich nicht gibt.

    @Darktutnix sagte in Überschreibung meines Pointers:

    Also Grundlegend muss ich [...]

    Ich glaube du hast da einen Knoten im Hirn.

    foo_type bar() { foo_type foo; return foo; }
    

    gibt ein temporäres Objekt vom Typ foo_type zurück. In

    bar();
    

    lebt dieses Temporary genau bis zum Semikolon. Keinen Wimpernschlag länger. Wenn du jetzt von diesem temporären Objekt

    foo_type *p = &bar(); // die Aresse nimmst
    

    ist das gut und schön, aber es bringt dir nix, weil der Zeiger schon in der nächsten Zeile in die Pampa zeigt weil das zurückgegebene Objekt dann nicht mehr existiert. Deshalb sollst du ein

    foo_type foo;  // nehmen (ob das ein template ist oder nicht ist egal)
    foo = bar();  // und das zurückgegebene Temporary darin speichern
    

    damit du noch lange Freude daran hast 🙂



  • Anscheinend hab ich es immer noch nicht richtig verstanden...

    if (temp->GetVar() > var) {
    			if (temp->GetLower() == nullptr) {
    				BaumKnoten<T> c = {};
    				c.SetVar(var);
    				temp->SetLower(&c);
    				return save;
    			}
    			else
    				temp = temp->GetLower();
    

    ich erzeuge erstmal nen leeren BaumKnoten dann überschreibe ich diesen mit dem was ich bei var bekomme und nehme mir dann den pointer und speicher den in meinem Baum ab ... trozdem ist es nur eine Temporär erstellte datei ... dat will nicht in meinen Schädel x.x

    (merke ich daran das er von selbst verschwiendet wie start vorher) und ich bekomme nichts ... weder warnungen noch fehlermeldungen x.x



  • Wann wird c zerstört? Ist die Adresse dann noch gültig?



  • @manni66 sagte in Überschreibung meines Pointers:

    Wann wird c zerstört? Ist die Adresse dann noch gültig?

    Er findet eine addresse irgendwo in der Pampa. Wie bei Start sobald ich die erste ausgabe mache. (vermutlich etwas früher der Debugger ist ja etwas Zeit verzögert)

    Dann wird aus dem ersten Zeiger in start ein extrem seltsamer, der sich ständig verändert.



  • Du sollst über die Frage nachdenken. Wann c zerstört wird ist klar definiert!



  • @manni66 sagte in Überschreibung meines Pointers:

    Du sollst über die Frage nachdenken. Wann c zerstört wird ist klar definiert!

    x.x ich glaub ich bin durch für heute ...

    Klar auf die Variabele C kann ich irgendwann nicht mehr zugreifen, aber deswegen muss sie doch noch da sein. Ich Zerstöre nirgendwo c expliziet. Und Garbish Collector existiert doch nicht ...



  • @Darktutnix sagte in Überschreibung meines Pointers:

    Klar auf die Variabele C kann ich irgendwann nicht mehr zugreifen, aber deswegen muss sie doch noch da sein. Ich Zerstöre nirgendwo c expliziet. Und Garbish Collector existiert doch nicht ...

    Das geht automatisch. Das ist doch gerade der Witz an C++:

    automatic storage duration. The storage for the object is allocated at the beginning of the enclosing code block and deallocated at the end. All local objects have this storage duration, except those declared static, extern or thread_local.


Anmelden zum Antworten