La question portait sur les fonctions simples c , pas c ++static
, comme expliqué dans les commentaires.
Ok, je comprends ce qu'est une variable static
, mais qu'est-ce qu'une fonction static
?
Et pourquoi est-ce que si je déclare une fonction, disons void print_matrix
, disons a.c
(SANS a.h
) et incluez "a.c"
- je reçois "print_matrix@@....) already defined in a.obj"
, MAIS si je le déclare comme static void print_matrix
alors il compile?
UPDATE Juste pour clarifier les choses - je sais qu'inclure .c
est mauvais, comme beaucoup d'entre vous l'ont souligné. Je le fais juste pour libérer temporairement de l'espace dans main.c
jusqu'à avoir une meilleure idée de la façon de regrouper toutes ces fonctions dans les fichiers .h
et .c
appropriés. Juste une solution temporaire et rapide.
Les fonctions static
sont des fonctions visibles uniquement par les autres fonctions du même fichier (plus précisément identiques nité de traduction).
EDIT: Pour ceux qui pensaient que l'auteur des questions voulait dire une 'méthode de classe': Comme la question est étiquetée C
il signifie une vieille fonction en clair. Pour les méthodes de classe (C++/Java/...), static
signifie que cette méthode peut être appelée sur la classe elle-même, aucune instance de cette classe n'est nécessaire.
Il existe une grande différence entre les fonctions statiques en C et les fonctions membres statiques en C++. En C, une fonction statique n'est pas visible en dehors de son unité de traduction, c'est-à-dire le fichier objet dans lequel elle est compilée. En d'autres termes, rendre une fonction statique limite sa portée. Vous pouvez penser à une fonction statique comme étant "privée" dans son fichier * .c (bien que cela ne soit pas strictement correct).
En C++, "statique" peut également s'appliquer à des fonctions membres et à des membres de données de classes. Un membre de données statique est également appelé "variable de classe", tandis qu'un membre de données non statique est une "variable d'instance". C'est la terminologie de Smalltalk. Cela signifie qu'il n'y a qu'une seule copie d'un membre de données statique partagée par tous les objets d'une classe, chaque objet possédant sa propre copie d'un membre de données non statique. Un membre de données statique est donc essentiellement une variable globale, c'est-à-dire un membre d'une classe.
Les fonctions membres non statiques peuvent accéder à tous les membres de la classe: statiques et non statiques. Les fonctions membres statiques ne peuvent fonctionner que sur les membres de données statiques.
Une façon de penser à cela est que, dans C++, les membres de données statiques et les fonctions de membre statique n'appartiennent à aucun objet, mais à la classe entière.
Le mot clé static a deux utilisations lorsqu'il s'agit de fonctions en C++.
La première consiste à marquer la fonction comme ayant une liaison interne afin qu'elle ne puisse pas être référencée dans d'autres unités de traduction. Cet usage est déconseillé en C++. Les espaces de noms non nommés sont préférés pour cet usage.
// inside some .cpp file:
static void foo(); // old "C" way of having internal linkage
// C++ way:
namespace
{
void this_function_has_internal_linkage()
{
// ...
}
}
La deuxième utilisation est dans le contexte d'une classe. Si une classe a une fonction membre statique, cela signifie que la fonction est membre de la classe (et dispose des droits d'accès habituels sur les autres membres), mais il n'est pas nécessaire de l'invoquer via un objet particulier. En d'autres termes, à l'intérieur de cette fonction, il n'y a pas de "ce" pointeur.
Exemple de portée minimale multi-fichiers exécutable
Ici, je montre comment static
affecte l'étendue des définitions de fonction 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
*/
/*void f() { puts("a f"); }*/
/* OK: only declared, not defined. Will use the one in main. */
void f(void);
/* OK: only visible to this file. */
static void sf() { puts("a sf"); }
void a() {
f();
sf();
}
principal c
#include <stdio.h>
void a(void);
void f() { puts("main f"); }
static void sf() { puts("main sf"); }
void m() {
f();
sf();
}
int main() {
m();
a();
return 0;
}
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
./main
Sortie:
main f
main sf
main f
a sf
Interprétation
sf
, une pour chaque fichierf
Comme d'habitude, plus la portée est petite, mieux c'est, alors déclarez toujours les fonctions static
si vous le pouvez.
En programmation C, les fichiers sont souvent utilisés pour représenter des "classes", et les fonctions static
représentent des méthodes "privées" de la classe.
Un modèle C courant consiste à passer une structure this
en tant que premier argument de "méthode", qui correspond essentiellement à ce que C++ fait sous le capot.
Que disent les normes à ce sujet
C99 N1256 draft 6.7.1 "Spécificateurs de classe de stockage" indique que static
est un "spécificateur de classe de stockage".
6.2.2/3 "Liens entre identificateurs" indique 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é à une liaison interne.
et 6.2.2/2 dit 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. Dans 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 f() { return 0; }
static int sf() { return 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: 000000000000000b 11 FUNC LOCAL DEFAULT 1 sf
9: 0000000000000000 11 FUNC GLOBAL DEFAULT 1 f
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 diffère.
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 fonctions 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 sf
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 fonctions statiques sur la table des symboles quand il n’ya pas d’optimisation? Peuvent-ils être utilisés pour quoi que ce soit?
Voir aussi
extern
est l'opposé de static
et les fonctions sont déjà extern
par défaut: Comment utiliser extern pour partager des variables entre les fichiers source?Espaces de noms anonymes C++
En C++, vous pouvez utiliser des espaces de noms anonymes au lieu de static, ce qui permet d'obtenir un effet similaire, mais masque en outre les définitions de type: espaces de noms non nommés/anonymes et fonctions statiques
Ce qui suit concerne les fonctions C simples. Dans une classe C++, le modificateur 'statique' a une autre signification.
Si vous n'avez qu'un fichier, ce modificateur ne fait aucune différence. La différence réside dans les projets plus importants avec plusieurs fichiers
En C, chaque "module" (une combinaison de sample.c et sample.h) est compilé indépendamment, puis tous ces fichiers objets compilés (sample.o) sont liés ensemble à un fichier exécutable par l'éditeur de liens.
Supposons que vous avez inclus plusieurs fichiers dans votre fichier principal et que deux d’entre eux ont une fonction utilisée en interne uniquement par souci de commodité, appelée add(int a, b)
- le compilateur créerait facilement des fichiers objet pour ces deux modules, mais l’éditeur de liens renvoie une erreur, car il trouve deux fonctions portant le même nom et il ne sait pas quelle fonction il doit utiliser (même s'il n'y a rien à relier, car elles ne sont pas utilisées ailleurs que dans son propre fichier).
C'est pourquoi vous faites de cette fonction, qui est uniquement utilisée en interne, une fonction statique. Dans ce cas, le compilateur ne crée pas le drapeau typique "vous pouvez lier cette chose" pour l'éditeur de liens, de sorte que l'éditeur de liens ne voit pas cette fonction et ne génère pas d'erreur.
Premièrement: c’est généralement une mauvaise idée d’inclure un fichier _.cpp
_ dans un autre fichier - cela pose des problèmes comme celui-ci :-) La méthode normale consiste à créer des unités de compilation distinctes et à ajouter un fichier d’en-tête pour le fichier inclus.
Deuxièmement:
C++ a une terminologie qui prête à confusion - je ne le savais pas jusqu'à ce que ce soit signalé dans les commentaires.
a) _static functions
_ - hérité de C et de ce dont vous parlez ici. En dehors de toute classe. Une fonction statique signifie qu’elle n’est pas visible en dehors de l’unité de compilation en cours. Dans votre cas, a.obj en a une copie et votre autre code en une copie indépendante. (Gonflement de l'exécutable final avec plusieurs copies du code).
b) static member function
- Quelle orientation d'objet appelle une méthode statique . Vit dans une classe. Vous appelez cela avec la classe plutôt que via une instance d'objet.
Ces deux définitions de fonctions statiques différentes sont complètement différentes. Faites attention, soyez des dragons.
les définitions de fonctions statiques marqueront ce symbole comme interne. Ainsi, il ne sera pas visible pour les liens de l'extérieur, mais uniquement pour les fonctions de la même unité de compilation, généralement le même fichier.
Une fonction statique est une fonction qui peut être appelée sur la classe elle-même, par opposition à une instance de la classe.
Par exemple, un non statique serait:
Person* tom = new Person();
tom->setName("Tom");
Cette méthode fonctionne sur une instance de la classe, pas sur la classe elle-même. Cependant, vous pouvez avoir une méthode statique qui peut fonctionner sans instance. Ceci est parfois utilisé dans le motif Factory:
Person* tom = Person::createNewPerson();
La réponse à la fonction statique dépend de la langue:
1) Dans les langues sans OOPS comme C, cela signifie que la fonction n’est accessible que dans le fichier où elle est définie.
2) Dans les langages avec OOPS comme C++, cela signifie que la fonction peut être appelée directement sur la classe sans en créer une instance.
Nit mineur: les fonctions statiques sont visibles par une unité de traduction qui, dans la plupart des cas, correspond au fichier dans lequel la fonction est définie. L'erreur que vous obtenez est couramment appelée violation de la règle de définition unique.
La norme dit probablement quelque chose comme:
"Chaque programme doit contenir exactement une définition de chaque fonction ou objet non intégré utilisé dans ce programme; aucun diagnostic requis."
C’est la façon de regarder les fonctions statiques. Ceci est cependant déconseillé en C++.
En C++, vous pouvez en outre déclarer des fonctions membres statiques. Ce sont principalement des métafonctions, c’est-à-dire qu’elles ne décrivent pas/ne modifient pas le comportement/l’état d’un objet particulier, mais agissent sur la classe entière elle-même. Cela signifie également qu'il n'est pas nécessaire de créer un objet pour appeler une fonction membre statique. En outre, cela signifie également que vous ne pouvez accéder aux variables de membre statiques qu'à partir d'une telle fonction.
J'ajouterais à l'exemple de Parrot le modèle Singleton basé sur ce type de fonction membre statique pour obtenir/utiliser un seul objet tout au long du cycle de vie d'un programme.