#cpp #функции
В каждой единице трансляции, объявление функции обязано предшествовать её вызову.
Если это условие выполняется, то порядок размещения определения уже не имеет особого
значения.
Какой вариант предпочтительней и почему?
(сначала функция объявлена, а затем определена или наоборот)
void someFunction(); // declaration
void someFunction() { cout << "hello" << endl; }
int main()
{
someFunction(); // function call
return 0;
}
или
void someFunction() { cout << "hello" << endl; }
void someFunction(); // declaration
int main()
{
someFunction(); // function call
return 0;
}
Ответы
Ответ 1
Какой вариант предпочтительней и почему? (сначала функция объявлена, а затем определена
или наоборот)
Хотелось бы узнать причину именно такого порядка размещения. Что это? удобство
восприятия программы, отдельное положение стандарта языка, код в этом случае быстрее
компилируется или есть ещё какая-то причина?
Причина та же самая, по которой вообще в язык добавлена возможность объявлять функции
без их определения.
Представьте что возможности объявить (без определения!) функции нет. И вам надо написать
две функции, вызывающие друг друга:
void SomeFirstFunction(int n)
{
cout << n << endl;
SomeSecondFunction(n);
}
void SomeSecondFunction(int n)
{
if (n > 0)
{
SomeFirstFunction(n - 1);
}
}
int main()
{
SomeFirstFunction(5);
}
Компилятор разбирает файл сверху вниз. Он доходит до строки
SomeSecondFunction(n);
В этот момент он понятия не имеет, что это за SomeSecondFunction и какие параметры
она принимает - она ведь еще не определена. И он падает с ошибкой:
In function 'void SomeFirstFunction(int)':
error: 'SomeSecondFunction' was not declared in this scope
Перестановка местами SomeSecondFunction и SomeFirstFunction, очевидно, не поможет.
Поэтому в язык добавлен хак - вы можете сказать компилятору "SomeSecondFunction -
это функция вот с такими параметрами, и она будет определена где-то ниже. Или даже
не ниже, а вообще в другом файле - вобщем, смело считай это функцией, генерируй код
для ее вызова, потом линковщик разберется где эта функция на самом деле лежит".
// предупредили компилятор
void SomeSecondFunction(int n);
void SomeFirstFunction(int n)
{
cout << n << endl;
// компилятор спокойно сгенерировал вызов еще не определенной (но объявленной!)
функции
SomeSecondFunction(n);
}
void SomeSecondFunction(int n)
{
if (n > 0)
{
SomeFirstFunction(n - 1);
}
}
При этом нет никакого смысла в объявлении SomeFirstFunction после ее определения
- компилятор и так знает что это за функция, ему от еще одного объявления уже известного
факта лучше не станет.
А вот в примерах из вашего вопроса объявление полностью избыточно. Т.е. ни один из
этих вариантов не предпочтительней - они оба бессмысленны, т.к. эквивалентны варианту
вообще без declaration:
void someFunction() { cout << "hello" << endl; }
int main()
{
someFunction(); // function call
return 0;
}
Ответ 2
Делать "наборот" нет никакого смысла. Потому что определение функции уже включает
в себя объявление.
Причина, по которой нужно объявлять до ее использования очень проста. Когда компилятор
встречает вызов функции, ему нужно коректно передать параметры, а также правильно сформировать
их "проталкивание" в стек/регистры. А также и решить, кто будет "подчищать" стек после
вызова.
Старый си допускал использование функций до их определений/объявлений, но при этом
он считал, что все аргументы функции типа int и возвращаемое значение такое же.
UPD
кратко. Это запрещено стандартом явно
3.2 One definition rule [basic.def.odr]
1 No translation unit shall contain more than one definition of any variable, function,
class type, enumeration
type, or template.
расширено, но сумбурно
В теории, все функции можно разместить так, что бы вызываемые функции были выше (раньше)
вызываемых. Это конечно требует дополнительных телодвижений (читать: компиляция будет
дольше или нужно ручками все будет расставлять), но возможно. Либо добавлять определение
к каждой единице, но это также сильно удлинит компиляцию. Плюс возникает риск того,
что в разных единицах компиляции будут разные версии одной и той же функции (из за
дефайнов, а это уже весело) либо просто будет дублирование кода, который линковщику
ещё и убрать нужно. Поэтому это и запрещено стандартом (
Просто выделение объявлений позволяет делать компиляцию в паралель (или даже на разных
машинах). Минимизируется наличие дубликатов функций (inline функции могут дублироваться).