16 votes

comment fonctionnent les variables statiques à l'intérieur des fonctions ?

Dans le code suivant :

int count(){
    static int n(5);
    n = n + 1;
    return n;
}

la variable n est instancié une seule fois lors du premier appel à la fonction.

Il devrait y avoir un drapeau ou quelque chose pour qu'il initialise la variable une seule fois J'ai essayé de regarder sur le code d'assemblage généré par gcc, mais je n'ai pas eu d'indice.

Comment le compilateur gère-t-il cela ?

19voto

NPE Points 169956

Ceci est, bien sûr, spécifique au compilateur.

La raison pour laquelle vous n'avez pas vu de contrôles dans l'assemblage généré est que, comme n est un int variable, g++ le traite simplement comme une variable globale pré-initialisée à 5.

Voyons ce qui se passe si nous faisons la même chose avec une std::string :

#include <string>

void count() {
    static std::string str;
    str += ' ';
}

L'assemblage généré se présente comme suit :

_Z5countv:
.LFB544:
        .cfi_startproc
        .cfi_personality 0x3,__gxx_personality_v0
        .cfi_lsda 0x3,.LLSDA544
        pushq   %rbp
        .cfi_def_cfa_offset 16
        movq    %rsp, %rbp
        .cfi_offset 6, -16
        .cfi_def_cfa_register 6
        pushq   %r13
        pushq   %r12
        pushq   %rbx
        subq    $8, %rsp
        movl    $_ZGVZ5countvE3str, %eax
        movzbl  (%rax), %eax
        testb   %al, %al
        jne     .L2                     ; <======= bypass initialization
        .cfi_offset 3, -40
        .cfi_offset 12, -32
        .cfi_offset 13, -24
        movl    $_ZGVZ5countvE3str, %edi
        call    __cxa_guard_acquire     ; acquire the lock
        testl   %eax, %eax
        setne   %al
        testb   %al, %al
        je      .L2                     ; check again
        movl    $0, %ebx
        movl    $_ZZ5countvE3str, %edi
.LEHB0:
        call    _ZNSsC1Ev               ; call the constructor
.LEHE0:
        movl    $_ZGVZ5countvE3str, %edi
        call    __cxa_guard_release     ; release the lock
        movl    $_ZNSsD1Ev, %eax
        movl    $__dso_handle, %edx
        movl    $_ZZ5countvE3str, %esi
        movq    %rax, %rdi
        call    __cxa_atexit            ; schedule the destructor to be called at exit
        jmp     .L2
.L7:
.L3:
        movl    %edx, %r12d
        movq    %rax, %r13
        testb   %bl, %bl
        jne     .L5
.L4:
        movl    $_ZGVZ5countvE3str, %edi
        call    __cxa_guard_abort
.L5:
        movq    %r13, %rax
        movslq  %r12d,%rdx
        movq    %rax, %rdi
.LEHB1:
        call    _Unwind_Resume
.L2:
        movl    $32, %esi
        movl    $_ZZ5countvE3str, %edi
        call    _ZNSspLEc
.LEHE1:
        addq    $8, %rsp
        popq    %rbx
        popq    %r12
        popq    %r13
        leave
        ret
        .cfi_endproc

La ligne que j'ai marquée avec le bypass initialization Le commentaire est l'instruction de saut conditionnel qui saute la construction si la variable pointe déjà sur un objet valide.

7voto

Kerrek SB Points 194696

Cela dépend entièrement de l'implémentation ; la norme du langage ne dit rien à ce sujet.

Dans la pratique, le compilateur inclut généralement une variable drapeau cachée quelque part qui indique si la variable statique a déjà été instanciée ou non. La variable statique et l'indicateur se trouveront probablement dans la zone de stockage statique du programme (par exemple le segment de données, et non le segment de pile), et non dans la mémoire de la portée de la fonction, donc vous devrez peut-être chercher dans l'assemblage. (La variable ne peut pas aller sur la pile d'appel, pour des raisons évidentes, donc c'est vraiment comme une variable globale. L'"allocation statique" couvre vraiment toutes sortes de variables statiques).

Mise à jour : Comme le souligne @aix, si la variable statique est initialisée à un expression constante En C++11, vous n'aurez peut-être même pas besoin d'un drapeau, car l'initialisation peut être effectuée au moment du chargement plutôt qu'au premier appel de fonction. En C++11, vous devriez être en mesure d'en tirer parti mieux qu'en C++03 grâce à la plus grande disponibilité des expressions constantes.

4voto

jpalecek Points 31928

Il est fort probable que cette variable soit traitée par gcc comme une variable globale ordinaire. Cela signifie que l'initialisation sera statiquement initialisée directement dans le binaire.

C'est possible, puisque vous l'initialisez par une constante. Si vous l'initialisiez, par exemple, avec la valeur de retour d'une autre fonction, le compilateur ajouterait un drapeau et sauterait l'initialisation en fonction de ce drapeau.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X