Rolf B: Begriff erklären

Beitrag lesen

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