Speicherbelegung von Objekten



  • Hallo,

    ich habe eine Frage zu der Speicherbelegung von Objekten einer Klasse. Angenommen meine Klasse (bzw. Struktur) sieht so aus:

    struct dummy_struct {
    
    static int i;
    static double x;
    
    int j;
    double y[10];
    int k;
    
    }
    

    Die statischen Elemente i und x werden nur einmal gespeichert, unabhängig davon, wie viele Objekte der Klasse ich nun erzeuge. Die eigentliche Frage ist: Kann ich dann davon ausgehen, dass j, y[] und k im Speicher direkt hintereinander abgelegt sind, und zwar in der Reihenfolge wie sie deklariert werden? Und belegt ein Objekt dieser Klasse dann auch nur genau so viel Speicher, wie j, y[] und k zusammen belegen würden? Ich muss dies wissen, weil ich mit dem Message Passing Interface Objekte einer Klasse mit Hilfe eines derived datatypes verschicken möchte.

    Vielen Dank!



  • Nein, siehe Padding.


  • Administrator

    Um das Padding zu umgehen, könnte man auf dem MSVC wohl #pragma pack(1) verwenden:
    http://msdn.microsoft.com/en-us/library/2e70t5y1.aspx

    Allerdings ist die Reihenfolge im Speicher von C++, soweit ich mich erinnere, nicht garantiert. Aber war es nicht in C? Also:

    extern "C"
    {
    
    #pragma pack(1)
    
    struct dummy_struct
    {
      static int i;
      static double x;
    
      int j;
      double y[10];
      int k;
    };
    
    }
    

    Aber da lehne ich mich weit aus dem Fenster. Mit Sicherheit kann ich es nicht sagen.

    Grüssli



  • Der C++-Standard meint dazu:

    9.2/12 schrieb:

    Nonstatic data members of a (non-union) class declared without an intervening access-specifier are allocated so that later members have higher addresses within a class object. The order of allocation of nonstatic data members separated by an access-specifier is unspecified (11.1). Implementation alignment requirements might cause two adjacent members not to be allocated immediately after each other; so might requirements for space for managing virtual functions (10.3) and virtual base classes (10.1).

    Also ist die Reihenfolge beim gleichen Zugriffsspezifizierer festgelegt. Das Padding kann wohl eher nicht compilerunabhängig angepasst werden...



  • Vielen Dank, mir ist einiges klar geworden. Ich werde wohl anderweitig sicherstellen müssen, dass die Daten in der richtigen Reihenfolge versendet werden.



  • Für alle, die es interessiert:
    Ein MPI derived datatype lässt sich aus einer C++ Klasse erstellen, indem man von der Funktion MPI_Address gebraucht macht. Dann ist es irrelevant, wie die Elemente eines Objekts der Klasse im Speicher liegen.



  • Timm1981 schrieb:

    Für alle, die es interessiert:
    Ein MPI derived datatype lässt sich aus einer C++ Klasse erstellen, indem man von der Funktion MPI_Address gebraucht macht. Dann ist es irrelevant, wie die Elemente eines Objekts der Klasse im Speicher liegen.

    Wo hast du denn das her? Glaube kein Wort davon.

    FYI: sog. "PODs" sind in C++ "layout compatible", d.h. haben dasselbe Speicher-Layout wie sie es in C hätten. Was PODs genau sind bitte woanders nachlesen. "static" Member dürften IMO nicht dazu führen dass aus einem POD ein nicht-POD wird.



  • Ich denke, es gibt ein kleines Missverständnis. Die Aussage, die ich aufgeschrieben habe, kommt aus der MPI-Dokumentation. Worauf ich hinauswollte: Mit der Address-Funktion von MPI lassen sich die Speicheradressen der Members eine Klasse bestimmen. Somit ist es letztlich egal, wo und wie im Speicher die Daten liegen, da MPI dank Address die Daten dort im Speicher findet, wo sie auch liegen. Ich muss also keinerlei Annahmen darüber machen, in welcher Form die Daten im Speicher abgelegt sind.



  • Ich hab auch noch einmal eine Frage, wenn ich jetzt ein POD-Type habe wie zum Beispiel folgendes:

    struct Info
    {
        int dies;
        int that;
        int bla;
        int blub;
    };
    

    Ist es dann der Fall, dass alle Variablen hintereinander im Speicher liegen? Und was ist wenn ich bei "blub" den Typ von int nach char verändere? Ich bin jetzt ein wenig verwirrt, denn hier zum Beispiel, unter "Reading and storing a MD2 model" wird einfach an die Adresse von dem Header geschrieben. Wäre dann ganz schön ungünstig wenn die Variablen nicht hintereinander im Speicher liegen.



  • Ich gehe ganz stark davon aus, dass die Daten seriell im Speicher liegen, und zwar direkt hintereinander (bis auf Padding). Und ich gehe auch stark davon aus, dass dies immer identisch ist, wenn man mehrere Objekte derselben Klasse hat, d.h. die relativen Adressen in verschiedenen Objekten sind gleich. Das sollte sich auch nicht ändern, wenn man statische Objekte drin hat, da die woanders gespeichert werden und das auch nur einmal pro Klasse.
    Da ich mir aber nicht ganz sicher bin, habe ich eben diesen Thread eröffnet.



  • @issen1:
    Die liegen schon brav hintereinander, bloss wie Timm1981 schon schrieb: es kann Padding geben.



  • Okay, werden die dann immer möglichst auf int gepaddet oder ist das unvorhersehbar?



  • Ich habe gestern mal was probiert. Ich habe zwei Integers hintereinander gehabt, gefolgt von einem Double. Alle drei haben dann 16 Bytes belegt (kein Padding). Dann habe ich nur einen Integer gefolgt von einem Double genommen, beide haben ebenfalls 16 Bytes belegt mit 4 Bytes Padding dazwischen. Auf meinem System wird also scheinbar auf Doubles gepaddet. Aber das scheint systemabhängig zu sein.



  • Dann schreib es doch in einen Stream und verwende den dann weiter.



  • Timm1981 schrieb:

    Ich habe gestern mal was probiert. Ich habe zwei Integers hintereinander gehabt, gefolgt von einem Double. Alle drei haben dann 16 Bytes belegt (kein Padding). Dann habe ich nur einen Integer gefolgt von einem Double genommen, beide haben ebenfalls 16 Bytes belegt mit 4 Bytes Padding dazwischen. Auf meinem System wird also scheinbar auf Doubles gepaddet. Aber das scheint systemabhängig zu sein.

    Es ist Compilerabhängig.
    Wenn ein System einen Standard für Compiler vorschreibt, dann ist es Systemabhängig 🙂

    MSVC macht es so, dass es eine "Obergrenze" für Padding gibt (die Option nennt sich "Struct Member Alignment"). Einstallbar sind 1, 2, 4, 8 oder 16 Byte, Default ist 8 (zumindest auf 32 Bit Systemen).
    Wenn MSVC jetzt ein neues Member platzieren will, dann guckt er
    a) wie gross ist das Member und
    b) wie hoch ist das "Struct Member Alignment"
    Der kleinere dieser beiden Werte bestimmt dann das Alignment des neuen Members, und damit das Padding.
    D.h. für nen einfachen int wird MSVC nie mehr als 3 Byte Padding einfügen. Für nen char überhaupt kein Padding. Und für grössere Dinge eben maximal 15 Byte.

    Ich *schätze* dass es andere Compiler *vermutlich* ähnlich machen werden, genau weiss ich es aber nur vom guten alten MSVC.

    p.S.: was bei Arrays gilt weiss ich grad nicht auswendig - also ob die Grösse des gesamten Arrays als "Grösse des Members" genommen wird, oder die Grösse der einzelnen Elemente des Arrays. Kann man aber auch alles in der MSDN lachlesen.


Log in to reply