Відповіді:
По-перше, inline
специфікація функції - лише натяк. Компілятор може (і часто це робить) повністю ігнорувати наявність або відсутність inline
кваліфікатора. З урахуванням сказаного компілятор може вбудовувати рекурсивну функцію стільки, скільки може розкрутити нескінченний цикл. Він просто повинен встановити обмеження на рівень, до якого він буде "розкручувати" функцію.
Оптимізуючий компілятор може перетворити цей код:
inline int factorial(int n)
{
if (n <= 1)
{
return 1;
}
else
{
return n * factorial(n - 1);
}
}
int f(int x)
{
return factorial(x);
}
в цей код:
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);
}
}
}
}
У цьому випадку ми в основному впорядкували функцію 3 рази. Деякі компілятори роблять виконати цю оптимізацію. Я пригадую, що MSVC ++ має налаштування для настройки рівня вбудовування, який би виконувався на рекурсивних функціях (я вважаю, до 20).
Дійсно, якщо ваш компілятор не діє розумно, він може спробувати вставити копії inline
функції d рекурсивно, створюючи нескінченно великий код. Однак, більшість сучасних компіляторів визнають це. Вони можуть:
У випадку 2, у багатьох компіляторів #pragma
ви можете встановити, щоб вказати максимальну глибину, на якій це слід зробити. У gcc ви також можете передати це з командного рядка --max-inline-insns-recursive
(див. Докладнішу інформацію тут ).
Деякі рекурсивні функції можуть бути перетворені в петлі, що ефективно нескінченно їх окреслює. Я вважаю, що gcc може це зробити, але я не знаю про інші компілятори.
Перегляньте вже наведені відповіді, чому це зазвичай не працює.
Як "виноска", ви можете досягти ефекту, який ви шукаєте (принаймні для фактора, яку ви використовуєте як приклад), використовуючи метапрограмування шаблонів . Вставка з Вікіпедії:
template <int N>
struct Factorial
{
enum { value = N * Factorial<N - 1>::value };
};
template <>
struct Factorial<0>
{
enum { value = 1 };
};
Компілятор зробить графік викликів, щоб виявити такі речі та запобігти їх. Так було б видно, що функція викликає себе, а не вбудована.
Але в основному це контролюється вбудованими ключовими словами та компіляторами (наприклад, ви можете мати автоматичні вбудовані невеликі функції навіть без ключового слова.) Важливо відзначити, що компіляції налагодження ніколи не повинні бути вкладеними, оскільки стовп виклику не буде збережений для дзеркала дзвінки, створені вами в коді.
"Як компілятор вирішує, вбудувати функцію чи ні?"
Це залежить від компілятора, параметрів, які були вказані, номера версії компілятора, можливо, скільки пам’яті доступно тощо.
Вихідний код програми все ще повинен підкорятися правилам вбудованих функцій. Незалежно від того, чи буде функція вбудована чи ні, ви повинні підготуватися до того, що вона буде вбудована (деяка невідома кількість разів).
Заява Вікіпедії про те, що рекурсивні макроси, як правило, є незаконними, виглядає досить погано. C і C ++ запобігають рекурсивним викликам, але блок перекладу не стає незаконним, містять макрокод, який виглядає так, як це було б рекурсивно. У асемблерах рекурсивні макроси, як правило, є законними.
Деякі компілятори (тобто Borland C ++) не містять вбудований код, який містить умовні висловлювання (якщо, випадок, тоді як і т. Д.), Тому рекурсивна функція у вашому прикладі не була б окреслена.