CPP-Klausur



  • Hallo Community,

    ich schreibe demnächst eine CPP-Klausur und habe einen Code, den ich nicht ganz verstehe. Habe es nicht so drauf mit Pointer und Adressen. Könnte mir hier jemand erklären, was in diesem Code gemacht wird?

    #include <iostream>
    using namespace std;
    
    int main() {
    	int m=44;
    	int *p=&m;
    	int &r=m;
    	int n=(*p)++;
    	int *q=p-1;
    	r=*(--p)+1;
    	++*q;
    	cout << n << endl << &m << endl << *p << endl << r << endl << *q;
    
    	return 0;
    }
    

    Ich weiss nur, dass als erstes "44" ausgegeben wird, da bei der Variable n der Wert ausgegeben wird, der bei *p gespeichert ist. Auf die anderen Variablen komme ich leider nicht drauf. 😕
    Wäre echt lieb, wenn mir hier jemand dabei helfen könnte. 😞
    ~
    Edit, Arcoth: Code-Tags~



  • Hi,

    bin selber noch C++ Neuling, versuchs aber trotzdem mal.

    Ausgabe von n=44, da int n=(*p)++ den Inhalt von p erst nach dieser Zeile erhöht

    &m gibt die Speicheradresse von m wieder (0x....)

    int *p=&m : p wird die Adresse von m zugewiesen
    (int n=(p)++ : Wert an dieser Adresse wird um 1 erhöht auf 45, nach dieser Zeile. Aber in diesem Fall für die Ausgabe irrelevant)
    r=
    (--p)+1; : p wird um 1 erniedrigt(also die Adresse auf die er zeigt, nicht der Inhalt, Rest der Zeile stellt keine Manipulation an p dar)
    => Ausgabe *p ergibt irgendeinen nicht definierten Wert, da er auf eine Adresse zeigt, der kein Wert zugewiesen wurde

    int &r=m : Referenz r auf Variable m wird erzeugt
    r=*(--p)+1 : r erhält folgenden Wert: Zeiger p wird erniedrigt und dessen Inhalt ausgelesen, dieser Inhalt wird noch um 1 erhöht
    => Ausgabe r ergibt wieder irgendeinen nicht definierten Wert

    int *q=p-1 : Pointer *q wird definiert und initialisiert auf die Adresse "links" von p, welche wieder mit irgendeinem nicht definierten Wert belegt ist
    ++*q: Inhalt der Adresse von q wird um 1 erhöht
    => Ausgabe *q ergibt nicht definierten Wert


  • Mod

    Das Programm erzeugt leider undefiniertes Verhalten, und ist damit gänzlich ungeeignet. Wer hat die Aufgabe geschrieben?



  • soweit ich das verstehe, meint Arcoth solche Sachen:

    int n = (*p)++;
    

    weil nicht definiert ist, ob das ++ vor oder nach der Zuweisung geschieht. Korrigiert mich wenns falsch ist.



  • Wann "++" ausgewertet wird ist in diesem Fall klar definiert. Nämlich nachdem der Wert von (*p) ermittelt wird -- steht ja hinten und nicht vorn dran.

    Was zu UB führt ist dass hier "ungültige" Zeiger erzeugt und dann obendrein noch dereferenziert werden.



  • hardware schrieb:

    soweit ich das verstehe, meint Arcoth solche Sachen:

    int n = (*p)++;
    

    weil nicht definiert ist, ob das ++ vor oder nach der Zuweisung geschieht. Korrigiert mich wenns falsch ist.

    ist falsch.

    int main() { 
        int m=44; 
        int *p=&m; 
    //    int &r=m; 
    //    int n=(*p)++; 
        int *q=p-1; 
    //    r=*(--p)+1;
        ++*q;
    //    ^ (zeigt eins vor 'int m' -> UB)
        cout << n << endl << &m << endl << *p << endl << r << endl << *q; 
    //                                                                ^ (zeigt eins vor 'int m' -> UB)
        return 0; 
    }
    


  • ok danke, da hab ich was falsches gemerkt, sehe jetzt auch das eigentliche UB.


  • Mod

    Das UB passiert beim p-1, denn 1 von p abzuziehen ist hier nicht erlaubt.



  • Arcoth schrieb:

    Das UB passiert beim p-1, denn 1 von p abzuziehen ist hier nicht erlaubt.

    N3337 schrieb:

    5.7 Additive operators
    2) `For subtraction, one of the following shall hold:

    — both operands have arithmetic or unscoped enumeration type; or

    — both operands are pointers to cv-qualified or cv-unqualified versions of the same completely-defined

    object type; or

    — the left operand is a pointer to a completely-defined object type and the right operand has integral or

    unscoped enumeration type.

    `

    [...]

    1. For the purposes of these operators, a pointer to a nonarray object behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type.

    so weit ich das sehe, ist also T a; T* b = &a-1; kein UB sondern lediglich impl.defined behaviour (was in b bzw *b steht). UB wirds damit erst beim Zugriff auf b.

    oder lieg ich da falsch?

    bb



  • #include <iostream> 
    using namespace std;
    
    int main() {
    	int m = 44;
    	int *p = &m;
    	int &r = m;
    	int n = *(p)++;
    	int *q = p - 1;
    	r = *(--p) + 1;
    	++*q;
    	cout << n << endl << &m << endl << *p << endl << r << endl << *q;
    
    	return 0;
    }
    

    So funktioniert es einwandfrei... 🙄



  • unskilled schrieb:

    so weit ich das sehe, ist also T a; T* b = &a-1; kein UB sondern lediglich impl.defined behaviour (was in b bzw *b steht). UB wirds damit erst beim Zugriff auf b.

    oder lieg ich da falsch?

    Soweit ich weiss liegt du da falsch.
    IIRC ist sogar das UB:

    int* p = new int(42);
    delete p;
    int* p2 = p; // <-- UB
    

    Und analog dazu auch

    int* p = new int(42);
    int* p2 = p - 1; // <-- UB
    

    und auch

    int i;
    int* p = &i;
    int* p2 = p - 1; // <-- UB
    


  • mrv3112 schrieb:

    #include <iostream> 
    using namespace std;
    
    int main() {
    	int m = 44;
    	int *p = &m;
    	int &r = m;
    	int n = *(p)++;
    	int *q = p - 1;
    	r = *(--p) + 1;
    	++*q;
    	cout << n << endl << &m << endl << *p << endl << r << endl << *q;
    
    	return 0;
    }
    

    So funktioniert es einwandfrei... 🙄

    Na sowas.
    Ist ja auch ganz anderer Code.
    *(p)++ ist nicht das selbe wie (*p)++

    UB ist es allerdings vermutlich immer noch. (Oder gilt die "one past last" Ausnahme für alle Objekte und nicht nur für Arrays?)



  • hustbaer schrieb:

    IIRC ist sogar das UB:

    int* p = new int(42);
    delete p;
    int* p2 = p; // <-- UB
    

    Und analog dazu auch

    int* p = new int(42);
    int* p2 = p - 1; // <-- UB
    

    und auch

    int i;
    int* p = &i;
    int* p2 = p - 1; // <-- UB
    

    Kannst du mir nochmal erklären, was das UB jetzt genau auslöst?
    Liegt das hier daran, dass mir der der Speicher vor p ggf nicht gehört oder ist das generell nicht erlaubt?

    int i1;
    int i2;
    int* p = &i2;
    int* pvor = p - 1; // <-- auch UB ?
    

  • Mod

    int* p = new int(42);
    delete p;
    int* p2 = p;
    

    UB in C++03, implementation-defined ab C++11 (using invalid pointer value)

    int* p = new int(42);
    p - 1;
    

    UB da p und p-1 nicht auf Elemente (bzw. 1 nach dem letzten Element) des gleichen Arrays zeigen

    int i1;
    int i2;
    int* p = &i2;
    int* pvor = p - 1; // <-- auch UB ?
    

    Kein Unterschied. Die Tatsache, dass eine bestimmte Implementation sich möglicherweise dazu entscheidet, i1 unmittelbar vor i2 anzulegen (und dann ist das Ergebnis nat. definiert, weil p gleichzeitig auf eins-hinter-i1 zeigt), bedeutet nicht, dass der Standard das generell verlangt.



  • Ok, danke für die Klarstellung.



  • hustbaer schrieb:

    UB ist es allerdings vermutlich immer noch. (Oder gilt die "one past last" Ausnahme für alle Objekte und nicht nur für Arrays?)

    siehe mein post. gilt auch für (nicht-array) objekte. sollte somit well defined sein:

    #include <iostream>
    using namespace std;
    
    int main() {
    	int m = 44;
    	int *p = &m; //*p = &m
    	int &r = m; //r = m
    	int n = *(p)++; //p = &m+1; n = 44
    	int *q = p - 1; //q = &m
    	r = *(--p) + 1; //p = &m //r=m=45
    	++*q; //q=r=m=46
    	cout << n << endl << &m << endl << *p << endl << r << endl << *q; //ok
    //44
    //[&m] (adresse von m)
    //46
    //46
    //46
    	return 0;
    }
    

Anmelden zum Antworten