2010-08-22 3 views
4

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

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

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

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

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

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

Я использую турбо 3.2


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

благодаря

+1

'inline' делает две вещи. Он позволяет определять функцию более одного раза (при условии, что определения одинаковы), и намекает, что компилятор должен встроить эту функцию. Второе использование полностью устарело, поскольку компилятор проигнорирует его и может встроить строки, не отмеченные в строке, а не встроенные строки, помеченные как встроенные (как и должно быть, поскольку он знает намного лучше, что должно быть вложенным). Но помимо тех, это все еще только функция, ничего больше не изменилось. Назовите функцию 'factorial' вместо' f', скомпилируйте с оптимизацией и не вводите ничего слишком большого с рекурсивной функцией. – GManNickG

+7

Кроме того, этот компилятор ужасно старый, вы хотите перейти к более новому. GCC является бесплатным (и MinGW является бесплатным), а также версия Express Visual Studio. – GManNickG

+0

это не мой ответ. Если у вас есть четкое представление о моем вопросе, то, пожалуйста, дайте ответ. – userBI

ответ

3

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

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

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

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

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

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

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

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

EDIT: Существует также, что называется Tail Recursion Optimization, который позволяет составителям преобразовать определенные типы рекурсивных алгоритмов итеративной, что делает их более кандидатов на встраивание. (В дополнение к тому, что они делают больше пространства стека эффективным).

+0

Сначала спасибо, но ваш ответ неоднозначен для меня. Я не совсем понимаю, что вы написали. – userBI

+0

@ R.K.Rahu: ОК, чтобы очистить его. ** a) ** да, это можно сделать, если параметр является константой, но не все компиляторы сделают это. Некоторые компиляторы дают вам возможность помочь ему принять это решение. ** b) ** есть вещь, называемая хвостовой рекурсией, которая может быть превращена в петлю, делающую вложение более вероятной. ** c) ** ключевое слово inline - это предложение, а не команда, поэтому компилятор может игнорировать его. –

+0

большое спасибо вы позаботитесь о моих комментариях и дадите ясное предложение ваш первый ответ также хорошо, но некоторые .. , но теперь я ясно, и я должен работать над ним. – userBI

0

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

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

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

4

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

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

С GCC 4,4

int fac = f(10); 

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

movl $3628800, 4(%esp) 

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

+0

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

0

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

BIGINT FAC = factorialOf (UserInput)

нет никакого способа, компилятор может определить, что один из ........

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

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

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

1

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

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

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

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

+0

жаль, что я ошибся в некоторых местах. Я пишу эти строки (т. Е. Цикл ...), потому что в turbo 3.2 он создает проблему. и одна вещь, которую вы сказали, что я должен объявить явно возвращаемый тип, я думаю, что вы пропустили здесь, я думаю, если мы не будем упоминать какой-либо тип, это значит, что это по умолчанию int. , и я также проверил его снова, и это сработало. – userBI

+0

@ R.K.Rahu: Нет, я не «пропустил» здесь. Ваше сообщение помечено тегом [C++]. В языке C++ возвращаемый тип функции * должен быть явно объявлен *. Без исключений. В C++ нет такой вещи, как «по умолчанию int». – AnT

+0

Извините, Андрей, я проверил снова из-за вас. Но я получил то же самое. Если мы не объявим тип возврата, то он автоматически определит тип «int». Я проверил его на версии 5.02 turbo, поэтому вы пропустили здесь. – userBI

0

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

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

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

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

Смежные вопросы