Два вопроса о встроенных функциях в C ++ - PullRequest
4 голосов
/ 22 августа 2010

У меня есть вопрос, когда я компилирую встроенную функцию в C ++.

Может ли рекурсивная функция работать со встроенной.Если да, то опишите, пожалуйста, как.

Я уверен, что цикл не может с ним работать, но я читал где-нибудь рекурсивный вариант, если мы передадим постоянные значения.

Мой друг отправил мне некоторую встроенную рекурсивную функцию в качестве постоянного параметра исказал мне, что это будет работать, но это не работает на моем ноутбуке, нет ошибок во время компиляции, но во время выполнения ничего не отображается, и я должен прекратить его принудительным прерыванием.

inline f(int n) {
    if(n<=1)
        return 1;
    else {
        n=n*f(n-1);
        return n;
    }
}

как это работает?

Я использую Turbo 3.2


Кроме того, если встроенный код функции слишком велик, может ли компилятор автоматически изменить его в обычной функции?

спасибо

Ответы [ 6 ]

4 голосов
/ 22 августа 2010

Эта конкретная функция определенно может быть встроена. Это потому, что компилятор может выяснить, что эту конкретную форму рекурсии (tail-recursion) можно тривиально превратить в нормальный цикл. И с нормальным циклом у него нет проблем с его наложением.

Компилятор может не только встроить его, он даже может вычислить результат для константы времени компиляции без генерации кода для функции.

с GCC 4.4

int fac = f(10); 

произвел эту инструкцию:

movl    $3628800, 4(%esp)

Вы можете легко проверить при проверке вывода сборки, что функция действительно встроена для ввода, который не известен во время компиляции.

3 голосов
/ 22 августа 2010

Полагаю, ваш друг пытался сказать, что, если ему дать константу, компилятор может полностью вычислить результат во время компиляции и просто вставить ответ на сайте вызова. C ++ 0x на самом деле имеет механизм для этого, называемый constexpr, но существуют ограничения на то, насколько сложным может быть код. Но даже с текущей версией c ++ это возможно. Это полностью зависит от компилятора.

Эта функция может быть хорошим кандидатом, поскольку она явно ссылается только на параметр для вычисления результата. Некоторые компиляторы даже имеют непереносимые атрибуты , чтобы помочь компилятору решить это. Например, gcc имеет атрибуты pure и const (перечисленные на той странице, на которую я только что ссылался), которая сообщает компилятору, что этот код работает только с параметрами и не имеет побочных эффектов, что повышает вероятность его вычисления во время компиляции. .

Даже без этого он все равно будет компилироваться! Причина в том, что компилятору разрешено не включать функцию, если он решит. Думайте о встроенном ключевом слове скорее как о предложении, чем о инструкции.

Предполагая, что компилятор не вычисляет все это во время компиляции, встраивание невозможно полностью без других примененных оптимизаций ( см. РЕДАКТИРОВАТЬ ниже ), поскольку он должен иметь фактическую функцию для вызова. Тем не менее, он может стать частично встроенным. В этом случае компилятор встроит начальный вызов, но также выдаст обычную версию функции, которая будет вызываться во время рекурсии.

Что касается вашего второго вопроса, то да, размер - это один из факторов, который компиляторы используют для принятия решения о целесообразности включения чего-либо.

Если запуск этого кода на вашем ноутбуке занимает очень много времени, возможно, вы просто задали ему очень большие значения, и просто требуется много времени для вычисления ответа ... Код выглядит хорошо, но продолжайте Имейте в виду, что значения выше 13! будут переполнены 32-битным int. Какое значение вы пытались передать?

Единственный способ узнать, что на самом деле происходит, - это скомпилировать его и посмотреть на сгенерированную сборку.

PS: вы можете заглянуть в более современный компилятор, если вас интересует оптимизация. Для Windows есть MingW и бесплатные версии Visual C ++. Для * NIX, конечно, есть g ++.

РЕДАКТИРОВАТЬ : Существует также вещь, называемая Оптимизация рекурсии хвоста , которая позволяет компиляторам преобразовывать определенные типы рекурсивных алгоритмов в итеративные, что делает их лучшими кандидатами для встраивания. (В дополнение к повышению эффективности стека).

