volatile



  • #include <iostream>
    #include <conio.h>
    
    using namespace std;
    
    int main()
    {
      int a, b;
      volatile int c, d;
      cout << &a << ' ' << &b << ' ' << &c << ' ' << &d << endl;
      cout <<  a << ' ' <<  b << ' ' <<  c << ' ' <<  d << endl;
      volatile int *pc, *pd;
      pc = &c;
      pd = &d;
      cout << pc << ' ' << pd;
    
      getch();
    }
    

    0x22ff5c 0x22ff60 1 1
    2013306216 2293664 4370432 4198592
    1 1

    was bedeutet hier die 1 ?



  • Kann da keiner Hilfestellung geben?



  • @work schrieb:

    was bedeutet hier die 1 ?

    ...dass 'cout' schrott ist.
    so:

    printf ("%p %p %p %p\n", &a, &b, &c, &d);
    

    sieht's schon besser aus 😉



  • net schrieb:

    @work schrieb:

    was bedeutet hier die 1 ?

    ...dass 'cout' schrott ist.
    so:

    printf ("%p %p %p %p\n", &a, &b, &c, &d);
    

    sieht's schon besser aus 😉

    Oder dass man kein C++ kann.
    Denn es wurde nicht nach void* gecastet.



  • Was bedeutet "volatile"?
    Wo kann man "volatile" sinnvoll einsetzen? Beispiele ?



  • rewe schrieb:

    Was bedeutet "volatile"?
    Wo kann man "volatile" sinnvoll einsetzen?

    'volatile' sagt dem compiler, dass er variablen nicht in registern halten soll (optimierung), sondern bei jedem zugriff die speicheradresse benutzen soll. ist manchmal nötig bei multithreading und interrupt-routinen, damit die sich nicht gegenseitig die inhalte überbügeln



  • @Shade Of Mine:

    #include <iostream>
    
    int main()
    {
      int a;
      volatile int b;
      std::cout << &a << ' ' << const_cast<int*>(&b);
      std::cin.get(); 
    }
    

    Umwandlung nach void* ist nicht notwendig, man muss allerdings den Qualifizierer "volatile" vom Datentyp entfernen (beim Qualifizierer "const" ist das nicht notwendig). http://www.henkessoft.de/cpp_school/const_cast.html

    Warum im anderen Fall die 1 erscheint ist mir allerdings unklar (dürfte in der Tat an cout liegen).

    Lässt man obiges Programm laufen, erhält man z.B. folgende Ausgabe:
    0x22ff78 0x22ff7c (aufsteigend, im Stack nicht normal)

    Kommentiert man volatile aus, so erhält man folgende Ausgabe:
    0x22ff7c 0x22ff78 (absteigend, im Stack normal)

    Kann das jemand erklären?



  • cout << boolalpha;
    

    dürfte die sache klar machen.

    const_cast ist außerdem nur dazu da, cv-qualifizierer wegzucasten, die nicht tatsächlich da sind.



  • Erhard Henkes schrieb:

    Lässt man obiges Programm laufen, erhält man z.B. folgende Ausgabe:
    0x22ff78 0x22ff7c (aufsteigend, im Stack nicht normal)
    Kommentiert man volatile aus, so erhält man folgende Ausgabe:
    0x22ff7c 0x22ff78 (absteigend, im Stack normal)

    die reihenfolge auf dem stack spielt eigentlich keine rolle. die variablen werden nicht mit 'push' auf den stack befördert, sondern es wird was vom stack pointer abgezogen, um platz zu machen (sub esp,8 für 2 ints unter win). mingw (bei mir) macht z.b. 22ff8c und 0x22ff88, vs.5 macht 0012FF6C und 0012FF60 (__int64 aber wieso 12 bytes abstand?), egal ob 'volatile' oder nicht.

    davie schrieb:

    cout << boolalpha;
    

    dürfte die sache klar machen.

    dass 'cout' bool meint?
    dummes cout ⚠



  • net schrieb:

    dass 'cout' bool meint?
    dummes cout ⚠

    Was hat das mit cout zu tun? Nix. Das liegt einzig an der Art und Weise wie C++ Überladung auflöst und welche impliziten Konvertierungen dabei angewendet werden.

    Es gibt zwar eine Funktion operator<<(const void*) und eine implizite Konvertierung von T* nach void*, da C++ nun aber mal versucht typsicher zu sein und deshalb cv-Qualifizierungen nur implizit hinzugefügt, nicht aber entfernt werden können, ist diese Funktion für ein Argument vom Typ volatile T* nicht aufrufbar.
    Jetzt gibt es noch ein paar andere operator<<-Funktionen (bzw. Methoden) die einen Zeiger erwarten, zu keinem davon ist aber ein volatile T* kompatibel.
    Bleiben die operator<<-Funktionen für die primitiven Typen. Die einzige legale Konvertierung für einen Pointer die hier möglich ist, ist die nach bool. Diese Konvertierung ist in diesem Fall sicher etwas unintuitiv. Auf der anderen Seite hat man schon vor Tausend Jahren Code à la:

    void func(const char* s)
    {
        if (s)
            ...
    }
    

    und dazu braucht man diese Konvertierung nun mal.

    Und nun zurück zum "dummen" cout. Das schöne an den iostreams ist doch, dass man sie beliebig erweitern kann:

    template <class T>
    ostream& operator<<(ostream& os, volatile T* ptr)
    {
         // ...
         os << const_cast<T*>(ptr);
         return os;     
    }
    

    const_cast ist außerdem nur dazu da, cv-qualifizierer wegzucasten, die nicht tatsächlich da sind.

    Das ist nicht ganz richtig. Erst eine bestimmte Form des Zugriffs ist Verboten. Wenn du also ein const int hast kannst du das const problemlos wegcasten solange du nur lesend auf das Ergebnis zugreifst. Das Schreiben führt hingegen zu UB.
    In diesem Fall kannst du das volatile problemlos wegcasten: es bezieht sich auf T bezieht, nicht auf den Pointer und die Ausgabe dereferenziert den Pointer nicht.

    BTW: Der Originalcode hat soewieso UB. Und zwar hier:

    cout <<  a << ' ' <<  b
    

    Hier wird auf nicht initialisierte ints zugegriffen. Keine gute Idee und nicht im Sinne des C++ Standards.



  • Wenn ich nen char[] volatile mache, dann kann ich mir sicher sein, dass der String nicht mit anderen zusammengefasst wird, oder?



  • Hm schon, aber was hat das mit volatile zu tun? Das einzige was zusammengelegt werden kann sind Stringliterale.



  • ...



  • Ich wollte das nur wissen, da ich mir vor kurzem überlegte wie man nen string in ner exe verändern kann und dazu müsste man halt sicher sein, dass sich keine strings überlappen.



  • Was heißt "String in ner exe verändern" und "sich überlappen"?



  • Ganz einfach exe öffnen String ändern und speichern.

    Mit überlappen meinte ich, dass Compiler doch beim optimieren z.B. Eis und Schokoladeneis zusammenfassen können, da ja die gleichen Buchstaben darin vorkommen, zumindest hab ich das so schon gelesen.



  • Lohnt sich das String zusammenlegen denn heute wirklich noch? Wo kann man so einen Kram nachlesen?



  • SirLant: "Eis" kommt in "Schokoladeneis" aber nicht vor 🙄



  • @SirLant
    Beim GCC gibt es die Option -fwritable-strings damit könntest du so etwas erreichen. Aber seit dem 3.4er ist die depprecated und im 4.0er wird sie entfernt (dem nächsten Release).

    Im normalfall sollte sich so etwas eh nicht lohnen, außer du programmierst irgend einen extrem limitierten µC



  • HumeSikkins schrieb:

    Die einzige legale Konvertierung für einen Pointer die hier möglich ist, ist die nach bool.

    das ist aber wirklich dämlich gemacht. cout äääh der compiler hätte wenigstens ein warning ausspucken können, zumal er ohne 'volatile' auch gut klar kommt. ein pointer hat mit einem bool wirklich nix gemeinsam.

    HumeSikkins schrieb:

    Diese Konvertierung ist in diesem Fall sicher etwas unintuitiv.

    etwas? hätte er wenigstens 'unsigned int' draus machen können

    HumeSikkins schrieb:

    Auf der anderen Seite hat man schon vor Tausend Jahren Code à la:

    void func(const char* s)
    {
        if (s)
            ...
    }
    

    und dazu braucht man diese Konvertierung nun mal.

    das stammt aus der vor-bool zeit. null ist 'falsch' alles andere 'wahr'. das gehört zu den altlasten, die c++ mit sich rumschleppt. in java z.b. geht das nicht. wenn es da char* gäbe müsste man schreiben 'if (s != null)'


Anmelden zum Antworten