web-dev-qa-db-fra.com

Que veut dire "statique" en C?

J'ai vu le mot static utilisé à différents endroits dans le code C; Cela ressemble-t-il à une fonction/classe statique en C # (où l'implémentation est partagée entre des objets)? 

941
David
  1. Une variable statique à l'intérieur d'une fonction conserve sa valeur entre les invocations.
  2. Une variable globale statique ou une fonction est "vue" uniquement dans le fichier dans lequel elle est déclarée

(1) est le sujet le plus étranger si vous êtes un débutant, alors voici un exemple:

#include <stdio.h>

void foo()
{
    int a = 10;
    static int sa = 10;

    a += 5;
    sa += 5;

    printf("a = %d, sa = %d\n", a, sa);
}


int main()
{
    int i;

    for (i = 0; i < 10; ++i)
        foo();
}

Cela imprime:

a = 15, sa = 15
a = 15, sa = 20
a = 15, sa = 25
a = 15, sa = 30
a = 15, sa = 35
a = 15, sa = 40
a = 15, sa = 45
a = 15, sa = 50
a = 15, sa = 55
a = 15, sa = 60

Cela est utile dans les cas où une fonction doit conserver un état entre les invocations et que vous ne souhaitez pas utiliser de variables globales. Attention, toutefois, cette fonctionnalité doit être utilisée avec parcimonie - votre code ne sera pas thread-safe et plus difficile à comprendre.

(2) Est largement utilisé comme fonctionnalité de "contrôle d'accès". Si vous avez un fichier .c implémentant certaines fonctionnalités, il n’expose généralement que quelques fonctions "publiques" aux utilisateurs. Le reste de ses fonctions devrait être static, afin que l'utilisateur ne puisse pas y accéder. C'est une encapsulation, une bonne pratique.

Citant Wikipedia :

Dans le langage de programmation C, static est utilisé avec les variables globales et fonctions pour définir leur portée sur le fichier contenant. Dans les variables locales, static est utilisé pour stocker la variable dans la mémoire allouée de manière statique au lieu de l'alloué automatiquement Mémoire. Alors que la langue ne le fait pas dicter la mise en œuvre de l'un ou l'autre type de mémoire, alloué statiquement la mémoire est généralement réservée dans les données segment du programme à compiler le temps, tandis que le automatiquement la mémoire allouée est normalement mis en œuvre en tant que pile d'appels transitoires.

Voir ici et ici pour plus de détails.

Et pour répondre à votre deuxième question, ce n'est pas comme en C #.

Cependant, en C++, static est également utilisé pour définir des attributs de classe (partagés entre tous les objets de la même classe) et des méthodes. En C, il n'y a pas de classe, cette fonctionnalité est donc inutile.

1328
Eli Bendersky

Il y a une autre utilisation non couverte ici, qui fait partie d'une déclaration de type tableau en tant qu'argument d'une fonction:

int someFunction(char arg[static 10])
{
    ...
}

Dans ce contexte, cela spécifie que les arguments passés à cette fonction doivent être un tableau de type char avec au moins 10 éléments. Pour plus d'informations voir ma question ici .

209
dreamlax

Réponse courte ... ça dépend.

  1. Les variables locales définies statiques ne perdent pas leur valeur entre les appels de fonction. En d'autres termes, ce sont des variables globales, mais étendues à la fonction locale dans laquelle elles sont définies.

  2. Les variables globales statiques ne sont pas visibles en dehors du fichier C dans lequel elles sont définies.

  3. Les fonctions statiques ne sont pas visibles en dehors du fichier C dans lequel elles sont définies.

149
cmcginty

Exemple de portée de variable multi-fichier

Ici, je montre comment la statique affecte l’étendue des définitions de fonctions dans plusieurs fichiers.

a.c

#include <stdio.h>

/*
Undefined behavior: already defined in main.
Binutils 2.24 gives an error and refuses to link.
https://stackoverflow.com/questions/27667277/why-does-borland-compile-with-multiple-definitions-of-same-object-in-different-c
*/
/*int i = 0;*/

/* Works in GCC as an extension: https://stackoverflow.com/a/3692486/895245 */
/*int i;*/

/* OK: extern. Will use the one in main. */
extern int i;

/* OK: only visible to this file. */
static int si = 0;

void a() {
    i++;
    si++;
    puts("a()");
    printf("i = %d\n", i);
    printf("si = %d\n", si);
    puts("");
}

principal c

#include <stdio.h>

int i = 0;
static int si = 0;

void a();    

void m() {
    i++;
    si++;
    puts("m()");
    printf("i = %d\n", i);
    printf("si = %d\n", si);
    puts("");
}

int main() {
    m();
    m();
    a();
    a();
    return 0;
}