1 голос
/ 22 августа 2010

Рекурсивная функция может быть встроена на определенную ограниченную глубину рекурсии.Некоторые компиляторы имеют опцию, которая позволяет вам указать, насколько глубоко вы хотите углубиться во встраивание рекурсивных функций.По сути, компилятор «выравнивает» несколько вложенных уровней рекурсии.Если выполнение достигает конца «сплющенного» кода, код вызывает себя обычным рекурсивным способом и так далее.Конечно, если глубина рекурсии является значением времени выполнения, компилятор должен проверять соответствующее условие каждый раз перед выполнением каждого исходного рекурсивного шага внутри «сплющенного» кода.Другими словами, нет ничего необычного в добавлении рекурсивной функции.Это как развернуть петлю.Не требуется, чтобы параметры были постоянными.

Что вы подразумеваете под «Я уверен, что цикл не может работать», неясно.Кажется, это не имеет особого смысла.Функции с циклом могут быть легко встроены, и в этом нет ничего странного.

Что вы пытаетесь сказать в своем примере, который «ничего не отображает», также неясен.В коде нет ничего, что могло бы «отображать» что-либо.Неудивительно, что это «ничего не показывает».Кроме того, вы разместили неверный код.Язык C ++ не допускает объявления функций без явного возвращаемого типа.

Что касается вашего последнего вопроса, да, компилятор полностью свободен для реализации встроенной функции как «нормальной» функции.Это не имеет ничего общего с функцией слишком большой.Все это связано с более или менее сложными эвристическими критериями, используемыми этим конкретным компилятором для принятия решения о встраивании функции.Это может принять во внимание размер.Это может принимать во внимание другие вещи.

0 голосов
/ 23 августа 2010

Помните, что ключевое слово inline просто отправляет запрос, а не команду компилятору.Компилятор может игнорировать этот запрос, если определение функции слишком длинное или слишком сложное, и компилировать функцию как обычную функцию.

в некоторых случаях, когда встроенные функции могут не работать, являются

  1. Для функций, возвращающих значения, если существует цикл, переключатель или переход.
  2. Для функций, не возвращающих значения, если существует оператор возврата.
  3. Если функция содержит статические переменные.
  4. Если встроенные функции являются рекурсивными.

, следовательно, в C ++ встроенные рекурсивные функции могут не работать.

0 голосов
/ 22 августа 2010

Следует помнить одну вещь: согласно стандарту, inline - это предложение, а не абсолютная гарантия.В случае рекурсивной функции компилятор не всегда сможет вычислить предел рекурсии - современные компиляторы становятся чрезвычайно умными, предыдущий ответ показывает, что компилятор оценивает встроенную константу и просто генерирует результат, но рассмотрим

bigint fac = factorialOf (userInput)

компилятор никак не может с этим разобраться ........

В качестве примечания, большинство компиляторов склонны игнорировать встроенные строкив отладочных сборках, если специально не указано иное - облегчает отладку

Хвостовые рекурсии могут быть преобразованы в циклы, если компилятор может удовлетворительно переставить внутреннее представление, чтобы в конце получить условный тест рекурсии.В этом случае он может выполнить генерацию кода для повторного выражения рекурсивной функции в виде простого цикла

Что касается таких вопросов, как перезапись хвостовой рекурсии, частичное расширение рекурсивных функций и т. Д., То они обычно управляются оптимизациейпереключатели - все современные компиляторы способны к значительной оптимизации, но иногда что-то идет не так.

0 голосов
/ 22 августа 2010

Вы можете встроить рекурсивные функции.Компилятор обычно разворачивает их на определенную глубину - в VS у вас может быть даже прагма для этого, и компилятор также может делать частичные вставки.По сути это превращает его в петли.Кроме того, как сказал @Evan Teran, компилятор не обязан включать функцию, которую вы предлагаете вообще.Это может полностью игнорировать вас, и это совершенно правильно.

Проблема с кодом не в этой встроенной функции.Я уверен, что постоянство или нет аргумента не имеет значения.

Кроме того, серьезно, получите новый компилятор.Существуют современные бесплатные компиляторы для любой операционной системы вашего ноутбука.

...