fonction statique en C


Réponses:


212

La création d'une fonction la staticmasque des autres unités de traduction, ce qui permet de fournir une encapsulation .

helper_file.c

int f1(int);        /* prototype */
static int f2(int); /* prototype */

int f1(int foo) {
    return f2(foo); /* ok, f2 is in the same translation unit */
                    /* (basically same .c file) as f1         */
}

int f2(int foo) {
    return 42 + foo;
}

main.c :

int f1(int); /* prototype */
int f2(int); /* prototype */

int main(void) {
    f1(10); /* ok, f1 is visible to the linker */
    f2(12); /* nope, f2 is not visible to the linker */
    return 0;
}

8
L'unité de traduction est-elle la terminologie correcte à utiliser ici? Le fichier objet ne serait-il pas plus précis? D'après ce que je comprends, une fonction statique est masquée dans l'éditeur de liens et l'éditeur de liens ne fonctionne pas sur les unités de traduction.
Steven Eckhoff

2
J'aurais dû dire aussi que j'aime penser que c'est caché du lien; cela semble plus clair comme ça.
Steven Eckhoff

1
donc, fonction interne (que nous sommes sûrs de ne pas l'appeler en dehors de son fichier c), nous devrions la mettre en fonction statique, non? Donc, nous pouvons être sûrs qu'il ne peut pas appeler ailleurs. Merci :)
hqt

1
Comment compilez-vous cela? Utilisez-vous #include <helper_file.c>? Je pense que cela en ferait une seule unité de traduction alors ...
Atcold

2
@Atcold: comme j'ai écrit le code, vous incluez simplement les 2 fichiers source dans la ligne de commande, comme ceci gcc -std=c99 -pedantic -Wall -Wextra main.c helper_file.c. Les prototypes des fonctions sont présents dans les deux fichiers source (pas besoin de fichiers d'en-tête). L'éditeur de liens résoudra les fonctions.
pmg

80

pmg est parfait pour l'encapsulation; au-delà de cacher la fonction à d'autres unités de traduction (ou plutôt, à cause de cela), la création de fonctions staticpeut également conférer des avantages en termes de performances en présence d'optimisations du compilateur.

Étant donné qu'une staticfonction ne peut pas être appelée de n'importe où en dehors de l'unité de traduction actuelle (à moins que le code ne prenne un pointeur vers son adresse), le compilateur contrôle tous les points d'appel.

Cela signifie qu'il est libre d'utiliser un ABI non standard, de l'intégrer entièrement ou d'effectuer un certain nombre d'autres optimisations qui pourraient ne pas être possibles pour une fonction avec un lien externe.


9
... sauf si l'adresse de la fonction est prise.
caf

1
@caf Qu'entendez-vous par l'adresse de la fonction est prise? Pour moi, la notion de fonctions / variables ayant des adresses ou ayant une adresse attribuée au moment de la compilation est un peu déroutante. Pouvez-vous s'il vous plaît élaborer?
SayeedHussain

2
@crypticcoder: Votre programme est chargé en mémoire, les fonctions ont donc également un emplacement mémoire et l'adresse peut être obtenue. Avec un pointeur de fonction, vous pouvez appeler l'un de ceux-ci. Si vous faites cela, cela réduit la liste des optimisations que le compilateur peut effectuer car le code doit rester intact au même endroit.

5
@crypticcoder: Je veux dire qu'une expression évalue un pointeur vers la fonction et fait quelque chose avec elle autre que d'appeler immédiatement la fonction. Si un pointeur vers une staticfonction échappe à l'unité de traduction actuelle, cette fonction peut être directement appelée à partir d'autres unités de traduction.
caf

@caf si l'adresse de la fonction est prise, le compilateur le détectera-t-il et désactivera-t-il les optimisations de fonction statiques mentionnées dans cette réponse (par exemple en utilisant un ABI non standard)? Je suppose qu'il le faudrait.
sevko

28

Le staticmot-clé en C est utilisé dans un fichier compilé (.c par opposition à .h) afin que la fonction n'existe que dans ce fichier.

