Speicherung von Variablen



  • Dieser Beitrag wurde gelöscht!


  • Sie haben welche.



  • Hallo, zunächst mal muss ich sagen, dass ich auch Anfänger bin. Meine Antwort kann also Fehler beinhalten.

    Soweit ich aber weiß funktioniert es so, dass der Compiler entscheidet wie groß ein Datentyp zu sein hat. Es ist zum Beispiel bei Integern nur genormt, dass zum Beispiel ein short int <= int <= long int sein muss.

    Warum nun das ganze? Warum überhaupt diese Datentypen?
    Dein Rechner muss einfach Wissen, wieviel Platz er im Arbeitsspeicher für deinen Wert reservieren muss. Es gibt Datentypen die wenig Platz beanspruchen zum Beispiel ein bool oder andere die größer sind zum Beispiel ein long long. Du musst selbst entscheiden, wann du welchen Datentyp benutzt. Willst du zum Beispiel nur eine kleine Zahl speichern, zum Beispiel von 0 - 100, benötigst du jetzt zum Beispiel keinen long. Ein short würde dafür auch ausreichen. Aber du musst auf passen, dass die Grenzen der Datentypen nie überschritten werden. Überlege also ob später vielleicht noch mit der Variable gerechnet wird und dabei vielleicht die Grenzen über- oder unterschritten wird.
    Du kannst übrigens abfragen wo die Grenzen deines Datentyps liegen. Hier ein Beispiel:

    #include <iostream>
    #include <limits>
    
    int main() {
    	std::cout << "Minimum int: " << std::numeric_limits<int>::min() << std::endl;
    	std::cout << "Maximum int: " << std::numeric_limits<int>::max() << std::endl;
    }
    


  • Warum löschst du immer deine Beträge/Themen?


  • Mod

    Die superschlaue Antwort ist erst einmal: Es gibt keinen direkten Zusammenhang. Variablen in einem Programm haben keine direkte Repräsentation in dem, was die Technik am Ende macht. Sie sind ein gutes Hilfsmittel für den Programmierer, die es ihm erlauben, komplexere Programmlogik in einer Hochsprache zu schreiben, die unterliegende Maschine funktioniert dann anders.

    Außer, dass die obige Antwort zwar komplett richtig ist, aber doch in die Irre führt, denn irgendwann speichert die Maschine ja doch einmal irgendwie Daten. Und oft könnte man sogar ziemlich genau auf eine konkrete Transistorgruppe im Speicherriegel zeigen und sagen "Dort! Da ist die Variable XYZ gespeichert". Bei einer recht hardwarenahen Sprache kann man sogar ziemlich genau sagen, wie konkret die Bits und Bytes eines bestimmten Datentyps aussehen. Die jetzt alle genau zu beschreiben spare ich mir mal aber Fließkommazahlen haben eine konkrete Norm (IEEE 754), an die sich eigentlich jeder hält. Bei Integern kann man noch diskutieren, in welcher Reihenfolge (im Sinne von "von niedrigen Adressen zu hohen Adressen hin") man die "Ziffern" speichern soll, aber jede konkrete Maschine (im Sinne von Prozessorfamilie) hat eine feste Art und Weise, wie si das handhabt. Zeichen sind auch Integer, bloß kürzer. Komplexe Datentypen sind irgendwie aus diesen Basistypen zusammengesetzt, etc.

    Es ist halt nichts konkret garantiert und gerade bei einfachen Beispielen hat man ganz oft Fälle, wo die zweite Antwort nicht gilt, weil der Compiler ganz clever alles komplett anders macht als man denken würde. Daher die erste Antwort.

    Zu Bits und Bytes: Auch hier wieder die schlaue Antwort: Es gibt keine Bits. Zumindest nicht auf Programmierebene. Das Byte ist per Definition die kleinste Einheit, auf die du als Programmierer Zugriff hast. Wenn du dir die Bits eines Zahlenwertes ausrechnest, dann ist das rein mathematisch zu verstehen (also das mit den Potenzen, etc.) und hat mit dem konkreten Zustand in der Hardware nichts zu tun.

    Und wieder relativierend: Natürlich liegen da irgendwo auch mal konkret Drähte, auf denen entweder Spannung liegt oder nicht. Man muss dafür aber schon ziemlich nahe mit dem Elektronenmikroskop ran gehen. Das kannst du dann die Bits der Hardware nennen. Aber als Programmierer einer Hochsprache kommst du da nicht dran, dafür musst du schon Prozessorhersteller sein. Aber natürlich haben diese Hardwarebits die gleiche Anordnung, als wenn man sich die mathematischen Bits ausrechnet. Denn das ist halt einfach die naheliegende Art und Weise so etwas zu bauen.



  • Also wenn ich dass das richtig verstehe, und ich mir einen integer mit zum beispiel 5 initialisiere, kann ich sozusagen noch nicht davon ausgehen, dass dieser Wert wirklich irgendwo im RAM steht oder? Kann ich davon ausgehen, wenns im Assembler-Code steht? Das ist nur hypothetisch gefragt.



  • @zhavok sagte in Speicherung von Variablen:

    Also wenn ich dass das richtig verstehe, und ich mir einen integer mit zum beispiel 5 initialisiere, kann ich sozusagen noch nicht davon ausgehen, dass dieser Wert wirklich irgendwo im RAM steht oder?

    Genau. zB: hier steht direkt das Ergebnis von 5 * value als Anweisung im Codesegment: mov esi, 25.

    @zhavok sagte in Speicherung von Variablen:

    Kann ich davon ausgehen, wenns im Assembler-Code steht?

    In der Regel schon.



  • Nein, davon kannst du nicht ausgehen. Wenn du eine Variable nur initialisierst wird der Compiler die zum Beispiel weg optimieren.
    Generell abstrahieren Hochsprachen so weit, dass der Code des Entwicklers auf unterschiedlicher Hardware die gleichen Ergebnisse liefert.
    Wenn man es genau nimmt, ist Assembler schon eine Abstraktion des eigentlichen Maschinencode.

    Wenn du mit Assembler eine 5 an eine Speicherstelle schreibst kannst du dir (hoffentlich) sicher sein, dass an der Speicherstelle eine Bit-Kombination steht, die genau der Assembler wieder als 5 interpretieren kann.


  • Mod

    @zhavok sagte in Speicherung von Variablen:

    Also wenn ich dass das richtig verstehe, und ich mir einen integer mit zum beispiel 5 initialisiere, kann ich sozusagen noch nicht davon ausgehen, dass dieser Wert wirklich irgendwo im RAM steht oder? Kann ich davon ausgehen, wenns im Assembler-Code steht? Das ist nur hypothetisch gefragt.

    Welche Anweisung soll dafür denn im Assemblercode stehen?

    MOV irgendeine_adresse, 5
    

    ?
    Vielleicht steht so etwas da drin, vielleicht auch nicht. Schau hier: https://godbolt.org/g/iVvMFN
    Aber gerade bei diesen einfachen Beispielen hat man halt sehr oft, dass eine Variable gar keine konkrete Adresse im Speicher ist, sondern dass sie vollständig in der Logik des Gesamtprogramms aufgeht. Obiges Beispiel hinter dem Link ist sehr bewusst so gewählt, dem Compiler solche Optimierungen unmöglich zu machen. Eine kleine Änderung und alles ist komplett weg:
    https://godbolt.org/g/Qckf24
    Oder etwas komplexer:
    https://godbolt.org/g/nh8PF8
    Weder i noch result noch die 0 oder die 5 existieren hier irgendwo im Maschinencode. Sie sind bloß indirekt da, insofern als dass das Programm sich exakt genau so verhält, als wären sie da gewesen. Und eax ist auch nirgendwo im RAM, sondern das ist eine Speicherzelle in der CPU. Der Wert bleibt dort bloß ganz flüchtig, bis der aufrufende Code der Funktion wahrscheinlich direkt damit weiter arbeitet, ohne dass der Wert 10 jemals auch nur die CPU verlässt.


  • Mod

    @swordfish sagte in Speicherung von Variablen:

    Genau. zB: hier steht direkt das Ergebnis von 5 * value als Anweisung im Codesegment: mov BYTE PTR [rsp+15], 10.

    Da rechnest du besser noch einmal nach 🙂

    Das Ergebnis steht in Zeile 3: mov esi, 25. Das mov BYTE PTR [rsp+15], 10 ist das '\n'.



  • @seppj sagte in Speicherung von Variablen:

    Da rechnest du besser noch einmal nach

    Ich sag nichts, wenn Du nichts sagst 👍 = ? :face_with_stuck-out_tongue_winking_eye:



  • Was bekomme ich dann also zurück, wenn ich zum Beispiel folgendes schreibe:

    #include <iostream>
    
    int main() {
    	int val = 18;
    	std::cout << &val << std::endl;
    }
    

    Bisher bin ich immer davon ausgegangen, dass es sich um die Adresse im Arbeitsspeicher handelt :smiling_face_with_open_mouth_cold_sweat: .
    Und warauf greife ich eigentlich mit

    WriteProcessMemory(handle, (LPVOID)0x861E74, &val, sizeof(val), 0);
    

    zu? Fragen über Fragen 😂



  • @zhavok sagte in Speicherung von Variablen:

    Was bekomme ich dann also zurück, wenn ich zum Beispiel folgendes schreibe:

    Wenn Du die Adresse einer Variable nimmst, dann "zwingst" Du den Compiler, auch Speicher dafür zu besorgen.

    @zhavok sagte in Speicherung von Variablen:

    Und warauf greife ich eigentlich mit WriteProcessMemory() zu?

    Das steht in der Dokumentation der Funktion.



  • @seppj sagte in Speicherung von Variablen:

    Welche Anweisung soll dafür denn im Assemblercode stehen?

    MOV irgendeine_adresse, 5
    

    ?

    Na selbst dann kommts evtl. nie in den RAM, das wuerde diesen Befehl sonst um Groessenordnungen ausbremsen.



  • @tggc sagte in Speicherung von Variablen:

    @seppj sagte in Speicherung von Variablen:

    Welche Anweisung soll dafür denn im Assemblercode stehen?

    MOV irgendeine_adresse, 5
    

    ?

    Na selbst dann kommts evtl. nie in den RAM, das wuerde diesen Befehl sonst um Groessenordnungen ausbremsen.

    Naja ich denke man kann bei Überlegungen dieser Art die diversen Caches schonmal vernachlässigen.



  • Aber man sollte die Prozessor-Register (d.h. bes. "Datenregister") hierbei noch erwähnen (wie man auch bei den geposteten Assembler-Codes sieht), d.h. bei der Optimierung werden lokale Variablen sowie Funktionsparameter (so gut es geht) auf diese verteilt, anstatt im RAM (z.B. Stack) abgelegt zu werden. Dafür gab es früher ja explizit in C und C++ das registerSchlüsselwort, bevor die Compileroptimierungen selber diese Entscheidung übernommen haben).



  • @hustbaer sagte in Speicherung von Variablen:

    @tggc sagte in Speicherung von Variablen:

    @seppj sagte in Speicherung von Variablen:

    Welche Anweisung soll dafür denn im Assemblercode stehen?

    MOV irgendeine_adresse, 5
    

    ?

    Na selbst dann kommts evtl. nie in den RAM, das wuerde diesen Befehl sonst um Groessenordnungen ausbremsen.

    Naja ich denke man kann bei Überlegungen dieser Art die diversen Caches schonmal vernachlässigen.

    Also oben war konkret die Frage obs im RAM steht. Entweder wir sind hier auf konzeptioneller Ebene und sagen, jede Info liegt in einem Art "RAM" - wir koennen aber nicht wissen wo der physikalisch ist. Oder wir unterscheiden hier genauer, dann sind wir aber mit Register/RAM aber noch lange nicht fertig - das wollte ich hier nur andeuten und das ist auch relativ wichtig wenn man per ASM programmiert.



  • @TGGC
    Naja gut da ist jetzt die Frage wie man "RAM" definiert.

    Ich meine auf jeden Fall: man kann auch wenn man Assembler diskutiert oft vernachlässigen dass es mehrere Cache-Ebenen zwischen der CPU und dem letztendlichen Hauptspeicher gibt. Weil die transparent sind. Dort wo der C(++) Programmierer sich meist nicht weiter darum kümmern muss ob seine Variable jetzt in einem Register liegt oder im Speicher oder gar nirgendwo, dort muss der Assembler-Programmierer sich meist nicht darum kümmern ob etwas in einem der Caches liegt oder (nur) im Hauptspeicher. Meist reicht einfach die Info dass es im Speicher an Adresse so-und-so liegt.

    Oder anders gesagt: es gibt nicht nur "die" konzeptionelle Ebene, es gibt davon viele. Eben auch die wo der Assembler Programmierer nur mit dem Konzept Speicher arbeitet und ignorieren kann wie das mit den Caches evtl. gehen könnte.

    Für Performance-Optimierung ist es natürlich wichtig zu wissen wie Caches üblicherweise funktionieren, aber ansonsten muss man mMn. nichtmal wissen dass es sie überhaupt gibt. Zumindest nicht so lange man Userland Code schreibt.



  • @hustbaer
    Ja, natuerlich. Wir sind hier aber im C++ Forum, also sehe ich wenig Sinn eine Frage nun gerade nur aus Sicht eines Assemblerprogrammierers zu beantworten.

    Und wie ich schon sagte, stimmt das auch für ASM nicht. Denn "in Adressraum schreiben" bedeutet auch in Assembler nicht automatisch "in RAM schreiben", da einem recht schnell das Konzept begegnet, in den normalen Adressraum andere Sachen einzublenden.



  • Ich merke schon. So einfach wie ich mir das vorgestellt habe läuft das leider nicht mit Variablen und dem RAM. Vielen Dank für eure Antworten.



  • @tggc sagte in Speicherung von Variablen:

    Und wie ich schon sagte, stimmt das auch für ASM nicht. Denn "in Adressraum schreiben" bedeutet auch in Assembler nicht automatisch "in RAM schreiben", da einem recht schnell das Konzept begegnet, in den normalen Adressraum andere Sachen einzublenden.

    Wenn man die Adresse von einer Funktion ala malloc bekommen hat, und dann da reinschreibt, dann bedeutet das schon "in RAM schreiben". Sonst ist irgendwo irgendwas gröber schief gelaufen.

    Mir geht's aber einfach nur darum dass es viele "Zwischenebenen" gibt auf denen man sinnvoll diskutieren/argumentieren kann. Und dass man nicht von komplett abstrakt gleich zu einer bis ins letzte detailierten Beschreibung der Hardware wechseln muss. Bzw. mMn. ist das sogar kaum jemals sinnvoll. Schon alleine deswegen weil man die Hardware meist gar nicht so genau kennt.

    Ist aber auch nicht so wichtig 🙂


Anmelden zum Antworten