web-dev-qa-db-fra.com

fonction statique en C

Quel est l'intérêt de rendre une fonction statique en C?

168
Cenoc

Faire une fonction static le cache des autres unités de traduction, ce qui aide à fournir 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;
}
210
pmg

pmg est parfaitement à propos de l'encapsulation; au-delà de cacher la fonction aux autres unités de traduction (ou plutôt, car ), rendre les fonctions static peut également conférer des avantages en termes de performances en présence des optimisations du compilateur.

Dans la mesure où une fonction static ne peut être appelée que de l'extérieur de l'unité de traduction en cours (à moins que le code prenne un pointeur sur son adresse), le compilateur contrôle tous les points d'appel qu'elle contient.

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

78
Stephen Canon

Le mot clé static en C est utilisé dans un fichier compilé (.c par opposition à .h), de sorte que la fonction n'existe que dans ce fichier.

Normalement, lorsque vous créez une fonction, le compilateur génère le texte 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 du même fichier peuvent appeler cette fonction (car cela peut être fait sans recourir à l'éditeur de liens), alors que l'éditeur de liens ne dispose d'aucune information permettant à d'autres fichiers d'accéder à la fonction.

28
3Doubloons

En regardant les messages 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 sur linux:

    gcc main.c header.h -o main
    

    réussira! Suite à cela si on court

    ./main
    

    La sortie sera

    Fonction appelante à l'intérieur de l'en-tête

    Quel 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
    

    va donner 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 supposé que

Le mot clé static empêche la fonction d'être définie dans un autre fichier que celui où elle est déclarée.

Corrigez-moi si je me trompe.

9
mercury0114

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

5
Computer Systems

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

Le contenu suivant est après avoir vidé le fichier .so dans quelque chose 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 habite très loin ou dans un segment différent du fichier objet.

Les lieurs utilisent quelque chose appelé PLT (table de liaison de procédure) et GOT (table de décalages globaux) pour comprendre les symboles auxquels ils ont accès.

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

Après le vidage de la section dynamique du fichier .so, nous obtenons un tas d'entrées, mais nous ne sommes intéressés que par la fonction f1 et f2.

La section dynamique ne contient l'entrée que pour f1, fonction à l'adresse 00000000000675 et pas pour f2!

Num: Valeur Taille Type Bind Vis Ndx Name

 9: 0000000000000675    23 FUNC    GLOBAL DEFAULT   11 f1

Et c'est tout !. De là, il est clair que l’éditeur de liens ne trouvera pas la fonction f2 car il ne figure pas dans la section dynamique du fichier .so.

3
human.js