Variable statique à l'intérieur d'une fonction en C

119

Qu'est-ce qui sera imprimé? 6 6 ou 6 7? Et pourquoi?

void foo()
{
    static int x = 5;
    x++;
    printf("%d", x);
}

int main()
{
    foo();
    foo();
    return 0;
}
Vadiklk
la source
54
Quel est le problème à essayer?
Andrew
12
Avez-vous essayé de saisir ceci et de voir par vous-même?
wilhelmtell
21
Je veux comprendre pourquoi.
Vadiklk
7
@Vadiklk alors posez une question commençant par "Pourquoi"
Andrey
1
ideone.com/t9Bbe À quoi vous attendriez-vous? Le résultat ne correspond-il pas à vos attentes? Pourquoi vous attendiez-vous à votre résultat?
eckes le

Réponses:

187

Il y a deux problèmes ici, la durée de vie et la portée.

La portée de la variable est l'endroit où le nom de la variable peut être vu. Ici, x n'est visible que dans la fonction foo ().

La durée de vie d'une variable est la période sur laquelle elle existe. Si x était défini sans le mot-clé static, la durée de vie serait de l'entrée dans foo () au retour de foo (); il serait donc réinitialisé à 5 à chaque appel.

Le mot-clé static agit pour étendre la durée de vie d'une variable à la durée de vie du programme; par exemple, l'initialisation se produit une seule fois et une seule fois, puis la variable conserve sa valeur - quelle qu'elle soit - sur tous les futurs appels à foo ().


la source
15
@devanl, oui nous le sommes.
orion elenzil
1
Simple et logique :)
Dimitar Vukman
dans quels scénarios nous devons déclarer une variable comme statique à l'intérieur d'une fonction ?, juste curieux de savoir car je ne l'ai jamais utilisé auparavant?
Akay
Je dirais merci, mais tout cela a été répondu tout en haut de la page. ça me fait rire que les gens ne se contentent pas d'exécuter leur propre code. xD
Puddle
Cette réponse est fausse. Au moment où vous pensez aux fonctions récursives, les définitions décrites ici n'expliquent pas le comportement!
Philip Couling
53

Sortie : 6 7

Raison : la variable statique n'est initialisée qu'une seule fois (contrairement à la variable automatique) et une définition plus poussée de la variable statique serait contournée pendant l'exécution. Et s'il n'est pas initialisé manuellement, il est automatiquement initialisé par la valeur 0. Alors,

void foo() {
    static int x = 5; // assigns value of 5 only once
    x++;
    printf("%d", x);
}

int main() {
    foo(); // x = 6
    foo(); // x = 7
    return 0;
}
Nitesh Borad
la source
10

6 7

le compilateur fait en sorte que l'initialisation de la variable statique ne se produise pas à chaque fois que la fonction est entrée

Chaim Geretz
la source
10

C'est la même chose que d'avoir le programme suivant:

static int x = 5;

void foo()
{
    x++;
    printf("%d", x);
}

int main()
{
     foo();
     foo();
     return 0;
}

Tout ce que fait le mot-clé static dans ce programme, c'est qu'il dit au compilateur (essentiellement) "hé, j'ai une variable ici à laquelle je ne veux pas que quelqu'un d'autre accède, ne dis à personne d'autre qu'elle existe".

Dans une méthode, le mot-clé static dit au compilateur la même chose que ci-dessus, mais aussi, «ne dites à personne que cela existe en dehors de cette fonction, il ne devrait être accessible qu'à l'intérieur de cette fonction».

J'espère que ça aide

Richard J. Ross III
la source
13
Eh bien, ce n'est pas la même chose. Il y a toujours le problème de la portée sur X. Dans cet exemple, vous pouvez poke and futz with xin main; c'est mondial. Dans l'exemple d'origine, il xétait local à foo, visible uniquement à l'intérieur de ce bloc, ce qui est généralement préférable: si foo existe pour être maintenu xde manière prévisible et visible, alors laisser les autres le pousser est généralement dangereux. Comme autre avantage de le garder dans la portée, foo() il reste également foo()portable.
user2149140
2
@ user2149140 'ne dites à personne que cela existe en dehors de cette fonction, il ne devrait être accessible qu'à l'intérieur de cette fonction'
DCShannon
3
Bien que vous ayez résolu le problème de la portée en raison de l'endroit où la variable est déclarée, la description de statique comme affectant la portée, plutôt que la durée de vie, semble incorrecte.
DCShannon
1
@Chameleon La question est étiquetée comme c, donc dans ce contexte, votre exemple serait illégal à l'échelle mondiale. (C nécessite des initialiseurs constants pour les globaux, C ++ ne le fait pas).
Richard J.Ross III
5

