web-dev-qa-db-fra.com

Qu'est-ce qu'une fonction "statique"?

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.

454
Slava V

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.

622
Johannes Weiss

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.

187
Dima

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.

74
Brian Neal

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;
}

GitHub en amont .

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

  • il y a deux fonctions distinctes sf, une pour chaque fichier
  • il n'y a qu'une seule fonction partagée f

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

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.

19
dersimn

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.

16
Douglas Leeder

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.

14
raimue

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();
8
Parrots

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.

7
user2410022

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.

6
dirkgently