GitHub en amont .

Compiler et exécuter:

gcc -c a.c -o a.o
gcc -c main.c -o main.o
gcc -o main main.o a.o

Sortie:

m()
i = 1
si = 1

m()
i = 2
si = 2

a()
i = 3
si = 1

a()
i = 4
si = 2

Interprétation

  • il y a deux variables distinctes pour si, une pour chaque fichier
  • il y a une seule variable partagée pour i

Comme d'habitude, plus l'étendue est petite, mieux c'est, alors déclarez toujours les variables static si vous le pouvez.

En programmation C, les fichiers sont souvent utilisés pour représenter des "classes", et les variables static représentent des membres statiques privés de la classe.

Que disent les normes à ce sujet

Brouillon C99 N1256 6.7.1 "Spécificateurs de classe de stockage" indique que static est un "spécificateur de classe de stockage".

6.2.2/3 "Liaisons d'identifiants" dit static implique internal linkage:

Si la déclaration d'un identificateur d'étendue de fichier pour un objet ou une fonction contient le spécificateur de classe de stockage static, cet identificateur est associé à un lien interne.

et 6.2.2/2 indique que internal linkage se comporte comme dans notre exemple:

Dans l'ensemble des unités de traduction et des bibliothèques constituant un programme complet, chaque déclaration d'un identifiant particulier avec une liaison externe désigne le même objet ou la même fonction. Au sein d'une unité de traduction, chaque déclaration d'un identifiant avec une liaison interne désigne le même objet ou la même fonction. 

où "unité de traduction est un fichier source après prétraitement.

Comment GCC l'implémente-t-il pour ELF (Linux)?

Avec la liaison STB_LOCAL.

Si nous compilons:

int i = 0;
static int si = 0;

et démontez la table des symboles avec:

readelf -s main.o

la sortie contient:

Num:    Value          Size Type    Bind   Vis      Ndx Name
  5: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    4 si
 10: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    4 i

la liaison est donc la seule différence significative entre eux. Value est juste leur décalage dans la section .bss, nous nous attendons donc à ce qu'il soit différent.

STB_LOCAL est documenté dans les spécifications ELF à l'adresse http://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.html :

STB_LOCAL Les symboles locaux ne sont pas visibles en dehors du fichier objet contenant leur définition. Les symboles locaux du même nom peuvent exister dans plusieurs fichiers sans interférer les uns avec les autres

ce qui en fait un choix parfait pour représenter static.

Les variables sans statique sont STB_GLOBAL, et la spécification dit:

Lorsque l'éditeur de liens combine plusieurs fichiers objets déplaçables, il ne permet pas plusieurs définitions de symboles STB_GLOBAL portant le même nom. 

ce qui est cohérent avec les erreurs de lien sur plusieurs définitions non statiques.

Si nous lançons l'optimisation avec -O3, le symbole si est entièrement supprimé de la table des symboles: il ne peut de toute façon pas être utilisé de l'extérieur. TODO pourquoi garder les variables statiques sur la table des symboles quand il n’ya pas d’optimisation? Peuvent-ils être utilisés pour quoi que ce soit? Peut-être pour le débogage.

Voir également

Espaces de noms anonymes C++

En C++, vous pouvez utiliser des espaces de noms anonymes au lieu de static, ce qui produit un effet similaire, mais masque en outre les définitions de types: espaces de noms anonymes/nommés et fonctions statiques

Ça dépend:

int foo()
{
   static int x;
   return ++x;
}

La fonction renverrait 1, 2, 3, etc. --- la variable n'est pas sur la pile.

a.c:

static int foo()
{
}

Cela signifie que cette fonction n'a de portée que dans ce fichier. Ainsi, a.c et b.c peuvent avoir différentes foo()s, et foo n'est pas exposé aux objets partagés. Donc, si vous avez défini foo dans a.c, vous ne pourrez y accéder à partir de b.c ou de tout autre endroit.

Dans la plupart des bibliothèques C, toutes les fonctions "privées" sont statiques et la plupart "publiques" ne le sont pas.

34
Artyom

Les gens continuent à dire que «statique» en C a deux significations. Je propose une autre façon de le voir qui lui donne un sens unique:

  • L'application de «statique» à un élément force celui-ci à avoir deux propriétés: (a) il n'est pas visible en dehors de la portée actuelle; (b) Il est persistant.

La raison pour laquelle il semble avoir deux significations est que, en C, chaque élément auquel «statique» peut être appliqué possède déjà l'une de ces propriétés, donc il {semble) comme si cela utilisation particulière ne concerne que l'autre.

