data type " int* " vs " char* "



  • Nathan schrieb:

    Der int, char Teil sagt nur, welcher Datentyp an dieser Adresse gespeichert ist.
    Das Problem bei der Ausgabe liegt lediglich daran, dass char* automatisch als Zeichenkette interpretiert wird. Die geht bis zum Zeichen '\0'. Wenn du das ausgibst, gibt der alle char's aus, die hinter der Startadresse im Speicher stehen, bis er ein '\0' findet. Deshalb der Zeichenmüll.
    Wenn du die Adresse haben willst, musst du nach void* casten, dann kommt die richtige Ausgabe.

    Aha ! Also "(char*) ptrx" bedeutet: Gehe and die Adresse ptrx und interpretiere den dort gespeicherten Wert als char.

    Ok. Aber warum gibt er bei (int*) die Adresse dann in Hexadezimal aus, und nicht in Dezimal - wie es std::cout ja auch bei allen anderen int tut ?



  • Ich habe einen void* cast hinzugefuegt und die Ausgabe ist die gleiche wie mit int* cast

    #include <iostream>
    
    int main(){
    
    char x = 'a';
    
    char* ptrx = &x;
    
    std::cout << "&x : " << &x << std::endl;
    std::cout << "x: " << x << " at " << (char*) ptrx << std::endl;
    
    std::cout << "(int*) ptrx : " << (int*) ptrx << std::endl;
    std::cout << "(void*) ptrx: " << (void*) ptrx << std::endl;
    return 0;
    
    }
    

    Ausgabe:

    &x : a�!�v�
    x: a at a�!�v�
    (int*) ptrx : 0x7fff76a221f7
    (void*) ptrx: 0x7fff76a221f7
    


  • Du musst auch die anderen Adressen casten, also auch die mit &, damit du die richtige Adresse hast.
    Ja, int* geht auch.
    Das hexadezimale liegt an einer Einstellung der Streams, denn ein int* ist kein int, weswegen da andere (Ausgabe-)regeln gelten.
    Der char* Cast bei ptrx ist übrigens überflüssig.



  • ...



  • Ja, ich hatte das noch im Kopf, weil der mir this-Zeiger nicht ausgeben wollte und ich die nach void* gecastet habe. Da dachte ich, dass das mit jedem Pointertyp so sein muss.



  • Nathan schrieb:

    Du musst auch die anderen Adressen casten, also auch die mit &, damit du die richtige Adresse hast.

    Schon klar.
    Ich wollte nur zeigen, dass (void*) und (int*) das gleiche anzeigt.

    Nathan schrieb:

    Ja, int* geht auch.
    Das hexadezimale liegt an einer Einstellung der Streams, denn ein int* ist kein int, weswegen da andere (Ausgabe-)regeln gelten.

    Swordfish schrieb:

    idrake schrieb:

    Aber warum gibt er bei (int*) die Adresse dann in Hexadezimal aus, und nicht in Dezimal - wie es std::cout ja auch bei allen anderen int tut ?

    Die Adresse eines int s ist ein anderer Typ als ein int . cout gibt jeden Pointertyp hexadezimal aus.

    Natuerlich, dass ein int und int* zwei verschiedene Datentypen sind ist mir sonnenklar 🙂

    Mir geht es jetzt darum, was der Compiler tut.

    Also:

    Wenn der Compiler bei "(char*) ptrx" alles an dieser Adresse (ptrx) als char behandelt, warum behandelt er dann nicht bei "(int*) ptrx" alles an dieser adresse als int ?

    Siehe:

    idrake schrieb:

    Nathan schrieb:

    Der int, char Teil sagt nur, welcher Datentyp an dieser Adresse gespeichert ist.
    Das Problem bei der Ausgabe liegt lediglich daran, dass char* automatisch als Zeichenkette interpretiert wird. Die geht bis zum Zeichen '\0'. Wenn du das ausgibst, gibt der alle char's aus, die hinter der Startadresse im Speicher stehen, bis er ein '\0' findet. Deshalb der Zeichenmüll.
    Wenn du die Adresse haben willst, musst du nach void* casten, dann kommt die richtige Ausgabe.

    Aha ! Also "(char*) ptrx" bedeutet: Gehe and die Adresse ptrx und interpretiere den dort gespeicherten Wert als char.

    Ok. Aber warum gibt er bei (int*) die Adresse dann in Hexadezimal aus, und nicht in Dezimal - wie es std::cout ja auch bei allen anderen int tut ?

    Edit:

    Um es anders zu formulieren:

    Wenn es stimmt, dass bei
    (char*) ptrx
    der Compiler zur Adresse ptrx geht und alles dort als char behandelt,

    dann muss analog, der Compiler bei
    (int*) ptrx
    zur Adresse ptrx gehen und dort alles als int behandeln.

    Aber das ist offensichtlich nicht so, weil cout es als hexadezimal und nicht als dezimal wie alle anderen int ausgiebt.

    Folgich stimmt entweder die Praemisse nicht, oder es handelt sich um eine Ausnahme.
    Was ist der Fall ?



  • idrake schrieb:

    Wenn der Compiler bei "(char*) ptrx" alles an dieser Adresse (ptrx) als char behandelt, warum behandelt er dann nicht bei "(int*) ptrx" alles an dieser adresse als int ?

    Er tut es.
    Für den Compiler sind die nachfolgenden sizeof(char) Bytes an Adresse ptrx Bestandteile eines chars. Wenn man auf den Wert zugrefit werden die sizeof(char) * CHAR_BIT Bits als char interpretiert.
    Das Gleiche gilt für int. Die nachfolgenden sizeof(int) * CHAR_BIT Bits werden als int interpretiert.
    Der Stream (in deinem Fall cout) sieht jedoch, dass er einen int* ausgeben soll.
    (Achtung Metaphern, nicht instant-flamen, bitt ;))
    Der Stream schaut nach, wie er einen int* ausgeben soll. Der hat gespeichert: gebe einen int* als hexadezimale Zahl aus, die die Adresse angibt.
    Bei char* schaut der ebenfalls nach und sieht: Ein char* ist eine Zeichenkette. Sie geht von der Startadresse bis zu '\0'. Interpretiere den Speicherbereich von char* bis zur ersten '\0' als char und gebe alles aus.
    Aber für den Compiler werden beide gleich behandelt.

    Edit:
    Hättest du die Streams geschrieben, hättest du sagen können:
    Wenn du einen int* siehst, gebe den Wert aus, auf den int* zeigt.



  • Nathan schrieb:

    idrake schrieb:

    Wenn der Compiler bei "(char*) ptrx" alles an dieser Adresse (ptrx) als char behandelt, warum behandelt er dann nicht bei "(int*) ptrx" alles an dieser adresse als int ?

    Er tut es.
    Für den Compiler sind die nachfolgenden sizeof(char) Bytes an Adresse ptrx Bestandteile eines chars. Wenn man auf den Wert zugrefit werden die sizeof(char) * CHAR_BIT Bits als char interpretiert.
    Das Gleiche gilt für int. Die nachfolgenden sizeof(int) * CHAR_BIT Bits werden als int interpretiert.

    Super beschrieben !

    Der Stream (in deinem Fall cout) sieht jedoch, dass er einen int* ausgeben soll.
    (Achtung Metaphern, nicht instant-flamen, bitt ;))
    Der Stream schaut nach, wie er einen int* ausgeben soll. Der hat gespeichert: gebe einen int* als hexadezimale Zahl aus, die die Adresse angibt.
    Bei char* schaut der ebenfalls nach und sieht: Ein char* ist eine Zeichenkette. Sie geht von der Startadresse bis zu '\0'. Interpretiere den Speicherbereich von char* bis zur ersten '\0' als char und gebe alles aus.
    Aber für den Compiler werden beide gleich behandelt.

    Edit:
    Hättest du die Streams geschrieben, hättest du sagen können:
    Wenn du einen int* siehst, gebe den Wert aus, auf den int* zeigt.

    Also ist das so richtig:

    (char*) ptrx 
    --> Compiler erzeugt bestimmten Binaercode Y anhand des Interpretationsalgorithmus gehoerend zu: Gehe zu Adresse ptrx, interpretiere als char 
    --> dieser Binaercode Y wird dann an std::cout uebergeben
    --> std::cout sieht nach wie es Datentypen char* ausgeben soll und interpretiert dann Binaercode Y anhand der Interpretationsalgorithmen fuer "char*"
    


  • Es ist ganz einfach so, dass die C++ Streams ungefähr diese Operatoren für Zeiger haben:

    ostream& operator<<(ostream& o, const char* p)
    {
        // Gib alle Zeichen aus bis ein Nullbyte kommt.
        while(*p)
            o.put(*(p++));
        return o;
    }
    
    ostream& operator<<(ostream& o, const void* p)
    {
        // Betrachte den Zeiger als int und gebe den Zahlenwert (=die Speicheraddresse) aus.
        return o << (uintptr_t)p;
    }
    

    Schreibst du jetzt zb

    int* foo;
    cout << foo;
    

    Sucht der Compiler einen passenden Operator. Und das ist eben #2, da er int* nach void* konvertieren kann, hingegen nicht int* in char*.

    Der erste Operator existiert nur, damit du Stringliterale bequem ausgeben kannst:

    cout << "Hallo Welt !";
    

    Ruft #1 auf, da es die Funktion ist, die am Besten passt.



  • Ethon schrieb:

    ostream& operator<<(ostream& o, const void* p)
    {[b]
        // Betrachte den Zeiger als int und gebe den Zahlenwert (=die Speicheraddresse) aus.[/b]
        return o << (uintptr_t)p;
    }
    

    Aber wenn er den Zeiger als int betrachtet, warum wird es dann in Hexadezimal ausgegeben und nicht in Dezimal wie es cout bei int fuer gewoehnlich tut ?



  • Weil halt gespeichert ist, dass Zeigertypen hexadezimal ausgegeben werden sollen.



  • Super, vielen vielen Dank, Leute !

    Ich denke jetzt hab ich es endlich halbwegs durchschaut.

    Noch kurz zur Ueberpruefung, ob ich es wirklich richtig verstanden habe:

    idrake schrieb:

    Also ist das so richtig:

    (char*) ptrx 
    --> Compiler erzeugt bestimmten Binaercode Y anhand des Interpretationsalgorithmus gehoerend zu: Gehe zu Adresse ptrx, interpretiere als char 
    --> dieser Binaercode Y wird dann an std::cout uebergeben
    --> std::cout sieht nach wie es Datentypen char* ausgeben soll und interpretiert dann Binaercode Y anhand der Interpretationsalgorithmen fuer "char*"
    


  • Jein.
    Der Compiler erzeugt Maschinencode mit Anweisungen, wie der Speicherbereich zu interpretieren ist.
    Die Auswahl des passenden operators (Ethon hat dir gezeigt, wie die aussehen) geschieht aber noch zur Compilezeit, nach strikt definierten Überladungsregeln. Es wird dann Maschinencode erzeugt, der den operator ausführt.



  • idrake schrieb:

    Ethon schrieb:

    ostream& operator<<(ostream& o, const void* p)
    {[b]
        // Betrachte den Zeiger als int und gebe den Zahlenwert (=die Speicheraddresse) aus.[/b]
        return o << (uintptr_t)p;
    }
    

    Aber wenn er den Zeiger als int betrachtet, warum wird es dann in Hexadezimal ausgegeben und nicht in Dezimal wie es cout bei int fuer gewoehnlich tut ?

    Weil ich zu faul war eine korrekte Implementierung zu tippen. 🤡
    Der Operator enthält natürlich noch Magie, um den Stream auf hex zu stellen und danach auf das, was er davor war.


Anmelden zum Antworten