Qu'est-ce qu'une fonction «statique» en C?

506

La question portait sur fonctions, pas static telles que clarifiées dans les commentaires.

Je comprends ce qu'est une staticvariable, mais qu'est-ce qu'une staticfonction?

Et pourquoi est-ce que si je déclare une fonction, disons void print_matrix, disons a.c(SANS a.h) et que j'inclus "a.c"- j'obtiens "print_matrix@@....) already defined in a.obj", MAIS si je la déclare comme static void print_matrixalors elle se compile?

MISE À JOUR Juste pour clarifier les choses - je sais que l'inclusion .cest mauvaise, comme beaucoup d'entre vous l'ont souligné. Je fais juste à l' espace temporairement clair main.cjusqu'à ce que j'ai une meilleure idée de la façon de regrouper toutes ces fonctions en bonne .het .cfichiers. Juste une solution temporaire et rapide.

Slava V
la source

Réponses:

685

staticles fonctions sont des fonctions qui ne sont visibles que par d'autres fonctions du même fichier (plus précisément la même unité de traduction ).

EDIT : Pour ceux qui pensaient que l'auteur des questions signifiait une «méthode de classe»: Comme la question est étiquetée, Cil signifie une ancienne fonction C simple. Pour les méthodes de classe (C ++ / Java / ...), cela staticsignifie que cette méthode peut être appelée sur la classe elle-même, aucune instance de cette classe n'est nécessaire.

Johannes Weiss
la source
2
En fait, je ne l'ai pas marqué c ++, certains administrateurs l'ont probablement fait, mais il s'agissait de C ++, alors quelle est la différence en C ++?
Slava V
16
Les méthodes C ++ sont souvent appelées «fonctions membres», je suis donc d'accord que C ++ introduit un peu d'ambiguïté. Ce n'est pas de votre faute - la langue utilise simplement le mot-clé pour deux choses différentes.
Chuck
2
Non, il signifie toujours une fonction C ++. Une fonction libre C ++ plutôt qu'une fonction membre C ++.
Courses de légèreté en orbite
3
@Chuck: la terminologie C ++ n'utilise jamais le mot "méthode"; c'est la terminologie Java - dans les documents standard C ++, elle est toujours appelée "fonction membre" (voir cette réponse ou ce glossaire des termes C ++ vs Java (par exemple, C ++ utilise "membre de données" et Java utilise "champ", etc.)).
ShreevatsaR
6
Pour clarifier un peu cette réponse: le nom de la fonction n'est visible que pour les autres parties de la même unité de traduction, sous la première déclaration de ce nom. La fonction peut être appelée à partir d'autres unités (et de parties antérieures de la même unité) par d'autres moyens, par exemple un pointeur de fonction.
MM
199

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, qui est le fichier objet dans lequel elle est compilée. En d'autres termes, rendre une fonction statique limite sa portée. Vous pouvez considérer une fonction statique comme étant "privée" à son fichier * .c (bien que ce ne soit pas strictement correct).

En C ++, "statique" peut également s'appliquer aux fonctions membres et aux membres de données des 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». Il s'agit de la terminologie 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, tandis que chaque objet a 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 données 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 qu'en C ++, les membres de données statiques et les fonctions membres statiques n'appartiennent à aucun objet, mais à la classe entière.