Par exemple, considérons les variables. Les variables déclarées en dehors des fonctions ont déjà une persistance (dans le segment de données), donc appliquer «statique» peut uniquement les rendre invisibles en dehors de la portée actuelle (unité de compilation). À l'inverse, les variables déclarées à l'intérieur de fonctions ont déjà une non-visibilité en dehors de la portée actuelle (fonction). Par conséquent, l'application de «statique» ne peut que les rendre persistantes.

Appliquer «statique» à des fonctions revient à l'appliquer à des variables globales: le code est nécessairement persistant (du moins dans le langage), de sorte que seule la visibilité peut être modifiée.

REMARQUE: ces commentaires s'appliquent uniquement à C. En C++, appliquer 'statique' à des méthodes de classe donne vraiment une signification différente au mot clé. De même pour l'extension de l'argument de tableau C99.

19
PMar

De Wikipedia:

Dans le langage de programmation C, static est utilisé avec les variables globales et les fonctions pour définir leur portée sur le fichier contenant. Dans les variables locales, static est utilisé pour stocker la variable dans la mémoire allouée de manière statique au lieu de la mémoire allouée automatiquement. Bien que le langage ne dicte la mise en œuvre d'aucun type de mémoire, la mémoire allouée statiquement est généralement réservée dans le segment de données du programme au moment de la compilation, tandis que la mémoire allouée automatiquement est normalement implémentée sous forme de pile d'appels transitoires.

12
OscarRyz

static signifie différentes choses dans différents contextes.

  1. Vous pouvez déclarer une variable statique dans une fonction C. Cette variable est uniquement visible dans la fonction, mais elle se comporte comme une variable globale dans la mesure où elle n'est initialisée qu'une seule fois et conserve sa valeur. Dans cet exemple, chaque fois que vous appelez foo(), un nombre croissant sera imprimé. La variable statique est initialisée une seule fois.

    void foo ()
    {
    static int i = 0;
    printf("%d", i); i++
    }
    
  2. Une autre utilisation de static consiste à implémenter une fonction ou une variable globale dans un fichier .c sans vouloir que son symbole soit visible en dehors du .obj généré par le fichier. par exemple.

    static void foo() { ... }
    
12
m-sharp

Je n'aime pas répondre à une vieille question, mais je ne pense pas que quiconque ait expliqué comment K & R l'explique dans la section A4.1 de "Le langage de programmation C".

