Respuestas:
Primero, la inline
especificación de una función es solo una pista. El compilador puede (y a menudo lo hace) ignorar por completo la presencia o ausencia de un inline
calificador. Dicho esto, un compilador puede en línea una función recursiva, tanto como puede desenrollar un bucle infinito. Simplemente tiene que colocar un límite en el nivel al que "desenrollará" la función.
Un compilador optimizador podría activar este código:
inline int factorial(int n)
{
if (n <= 1)
{
return 1;
}
else
{
return n * factorial(n - 1);
}
}
int f(int x)
{
return factorial(x);
}
en este código:
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);
}
}
}
}
En este caso, básicamente hemos incorporado la función 3 veces. Algunos compiladores hacen realizar esta optimización. Recuerdo que MSVC ++ tenía una configuración para ajustar el nivel de línea que se realizaría en funciones recursivas (hasta 20, creo).
De hecho, si su compilador no actúa de manera inteligente, puede intentar insertar copias de su inline
función d de forma recursiva, creando un código infinitamente grande. Sin embargo, la mayoría de los compiladores modernos lo reconocerán. Pueden:
Para el caso 2, muchos compiladores tienen #pragma
s que puede configurar para especificar la profundidad máxima a la que se debe hacer esto. En gcc , también puede pasar esto desde la línea de comandos con --max-inline-insns-recursive
(vea más información aquí ).
El compilador crea un gráfico de llamadas; cuando se detecta un ciclo que se llama a sí mismo, la función ya no está en línea después de una cierta profundidad (n = 1, 10, 100, cualquiera que sea el compilador sintonizado).
Algunas funciones recursivas se pueden transformar en bucles, que efectivamente los alinea infinitamente. Creo que gcc puede hacer esto, pero no sé sobre otros compiladores.
Vea las respuestas ya dadas por qué esto no suele funcionar.
Como una "nota al pie", puede lograr el efecto que está buscando (al menos para el factorial que está usando como ejemplo) usando la metaprogramación de plantilla . Pegando de Wikipedia:
template <int N>
struct Factorial
{
enum { value = N * Factorial<N - 1>::value };
};
template <>
struct Factorial<0>
{
enum { value = 1 };
};
El compilador hará un gráfico de llamadas para detectar este tipo de cosas y evitarlas. Entonces vería que la función se llama a sí misma y no en línea.
Pero principalmente está controlado por la palabra clave en línea y los modificadores del compilador (por ejemplo, puede tener funciones pequeñas en línea automáticamente incluso sin la palabra clave). Es importante tener en cuenta que las compilaciones de depuración nunca deben estar en línea ya que la pila de llamadas no se conservará en espejo Las llamadas que creó en código.
"¿Cómo decide el compilador si en línea una función o no?"
Eso depende del compilador, las opciones que se especificaron, el número de versión del compilador, tal vez la cantidad de memoria disponible, etc.
El código fuente del programa todavía tiene que obedecer las reglas para las funciones en línea. Independientemente de si la función se integra o no, debe prepararse para la posibilidad de que se incorpore (algunas veces se desconoce).
La declaración de Wikipedia de que las macros recursivas son típicamente ilegales parece poco informada. C y C ++ evitan las invocaciones recursivas, pero una unidad de traducción no se vuelve ilegal al contener código macro que parece que hubiera sido recursivo. En los ensambladores, las macros recursivas suelen ser legales.
Algunos compiladores (es decir, Borland C ++) no incluyen código en línea que contenga sentencias condicionales (if, case, while, etc.), por lo que la función recursiva en su ejemplo no estaría en línea.