Dima
la source
42
C ++ a également un fichier statique. Pas besoin de mettre C dans tout ça.
Courses de légèreté en orbite
17
En C ++, une fonction statique est une fonction statique. Une fonction membre statique est une fonction membre statique, également appelée méthode. Le fait que C n'a pas de membres ne signifie pas que les fonctions sont "C".
Gerasimos R
3
y a-t-il une différence entre la var globale et la var statique de classe (sauf l'espace de noms)?
Alexander Malakhov
3
L'espace de noms est la principale différence. L'autre différence est que vous pouvez rendre un membre de données statiques privé et donc uniquement accessible à partir des fonctions membres de la classe. En d'autres termes, vous avez beaucoup plus de contrôle sur un membre de données statiques que sur une variable globale.
Dima
2
Quelqu'un pourrait-il expliquer pourquoi penser une fonction statique comme privée à son fichier .c n'est pas strictement correct? Que reste-t-il à dire?
YoTengoUnLCD
78

Il existe deux utilisations du mot-clé static lorsqu'il s'agit de fonctions en C ++.

La première consiste à marquer la fonction comme ayant un lien interne afin qu'elle ne puisse pas être référencée dans d'autres unités de traduction. Cette utilisation est déconseillée en C ++. Les espaces de noms sans nom sont préférés pour cette utilisation.

// 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 se situe 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 a l'accès habituel aux autres membres), mais elle n'a pas besoin d'être invoquée via un objet particulier. En d'autres termes, à l'intérieur de cette fonction, il n'y a pas de pointeur "this".

Brian Neal
la source
1
La question concerne l'électricité statique en c.
Deqing
8
@Deqing la question était à l'origine balisée C ++ et l'auteur parle d'utiliser des fichiers ".cpp".
Brian Neal
57

Exemple de portée multi-fichiers exécutable minimal

Ici, j'illustre comment staticaffecte la portée des définitions de fonction sur plusieurs fichiers.

ac

#include <stdio.h>

/* Undefined behavior: already defined in main.
 * Binutils 2.24 gives an error and refuses to link.
 * /programming/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

Production:

main f
main sf
main f
a sf

Interprétation

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

Comme d'habitude, plus la portée est petite, mieux c'est, alors déclarez toujours les fonctions staticsi vous le pouvez.

En programmation C, les fichiers sont souvent utilisés pour représenter des "classes" et les staticfonctions représentent des méthodes "privées" de la classe.

Un modèle C commun consiste à passer une thisstructure comme premier argument de "méthode", ce qui est essentiellement ce que fait C ++ sous le capot.

Ce que les normes en disent

C99 N1256 draft 6.7.1 "Storage-class specifiers" dit qu'il statics'agit d'un "stockage-class specifier".

6.2.2 / 3 "Liens d'identifiants" dit staticimplique internal linkage:

Si la déclaration d'un identificateur de portée de fichier pour un objet ou une fonction contient le spécificateur de classe de stockage statique, l'identificateur a une liaison interne.

et 6.2.2 / 2 dit que internal linkagese comporte comme dans notre exemple:

Dans l'ensemble d'unités de traduction et de bibliothèques qui constitue un programme entier, chaque déclaration d'un identifiant particulier avec liaison externe désigne le même objet ou la même fonction. Au sein d'une unité de traduction, chaque déclaration d'un identifiant avec lien interne dénote le même objet ou fonction.

où "unité de traduction" est un fichier source après le prétraitement.

Comment GCC l'implémente-t-il pour ELF (Linux)?

Avec la STB_LOCALreliure.

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

donc la liaison est la seule différence significative entre eux. Valueest juste leur décalage dans la .bsssection, nous nous attendons donc à ce qu'il diffère.

STB_LOCALest documenté sur la spécification ELF à 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. Des 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 d'objets déplaçables, il n'autorise pas plusieurs définitions de symboles STB_GLOBAL avec le même nom.

ce qui est cohérent avec les erreurs de liaison sur plusieurs définitions non statiques.

Si nous augmentons l'optimisation avec -O3, le sfsymbole est entièrement supprimé de la table des symboles: il ne peut en aucun cas être utilisé de l'extérieur. TODO pourquoi garder les fonctions statiques sur la table des symboles quand il n'y a pas d'optimisation? Peuvent-ils être utilisés pour quelque chose?

Voir également

Espaces de noms anonymes C ++

En C ++, vous souhaiterez peut-être utiliser des espaces de noms anonymes au lieu de statiques, ce qui produit un effet similaire, mais masque davantage les définitions de type: espaces de noms sans nom / anonymes vs fonctions statiques

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
la source
3
Remarque: void f() { puts("sf"); }(c'est-à-dire deux définitions de f()) provoque un comportement indéfini sans diagnostic requis. C'est un problème de qualité de l'éditeur de liens pour voir réellement un message d'erreur.
MM
2
C'est l'explication la meilleure et la plus précise! Que toi!
Aqua
20

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 seul fichier, ce modificateur ne fait absolument aucune différence. La différence vient des projets plus importants avec plusieurs fichiers:

En C, chaque "module" (une combinaison de sample.c et sample.h) est compilé indépendamment et ensuite chacun de ces fichiers objets compilés (sample.o) est lié ensemble à un fichier exécutable par l'éditeur de liens.

Disons que vous avez plusieurs fichiers que vous incluez dans votre fichier principal et que deux d'entre eux ont une fonction qui n'est utilisée qu'en interne pour plus de commodité appelée add(int a, b)- le compilateur créerait facilement des fichiers objets pour ces deux modules, mais l'éditeur de liens générera une erreur, car il trouve deux fonctions avec le même nom et il ne sait pas laquelle il doit utiliser (même s'il n'y a rien à lier, car elles ne sont pas utilisées ailleurs mais dans son propre fichier).

C'est pourquoi vous faites de cette fonction, qui n'est utilisée qu'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.

dersimn
la source
16

Premièrement: c'est généralement une mauvaise idée d'inclure un .cppfichier dans un autre fichier - cela conduit à des problèmes comme celui-ci :-) La manière normale est de créer des unités de compilation séparées et d'ajouter un fichier d'en-tête pour le fichier inclus.

Deuxièmement:

C ++ a une terminologie déroutante ici - je ne le savais pas jusqu'à ce que cela soit indiqué dans les commentaires.

a) static functions- hérité de C, et de quoi 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 actuelle - donc dans votre cas a.obj a une copie et votre autre code a une copie indépendante. (Ballonnement de l'exécutable final avec plusieurs copies du code).

b) static member function- ce que l'Orientation 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. Soyez prudent - voici des dragons.

Douglas Leeder
la source
Eh bien, je le fais juste pour libérer de l'espace TEMPORAIREMENT dans main.cpp jusqu'à ce que je décide comment organiser le fichier en bibliothèques avec les .hpp appropriés. Y a-t-il une meilleure idée de comment procéder?
Slava V
1
La terminologie correcte en C ++ est fonction membre, pas méthode. Il n'y a pas de "méthodes" dans le jargon juridique C ++. La méthode est un terme OO général. C ++ les implémente via les fonctions membres.
Brian Neal
14

les définitions de fonctions statiques marqueront ce symbole comme interne. Il ne sera donc pas visible pour les liens de l'extérieur, mais uniquement pour les fonctions dans la même unité de compilation, généralement le même fichier.

raimue
la source
7

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 avoir d'instance. Ceci est parfois utilisé dans le modèle Factory:

Person* tom = Person::createNewPerson();
Perroquets
la source
2
Il me semble que vous parlez de "méthode" statique, pas de "fonction" ??
Slava V
J'ai supposé que vous parliez de fonctions statiques dans une classe.
Parrots
Si je savais que les "méthodes" sont appelées "fonctions de méthode" en C ++, je serais plus clair à ce sujet. Eh bien, maintenant je le fais :) Merci quand même
Slava V
5
Il n'y a pas de "méthodes" en C ++, juste des fonctions. Le standard C ++ ne mentionne jamais les "méthodes", juste les "fonctions".
Brian Neal
1
@Puddle Je sais ce que vous dites mais dans la norme C ++ il n'y a pas de définition d'une "méthode". C ++ n'a que des fonctions, de différents types. "Méthode" est un terme OO général et est utilisé dans d'autres langages et de manière informelle en C ++. Une méthode est officiellement connue sous le nom de "fonction membre" en C ++.
Brian Neal
7

Nit mineur: les fonctions statiques sont visibles pour une unité de traduction, qui dans la plupart des cas pratiques est le fichier dans lequel la fonction est définie. L'erreur que vous obtenez est communément 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 en ligne utilisé dans ce programme; aucun diagnostic requis."

C'est la façon C de voir les fonctions statiques. Ceci est cependant obsolète en C ++.

En C ++, en outre, vous pouvez déclarer des fonctions membres statiques. Ce sont principalement des métafonctions, c'est-à-dire qu'elles ne décrivent / modifient pas le comportement / l'état d'un objet particulier mais agissent sur l'ensemble de la classe elle-même. Cela signifie également que vous n'avez pas besoin de créer un objet pour appeler une fonction membre statique. En outre, cela signifie également que vous n'accédez aux variables membres statiques qu'à partir d'une telle fonction.

J'ajouterais à l'exemple de Parrot le modèle Singleton qui est basé sur ce type de fonction membre statique pour obtenir / utiliser un seul objet pendant toute la durée de vie d'un programme.

dirkgently
la source
7

La réponse à la fonction statique dépend de la langue:

1) Dans les langages 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 d'instance.

user2410022
la source
Ce n'est pas vrai. L'explication de votre deuxième paragraphe fait référence aux " fonctions membres statiques " d'une classe, et non aux " fonctions statiques ". En C ++, une fonction qualifiée avec statica également une portée de fichier, comme elle l'est en C.
RobertS prend en charge Monica Cellio
0

Puisque la fonction statique n'est visible que dans ce fichier. En fait, le compilateur peut faire une optimisation pour vous si vous déclarez "statique" à une fonction.

Voici un exemple simple.

principal c

#include <stdio.h>

static void test() 
{
    ghost(); // This is an unexist function.
}

int main()
{
    int ret = 0;

#ifdef TEST
#else
    test();
#endif
    return (ret);
} 

Et compilez avec

gcc -o main main.c

Vous verrez que cela a échoué. Parce que vous n'implémentez même pas la fonction ghost ().

Mais que faire si nous utilisons la commande suivante.

gcc -DTEST -O2 -o main main.c

Il réussit et ce programme peut être exécuté normalement.

Pourquoi? Il y a 3 points clés.

  1. -O2: Niveau d'optimisation du compilateur au moins 2.
  2. -DTEST: Définissez TEST, donc test () ne sera pas appelé.
  3. Défini "statique" pour tester ().

Ce n'est que si ces 3 conditions sont toutes remplies que vous pouvez passer la compilation. En raison de cette déclaration "statique", le compilateur peut confirmer que test () ne sera JAMAIS appelé dans un autre fichier. Votre compilateur peut supprimer test () lors de la compilation. Comme nous n'avons pas besoin de test (), peu importe si ghost () est défini ou implémenté.

PoJyun Chiou
la source
0

" Qu'est-ce qu'une staticfonction " "en C? "

Commençons par le début.

Tout est basé sur une chose appelée "liaison":

" Un identifiant déclaré dans différentes étendues ou dans la même étendue plusieurs fois peut être fait pour faire référence au même objet ou à la même fonction par un processus appelé liaison. 29) Il existe trois types de liaison: externe, interne et aucune. "

Source: C18, 6.2.2 / 1


"Dans l'ensemble d'unités de traduction et de bibliothèques qui constitue un programme entier, chaque déclaration d'un identifiant particulier avec liaison externe dénote le même objet ou fonction. Dans une unité de traduction, chaque déclaration d'un identifiant avec liaison interne dénote le même objet ou fonction . Chaque déclaration d'un identifiant sans lien indique une entité unique. "

Source: C18, 6.2.2 / 2


Si une fonction est définie sans spécificateur de classe de stockage, la fonction a une externliaison par défaut par défaut:

"Si la déclaration d'un identifiant pour une fonction n'a pas de spécificateur de classe de stockage, sa liaison est déterminée exactement comme si elle avait été déclarée avec le spécificateur de classe de stockage extern ."

Source: C18, 6.2.2 / 5

Cela signifie que - si votre programme contient plusieurs unités de traduction / fichiers source ( .cou .cpp) - la fonction est visible dans toutes les unités de traduction / fichiers source de votre programme.

Cela peut être un problème dans certains cas. Que faire si vous souhaitez utiliser deux fonctions différentes (définitions), mais avec le même nom de fonction dans deux contextes différents (en fait le fichier-contexte).

En C et C ++, le staticqualificatif de classe de stockage appliqué à une fonction à portée de fichier (pas une fonction membre statique d'une classe en C ++ ou une fonction dans un autre bloc) vient maintenant pour aider et signifie que la fonction respective n'est visible qu'à l'intérieur de l'unité de traduction / le fichier source dans lequel il a été défini et non dans les autres TLU / fichiers.

"Si la déclaration d'un identificateur de portée de fichier pour un objet ou une fonction contient le spécificateur de classe de stockage statique , l'identifiant a une liaison interne. 30)"


30) Une déclaration de fonction peut contenir les spécificateurs de classe de stockage uniquement si elle est à la portée du fichier; voir 6.7.1.

Source: C18, 6.2.2 / 3


Ainsi, une staticfonction n'a de sens que si:

  1. Votre programme est composé de plusieurs unités de traduction / fichiers source ( .cou .cpp).

    et

  2. Vous souhaitez limiter l'étendue d'une fonction au fichier dans lequel la fonction spécifique est définie.

Dans le cas contraire les deux de ces exigences correspondent, vous n'avez pas besoin d'envelopper autour de votre tête sur l' admissibilité d' une fonction static.


Notes annexes:

  • Comme déjà mentionné, une staticfonction n'a absolument aucune différence entre C et C ++, car il s'agit d'une fonction C ++ héritée de C.

    Il n'a pas d'importance que dans la communauté C ++, il y ait un débat déchirant sur la dépréciation des fonctions qualificatives par staticrapport à l'utilisation d' espaces de noms sans nom à la place, initialement initialisé par un paragraphe mal placé dans la norme C ++ 03, déclarant l'utilisation de fonctions statiques obsolètes qui ont été bientôt révisées par le comité lui-même et supprimées en C ++ 11.

    Cela a fait l'objet de diverses questions SO:

    Espaces de noms sans nom / anonymes vs fonctions statiques

    Supériorité de l'espace de noms sans nom sur statique?

    Pourquoi un espace de noms sans nom est une alternative "supérieure" à statique?

    Obsolescence du mot-clé statique ... pas plus?

    En fait, il n'est pas encore obsolète selon la norme C ++. Ainsi, l'utilisation des staticfonctions est toujours légitime. Même si les espaces de noms sans nom présentent des avantages, la discussion sur l’utilisation ou la non-utilisation de fonctions statiques en C ++ est sujette à une seule opinion (fondée sur l’opinion) et ne convient pas à ce site Web.

RobertS soutient Monica Cellio
la source