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 */staticint 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){return42+ 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 */return0;}
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.
@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.
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:000000000000067523 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 */staticvoid function1(void){
puts("function1 called");}And store the following code in another file ab1.c
/* file ab1.c */int main(void){
function1();
getchar();return0;}/* 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 */
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
main.c :
la source
#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.
la source
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.
la source
En regardant les articles ci-dessus, je voudrais souligner un détail.
Supposons que notre fichier principal ("main.c") ressemble à ceci:
Considérons maintenant trois cas:
Cas 1: Notre fichier d'en-tête ("header.h") ressemble à ceci:
Ensuite, la commande suivante sous linux:
réussira ! Suite à ça si on court
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:
et nous avons aussi un autre fichier "header.c", qui ressemble à ceci:
Puis la commande suivante
donnera une erreur.
Cas 3:
Similaire au cas 2, sauf que maintenant notre fichier d'en-tête ("header.h") est:
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.
la source
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 source
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
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.
la source
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.
la source