Une variable statique à l'intérieur d'une fonction a une durée de vie tant que votre programme s'exécute. Elle ne sera pas allouée à chaque fois que votre fonction est appelée et désallouée au retour de votre fonction.

Donotalo
la source
Dire cela est comme une variable "globale" et dire SAUF que vous ne pouvez pas y accéder est un oxymore. Global signifie accessible partout. Ce qui dans ce cas d'une fonction statique à l'intérieur d'une fonction, il n'est PAS accessible partout. Le problème dans OP, comme d'autres l'ont noté, concerne la portée et la durée de vie. Veuillez ne pas confondre les gens avec l'utilisation du terme «global» et les induire en erreur sur la portée de la variable.
ChuckB
@ChuckB: C'est exact. Corrigé. Eh bien, ça fait 6 ans. Ma réponse précédente avait la perception d'il y a 6 ans!
Donotalo
5

Sortie: 6,7

Raison

La déclaration de xest à l'intérieur foomais l' x=5initialisation a lieu à l'extérieur de foo!

Ce que nous devons comprendre ici, c'est que

static int x = 5;

n'est pas la même chose que

static int x;
x = 5;

D'autres réponses ont utilisé les mots importants ici, portée et durée de vie, et ont souligné que la portée de xva du point de sa déclaration dans la fonction fooà la fin de la fonction foo. Par exemple, j'ai vérifié en déplaçant la déclaration à la fin de la fonction, et cela rend xnon déclaré à l' x++;instruction.

Ainsi, la partie static int x(portée) de l'instruction s'applique réellement là où vous la lisez, quelque part DANS la fonction et seulement à partir de là, pas au-dessus d'elle à l'intérieur de la fonction.

Cependant, la partie x = 5(durée de vie) de l'instruction est l' initialisation de la variable et se produit À L'EXTÉRIEUR de la fonction dans le cadre du chargement du programme. La variable xest née avec une valeur de 5quand le programme se charge.

J'ai lu ceci dans l'un des commentaires: " De plus, cela ne résout pas la partie vraiment déroutante, qui est le fait que l'initialiseur est ignoré lors des appels suivants. " Il est ignoré sur tous les appels. L'initialisation de la variable est en dehors du code de fonction proprement dit.

La valeur de 5 est théoriquement définie indépendamment du fait que foo soit appelé ou non, bien qu'un compilateur puisse optimiser la fonction si vous ne l'appelez nulle part. La valeur de 5 doit être dans la variable avant que foo ne soit appelé.

À l'intérieur de foo, il static int x = 5;est peu probable que l'instruction génère du code.

J'ai trouvé l'adresse xutilisée lorsque j'ai mis une fonction foodans un de mes programmes, puis j'ai (correctement) deviné que le même emplacement serait utilisé si je réexécutais le programme. La capture d'écran partielle ci-dessous montre qu'elle xa la valeur 5même avant le premier appel à foo.

Point de rupture avant le premier appel à foo

Ivan
la source
2

