pl: Begriff erklären

Für meinen Wiki/Blog suche ich eine möglichst verständliche Erklärung für den Begiff statisch. Bspw., was es heißt, daß sich eine Variable statisch verhält.

Was ich nicht suche ist, was PHP's Schlüsselwort static macht. Die Formulierung soll also allgemeinverständlich und nicht PHP'spezifisch sein.

Danke im Vorab!

  1. Für meinen Wiki/Blog suche ich eine möglichst verständliche Erklärung für den Begiff statisch.

    https://www.duden.de/rechtschreibung/statisch

  2. Hallo pl,

    da Du das nachher als dein geistiges Produkt auf deiner Homepage präsentieren dürftest, kannst Du das gerne selbst recherchieren.

    Ungefähre Faustregel: Ein Dings in einer Programmiersprache ist statisch, wenn Speicher dafür nur einmal allociert wird und während des Programmlaufs bestehen bleibt. Aber diese Faust umfasst lange nicht alle Aspekte, und man kann es nicht von der Programmiersprache trennen. Die static-Semantik ist Teil der Sprachdefinition und nicht übergreifend konsistent. Nicht mal innerhalb einer Sprache - siehe static in C/C++.

    Rolf

    --
    sumpsi - posui - clusi
    1. Ungefähre Faustregel: Ein Dings in einer Programmiersprache ist statisch, wenn Speicher dafür nur einmal allociert wird

      Das ist dynamische Speicherverwaltung, das meinte ich nicht. Also ich meine statisch bezogen auf den Scope bzw. Geltungsbereich (Klasse, Funktion). In einem, maximal zwei Sätzen.

      MFG

      1. Hello,

        static bedeutet "genau festgelegt", sowohl in der Größe, als auch in der Lage, bei Programmen schon mit bzw. sogar vor Programmbeginn.

        Es gibt das Objekt/die Variable nur genau einmal an genau einer bekannten Stelle, egal, ob es/sie benutzt wird, oder nicht. Also genau das Gegenteil von dynamisch vereinbart.

        Bei Dateien gibt es etwas Ähnliches. Da nennt sich das direktgestreut. Die Lage der Daten in der Datei liegt dann genau fest, egal ob sie vorhanden sind, oder noch nicht.

        Glück Auf
        Tom vom Berg

        --
        Es gibt nichts Gutes, außer man tut es!
        Das Leben selbst ist der Sinn.
        1. Statisch beschreibt nicht den Ort sondern das Verhalten. Eine scriptweite Variable z.B. verhält sich statisch: Egal ob sie direkt oder über eine Funktion verändert wird, sie behält ihren Zustand. D.h., wer auch immer eine statische Variable aufruft um sie zu verändern findet sie so vor wie sie beim letzten Aufruf verlassen wurde.

          $num = 1;
          
          $num++;
          cnt();
          echo $num; # 3
          
          function cnt(){
              global $num;
              $num++;
          }
          

          MFG

          1. Tach!

            Statisch beschreibt nicht den Ort sondern das Verhalten.

            Variablen "verhalten" sich nicht. Sie sind nur ein Container für einen Wert. Und statisch beschreibt, dass der Ort fest definiert ist. Also jeder Zugriff liest oder schreibt den Wert an diesem Ort.

            Eine scriptweite Variable z.B. verhält sich statisch: Egal ob sie direkt oder über eine Funktion verändert wird, sie behält ihren Zustand.

            Globale Variabeln befinden sich in dem Speicherbereich für globale Variablen. Der ist von Beginn an festgelegt und bleibt über die Scriptlaufzeit am selben Ort. Variablen in Funktionen landen irgendwo auf dem Stack, abhängig von seinem aktuellen Füllstand.

            D.h., wer auch immer eine statische Variable aufruft findet sie so vor wie sie beim letzten Aufruf verlassen wurde.

            Klar, ist aber kein "Verhalten", sondern ergibt sich daraus, dass sie die gesamte Script-Laufzeit an einem konkreten Ort liegen.

            dedlfix.

            1. Hallo,

              … Und statisch beschreibt, dass der Ort fest definiert ist. Also jeder Zugriff liest oder schreibt den Wert an diesem Ort.

              so habe ich das auch in Erinnerung.

              Gruß
              Jürgen

            2. Hallo dedlfix,

              Variablen "verhalten" sich nicht. Sie sind nur ein Container für einen Wert.

              Eine scriptweite Variable z.B. verhält sich statisch: Egal ob sie direkt oder über eine Funktion verändert wird, sie behält ihren Zustand.

              Und haben deshalb auch keinen Zustand.

              Bis demnächst
              Matthias

              --
              Du kannst das Projekt SELFHTML unterstützen,
              indem du bei Amazon-Einkäufen Amazon smile (Was ist das?) nutzt.
            3. Hallo,

              Statisch beschreibt nicht den Ort sondern das Verhalten.

              Variablen "verhalten" sich nicht. Sie sind nur ein Container für einen Wert.

              korrekt. Und der Begriff Verhalten ist hier einfach schlecht gewählt.

              Und statisch beschreibt, dass der Ort fest definiert ist. Also jeder Zugriff liest oder schreibt den Wert an diesem Ort.

              Zumindest aus der Sicht des Programms. Wenn in Javascript etwa der GC zuschlägt, kann eine Variable durchaus an eine andere Stelle im Arbeitsspeicher verschoben werden. Aber das geschieht transparent und für das Script nicht feststellbar.

              Globale Variabeln befinden sich in dem Speicherbereich für globale Variablen. Der ist von Beginn an festgelegt und bleibt über die Scriptlaufzeit am selben Ort. Variablen in Funktionen landen irgendwo auf dem Stack, abhängig von seinem aktuellen Füllstand.

              Ja, das ist eine gängige Umsetzung des Konzepts. Es muss aber nicht unbedingt der Stack sein; andere Implementierungen sind ebenso denkbar.

              Klar, ist aber kein "Verhalten", sondern ergibt sich daraus, dass sie die gesamte Script-Laufzeit an einem konkreten Ort liegen.

              Verhalten wie: "Die Gesamtheit der Symptome erweckt den Eindruck, dass ..."

              Ciao,
               Martin

              --
              Ich stamme aus Ironien, einem Land am sarkastischen Ozean.
            4. Hello,

              Globale Variabeln befinden sich in dem Speicherbereich für globale Variablen. Der ist von Beginn an festgelegt und bleibt über die Scriptlaufzeit am selben Ort. Variablen in Funktionen landen irgendwo auf dem Stack, abhängig von seinem aktuellen Füllstand.

              Spannend sind dann

              statisch deklarierte Variablen in Funktionen.

              Da wird die Adresse der Variable im Datensegment auf dem Stack übergeben. Die Variable ist aber im übrigen Programm vor anderen Zugriffen/Funktionen geschützt, also "unsichtbar". Wird die Funktion an anderer Stelle erneut aufgerufen, kann diese wieder auf die Variable zugreifen und diese nimmt den letzten Wert wieder an bzw. enthält ihn noch.

              Glück Auf
              Tom vom Berg

              --
              Es gibt nichts Gutes, außer man tut es!
              Das Leben selbst ist der Sinn.
              1. Hallo TS,

                statisch deklarierte Variablen in Funktionen.

                Das ist eine C/C++ Spezialität. Gibt's das auch anderswo? In PHP nicht, soweit ich weiß.

                Aber auch da gilt: Die leben nicht auf dem Stack, sondern nur einmal während der Programmlaufzeit. Durch static wird lediglich die Sichtbarkeit eingeschränkt (was die C-Macher dann auf den bekloppten Trip brachte, die Sichtbarkeit einer globalen Variablen oder Funktion mittels static auf ein sourcefile zu begrenzen, statt ein neues Keyword wie 'private' einzuführen.

                Da wird die Adresse der Variable im Datensegment auf dem Stack übergeben

                Echt jetzt?

                void foo() {
                   static int bar;
                   bar = bar + 1;
                   showValue(&bar);
                }
                

                Wenn ich foo aufrufe, bekommt sie die Adresse von bar übergeben? Fände ich merkwürdig. Oder übergibt foo die Adresse von bar lediglich via Stack[1] an showValue? Das wäre dann normales Verhalten. Ohne & würde der Wert von bar auf den Stack gelegt.

                Rolf

                --
                sumpsi - posui - clusi

                1. okay, je nach calling convention kann es auch ein Register sein ↩︎

                1. Tach!

                  statisch deklarierte Variablen in Funktionen.

                  Das ist eine C/C++ Spezialität. Gibt's das auch anderswo? In PHP nicht, soweit ich weiß.

                  Doch, auch da.

                  Da wird die Adresse der Variable im Datensegment auf dem Stack übergeben

                  Warum sollte das so stattfinden? Der Compiler muss doch dabei nicht den Umweg über den Stack gehen, sondern kann die Adresse direkt angeben, wie bei anderen globalen Variablen auch.

                  dedlfix.

                  1. Hello,

                    Da wird die Adresse der Variable im Datensegment auf dem Stack übergeben

                    Warum sollte das so stattfinden? Der Compiler muss doch dabei nicht den Umweg über den Stack gehen, sondern kann die Adresse direkt angeben, wie bei anderen globalen Variablen auch.

                    Wo werden denn die variablen Teile von Funktionen instanziiert?

                    Der Code selber wird direkt aus der Funktionsdefinition in einem Codesegment geholt, muss also nicht kopiert werden. Aber alle Values werden auf dem Stack geführt und die Referenzen auf dynamische Speicherbereiche, statische Variablen und globale Variablen auch.

                    Ich wüsste nicht, wo ein "Executor" eines OS das sonst lassen sollte.
                    Der Compiler hat in dem Moment ja nichts mehr damit zu tun. Der führt das Programm nicht aus.

                    Glück Auf
                    Tom vom Berg

                    --
                    Es gibt nichts Gutes, außer man tut es!
                    Das Leben selbst ist der Sinn.
                    1. Tach!

                      Da wird die Adresse der Variable im Datensegment auf dem Stack übergeben

                      Warum sollte das so stattfinden? Der Compiler muss doch dabei nicht den Umweg über den Stack gehen, sondern kann die Adresse direkt angeben, wie bei anderen globalen Variablen auch.

                      Wo werden denn die variablen Teile von Funktionen instanziiert?

                      Auf dem Stack, weil sie am Ende aufgeräumt werden.

                      Der Code selber wird aus dem Static-Objekt/der Funktionsdefinition eines Codesegmentes geholt, muss also nicht kopiert werden. Aber alle Values werden auf dem Stack geführt und die Referenzen auf dynamische Speicherbereiche, statische Variablen und globale Variablen auch.

                      Warum? Das wäre nur eine unnötige Indirektion bei Werten, die nicht im Stack liegen.

                      Ich wüsste nicht, wo ein Compiler bzw. der "Executor" das sonst lassen sollte.

                      Wenn wir von der klassischen Architektur ausgehen - Programm, Datensegment, Heap, Stack - dann im Datensegment.

                      dedlfix.

                      1. Hello,

                        Tach!

                        Da wird die Adresse der Variable im Datensegment auf dem Stack übergeben

                        Warum sollte das so stattfinden? Der Compiler muss doch dabei nicht den Umweg über den Stack gehen, sondern kann die Adresse direkt angeben, wie bei anderen globalen Variablen auch.

                        Wo werden denn die variablen Teile von Funktionen instanziiert?

                        Auf dem Stack, weil sie am Ende aufgeräumt werden.

                        Der Code selber wird aus dem Static-Objekt/der Funktionsdefinition eines Codesegmentes geholt, muss also nicht kopiert werden. Aber alle Values werden auf dem Stack geführt und die Referenzen auf dynamische Speicherbereiche, statische Variablen und globale Variablen auch.

                        Warum? Das wäre nur eine unnötige Indirektion bei Werten, die nicht im Stack liegen.

                        Ich wüsste nicht, wo ein Compiler bzw. der "Executor" das sonst lassen sollte.

                        Wenn wir von der klassischen Architektur ausgehen - Programm, Datensegment, Heap, Stack - dann im Datensegment.

                        Ja, eben!

                        • Call by Value
                        • Call by Reference
                        • Datensegment
                        • Codesegment
                        • Instruction Pointer

                        Funktionen werden auf dem Stack ausgeführt, bedeutet doch, dass ihre sämtlichen benötigten Werte und Referenzen dort übergeben werden. Der Funktionscode selber muss nicht kopiert werden. Er enthält Displacements relativ zum Stackbeginn für jede Variable. Für Objekte, statische und globale Variablen enthält der Code einen Load by Reference und bekommt eben diese Referenz vom Stack. Er kennt nur das Displacement der Adresse auf dem Stack. Handelt es sich um eine direkte Variablenübergabe (Kopie), enthält der Code nur ein Direct Load vom Stack. Direct Load bedeutet, dass er das Displacement der Variable auf dem Stack kennt.

                        Dass bei heutigen Konzepten vermutlich alles pef Load by Reference (indirekter Adressierung) gemacht wird, steht auf einem anderen Blatt. Darum sind ja moderne Programme so,aufgeblasen. Was myn früher noch mif 25kBytes programmieten konnte (Textverarbeotungen) benötigt heute fast eind eigen Festplatte :-O

                        Glück Auf
                        Tom vom Berg

                        --
                        Es gibt nichts Gutes, außer man tut es!
                        Das Leben selbst ist der Sinn.
                        1. Tach!

                          Funktionen werden auf dem Stack ausgeführt, bedeutet doch, dass ihre sämtlichen benötigten Werte und Referenzen dort übergeben werden.

                          Das wäre nicht sinnvoll, bei Werten, die global oder statisch sind.

                          Der Funktionscode selber muss nicht kopiert werden. Er enthält Displacements relativ zum Stackbeginn für jede Variable. Für Objekte, statische und globale Variablen enthält der Code einen Load by Reference und bekommt eben diese Referenz vom Stack, weil sie ja mit seiner Hilfe mittels Displacement loziiert werden kann.

                          Warum soll man sowas tun wollen? Um unnötige Laufzeit beim Auflösen der Adressen zu verbraten? Warum genau sollte der Compiler nicht die Adresse auf dem Datensegment in den Programmcode kompilieren?

                          Handelt es sich um eine direkte Variablenübergabe (Kopie), enthält der Code nur ein Direct Load vom Stack. Direct Load bedeutet, dass er das Displacement der Variable auf dem Stack kennt.

                          Eine Kopie ist ja wohl kein Zugriff auf den globalen Wert.

                          Zu Direct Load finde ich nur Marketing- und Logistik-Seiten.

                          dedlfix.

                          1. Hello,

                            Tach!

                            Funktionen werden auf dem Stack ausgeführt, bedeutet doch, dass ihre sämtlichen benötigten Werte und Referenzen dort übergeben werden.

                            Das wäre nicht sinnvoll, bei Werten, die global oder statisch sind.

                            Genau! Deshalb wird für diese ja auch eine Referenz übergeben an den Stack und im Code ein Load by Reference programmiert.

                            Der Funktionscode selber muss nicht kopiert werden. Er enthält Displacements relativ zum Stackbeginn für jede Variable. Für Objekte, statische und globale Variablen enthält der Code einen Load by Reference und bekommt eben diese Referenz vom Stack, weil sie ja mit seiner Hilfe mittels Displacement loziiert werden kann.

                            Warum soll man sowas tun wollen? Um unnötige Laufzeit beim Auflösen der Adressen zu verbraten? Warum genau sollte der Compiler nicht die Adresse auf dem Datensegment in den Programmcode kompilieren?

                            Weil der Compiler beachten muss, dass eine Funktion aus unterschiedlichen Kontexten heraus aufgerufen werden kann. Und dann gibt es da noch die Namespaces.

                            Handelt es sich um eine direkte Variablenübergabe (Kopie), enthält der Code nur ein Direct Load vom Stack. Direct Load bedeutet, dass er das Displacement der Variable auf dem Stack kennt.

                            Eine Kopie ist ja wohl kein Zugriff auf den globalen Wert.

                            Nee, hab ich auch nicht behauptet.

                            Zu Direct Load finde ich nur Marketing- und Logistik-Seiten.

                            Wenn ich Zeit habe, suche ich mal die Mnemonics aus dem MASM raus und sehe nach, wie die das 1980 benannt haben. Da gab es ja noch kein Internet und es hat wohl keiner für nötig gehalten, die alten Bücher mal einzutippen ;-)

                            Übergangsweise nennen wir es einfach direkte Adressierung und indirekte Adressierung. Wärst Du damit erst einmal einverstanden?

                            Glück Auf
                            Tom vom Berg

                            --
                            Es gibt nichts Gutes, außer man tut es!
                            Das Leben selbst ist der Sinn.
                            1. Tach!

                              Weil der Compiler beachten muss, dass eine Funktion aus unterschiedlichen Kontexten heraus aufgerufen werden kann. Und dann gibt es da noch die Namespaces.

                              Da verstehe ich nicht, was das für einen Einfluss auf den Speicherort der Variable haben soll, und warum dieser Ort nicht direkt angesprochen werden kann.

                              dedlfix.

                              1. Tach!

                                Weil der Compiler beachten muss, dass eine Funktion aus unterschiedlichen Kontexten heraus aufgerufen werden kann. Und dann gibt es da noch die Namespaces.

                                Da verstehe ich nicht, was das für einen Einfluss auf den Speicherort der Variable haben soll, und warum dieser Ort nicht direkt angesprochen werden kann.

                                Du hast selbstvertändlich Recht. Für alle Deklarationen im Funktionskörper können die Adressen bzw. deren Offsets im DS direkt berechnet und eingesetzt werden.

                                Ich hatte da fälschlicherweise immer die Funktionsargumente aus der Signatur im Kopf. Die werden, wie beschrieben, über den Stack übergeben.

                                Bis nächste Woche Tom aus der Pampa (wenn die Verbindung nicht abreißt)

                      2. Hallo dedlfix,

                        Doch, auch da.

                        Ah ok, danke.

                        Programm, Datensegment, Heap, Stack

                        Muss man nicht Datensegmente sagen? Zumindest ist zwischen initialisierten (.data) und uninitialisierten (.bss) Daten zu unterscheiden. Heap und Stack sind ja typischerweise dynamisch allocierte Zusatzbereiche (ok, in kleinen Speichermodellen aus der x86-Zeit mag das anders gewesen sein).

                        Rolf

                        --
                        sumpsi - posui - clusi
                        1. Tach!

                          Programm, Datensegment, Heap, Stack

                          Muss man nicht Datensegmente sagen? Zumindest ist zwischen initialisierten (.data) und uninitialisierten (.bss) Daten zu unterscheiden.

                          Ich weiß es nicht. Diese Aufteilung stammt aus der Zeit um Turbo Pascal. Wie das heutige Programme verwalten, weiß ich nicht, ist mir aber auch aus Sicht der High-Level-Sprachen ziemlich egal. Kann man wissen, bringt einen aber auch nicht weiter, wenn man nicht mit dem Debugger im Binärcode rumfummeln muss.

                          dedlfix.

                    2. Hallo TS,

                      hui, da ist man mal kurz nicht da...

                      Aus meiner Sicht ist eine static-Variable in einer Funktion Syntaxzucker für eine globale Variable, die nur im Scope dieser Funktion angesprochen werden darf.

                      Ob ich nun (in C) programmiere:

                      int foo = 0;
                      
                      void bar() {
                         foo = foo + 1;
                      }
                      

                      oder

                      
                      void bar() {
                         static int foo = 0;
                         foo = foo + 1;
                      }
                      

                      sollte von der Wirkungsweise her keinen Unterschied machen (solange Rolf und Herr B sich einig sind, dass foo nur von bar() verwendet werden darf).

                      Ich habe das mal ausprobiert. Auf meinem Visual Studio habe ich die C/C++ Workload nicht aktiviert, aber es gibt ja Online Compiler mit Assembler-Ausgabe.

                      Das ist mein Test: Es gibt drei Variablen, bar1, bar2 und bar3. bar1 ist global static - was letztlich nur dazu führt, dass das Symbol bar1 für den Linker nicht sichtbar ist. bar2 ist global, und bar3 ist local static.

                      static int bar1 = 0;
                      int bar2 = 0;
                      
                      int sumUp(int num) {
                         static int bar3 = 0;
                         return bar3 += num;
                      }
                      

                      Ich habe jetzt dreimal compiliert, mit dem Unterschied, dass ich in der return-Zeile nacheinander bar1, bar2 und bar3 eingesetzt habe. Compiler war x86-64 gcc 9.2

                      sumUp:
                       push   rbp
                       mov    rbp,rsp
                       mov    DWORD PTR [rbp-0x4],edi
                       mov    edx,DWORD PTR [rip+0x200b1d]        # 60102c <bar1>
                       mov    eax,DWORD PTR [rbp-0x4]
                       add    eax,edx
                       mov    DWORD PTR [rip+0x200b12],eax        # 60102c <bar1>
                       mov    eax,DWORD PTR [rip+0x200b0c]        # 60102c <bar1>
                       pop    rbp
                       ret    
                      
                      sumUp:
                       push   rbp
                       mov    rbp,rsp
                       mov    DWORD PTR [rbp-0x4],edi
                       mov    edx,DWORD PTR [rip+0x200b21]        # 601030 <bar2>
                       mov    eax,DWORD PTR [rbp-0x4]
                       add    eax,edx
                       mov    DWORD PTR [rip+0x200b16],eax        # 601030 <bar2>
                       mov    eax,DWORD PTR [rip+0x200b10]        # 601030 <bar2>
                       pop    rbp
                       ret    
                      
                      sumUp:
                       push   rbp
                       mov    rbp,rsp
                       mov    DWORD PTR [rbp-0x4],edi
                       mov    edx,DWORD PTR [rip+0x200b25]        # 601034 <bar3.1910>
                       mov    eax,DWORD PTR [rbp-0x4]
                       add    eax,edx
                       mov    DWORD PTR [rip+0x200b1a],eax        # 601034 <bar3.1910>
                       mov    eax,DWORD PTR [rip+0x200b14]        # 601034 <bar3.1910>
                       pop    rbp
                       ret    
                      

                      Es ist immer der gleiche Code, nur die Adressen unterscheiden sich um jeweils 4 Bytes. Er addressiert IP-relativ, na gut. Klingt nach Memory Model tiny oder small.

                      Der MSVC zeigt es noch schöner:

                      PUBLIC  _bar2
                      _BSS    SEGMENT
                      _bar1   DD    01H DUP (?)
                      _bar2   DD    01H DUP (?)
                      ?bar3@?1??sumUp@@9@9 DD 01H DUP (?)           ; `sumUp'::`2'::bar3
                      _BSS    ENDS
                      PUBLIC  _sumUp
                      
                      _TEXT   SEGMENT
                      _num$ = 8                                         ; size = 4
                      _sumUp  PROC
                              push    ebp
                              mov     ebp, esp
                              mov     eax, DWORD PTR ?bar3@?1??sumUp@@9@9
                              add     eax, DWORD PTR _num$[ebp]
                              mov     DWORD PTR ?bar3@?1??sumUp@@9@9, eax
                              mov     eax, DWORD PTR ?bar3@?1??sumUp@@9@9
                              pop     ebp
                              ret     0
                      _sumUp  ENDP
                      _TEXT   ENDS
                      

                      bar1, bar2 und bar3 unterscheiden sich nur darin, ob sie public sind oder nicht, und ob der Name vermangelt wird. Sie liegen alle 3 hintereinander im BSS Segment. static in einer Funktion ist Syntaxzucker, weiter nichts.

                      Aber: Das ist C/C++. In anderen Sprachen mag es anders realisiert sein. PHP ist so dynamisch - ich traue den Brüdern zu, dass sie ein internes Flag setzen, das angibt ob die Initialisierung schon gelaufen ist. Wobei das auch in PHP nicht wichtig ist, weil der Initializer-Ausdruck konstant sein muss.

                      Rolf

                      --
                      sumpsi - posui - clusi
                2. Hello,

                  statisch deklarierte Variablen in Funktionen.

                  Das ist eine C/C++ Spezialität. Gibt's das auch anderswo? In PHP nicht, soweit ich weiß.

                  Das gibt es in PHP sowohl für Variablen in der imperativen Programmierung, als auch für Methoden in der OOP.

                  Glück Auf
                  Tom vom Berg

                  --
                  Es gibt nichts Gutes, außer man tut es!
                  Das Leben selbst ist der Sinn.
          2. Interessante Erklärungen 😉

            Um nochmal auf mein Beispiel zurückzukommen:

            $num = 1;
            
            $num++;
            cnt();
            echo $num; # 3
            
            function cnt(){
                global $num;
                $num++;
            }
            

            Man kann auch static davor schreiben:

            static $num = 1;
            
            $num++;
            cnt();
            echo $num; # 3
            
            function cnt(){
                global $num;
                $num++;
            }
            

            Es ändert nichts am Ergebnis. $num verhält sich statisch, so oder so.

            Schönen Abend noch.

            1. Hello,

              Interessante Erklärungen 😉

              Um nochmal auf mein Beispiel zurückzukommen:

              $num = 1;
              
              $num++;
              cnt();
              echo $num; # 3
              
              function cnt(){
                  global $num;
                  $num++;
              }
              

              Man kann auch static davor schreiben:

              static $num = 1;
              
              $num++;
              cnt();
              echo $num; # 3
              
              function cnt(){
                  global $num;
                  $num++;
              }
              

              Es ändert nichts am Ergebnis. $num verhält sich statisch, so oder so.

              Es ändert sich nur die Sichtbarkeit der Variable für andere Funktionen oder Programmteile. Bei GLOBAL dürfen alle mal, bei STATIC bleiben die Variablen ihrer Funktion vorbehalten.

              Wie genau und sicher das nun vom jeweiligen Compiler vorbereitet wird, damit der Executor des OS das dann auch beachten muss, wird unterschiedlich gehandhabt.

              Wenn man es richtig™ machen will, muss für static variables ein eigener Descriptor in der local descritor table angelegt werden mit dem passenden Attribut, wer ihn benutzen darf.

              Das macht aber der Compiler für dich. Du kannst das nur beeinflussen, wenn Du Low-Level-Assembler programmierst.

              Glück Auf
              Tom vom Berg

              --
              Es gibt nichts Gutes, außer man tut es!
              Das Leben selbst ist der Sinn.
              1. Ein statisches Verhalten ist nicht nur auf einen bestimmten Scope beschränkt. So hat eine statische Variable innerhalb einer Funkion beim nächsten Aufruf der Funktion den Wert den sie beim letzten Aufruf hatte. Genau dieses Verhalten nennt man statisch. Globale Variablen verhalten sich grundsätzlich statisch. Ebenso verhalten sich Klassenvariablen statisch.

                MFG

              2. Hallo TS,

                Wenn man es richtig™ machen will, muss für static variables ein eigener Descriptor in der local descritor table angelegt werden mit dem passenden Attribut, wer ihn benutzen darf.

                Das macht aber der Compiler für dich. Du kannst das nur beeinflussen, wenn Du Low-Level-Assembler programmiers

                Oha. Bist Du da im Schweinsgalopp in den Wald geritten? Oder ist meine Erinnerung total daneben?

                GDT und LDT sind Mechanismen des Intel 286, um den Speicher zu verwalten. Ein Prozess bekommt eine eigene LDT, und das OS vermerkt darin die Speichersegmente, die ihm gehören. Das OS, nicht der Compiler! Compiler und Linker bestellen lediglich die Segmente mittels Einträgen im EXE. Wieviele Code- und Datensegmente bestellt werden, ist eine Frage des Speichermodells (tiny, small, compact, medium, large, huge). Benutzungsrechte für Segmente innerhalb eines Prozesses werden durch die LDT meines Wissens überhaupt nicht festgelegt. Man kann Segmente höchstens schreibschützen, damit Code nicht überschrieben werden kann.

                Das ist aber ein Relikt aus der 16 Bit Welt. In der 32- und 64-Bit Welt befindet sich ein Programm normalerweise in einem einzigen Segment, und Dinge wie Schreibschutz von Code oder Datenausführungsverhinderung werden eine Ebene tiefer, in der page table, abgehandelt.

                Rolf

                --
                sumpsi - posui - clusi
                1. Hello Rolf,

                  Oha. Bist Du da im Schweinsgalopp in den Wald geritten? Oder ist meine Erinnerung total daneben?

                  GDT und LDT sind Mechanismen des Intel 286, um den Speicher zu verwalten. Ein Prozess bekommt eine eigene LDT, und das OS vermerkt darin die Speichersegmente, die ihm gehören. Das OS, nicht der Compiler! Compiler und Linker bestellen lediglich die Segmente mittels Einträgen im EXE.

                  ... Mechanismen seit Einführung des Protected Mode bei Intel-Prozessoren und kompariblen.

                  Ich habe auch bisher nichts darüber finden können, dass das inzwischen anders geregelt würde, außer z. B. bei Grafikprozessoren, die 150 Kerne und mehr haben.

                  Und selbstverständlich ist es eine Sache der Hardware und nicht erst des Betriebssystems. Es ist also Sache der fest eingeprägten Microprogrammierung. Sonst wären die Mechanismem allzu leicht zu umgehen. Die Programme nebst OS können entsprechende Fenster bei der Hardware "beantragen".

                  Glück Auf
                  Tom vom Berg

                  --
                  Es gibt nichts Gutes, außer man tut es!
                  Das Leben selbst ist der Sinn.
                  1. Moin,

                    GDT und LDT sind Mechanismen des Intel 286, um den Speicher zu verwalten. Ein Prozess bekommt eine eigene LDT, und das OS vermerkt darin die Speichersegmente, die ihm gehören. Das OS, nicht der Compiler! Compiler und Linker bestellen lediglich die Segmente mittels Einträgen im EXE.

                    ... Mechanismen seit Einführung des Protected Mode bei Intel-Prozessoren und kompariblen.

                    Die Hervorhebung des "seit" ist gut. Tatsächlich funktioniert das meines Wissens immer noch so. Gut, das Adressierungsschema hat sich seither von einer Generation zur nächsten immer wieder etwas geändert, das grundlegende Konzept ist aber AFAIK immer noch das gleiche.

                    Und selbstverständlich ist es eine Sache der Hardware und nicht erst des Betriebssystems.

                    Beides! Die Hardware (also die CPU) stellt die Möglichkeiten zur Verfügung, und das OS verwendet und verwaltet sie. Das OS nimmt beispielsweise Wünsche der Applikation nach Arbeitsspeicher entgegen, versucht sie zu erfüllen, trägt passende Werte in die entsprechenden Descriptor-Tabellen ein und gibt der Applikation dann die Rückmeldung: Hier, nimm das.
                    Es ist dann der Hardware (CPU) überlassen, Speicherzugriffe abzublocken ("Protection Fault"), die nicht zu den Grenzen (Adressbereich) oder Privilegien (z.B. Schreibzugriff) passen. Das OS hat wiederum seine Handler-Funktionen, die in diesem Fall aufgerufen werden und geeignet reagieren, z.B. die meuternde Anwendung rauskicken oder den Debugger anwerfen.

                    Es ist also Sache der fest eingeprägten Microprogrammierung.

                    Das Zusammenspiel beider Ebenen macht erst etwas Brauchbares daraus.

                    Ciao,
                     Martin

                    --
                    Ich stamme aus Ironien, einem Land am sarkastischen Ozean.
                    1. Hallo Martin,

                      Die Hervorhebung des "seit" ist gut.

                      Okay, ich habe "das gibt's noch" und "das braucht man noch" verwechselt. Und Anwendungen für PAE vergessen.

                      Die 386er haben 4GB Segmente und ein Programm muss im Normalfall seine Segmentregister nicht mehr ändern, wenn es läuft. Es sei denn, man hat einen Prozessor mit mehr als 4GB Adressraum (PAE) und schreibt eine Anwendung für Monsterdatenmengen (DB-Server, CAD, ...).

                      Es bedeutet aber nicht, dass das Segmentierungsmodell der x86 Prozessoren relevant für das Konzept statischer Variablen ist. Oder dass es sinnvoll ist, auf x86-Prozessoren ein eigenes Segment für statische Variablen vorzuhalten. Meine Erwartung wäre, dass sich statische Variablen und alle Ankerdaten für die Speicherverwaltung der Runtime-Umgebung in .data oder .bss Segment befinden, und dass es genau zwei dieser Segmente gibt.

                      Eine Runtime könnte natürlich hergehen und User-Daten in ein anderes Segment als die Runtime-Daten legen. Das würde verhindern, dass ein irrlaufender Pointer des User-Bereichs die Daten der Runtime schreddert; es ist nur sehr aufwändig zu realisieren und kostet Laufzeit, bzw. vergrüßert die Programme, weil viele Instruktionen dann Segmentprefixe brauchen. Aber auch dann würde ich noch erwarten, dass statische Variablen und Heap im gleichen .bss Segment liegen. Man verwendet nicht ohne Not zu viele Segmente, weil das Laden eines Segmentregisters ab .286 mehr ist als 32 Bits in ein Register zu schieben und Zeit kostet.

                      Rolf

                      --
                      sumpsi - posui - obstruxi
                      1. Hallo Rolf,

                        Die Hervorhebung des "seit" ist gut.

                        Okay, ich habe "das gibt's noch" und "das braucht man noch" verwechselt. Und Anwendungen für PAE vergessen.

                        ja, und du scheinst einen Zweck der Segmentierung zu vergessen. Der eine ist die Vergrößerung des Adressraums durch Segmente, innerhalb derer dann wieder "herkömmlich" adressiert wird. Das war das Konzept der 16bit-Architektur der 8086er-Prozessoren (und AFAIK auch noch 80186).

                        Ein anderer ist aber die Abgrenzung unterschiedlicher Adressräume voneinander. Das wurde bei der Intel-Familie mit dem 286er eingeführt. Damit kann man die Adressräume von Betriebssystem und Anwendersoftware voneinander trennen, so dass die Anwendung nicht mehr auf Speicherbereiche zugreifen kann, die dem OS "gehören" - obwohl sie es von der Adressierung her könnte. Wenn man den Gedanken konsequent weiterverfolgt, kann man so auch die Speicherbereiche mehrerer Anwendungen untereinander abgrenzen. Windows macht das AFAIK seit 4.0 so, davor gab es nur die Trennung zwischen OS und der Gesamtheit aller Anwendungen. So konnte ein amoklaufendes Programm alle anderen mit in den Abgrund reißen.

                        Die 386er haben 4GB Segmente und ein Programm muss im Normalfall seine Segmentregister nicht mehr ändern, wenn es läuft.

                        Ja.

                        Es bedeutet aber nicht, dass das Segmentierungsmodell der x86 Prozessoren relevant für das Konzept statischer Variablen ist. Oder dass es sinnvoll ist, auf x86-Prozessoren ein eigenes Segment für statische Variablen vorzuhalten.

                        Stimmt, das hat damit überhaupt nichts zu tun.

                        Man verwendet nicht ohne Not zu viele Segmente, weil das Laden eines Segmentregisters ab .286 mehr ist als 32 Bits in ein Register zu schieben und Zeit kostet.

                        So isses.

                        Ciao,
                         Martin

                        --
                        "Wenn du mit Corona ein Problem hast, trink doch stattdessen mal ein Beck's!" (zynisch-flacher Witz meines Nachbarn)
      2. Hallo pl,

        static und Scope haben miteinander nichts zu tun, wenn man die Quirks von C außer Acht lässt. Durch static wird die Lebensdauer bestimmt, d.h. es geht um die Frage: wann wird die Variable allociert und wie lange bleibt sie allociert. Das "Wie" der Allocierung kann man hier außer Acht lassen.

        static bedeutet: Nur einmal, und dann bleibt sie bestehen. Wo sie allociert wird, auf dem Heap oder im Basic Service Set (.bss Segment), ist je Compiler/Interpreter unterschiedlich.

        In Sprachen wie PHP lebt alles auf dem Heap, da gibt's statische Variablen als statische Eigenschaften von Klassen. Die leben solange wie die Klasse und werden als Teil der Klassendefinition verwaltet.

        In Sprachen wie C# oder Java weiß ich nicht genau wie es funktioniert; da muss alles eine Klasse sein, und zumindest statische Konstruktoren werden nach Gusto der Runtime aufgerufen. Eine statische Variable vom Typ Object lebt als Objektreferenz in der Klasse (ob Heap oder BSS sei dahingestellt) und ihr Inhalt lebt auf dem Heap.

        In C/C++ leben statische Variablen definitiv im BSS.

        Rolf

        --
        sumpsi - posui - clusi
  3. Für meinen Wiki/Blog suche ich eine möglichst verständliche Erklärung für den Begiff statisch. Bspw., was es heißt, daß sich eine Variable statisch verhält.

    Was ich nicht suche ist, was PHP's Schlüsselwort static macht. Die Formulierung soll also allgemeinverständlich und nicht PHP'spezifisch sein.

    Ergänzung: Statisches Verhalten anhand von Codebeispielen

    In Perl ist es schon immer üblich, z.B. sowas zu machen:

    $Scaliger5::EXCEPTION = 1;
    $Scaliger5::VALIDATE = 1; # Objekt wird automatisch validiert
    $sca = Scaliger5->new(5,10,1582);
    

    Da werden also Klassenvariablen gesetzt, die das Verhalten während der Anwendung beeinflussen. So soll z.B: eine Exception mit Backtrace geworfen werden, wenn falsche Argumente übergeben werden und die Instanz aufgrund eines ungültigen Datums nicht erstellt werden konnte. Das ist insbesondere beim Entwickeln wichtig, denn der Backtrace zeigt die Zeile wo's geknallt hat.

    Dasselbe kann man natürlich auch in PHP machen:

    Scaliger5::$EXCEPTION = 1;
    Scaliger5::$VALIDATE = 1;
    $sca = new Scaliger5(5,10,1582);
    

    Und auch den Fehler über eine statische Variable (Klassenvariable) abfragen:

    Scaliger::$EXCEPTION = 0;
    $sca = new Scaliger5(5,10,1582);
    if( $sca->errstr() ){} # Scaliger::$ERRSTR;
    

    Wofür das Werfen einer etwaigen Exception unterdrückt wurde. Letzteres bietet sich dann an wenn Benutzereingaben vorliegen und z.B. ein ungültiges Datum eingegeben wurde. So gibt es keinen Backtrace un der Anwender bekommt nur die Fehlermeldung die schon in der Klasse fix und fertig ausformuliert ist. Hier könnte man sich auch über die Mehrsprachigkeit kümmern.

    Das wäre also eine Anwendung von Klassenvariablen die sich grundsätzlich immer statisch verhalten.

    MFG

    1. Tach!

      Ergänzung: Statisches Verhalten anhand von Codebeispielen

      Kann man so machen, geht aber auch anders, besonders wenn man das Single Responsibility Principle als erstrebenswert ansieht. Die Einheiten sollten nur ihre Aufgabe tun und nicht noch Verwaltungskram drumherum berücksichtigen. Das erhöht nur ihre Komplexität.

      Da werden also Klassenvariablen gesetzt, die das Verhalten während der Anwendung beeinflussen.

      Und sämtliche Instanzen der Klasse müssen sich nach diesen (klassen)globalen Werten richten. Wenn man mehrere Instanzen hat, die sich diesbezüglich unterschiedlich verhalten sollen, verwaltet man zu Fuß diese globalen Werte, je nachdem mit welcher Instanz man gerade arbeitet.

      So soll z.B: eine Exception mit Backtrace geworfen werden, wenn falsche Argumente übergeben werden und die Instanz aufgrund eines ungültigen Datums nicht erstellt werden konnte.

      Ob Exceptions geworfen werden sollen, muss die Klasse nicht selbst wissen. Es reicht, dass sie eine Exception wirft, wenn sie ein Problem hat. Ob der Verwender darauf reagieren möchte oder nicht, kann er selbst festlegen, indem er einen try-catch-Block nach seinen Wünschen definiert.

      Das ist insbesondere beim Entwickeln wichtig, denn der Backtrace zeigt die Zeile wo's geknallt hat.

      Die Informationen zur problematischen Stelle ist sowieso on der Exception enthalten. Das ist also kein Vorteil, wenn man das Werfen der Exception schaltbar macht.

      Dasselbe kann man natürlich auch in PHP machen:

      Scaliger5::$EXCEPTION = 1;
      Scaliger5::$VALIDATE = 1;
      $sca = new Scaliger5(5,10,1582);
      

      Ob validiert werden soll oder nicht, ist auch besser so gelöst, dass die Validierung eine separate Methode ist, die man aufrufen kann oder auch nicht. Das muss auch nicht global für alle Instanzen gesteuert werden.

      Und auch den Fehler über eine statische Variable (Klassenvariable) abfragen:

      Wozu, wenn der Text bereits in der Exception enthalten ist, oder im Rückgabewert der Validierungsmethode stehen kann? Hat man in separat als Eigenschaft der Instanz, muss man auch immer dafür sorgen, dass er nach der Verwendung wieder gelöscht wird, damit er bei der nächsten Verwendung keinen gar nicht aufgetretenen Fehler signalisiert.

      Wofür das Werfen einer etwaigen Exception unterdrückt wurde. Letzteres bietet sich dann an wenn Benutzereingaben vorliegen und z.B. ein ungültiges Datum eingegeben wurde.

      Bekommt man hin, indem man isValid() oder validate() anbietet. Kein Grund, das global zu steuern.

      So gibt es keinen Backtrace un der Anwender bekommt nur die Fehlermeldung die schon in der Klasse fix und fertig ausformuliert ist. Hier könnte man sich auch über die Mehrsprachigkeit kümmern.

      Was der Verwender braucht, sollte der Verwender entscheiden können. Die Datumsberechnungsklasse muss von der Umgebung nichts wissen, in der sie läuft, und auch nicht dementsprechend formatierte Ausgaben erzeugen.

      Das wäre also eine Anwendung von Klassenvariablen die sich grundsätzlich immer statisch verhalten.

      Da gibt es bessere Einsatzgebiete.

      dedlfix.