Est-ce que «inline» sans «static» ou «extern» est utile en C99?

Quand j’essaye de construire ce code

inline void f() {} int main() { f(); } 

en utilisant la ligne de commande

 gcc -std=c99 -oa ac 

Je reçois une erreur de l’éditeur de liens (référence indéfinie à f ). L’erreur disparaît si j’utilise static inline ou extern inline static inline au lieu de simplement en inline , ou si je comstack avec -O (donc la fonction est en fait en ligne).

Ce comportement semble être défini au paragraphe 6.7.4 (6) de la norme C99:

Si toutes les déclarations de scope de fichier pour une fonction dans une unité de traduction incluent le spécificateur de fonction inline sans extern , la définition dans cette unité de traduction est une définition inline. Une définition en ligne ne fournit pas de définition externe pour la fonction et n’interdit pas une définition externe dans une autre unité de traduction. Une définition en ligne fournit une alternative à une définition externe, qu’un traducteur peut utiliser pour implémenter un appel à la fonction dans la même unité de traduction. Il n’est pas spécifié si un appel à la fonction utilise la définition en ligne ou la définition externe.

Si je comprends bien tout cela, une unité de compilation avec une fonction définie en inline comme dans l’exemple ci-dessus ne comstack de manière cohérente que s’il existe également une fonction externe du même nom et je ne sais jamais si ma propre fonction ou la fonction externe est appelée.

Ce comportement n’est-il pas complètement idiot? Est-il utile de définir une fonction inline sans static ni extern en C99? Est-ce que je manque quelque chose?

Résumé des réponses

Bien sûr, il me manquait quelque chose et le comportement n’est pas idiot. 🙂

Comme l’ explique Nemo , l’idée est de mettre la définition de la fonction

 inline void f() {} 

dans le fichier d’en-tête et seulement une déclaration

 extern inline void f(); 

dans le fichier .c correspondant. Seule la déclaration extern déclenche la génération d’un code binary visible de l’extérieur. Et il n’ya en effet aucune utilisation de l’ inline dans un fichier .c – elle n’est utile que dans les en-têtes.

Comme l’explique la raison d’être du comité C99 cité dans la réponse de Jonathan , inline est tout au sujet des optimisations du compilateur qui nécessitent la définition d’une fonction pour qu’elle soit visible sur le site d’un appel. Cela ne peut être réalisé qu’en plaçant la définition dans l’en-tête et, bien sûr, une définition dans un en-tête ne doit pas émettre de code à chaque fois que le compilateur le voit. Mais comme le compilateur n’est pas obligé d’inclure une fonction, une définition externe doit exister quelque part.

En fait, cette excellente réponse répond également à votre question, je pense:

extern inline

L’idée est que “inline” peut être utilisé dans un fichier d’en-tête, puis “extern inline” dans un fichier .c. “extern inline” indique comment le fichier object doit contenir le code généré (visible de l’extérieur).

[mettre à jour, pour élaborer]

Je ne pense pas que “inline” soit utilisé (sans “static” ou “extern”) dans un fichier .c. Mais dans un fichier d’en-tête, cela a du sens, et il nécessite une déclaration “extern inline” correspondante dans certains fichiers .c pour générer le code autonome.

De la norme (ISO / IEC 9899: 1999) elle-même:

Annexe J.2 Comportement non défini

  • Une fonction avec liaison externe est déclarée avec un spécificateur de fonction en inline , mais n’est pas également définie dans la même unité de traduction (6.7.4).

Le comité C99 a rédigé une justification , qui dit:

6.7.4 Spécificateurs de fonction

Une nouvelle fonctionnalité de C99: Le mot-clé inline , adapté de C ++, est un spécificateur de fonction qui ne peut être utilisé que dans les déclarations de fonction. Il est utile pour les optimisations de programme qui nécessitent la définition d’une fonction pour être visible sur le site d’un appel. (Notez que la norme ne tente pas de spécifier la nature de ces optimisations.)

La visibilité est assurée si la fonction a une liaison interne ou si elle a un lien externe et que l’appel se trouve dans la même unité de traduction que la définition externe. Dans ces cas, la présence du mot-clé inline dans une déclaration ou une définition de la fonction n’a pas d’autre effet que d’indiquer une préférence pour que les appels de cette fonction soient optimisés par rapport aux appels d’autres fonctions déclarées sans le mot-clé inline .

La visibilité est un problème pour un appel d’une fonction avec une liaison externe où l’appel se trouve dans une unité de traduction différente de la définition de la fonction. Dans ce cas, le mot-clé inline permet à l’unité de traduction contenant l’appel de contenir également une définition locale ou inline de la fonction.

Un programme peut contenir une unité de traduction avec une définition externe, une unité de traduction avec une définition en ligne et une unité de traduction avec une déclaration mais pas de définition pour une fonction. Les appels dans cette dernière unité de traduction utiliseront la définition externe comme d’habitude.