Normalement, lorsque vous créez une fonction, le compilateur génère des données que l'éditeur de liens peut utiliser pour lier un appel de fonction à cette fonction. Si vous utilisez le mot-clé static, d'autres fonctions dans le même fichier peuvent appeler cette fonction (car cela peut être fait sans recourir à l'éditeur de liens), tandis que l'éditeur de liens n'a aucune information permettant aux autres fichiers d'accéder à la fonction.


1
3Doub: L'utilisation du mot «cruft» est plus précise que vous ne le pensez. Dans le contexte de la question, «cruft» est le mot juste à utiliser ici.
Erik Aronesty

@ 3Doubloons Je suis d'accord pour dire que c'est simplifié, mais je pense que cela le rend beaucoup plus facile à comprendre pour les débutants.
Ingo Bürk

11

En regardant les articles ci-dessus, je voudrais souligner un détail.

Supposons que notre fichier principal ("main.c") ressemble à ceci:

#include "header.h"

int main(void) {
    FunctionInHeader();
}

Considérons maintenant trois cas:

  • Cas 1: Notre fichier d'en-tête ("header.h") ressemble à ceci:

    #include <stdio.h>
    
    static void FunctionInHeader();
    
    void FunctionInHeader() {
        printf("Calling function inside header\n");
    }

    Ensuite, la commande suivante sous linux:

    gcc main.c header.h -o main

    réussira ! Suite à ça si on court

    ./main

    La sortie sera

    Fonction d'appel dans l'en-tête

    C'est ce que cette fonction statique devrait imprimer.

  • Cas 2: Notre fichier d'en-tête ("header.h") ressemble à ceci:

    static void FunctionInHeader();     

    et nous avons aussi un autre fichier "header.c", qui ressemble à ceci:

    #include <stdio.h>
    
    #include "header.h"
    
    void FunctionInHeader() {
        printf("Calling function inside header\n");
    }

    Puis la commande suivante

    gcc main.c header.h header.c -o main

    donnera une erreur.

  • Cas 3:

    Similaire au cas 2, sauf que maintenant notre fichier d'en-tête ("header.h") est:

    void FunctionInHeader(); // keyword static removed

    Ensuite, la même commande que dans le cas 2 réussira, et l'exécution ultérieure de ./main donnera le résultat attendu.

Donc, à partir de ces tests (exécutés sur une machine Acer x86, Ubuntu OS), j'ai fait l'hypothèse que

Le mot clé static empêche la fonction d'être appelée dans un autre fichier * .c que celui où il est défini.

Corrigez-moi si je me trompe.


5

Les programmeurs C utilisent l'attribut static pour masquer les déclarations de variables et de fonctions à l'intérieur des modules, tout comme vous utiliseriez des déclarations publiques et privées en Java et C ++. Les fichiers source C jouent le rôle de modules. Toute variable ou fonction globale déclarée avec l'attribut static est privée pour ce module. De même, toute variable ou fonction globale déclarée sans l'attribut static est publique et est accessible par tout autre module. Il est recommandé de protéger vos variables et vos fonctions avec l'attribut static dans la mesure du possible.


4

La réponse de pmg est très convaincante. Si vous souhaitez savoir comment les déclarations statiques fonctionnent au niveau de l'objet, les informations ci-dessous pourraient vous intéresser. J'ai réutilisé le même programme écrit par pmg et je l'ai compilé dans un fichier .so (objet partagé)

Le contenu suivant est après le vidage du fichier .so dans quelque chose de lisible par l'homme

0000000000000675 f1 : adresse de la fonction f1

000000000000068c f2 : adresse de la fonction f2 (staticc)

notez la différence dans l'adresse de la fonction, cela signifie quelque chose. Pour une fonction déclarée avec une adresse différente, cela peut très bien signifier que f2 vit très loin ou dans un segment différent du fichier objet.

Les éditeurs de liens utilisent quelque chose appelé PLT (Procedure Linkage Table) et GOT (Global Offsets Table) pour comprendre les symboles auxquels ils ont accès.

Pour l'instant, pensez que GOT et PLT lient par magie toutes les adresses et une section dynamique contient des informations sur toutes ces fonctions qui sont visibles par l'éditeur de liens.

Après avoir vidé la section dynamique du fichier .so, nous obtenons un tas d'entrées mais uniquement intéressés par les fonctions f1 et f2 .

La section dynamique contient l'entrée uniquement pour la fonction f1 à l'adresse 0000000000000675 et non pour f2 !

Num: Valeur Taille Type Lier Vis Ndx Nom

 9: 0000000000000675    23 FUNC    GLOBAL DEFAULT   11 f1

Et c'est tout !. À partir de là, il est clair que l'éditeur de liens ne parviendra pas à trouver la fonction f2 car elle n'est pas dans la section dynamique du fichier .so.


0

Lorsqu'il est nécessaire de restreindre l'accès à certaines fonctions, nous utiliserons le mot clé static lors de la définition et de la déclaration d'une fonction.

            /* file ab.c */ 
static void function1(void) 
{ 
  puts("function1 called"); 
} 
And store the following code in another file ab1.c

/* file ab1.c  */ 
int main(void) 
{ 
 function1();  
  getchar(); 
  return 0;   
} 
/* in this code, we'll get a "Undefined reference to function1".Because function 1 is declared static in file ab.c and can't be used in ab1.c */

Cette réponse n'est pas très utile.
fiscblog
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.