web-dev-qa-db-fra.com

C / C ++: Fonction statique dans le fichier d'en-tête, qu'est-ce que cela signifie?

Je sais ce que cela signifie lorsque la fonction statique est déclarée dans le fichier source. Je lis du code, j'ai constaté que la fonction statique dans les fichiers d'en-tête pouvait être invoquée dans d'autres fichiers.

49
Jarod

La fonction est-elle définie dans le fichier d'en-tête? Pour que le code réel soit donné directement dans la fonction, comme ceci:

static int addTwo(int x)
{
  return x + 2;
}

Ensuite, c'est juste un moyen de fournir une fonction utile à de nombreux fichiers C différents. Chaque fichier C qui inclut l'en-tête aura sa propre définition qu'il peut appeler. Bien sûr, cela gaspille de la mémoire et c'est (à mon avis) une chose assez laide à faire, car avoir du code exécutable dans un en-tête n'est généralement pas une bonne idée.

N'oubliez pas que #include: Ing un en-tête colle simplement le contenu de l'en-tête (et tous les autres en-têtes qu'il inclut) dans le fichier C comme vu par le compilateur. Le compilateur ne sait jamais que la définition d'une fonction particulière provient d'un fichier d'en-tête.

[~ # ~] update [~ # ~] : Dans de nombreux cas, c'est en fait une bonne idée de faire quelque chose comme ci-dessus, et je me rends compte La réponse semble très noire à ce sujet, ce qui simplifie un peu les choses. Par exemple, le code qui modélise (ou utilise simplement) fonctions intrinsèques peut être exprimé comme ci-dessus, et avec un mot clé explicite inline même:

static inline int addTwo(int *x)
{
  __add_two_superquickly(x);
}

Ici, la fonction __add_two_superquickly() est un élément intrinsèque fictif, et puisque nous voulons que la fonction entière soit compilée en une seule instruction, nous voulons vraiment qu'elle soit intégrée. Pourtant, ce qui précède est plus propre que l'utilisation d'une macro.

L'avantage par rapport à l'utilisation directe de l'intrinsèque est bien sûr que l'envelopper dans une autre couche d'abstraction permet de construire le code sur des compilateurs dépourvus de cet intrinsèque particulier, en fournissant une implémentation alternative et en choisissant la bonne en fonction du compilateur utilisé. .

61
unwind

Il créera effectivement une fonction statique distincte avec le même nom dans chaque fichier cpp dans lequel il est inclus. Il en va de même pour les variables globales.

14
sharptooth

Comme d'autres le disent, elle a exactement la même signification qu'une fonction static dans le .c fichier lui-même. En effet, il n'y a pas de différence sémantique entre .c et .h des dossiers; il n'y a que l'unité de compilation composée du fichier réellement passé au compilateur (généralement nommé .c) avec le contenu de tous les fichiers nommés dans #include lignes (généralement nommées .h) inséré dans le flux tel qu'il est vu par le préprocesseur.

La convention selon laquelle la source C se trouve dans un fichier nommé .c et les déclarations publiques sont dans des fichiers nommés .h n'est qu'une convention. Mais c'est généralement une bonne chose. En vertu de cette convention, les seules choses qui devraient apparaître dans .h les fichiers sont des déclarations afin que vous évitiez généralement d'avoir le même symbole défini plus d'une fois dans un même programme.

Dans ce cas particulier, le mot clé static rend le symbole privé au module, il n'y a donc pas de conflit à définitions multiples en attente de problème. Donc, dans ce sens, il est sûr de le faire. Mais en l'absence de garantie que la fonction serait en ligne, vous prenez le risque que la fonction soit instanciée dans chaque module arrivé à #include ce fichier d'en-tête qui au mieux est un gaspillage de mémoire dans le segment de code.

Je ne sais pas quels cas d'utilisation justifieraient de le faire dans un en-tête public généralement disponible.

Si la .h le fichier est un code généré et inclus uniquement dans un seul .c fichier, alors je nommerais personnellement le fichier autrement que .h pour souligner qu'il ne s'agit pas du tout d'un en-tête public. Par exemple, un utilitaire qui convertit un fichier binaire en une définition de variable initialisée peut écrire un fichier destiné à être utilisé via #include et pourrait très bien contenir une déclaration static de la variable, et peut-être même static des définitions d'accesseur ou d'autres fonctions utilitaires associées.

7
RBerteig

Si vous définissez la fonction dans un fichier d'en-tête (pas simplement la déclarez), une copie de la fonction sera générée dans chaque unité de traduction (essentiellement dans chaque fichier cpp qui inclut cet en-tête).

Cela peut augmenter la taille de votre exécutable, mais cela peut être négligeable si la fonction est petite. L'avantage est que la plupart des compilateurs peuvent aligner la fonction, ce qui peut augmenter les performances du code.

Mais il peut y avoir une grosse différence dans ce processus qui n'a été mentionnée dans aucune réponse. Si votre fonction utilise une variable locale statique telle que:

static int counter()
{
    static int ctr = 0;
    return ctr++;
}

Plutôt que:

//header
int counter();

//source
int counter()
{
    static int ctr = 0;
    return ctr++;
}

Ensuite, chaque fichier source comprenant cet en-tête aura son propre compteur. Si la fonction est déclarée à l'intérieur de l'en-tête et définie dans un fichier source, le compteur sera partagé avec l'ensemble de votre programme.

Dire que la seule différence sera la performance et la taille du code est faux.

4
galinette

Il est utile dans certaines bibliothèques "en-tête uniquement" avec de petites fonctions en ligne. Dans un tel cas, vous voulez toujours faire une copie de la fonction, ce n'est donc pas un mauvais modèle. Cependant, cela vous permet d'insérer facilement des parties d'interface et d'implémentation distinctes dans le fichier d'en-tête unique:

// header.h

// interface part (for user?!)
static inline float av(float a, float b);

// implementation part (for developer)
static inline float av(float a, float b)
{
    return (a+b)/2.f;
}

La bibliothèque mathématique de vecteur Apple dans le cadre GLK utilise une telle construction (par exemple GLKMatrix4.h).

1
mawigator

Il n'y a pas de différence sémantique dans la définition du fichier source ou du fichier d'en-tête, fondamentalement les deux signifient la même chose en C simple lorsque vous utilisez un mot-clé statique, vous limitez la portée.

Cependant, il y a un problème à l'écrire dans le fichier d'en-tête, car chaque fois que vous incluez l'en-tête dans un fichier source, vous aurez une copie de la fonction avec la même implémentation, ce qui est très similaire à une fonction normale définie dans l'en-tête fichier. En ajoutant la définition dans l'en-tête, vous n'atteignez pas le but de la fonction statique.

Par conséquent, je suggère que vous ne deviez avoir votre implémentation que dans votre fichier source et non dans l'en-tête.

1
legend2804