Une définition en ligne d’une fonction est considérée comme une définition différente de la définition externe. Si un appel à une fonction func avec un lien externe se produit lorsqu’une définition en ligne est visible, le comportement est le même que si l’appel a été effectué sur une autre fonction, disons __func , avec un lien interne. Un programme conforme ne doit pas dépendre de la fonction appelée. C’est le modèle en ligne de la norme.

Un programme conforme ne doit pas s’appuyer sur l’implémentation à l’aide de la définition inline, ni s’appuyer sur l’implémentation à l’aide de la définition externe. L’adresse d’une fonction est toujours l’adresse correspondant à la définition externe, mais lorsque cette adresse est utilisée pour appeler la fonction, la définition en ligne peut être utilisée. Par conséquent, l’exemple suivant peut ne pas se comporter comme prévu.

 inline const char *saddr(void) { static const char name[] = "saddr"; return name; } int compare_name(void) { return saddr() == saddr(); // unspecified behavior } 

Étant donné que l’implémentation peut utiliser la définition inline pour l’un des appels à saddr et utiliser la définition externe pour l’autre, l’opération d’égalité n’est pas garantie à 1 (true). Cela montre que les objects statiques définis dans la définition en ligne sont distincts de leur object correspondant dans la définition externe. Cela a motivé la contrainte de ne même pas définir un object non const de ce type.

L’inlining a été ajouté à la norme de manière à pouvoir être implémenté avec la technologie de l’éditeur de liens existante, et un sous-ensemble de l’inclusion C99 est compatible avec C ++. Cela a été réalisé en exigeant que exactement une unité de traduction contenant la définition d’une fonction inline soit spécifiée comme celle fournissant la définition externe de la fonction. Comme cette spécification consiste simplement en une déclaration qui ne contient pas le mot-clé inline , ou qui contient à la fois inline mots-clés inline et extern , elle sera également acceptée par un traducteur C ++.

L’insertion dans C99 étend la spécification C ++ de deux manières. Premièrement, si une fonction est déclarée en inline dans une unité de traduction, elle n’a pas besoin d’être déclarée en inline dans toutes les autres unités de traduction. Cela permet, par exemple, une fonction de bibliothèque qui doit être intégrée dans la bibliothèque mais disponible uniquement via une définition externe ailleurs. L’alternative d’utiliser une fonction wrapper pour la fonction externe nécessite un nom supplémentaire; et cela peut également avoir un impact négatif sur les performances si un traducteur ne fait pas de substitution en ligne.

Deuxièmement, l’exigence que toutes les définitions d’une fonction en ligne soient «exactement les mêmes» est remplacée par l’exigence que le comportement du programme ne dépende pas du fait qu’un appel soit implémenté avec une définition en ligne visible ou une définition externe. fonction. Cela permet à une définition en ligne d’être spécialisée pour son utilisation dans une unité de traduction particulière. Par exemple, la définition externe d’une fonction de bibliothèque peut inclure une validation d’argument inutile pour les appels provenant d’autres fonctions de la même bibliothèque. Ces extensions offrent certains avantages. et les programmeurs soucieux de compatibilité peuvent simplement respecter les règles C ++ plus ssortingctes.

Notez qu’il n’est pas approprié pour les implémentations de fournir des définitions en ligne des fonctions de bibliothèque standard dans les en-têtes standard car cela peut casser certains codes hérités qui redéfinissent les fonctions de bibliothèque standard après avoir inclus leurs en-têtes. Le mot clé en inline est uniquement destiné à fournir aux utilisateurs un moyen portable de suggérer l’installation de fonctions. Étant donné que les en-têtes standard ne doivent pas nécessairement être portables, les implémentations ont d’autres options dans le sens de:

 #define abs(x) __builtin_abs(x) 

ou d’autres mécanismes non portables pour intégrer des fonctions de bibliothèque standard.

> Je reçois une erreur de l’éditeur de liens (référence indéfinie à f )

Fonctionne ici: Linux x86-64, GCC 4.1.2. Peut être un bug dans votre compilateur; Je ne vois rien dans le paragraphe cité de la norme qui interdit le programme donné. Notez l’utilisation de if plutôt que iff .

Une définition en ligne fournit une alternative à une définition externe , qu’un traducteur peut utiliser pour implémenter un appel à la fonction dans la même unité de traduction.

Donc, si vous connaissez le comportement de la fonction f et que vous voulez l’appeler dans une boucle serrée, vous pouvez copier-coller sa définition dans un module pour empêcher les appels de fonctions; ou , vous pouvez fournir une définition qui, pour les besoins du module actuel, est équivalente (mais ignore la validation des entrées ou l’optimisation que vous pouvez imaginer). L’auteur du compilateur a toutefois la possibilité d’optimiser la taille du programme à la place.