Quelle est l’utilisation du 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 C ++. Comment ça marche en C?
  3. Que fait réellement extern inline void f(void); faire?

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

Remarque: lorsque je parle de fichiers .h et de fichiers .h dans cette réponse, je suppose que vous avez disposé votre code correctement, c’est-à-dire que les fichiers .h n’incluent que les fichiers .h . La différence 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. Leur comportement est identique (en supposant que vous ne les re-déclarez pas différemment dans le même TU bien sûr!) Le seul effet pratique peut être de forcer le compilateur à s’optimiser différemment.

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

Ceci est expliqué par cette réponse et aussi ce fil .

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

En ISO C, il ne fournit pas de définition externe. En ISO C ++, il fournit une définition externe. Cependant, C ++ a une règle supplémentaire (ce que C n’a pas), qui veut que s’il existe plusieurs définitions externes d’une fonction inline , le compilateur la sortinge et en choisit une.

extern inline void f(void); dans un fichier .c dans ISO C est destiné à être associé à l’utilisation de inline void f(void) {} dans les fichiers d’en-tête. La définition externe de la fonction est émise dans cette unité de traduction. Si vous ne le faites pas, il n’y a pas de définition externe et vous risquez donc d’obtenir une erreur de lien (il n’est pas précisé si un appel particulier de f lié ou non à la définition externe).

En d’autres termes, dans l’ISO C, vous pouvez sélectionner manuellement l’emplacement de la définition externe; ou supprimer entièrement la définition externe en utilisant static inline utilisation static inline partout; mais dans ISO C ++, le compilateur choisit si et où une définition externe doit aller.

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

Pour compliquer davantage les choses, GNU C ++ vous permet d’écrire en static inline une static inline extern inline en code C ++. Je ne voudrais pas deviner ce que cela fait exactement.

Je n’ai jamais vraiment trouvé d’utilisation du mot clé inline dans mes programmes C, et lorsque je vois ce mot clé dans le code des autres utilisateurs, il est presque toujours statique en ligne.

Beaucoup de codeurs ne savent pas ce qu’ils font et assemblent simplement quelque chose qui semble fonctionner. Un autre facteur ici est que le code que vous observez a peut-être é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. Par conséquent, le fait de disposer d’un fichier .h avec une fonction plain inline incluse à partir de deux unités de traduction provoque un comportement indéfini.

Donc, si le codeur veut fournir l’indicateur d’optimisation inline dans GNU C, alors static inline est requirejs. Puisque static inline fonctionne à la fois en ISO C et en GNU C, il est naturel que les gens se soient arrangés pour cela et aient constaté que cela semblait fonctionner sans donner d’erreur.

, 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.

Le code AC 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’appeler plus rapidement cette fonction. Pour ce faire, GCC peut notamment intégrer le code de cette fonction dans le code de ses appelants. Cela accélère l’exécution en éliminant le surcoût de l’appel de fonction; de plus, si l’une des valeurs d’arguments réelles est constante, leurs valeurs connues peuvent permettre des simplifications au moment de la compilation, de sorte que le code de la fonction inline ne doit pas nécessairement être inclus. L’effet sur la taille du code est moins prévisible. le code object peut être plus grand ou plus petit avec une fonction en ligne, selon le cas.

Ainsi, il demande 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 des fonctions de petite taille, telles que définir / effacer un indicateur ou une bascule de bits exécutées de manière répétée, en inline , cela peut faire une grande différence de performances en termes de temps, mais au désortingment de la taille du code.


inline non statique et inline statique

Toujours en référence à gcc.gnu.org ,

Lorsqu’une fonction en ligne n’est pas statique, le compilateur doit supposer qu’il peut y avoir des appels provenant 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, les appels qu’il contient ne peuvent donc pas être intégrés. Par conséquent, une fonction inline non statique est toujours compilée seule de la manière habituelle.


extern inline?

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

Si vous spécifiez à la fois inline et extern dans la définition de la fonction, celle-ci est utilisée uniquement pour l’inline. 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 simplement 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 est de mettre une définition de fonction dans un fichier d’en-tête avec ces mots-clés et de placer une autre copie de la définition (inline et extern manquante) dans un fichier de bibliothèque. La définition dans le fichier d’en-tête entraîne la plupart des appels à la fonction. S’il rest des utilisations de la fonction, elles font référence à la copie unique dans la bibliothèque.


Résumer:

  1. Pour inline void f(void){} , la définition inline n’est valide que dans l’unité de traduction en cours.
  2. static inline void f(void) {} La classe de stockage étant static , l’identifiant est associé à un lien interne et la définition inline est invisible dans les autres unités de traduction.
  3. Pour extern inline void f(void); Étant donné que la classe de stockage est extern , l’identificateur a une liaison externe et la définition en ligne fournit également la définition externe.

A partir 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 inline est une fonction inline. Faire d’une fonction une fonction en ligne suggère que les appels à cette fonction soient aussi rapides que possible. 138) La mesure dans laquelle ces suggestions sont efficaces est définie par la mise en œuvre . 139)

138) En utilisant, par exemple, une alternative au mécanisme d’appel de fonction habituel, tel que la substitution en ligne . La substitution en ligne n’est pas une substitution textuelle , elle ne crée pas de nouvelle fonction. Par exemple, par exemple, le développement d’une macro utilisée dans le corps de la fonction utilise la définition qu’il avait au point où le corps de la fonction apparaît et non à l’endroit où la fonction est appelée; et les identificateurs font référence aux déclarations dans la scope 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 apparaissent 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 à des appels entrant dans le champ d’une déclaration inline.

Il suggère au compilateur que cette fonction est largement utilisée et demande à préférer la rapidité d’appel de cette fonction. Mais avec un compilateur intelligent moderne, cela peut être plus ou moins hors de propos, car les compilateurs peuvent décider si une fonction doit être en ligne et ignorer la requête en ligne des utilisateurs, car les compilateurs modernes peuvent très efficacement décider de la manière d’appeler les fonctions.

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

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

inline void f(void) {} en C ne fonctionne pas comme 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 réellement extern inline void f (void)? faire?

Cela fournira un lien externe à f . Comme 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 l’ inline .

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 appelle cela une définition en ligne.
Aucun code d’object 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; } 

Voici une référence qui peut vous donner plus de clarté sur les fonctions inline en C et également sur l’utilisation d’inline & extern.

Comme un mot “En-ligne” dit “Dans” “Ligne”, l’ajout de ce mot clé à la fonction affecte le programme en cours d’exécution. Lorsqu’un programme est compilé, le code écrit à l’intérieur de la fonction est collé sous l’appel de fonction, car les appels de fonction sont plus coûteux code en ligne, donc cela optimise le code. Donc, void inline statique f (void) {} ​​et void statique f (void) {}, dans ce mot clé en ligne fait la différence au moment de l’exécution. Mais lorsque la fonction comporte trop de lignes de code, cela n’affectera pas le temps d’exécution. Si vous ajoutez statique avant fonction, la durée de vie de la fonction est la durée de vie de l’ensemble du programme. Et l’utilisation de cette fonction est limitée à ce fichier uniquement. Pour en savoir plus sur extern, vous pouvez vous référer à – Effets du mot clé extern sur les fonctions C