La sortie sera 6 7. Une variable statique (qu'elle soit à l'intérieur d'une fonction ou non) est initialisée exactement une fois, avant l'exécution d'une fonction de cette unité de traduction. Après cela, il conserve sa valeur jusqu'à ce qu'il soit modifié.

Jerry Coffin
la source
1
Êtes-vous sûr que le statique est initialisé avant l'appel de la fonction et non lors du premier appel de la fonction?
Jesse Pepper
@JessePepper: Au moins si la mémoire est bonne, cela dépend si vous parlez de C ++ 98/03 ou C ++ 11. En C ++ 98/03, je crois que c'est comme décrit ci-dessus. En C ++ 11, le threading rend cela pratiquement impossible à faire, donc l'initialisation est effectuée lors de la première entrée dans la fonction.
Jerry Coffin
2
Je pense que vous vous trompez en fait. Je pense que même avant C ++ 11, il n'a été initialisé que lorsque la fonction est appelée. Ceci est important pour une solution commune au problème de dépendance d'initialisation statique.
Jesse Pepper
2

Vadiklk,

Pourquoi ...? La raison en est que la variable statique n'est initialisée qu'une seule fois et conserve sa valeur tout au long du programme. signifie que vous pouvez utiliser une variable statique entre les appels de fonction. il peut également être utilisé pour compter "combien de fois une fonction est appelée"

main()
{
   static int var = 5;
   printf("%d ",var--);
   if(var)
      main();
} 

et la réponse est 5 4 3 2 1 et non 5 5 5 5 5 5 .... (boucle infinie) comme vous vous y attendez. encore une fois, la raison est que la variable statique est initialisée une fois, lors du prochain appel de main (), elle ne sera pas initialisée à 5 car elle est déjà initialisée dans le programme. Nous pouvons donc changer la valeur mais ne pouvons pas la réinitialiser. Voilà comment fonctionne la variable statique.

ou vous pouvez considérer comme par stockage: les variables statiques sont stockées sur la section de données d'un programme et les variables qui sont stockées dans la section de données sont initialisées une fois. et avant l'initialisation, ils sont conservés dans la section BSS.

À leur tour, les variables automatiques (locales) sont stockées sur la pile et toutes les variables de la pile sont réinitialisées à tout moment lorsque la fonction est appelée en tant que nouveau FAR (enregistrement d'activation de fonction) est créé pour cela.

ok pour plus de compréhension, faites l'exemple ci-dessus sans "statique" et faites-vous savoir quelle sera la sortie. Cela vous fait comprendre la différence entre les deux.

Merci Javed

Javed
la source
1

Lisons simplement l'article de Wikipedia sur les variables statiques ...

Variables locales statiques: les variables déclarées comme statiques à l'intérieur d'une fonction sont allouées statiquement tout en ayant la même portée que les variables locales automatiques. Par conséquent, toutes les valeurs que la fonction met dans ses variables locales statiques lors d'un appel seront toujours présentes lorsque la fonction est appelée à nouveau.

Andrew White
la source
5
C'est terrible! "les variables déclarées statiques à l'intérieur d'une fonction sont allouées statiquement" - cela n'explique rien, sauf si vous savez déjà ce que cela signifie!
@Blank: eh bien, c'est ce à quoi je pensais que la deuxième phrase était destinée. Bien que je suppose que vous avez raison, cela devrait être mieux formulé.
Andrew White
De plus, cela ne résout pas la partie vraiment déroutante, qui est le fait que l'initialiseur est ignoré lors des appels suivants.
Tom Auger
alloué statiquement signifie pas de pile, ni de tas.
Chameleon
1

Vous obtiendrez 6 7 imprimés comme, comme cela est facilement testé, et voici la raison: Quand foo son premier appel, la variable statique x est initialisée à 5. Ensuite, elle est incrémentée à 6 et imprimée.

Passons maintenant au prochain appel à foo. Le programme ignore l'initialisation de la variable statique et utilise à la place la valeur 6 qui a été affectée à x la dernière fois. L'exécution se déroule normalement, vous donnant la valeur 7.

Ken Wayne VanderLinde
la source
1
6 7

x est une variable globale qui n'est visible que depuis foo (). 5 est sa valeur initiale, telle qu'elle est stockée dans la section .data du code. Toute modification ultérieure écrase la valeur précédente. Il n'y a pas de code d'affectation généré dans le corps de la fonction.

mouviciel
la source
1

6 et 7 Parce que la variable statique ne s'initialise qu'une seule fois, Donc 5 ++ devient 6 au 1er appel 6 ++ devient 7 au 2ème appel Remarque - lorsque le 2ème appel se produit, la valeur x est 6 au lieu de 5 car x est une variable statique.

Tushar shirsath
la source
0

En C ++ 11 au moins, lorsque l'expression utilisée pour initialiser une variable statique locale n'est pas une 'constexpr' (ne peut pas être évaluée par le compilateur), l'initialisation doit se produire lors du premier appel à la fonction. L'exemple le plus simple consiste à utiliser directement un paramètre pour initialiser la variable statique locale. Ainsi, le compilateur doit émettre du code pour deviner si l'appel est le premier ou non, ce qui à son tour nécessite une variable booléenne locale. J'ai compilé un tel exemple et vérifié que cela est vrai en voyant le code d'assemblage. L'exemple peut être comme ceci:

void f( int p )
{
  static const int first_p = p ;
  cout << "first p == " << p << endl ;
}

void main()
{
   f(1); f(2); f(3);
}

bien sûr, lorsque l'expression est 'constexpr', cela n'est pas obligatoire et la variable peut être initialisée au chargement du programme en utilisant une valeur stockée par le compilateur dans le code d'assemblage de sortie.

user5122888
la source