Our Blog

Разница между прототипом и определением функции

В начале изучения программирования на C++ важно понять разницу между прототипом функции и ее определением. Этот мощный механизм положен в основу многих приемов программирования.

Прототип функции – это объявление функции, в котором указывается ее имя, типы аргументов и возвращаемый тип данных.
Пример:

int func1 (int n, char a);

Этот прототип объявляет функцию с именем «func1», принимающую два аргумента: «n» целого типа и «a» символьного типа — и возвращающую целое число.
Определение функции содержит, помимо объявления, тело функции, представляющее собой последовательность операторов и описаний в фигурных скобках.
Пример:

int func1 (int n) {
            return n*n;
}

В данном определении функция «func1» принимает один аргумент целого типа «n» и возвращает число, равное квадрату аргумента.

Разница между прототипом и определением функции заключается в том, что прототип не имеет тела функции и его заголовок оканчивается точкой с запятой. Такое различие объясняется задачами, которые выполняет прототип. Он позволяет компилятору построить обращение к функции, в то время как сам код может быть построен позже. Также использование прототипов является гарантией того, что функция не будет вызвана с неверными параметрами.
Все типы в определении функции и её объявлениях обязаны совпадать. Однако имена аргументов совпадать не обязаны, так как они не являются частью типа.
Бывают случаи, когда в определении функции часть аргументов не используется:

void search(table* t, const char* key, const char*)
   {
       //третий аргумент не используется
   }

Как показано в данном примере, неиспользуемому аргументу можно не давать имени вообще. Обычно, неименованные аргументы в определениях функции появляются либо вследствии произведенных упрощений, либо в качестве резерва на будущее. В обоих случаях само наличие реально неиспользуемого аргумента в определении функции позволяет не трогать при этом кленский код, вызывающий функцию
Описывать функции (речь про прототипы) можно тоже по-разному. Помимо базового варианта, если ещё парочка.
Чтобы избежать расходов на вызов функции, функцию можно описать как inline (#1), а чтобы обеспечить более быстрый доступ к параметрам, их можно описать как register (#2). Оба средства могут использоваться неправильно, и их следует избегать везде, где есть какие-либо сомнения в их полезности.
#1 Если часто повторяется обращение к очень маленькой функции, то вы можете начать беспокоиться о стоимости вызова функции. Обращение к функции члену не дороже обращения к функции не члену с тем же числом параметров (надо помнить, что функция член всегда имеет хотя бы один параметр), и вызовы в функций в C++ примерно столь же эффективны, сколь и в любом языке. Однако для слишком маленьких функций может встать вопрос о накладных расходах на обращение. В этом случае можно рассмотреть возможность спецификации функции как inline-подставляемой. Если вы поступите таким образом, то компилятор сгенерирует для функции соответствующий код в месте ее вызова. Семантика вызова не изменяется.

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

Comments ( 0 )
    -->