Quel est l'intérêt de rendre une fonction statique en C?
Quel est l'intérêt de rendre une fonction statique en C?
Réponses:
La création d'une fonction la static
masque 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;
}
#include <helper_file.c>
? Je pense que cela en ferait une seule unité de traduction alors ...
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 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 static
peut également conférer des avantages en termes de performances en présence d'optimisations du compilateur.
Étant donné qu'une static
fonction 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.
static
fonction échappe à l'unité de traduction actuelle, cette fonction peut être directement appelée à partir d'autres unités de traduction.
Le static
mot-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.
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.
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.
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.
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 */