inline int factorial(int n)
{
if(!n) return 1;
else return n*factorial(n-1);
}
En lisant this , j'ai trouvé que le code ci-dessus conduirait à une "compilation infinie" s'il n'était pas géré correctement par le compilateur.
Comment le compilateur décide-t-il d'inclure ou non une fonction?
Tout d'abord, la spécification inline
sur une fonction n'est qu'un indice. Le compilateur peut (et le fait souvent) ignorer complètement la présence ou l'absence d'un qualificatif inline
. Cela dit, un compilateur peut inline une fonction récursive, tout comme il peut dérouler une boucle infinie. Il lui suffit de limiter le niveau auquel il "déroulera" la fonction.
Un compilateur d'optimisation pourrait transformer ce code:
inline int factorial(int n)
{
if (n <= 1)
{
return 1;
}
else
{
return n * factorial(n - 1);
}
}
int f(int x)
{
return factorial(x);
}
dans ce code:
int factorial(int n)
{
if (n <= 1)
{
return 1;
}
else
{
return n * factorial(n - 1);
}
}
int f(int x)
{
if (x <= 1)
{
return 1;
}
else
{
int x2 = x - 1;
if (x2 <= 1)
{
return x * 1;
}
else
{
int x3 = x2 - 1;
if (x3 <= 1)
{
return x * x2 * 1;
}
else
{
return x * x2 * x3 * factorial(x3 - 1);
}
}
}
}
Dans ce cas, nous avons essentiellement intégré la fonction 3 fois. Certains compilateurs do effectuent cette optimisation. Je me souviens que MSVC++ avait un paramètre pour régler le niveau d'inline qui serait effectué sur les fonctions récursives (jusqu'à 20, je crois).
En effet, si votre compilateur n'agit pas intelligemment, il peut essayer d'insérer des copies de votre fonction inline
d de manière récursive, créant un code infiniment grand. La plupart des compilateurs modernes le reconnaîtront cependant. Ils peuvent soit:
Pour le cas 2, de nombreux compilateurs ont #pragma
s vous pouvez définir pour spécifier la profondeur maximale à laquelle cela doit être effectué. Dans gcc , vous pouvez également le transmettre depuis la ligne de commande avec --max-inline-insns-recursive
(voir plus d'informations ici ).
AFAIK GCC effectuera l'élimination des appels de queue sur les fonctions récursives, si possible. Cependant, votre fonction n'est pas récursive.
Le compilateur crée un graphe d'appel; lorsqu'un cycle est détecté en appelant lui-même, la fonction n'est plus insérée après une certaine profondeur (n = 1, 10, 100, quel que soit le compilateur réglé).
Certaines fonctions récursives peuvent être transformées en boucles, ce qui les ajuste effectivement à l'infini. Je crois que gcc peut le faire, mais je ne connais pas les autres compilateurs.
Voir les réponses déjà données pour savoir pourquoi cela ne fonctionne généralement pas.
En tant que "note de bas de page", vous pouvez obtenir l'effet que vous recherchez (au moins pour la factorielle que vous utilisez comme exemple) en utilisant métaprogrammation du modèle . Collage de Wikipedia:
template <int N>
struct Factorial
{
enum { value = N * Factorial<N - 1>::value };
};
template <>
struct Factorial<0>
{
enum { value = 1 };
};
Le compilateur fera un graphe d'appel pour détecter ce genre de choses et les empêcher. Il verrait donc que la fonction s'appelle elle-même et non pas en ligne.
Mais il est principalement contrôlé par le mot-clé en ligne et les commutateurs du compilateur (par exemple, vous pouvez l'avoir de petites fonctions en ligne automatique même sans le mot-clé.) les appels que vous avez créés dans le code.
"Comment le compilateur décide-t-il d'inclure ou non une fonction?"
Cela dépend du compilateur, des options spécifiées, du numéro de version du compilateur, de la quantité de mémoire disponible, etc.
Le code source du programme doit toujours obéir aux règles des fonctions intégrées. Que la fonction soit ou non en ligne, vous devez vous préparer à la possibilité qu'elle soit en ligne (un nombre de fois inconnu).
La déclaration de Wikipédia selon laquelle les macros récursives sont généralement illégales semble plutôt mal informée. C et C++ empêchent les invocations récursives mais une unité de traduction ne devient pas illégale en contenant du code macro qui semble avoir été récursif. Dans les assembleurs, les macros récursives sont généralement légales.
Certains compilateurs (c'est-à-dire Borland C++) n'incluent pas de code en ligne qui contient des instructions conditionnelles (if, case, while etc.), de sorte que la fonction récursive de votre exemple ne serait pas insérée.