web-dev-qa-db-fra.com

À quoi sert le mot-clé `inline` en C?

J'ai lu plusieurs questions dans stackoverflow sur inline en C mais je ne suis toujours pas clair à ce sujet.

  1. static inline void f(void) {} n'a aucune différence pratique avec static void f(void) {}.
  2. inline void f(void) {} en C ne fonctionne pas comme la manière C++. Comment ça marche en C?
  3. Que fait réellement extern inline void f(void);?

Je n'ai jamais vraiment trouvé d'utilisation du mot clé inline dans mes programmes C, et quand je vois ce mot clé dans le code des autres, c'est presque toujours static inline, dans lequel je ne vois aucune différence avec juste static.

34
xiver77

Remarque: lorsque je parle de fichiers .c Et de fichiers .h Dans cette réponse, je suppose que vous avez correctement défini votre code, c'est-à-dire que les fichiers .c Incluent uniquement .h des dossiers. La distinction est qu'un fichier .h Peut être inclus dans plusieurs unités de traduction.

static inline void f(void) {} n'a aucune différence pratique avec static void f(void) {}.

En ISO C, c'est correct. Ils ont un comportement identique (en supposant que vous ne les déclariez pas différemment de la même manière TU bien sûr!), Le seul effet pratique peut être d'amener le compilateur à optimiser différemment.

inline void f(void) {} en C ne fonctionne pas comme la manière C++. Comment ça marche en C? Que fait réellement extern inline void f(void);?

Cela s'explique par cette réponse et aussi ce fil .

En ISO C et C++, vous pouvez librement utiliser inline void f(void) {} dans les fichiers d'en-tête - bien que pour des raisons différentes!

Dans ISO C, il ne fournit pas du tout de définition externe. En ISO C++, il fournit une définition externe; cependant C++ a une règle supplémentaire (que C n'a pas), que s'il y a plusieurs définitions externes d'une fonction inline, alors le compilateur la trie et en choisit une.

extern inline void f(void); dans un fichier .c en ISO C est destiné à être associé à l'utilisation de inline void f(void) {} dans les fichiers d'en-tête. Il provoque la définition externe de la fonction à émettre dans cette unité de traduction. Si vous ne le faites pas, il n'y a pas de définition externe, et vous pouvez donc obtenir une erreur de lien (il n'est pas spécifié si un appel particulier de f établit ou non un lien vers la définition externe).

En d'autres termes, dans ISO C, vous pouvez sélectionner manuellement où va la définition externe; ou supprimez entièrement la définition externe en utilisant static inline partout; mais en ISO C++, le compilateur choisit si et où irait une définition externe.

Dans GNU C, les choses sont différentes (plus de détails ci-dessous).

Pour compliquer encore les choses, GNU C++ vous permet d'écrire static inline Un extern inline En code C++ ... Je ne voudrais pas deviner ce que cela fait exactement

Je n'ai jamais vraiment trouvé une utilisation du mot-clé en ligne dans mes programmes C, et quand je vois ce mot-clé dans le code des autres, c'est presque toujours en ligne statique

De nombreux codeurs ne savent pas ce qu'ils font et mettent simplement en place quelque chose qui semble fonctionner. Un autre facteur ici est que le code que vous regardez peut avoir été écrit pour GNU C, pas ISO C.

Dans GNU C , plain inline se comporte différemment de ISO C. Il émet en fait une définition visible de l'extérieur, donc avoir un fichier .h Avec un plain inline la fonction incluse à partir de deux unités de traduction provoque un comportement indéfini.

Donc, si le codeur veut fournir le conseil d'optimisation inline dans GNU C, alors static inline Est requis. Puisque static inline Fonctionne dans les deux ISO C et GNU C, il est naturel que les gens finissent par se contenter de cela et voient que cela semble fonctionner sans donner d'erreurs.

, dans lequel je ne vois aucune différence avec juste statique.

La différence réside simplement dans l'intention de fournir au compilateur un indice d'optimisation de la vitesse par rapport à la taille. Avec les compilateurs modernes, cela est superflu.

20
M.M

Un code C peut être optimisé de deux manières: pour la taille du code et pour le temps d'exécution.

fonctions en ligne:

gcc.gnu.org dit,

En déclarant une fonction en ligne, vous pouvez demander à GCC d'effectuer des appels à cette fonction plus rapidement. GCC peut y parvenir en intégrant le code de cette fonction dans le code de ses appelants. Cela rend l'exécution plus rapide en éliminant la surcharge d'appel de fonction; en outre, si l'une des valeurs d'argument réelles est constante, leurs valeurs connues peuvent permettre des simplifications au moment de la compilation de sorte que tout le code de la fonction en ligne n'a pas besoin d'être inclus. L'effet sur la taille du code est moins prévisible; le code objet peut être plus grand ou plus petit avec fonction en ligne, selon le cas particulier.

Ainsi, il indique au compilateur de construire la fonction dans le code où elle est utilisée dans le but d'améliorer le temps d'exécution.

Si vous déclarez de petites fonctions comme définir/effacer un indicateur ou une bascule de bits qui sont exécutées de manière répétée, inline, cela peut faire une grande différence de performances en termes de temps, mais au prix de la taille du code.