En bref, le mot statique est utilisé avec deux significations:

  1. Static est l'une des deux classes de stockage (l'autre étant Automatic). Un objet statique conserve sa valeur entre les invocations. Les objets déclarés en dehors de tous les blocs sont toujours statiques et ne peuvent pas être automatisés.
  2. Mais, lorsque static mot clé est utilisé avec une déclaration dans le code , Il donne un lien interne à cet objet afin qu'il ne puisse être utilisé que dans cette unité de traduction. Mais si le mot clé est utilisé dans une fonction, il change la classe de stockage de l'objet (l'objet ne serait de toute façon visible que dans cette fonction). Le contraire de static est le mot clé extern, qui donne un lien externe à un objet.

Peter Van Der Linden donne ces deux significations dans "Programmation Expert C":

  • Dans une fonction, conserve sa valeur entre les appels.
  • Au niveau de la fonction, visible uniquement dans ce fichier.
6
nobism

Si vous déclarez une variable dans une fonction statique, sa valeur ne sera pas stockée dans la pile d'appels de fonction et restera disponible lorsque vous appelez à nouveau la fonction.

Si vous déclarez une variable globale statique, sa portée sera limitée au fichier dans lequel vous l'avez déclarée. Ceci est légèrement plus sûr qu'un global normal qui peut être lu et modifié tout au long de votre programme.

5
Sam Hoice

En C, statique a deux significations, en fonction de la portée de son utilisation. Dans la portée globale, lorsqu'un objet est déclaré au niveau du fichier, cela signifie que cet objet est uniquement visible dans ce fichier.

Pour toute autre portée, il déclare un objet qui conservera sa valeur entre les différentes heures de saisie de la portée particulière. Par exemple, si un int est défini dans une procédure:

void procedure(void)
{
   static int i = 0;

   i++;
}

la valeur de 'i' est initialisée à zéro lors du premier appel à la procédure et conservée à chaque appel ultérieur de la procédure. si 'i' était imprimé, il donnerait une séquence de 0, 1, 2, 3, ...

5
Gary

Si vous déclarez ceci dans un fichier mytest.c:

static int my_variable;

Ensuite, cette variable ne peut être vue qu'à partir de ce fichier. La variable ne peut être exportée nulle part ailleurs.

Si vous déclarez à l'intérieur d'une fonction, la valeur de la variable conservera sa valeur à chaque appel de la fonction.

Une fonction statique ne peut pas être exportée de l'extérieur du fichier. Ainsi, dans un fichier * .c, vous masquez les fonctions et les variables si vous les déclarez statiques.

4
ant2009

Il est important de noter que les variables statiques dans les fonctions sont initialisées à la première entrée dans cette fonction et persistent même après la fin de leur appel. dans le cas de fonctions récursives, la variable statique n'est initialisée qu'une seule fois et persiste également pour tous les appels récursifs et même après que l'appel de la fonction est terminé.

Si la variable a été créée en dehors d'une fonction, cela signifie que le programmeur ne peut utiliser que la variable du fichier source que la variable a été déclarée.

3
Starhowl

Les variables statiques en C ont la durée de vie du programme.

Si elles sont définies dans une fonction, elles ont une portée locale, c’est-à-dire qu’elles ne sont accessibles qu’à l’intérieur de ces fonctions. La valeur des variables statiques est préservée entre les appels de fonction.

Par exemple:

void function()
{
    static int var = 1;
    var++;
    printf("%d", var);
}

int main()
{
    function(); // Call 1
    function(); // Call 2
}

Dans le programme ci-dessus, var est stocké dans le segment de données. Sa durée de vie correspond à l'ensemble du programme en C.

Après l'appel de fonction 1, var devient 2. Après l'appel de fonction 2, var devient 3.

La valeur de var n'est pas détruite entre les appels de fonctions.

Si var avait entre variable non statique et variable locale, elle serait stockée dans le segment de pile du programme C. Etant donné que le cadre de pile de la fonction est détruit après son retour, la valeur de var est également détruite.

Les variables statiques initialisées sont stockées dans le segment de données du programme C, tandis que les variables non initialisées sont stockées dans le segment BSS.

Une autre information à propos de static: si une variable est globale et statique, elle a la durée de vie du programme C, mais elle a une portée de fichier. Il n'est visible que dans ce fichier.

Pour essayer ceci:

fichier1.c

static int x;

int main()
{
    printf("Accessing in same file%d", x):
}

fichier2.c

    extern int x;
    func()
    {
        printf("accessing in different file %d",x); // Not allowed, x has the file scope of file1.c
    }

run gcc -c file1.c

gcc -c file2.c

Maintenant, essayez de les lier en utilisant:

gcc -o output file1.o file2.o

Cela donnerait une erreur de l'éditeur de liens car x a la portée du fichier file1.c et que l'éditeur de liens ne serait pas en mesure de résoudre la référence à la variable x utilisée dans fichier2.c.

Références:

  1. http://en.wikipedia.org/wiki/Translation_unit_(programming)
  2. http://en.wikipedia.org/wiki/Call_stack
3
Sahil Manchanda

Une variable statique est une variable spéciale que vous pouvez utiliser dans une fonction. Elle enregistre les données entre les appels et ne les supprime pas entre les appels. Par exemple:

void func(){
    static int count; // If you don't declare its value, the value automatically initializes to zero
    printf("%d, ", count);
    count++;
}

void main(){
    while(true){
        func();
    }
}

Le résultat:

0, 1, 2, 3, 4, 5, ...

2
Yagel

Une valeur de variable statique persiste entre différents appels de fonction et sa portée est limitée au bloc local Une variable statique toujours initialisée avec la valeur 0

1
Jonathon

Il y a 2 cas:

(1) Variables locales déclarées static: allouées dans un segment de données au lieu d'une pile. Sa valeur est conservée lorsque vous appelez à nouveau la fonction.

(2) Variables ou fonctions globales déclarées static: Unité de compilation extérieure invisible (c’est-à-dire que sont des symboles locaux dans la table des symboles lors de la liaison).

1
Jonny Kong

En programmation C, statique est un mot clé réservé qui contrôle à la fois la durée de vie et la visibilité. Si nous déclarons une variable comme statique dans une fonction, elle ne sera visible que dans cette fonction. Dans cette utilisation, la durée de vie de cette variable statique commencera lors de l'appel d'une fonction et sera détruite après l'exécution de cette fonction. vous pouvez voir l'exemple suivant:

#include<stdio.h> 
int counterFunction() 
{ 
  static int count = 0; 
  count++; 
  return count; 
} 

int main() 
{ 
  printf("First Counter Output = %d\n", counterFunction()); 
  printf("Second Counter Output = %d ", counterFunction()); 
  return 0; 
}

Le programme ci-dessus nous donnera cette sortie:
Premier compteur sortie = 1 
Deuxième sortie du compteur = 1 
Parce que dès que nous appellerons la fonction, elle initialisera le compte = 0. Et pendant que nous exécuterons la fonction counterFunction, elle détruira la variable count.

0
Makdia Hussain