Speicherbelegung von Konstanten



  • Hallo!

    Ich hätte mal ein grundsätzliche Frage zur Speicherbelegung von Konstanten in C++ bzw. auch - falls übertragbar - in anderen Sprachen.

    Folgendes Beispiel:

    const int FOO_INT = 100;
    
    #define FOO_INT_OLD = 100;
    
    const char * const FOO_STRING = "Hallo Welt";
    
    void foo(void)
    {
        std::cout << "Hallo Welt" << std::endl;
    }
    

    Bei #define ist mir bekannt, dass der Compiler überall wo es vorkommt den jeweilgen Wert dafür einsetzt und somit kein Extraspeicher belegt wird sondern lediglich den dafür benötigte. Wie verhält es sich bei der C++ Konstanten in Zeile 1. Macht der Compiler hier absolut das gleiche wie für #define und setzt einfach den Wert dort ein oder belegt die Konstante zusätzlich sizeof(int) und nimmt somit mehr Speicher in Anspruch als ein #define.

    Und wie verhält es sich bei Strings? Also wenn ich jetzt in der Funktion in Zeile 9 statt des Strings die Konstate FOO_STRING einsetze...

    P.S.: Die Fragen beschäftigen mich eigentlich schon seit dem ich angefangen habe C++ zu erlernen und unter anderem Scott Meyers mir nahe legte, #define und inline Funktionen zu nutzen. Doch je länger ich darüber nachdachte kam ich immer wieder zu unsterschiedlichen "Denkergebnissen".



  • Bei den Strings ist es noch "einfach": Jeder String der zur Laufzeit gebraucht wird, muss im Programm irgendwo vorkommen. Er ist also immer da, belegt immer Speicher. Ob er jetzt direkt in einer Anweisung benutzt, oder einem Zeiger zugewiesen wird ist dabei unerheblich. Wie oft er da ist, hängt vom Compiler ab. Wenn ein Compiler String-Pooling beherrscht, kannst Du so oft Du möchtest "Hello World" in Deinem Quelltext (einer ÜE) stehen haben, der Compiler kehrt alles zusammen und legt den String nur einmal ab.

    In diesem Fall, und wenn der Compiler kein String-Pooling beherrscht, ist die Ablage in einer Konstanten auf jeden Fall platzsparender als ein Makro oder das Ausschreiben des Strings an jeder Stelle seiner Benutzung (was nach dem Präprozessorlauf aufs selbe hinausläuft).

    Eine static Memberkonstante (integralen Typs) z.B. darf an der Stelle ihrer Definition initialisiert werden. In diesem Fall geht der Compiler tatsächlich her und ersetzt jedes Vorkommen der Konstante durch den Wert. ABER: Man könnte mit &klasse::konstante die Adresse der Konstanten erfragen, in welchem Fall die Konstante zwangsläufig Speicher belegen müsste, denn nur Speicher hat Adressen. In diesem Fall darf der Compiler weiterhin Konstanten ersetzen, der Programmierer muss aber gleichzeitig Storage bereitstellen. Geschieht dies nicht, gibt's einen Linkerfehler. Hier entscheidet also der Programmierer, ob das Ding Platz belegt oder nicht.

    Bei freistehenden Konstanten (die nicht-static sind) weiss ich es nicht mit Bestimmtheit. Bei einer "static const int x = 12" würde ich aber davon ausgehen, dass der Compiler Konstantenersetzung macht UND Speicherplatz bereitstellt, diesen aber wieder wegoptimiert, wenn die Adresse von x innerhalb der ÜE nicht abgefragt wird. Bei der nicht-static Variante würde ich meinen der Compiler muss immer Speicherplatz bereitstellen, denn er kann nicht wissen, ob die Variable in anderen ÜE extern eingebunden und benutzt wird.



  • Eine "echte" Konstante (also "const int bla") benötigt normalerweise auch Speicher - aber wenn möglich, kann der Compiler den rausoptimieren und den Wert direkt dort einsetzen, wo er benötigt wird.

    Ein String-Literal benötigt auf jeden Fall Platz, wo es abgelegt wird - allerdings können deine beiden Literale übereinander geparkt werden*. Zusätzlich benötigst du noch Platz für den char-Zeiger, wenn du ihn dir irgendwo merken willst.

    *Ob der Compiler wirklich alle Vorkommen von "Hallo Welt" auf das selbe String-Literal zusammenkürzt, ist aber nicht vorgeschrieben - wenn du überall im Programm FOO_STRING verwendest, hast du garantiert nur eine Version des Literals.



  • LordJaxom schrieb:

    Bei freistehenden Konstanten (die nicht-static sind) weiss ich es nicht mit Bestimmtheit.

    Die haben in C++ internal linkage, in C external linkage. (Für den Ursprungsposter: Das heißt, die Konstante kann in C mittels extern in andere Übersetzungseinheiten "importiert" werden, so dass der Compiler nicht ermitteln kann, wo die Konstante überall gebraucht wird. Er kann sie somit auch nicht wegoptimieren, das könnte höchstens der Linker machen. In C++ kann er sie wegoptimieren.)



  • Vielen Dank an Euch beiden. Das hat schonmal viel Licht ins Dunkel gebracht.
    Ich kann zwar den Compiler "verstehen", dass er vorerst bei nicht statischen globalen Konstanten Speicher bereitstellt allerdings sollte er diese sinnigerweise dann auch wegoptimieren können.

    Es fällt zwar bei heutigen Rechnern nicht mehr wirklich ins Gewicht aber dennoch wäre es m. M. n. ein echte Verschwendung von Resourcen wenn der Compiler keine "Wegoptimierung" beherrscht, denn wenn ich z.B. ein Windows Steuerelement schreibe mit zig versch. Flags und Stilen habe ich nachher alleine 200 Bytes verbraucht. Nur dafür.

    P.S. Steht so etwas irgendwo? Also genaue Complier Specs ö.ä.?


Anmelden zum Antworten