en ligne non statique et en ligne statique

Se référant à nouveau à gcc.gnu.org ,

Lorsqu'une fonction en ligne n'est pas statique, le compilateur doit supposer qu'il peut y avoir des appels à partir d'autres fichiers source; puisqu'un symbole global ne peut être défini qu'une seule fois dans n'importe quel programme, la fonction ne doit pas être définie dans les autres fichiers source, donc les appels qui y sont contenus ne peuvent pas être intégrés. Par conséquent, une fonction en ligne non statique est toujours compilée seule de la manière habituelle.


externe en ligne?

Encore une fois, gcc.gnu.org , dit tout:

Si vous spécifiez à la fois inline et extern dans la définition de fonction, la définition est utilisée uniquement pour l'inlining. En aucun cas, la fonction n'est compilée seule, même si vous vous référez explicitement à son adresse. Une telle adresse devient une référence externe, comme si vous aviez seulement déclaré la fonction et ne l'aviez pas définie.

Cette combinaison d'inline et d'extern a presque l'effet d'une macro. La façon de l'utiliser consiste à placer une définition de fonction dans un fichier d'en-tête avec ces mots clés et à placer une autre copie de la définition (manquant inline et extern) dans un fichier de bibliothèque. La définition dans le fichier d'en-tête entraîne la plupart des appels à la fonction en ligne. S'il reste des utilisations de la fonction, elles se réfèrent à la copie unique dans la bibliothèque.


Résumer:

  1. Pour inline void f(void){}, inline, la définition n'est valide que dans l'unité de traduction actuelle.
  2. Pour static inline void f(void) {} Puisque la classe de stockage est static, l'identifiant a une liaison interne et la définition inline est invisible dans les autres unités de traduction.
  3. Pour extern inline void f(void); Puisque la classe de stockage est extern, l'identifiant a une liaison externe et la définition en ligne fournit également la définition externe.
22
WedaPashi

De 6.7.4 Spécificateurs de fonction dans les spécifications C11

6 Une fonction déclarée avec un spécificateur de fonction en ligne est une fonction en ligne. Faire d'une fonction une fonction en ligne suggère que les appels à la fonction soient aussi rapides que possible.138)La mesure dans laquelle ces suggestions sont efficaces est définie par l'implémentation.139)

138) En utilisant, par exemple, une alternative au mécanisme d'appel de fonction habituel, comme la substitution en ligne . La substitution en ligne est pas la substitution textuelle, ni ne crée une nouvelle fonction. Par conséquent, par exemple, l'expansion d'une macro utilisée dans le corps de la fonction utilise la définition qu'elle avait au moment où le corps de la fonction apparaît, et non à l'endroit où la fonction est appelée; et les identifiants font référence aux déclarations de portée où se trouve le corps. De même, la fonction a une seule adresse, quel que soit le nombre de définitions en ligne qui se produisent en plus de la définition externe.

139) Par exemple, une implémentation peut ne jamais effectuer de substitution en ligne, ou peut uniquement effectuer des substitutions en ligne aux appels dans le cadre d'une déclaration en ligne.

Il suggère au compilateur que cette fonction est largement utilisée et demande de préférer la vitesse dans l'invocation de cette fonction. Mais avec un compilateur intelligent moderne, cela peut être plus ou moins inutile car les compilateurs peuvent décider si une fonction doit être insérée et ignorer la demande en ligne des utilisateurs, car les compilateurs modernes peuvent très efficacement décider comment appeler les fonctions.

static inline void f(void) {} n'a aucune différence pratique avec static void f(void) {}.

Alors oui avec des compilateurs modernes la plupart du temps aucun. Avec tous les compilateurs, il y a non différences de sortie pratiques/observables.

inline void f(void) {} en C ne fonctionne pas comme la manière C++. Comment ça marche en C?

Une fonction qui est en ligne n'importe où doit être en ligne partout en C++ et l'éditeur de liens ne se plaint pas d'une erreur de définition multiple (la définition doit être la même).

Que fait en fait extern inline void f (void); faire?

Cela fournira un lien externe vers f. Étant donné que le f peut être présent dans une autre unité de compilation, un compilateur peut choisir un mécanisme d'appel différent pour accélérer les appels ou peut ignorer complètement le inline.

5
Mohit Jain

Une fonction où toutes les déclarations (y compris la définition) mentionnent inline et jamais extern.
Il doit y avoir une définition dans la même unité de traduction. La norme y fait référence en tant que définition en ligne.
Aucun code objet autonome n'est émis, cette définition ne peut donc pas être appelée à partir d'une autre unité de traduction.

Dans cet exemple, toutes les déclarations et définitions utilisent inline mais pas extern:

// a declaration mentioning inline     
inline int max(int a, int b);

// a definition mentioning inline  
inline int max(int a, int b) {  
  return a > b ? a : b;  
}

Ici est une référence qui peut vous donner plus de clarté sur les fonctions inline en C et aussi sur l'utilisation de inline & extern.

3
Alekhya Vemavarapu