Вызов f(y)
T x=y;
произошла и если обе переменные с именем x
могут принимать одно и то же значение. Рассмотрим пример.void f(double);
void g(int y)
{
f(y);
double x(y); // инициализируем переменную x значением
// переменной y (см. раздел 8.2.2)
}
Обратите внимание на то, что для инициализации переменной x
y
необходимо преобразовать переменную типа int
в переменную типа double
. То же самое происходит при вызове функции f
. Значение типа double
, полученное функцией f
, совпадает со значением, хранящимся в переменной x
. double
int
, редко можно оправдать.void ff(int);
void gg(double x)
{
ff(x); // как понять, имеет ли это смысл?
}
Если вы действительно хотите усечь значение типа double
int
, то сделайте это явно.void ggg(double x)
{
int x1 = x; // усечение x
int x2 = int(x);
ff(x1);
ff(x2);
ff(x); // усечение x
ff(int(x));
}
Таким образом, следующий программист, просматривая этот код, сможет увидеть, что вы действительно думали об этой проблеме.
8.5.8. Реализация вызова функции
Как же на самом деле компилятор выполняет вызов функции? Функции expression
term
и primary
, описанные в главах 6 и 7, прекрасно подходят для иллюстрации этой концепции за исключением одной детали: они не принимают никаких аргументов, поэтому на их примере невозможно объяснить механизм передачи параметров. Однако погодите! Они ts
класса Token_stream
для получения входной информации; объект ts
является глобальной переменной. Это несколько снижает прозрачность работы программы. Мы можем улучшить эти функции, позволив им принять аргумент типа Token_stream&
. Благодаря этому нам не придется переделывать ни один вызов функции.Во-первых, функция expression совершенно очевидна; она имеет один аргумент (ts
left
и t
).double expression(Token_stream& ts)
{
double left = term(ts);
Token t = ts.get;
// ...
}
Во-вторых, функция term
expression
, за исключением того, что имеет дополнительную локальную переменную (d
), которая используется для хранения результата деления (раздел case '/'
).double term(Token_stream& ts)
{
double left = primary(ts);
Token t = ts.get;
// ...
case '/':
{
double d = primary(ts);
// ...
}
// ...
}
В-третьих, функция primary
term
, за исключением того, что у нее нет локальной переменной left
.double primary(Token_stream& ts)
{
Token t = ts.get;
switch (t.kind) {
case '(':
{ double d = expression(ts);
// ...
}
// ...
}
}
Теперь у этих функций нет скрытых глобальных переменных, и они превосходно подходят для иллюстрации: у них есть аргумент и локальные переменные, и они вызывают друг друга. Возможно, вы захотите освежить память и еще раз посмотреть, как выглядят эти функции в законченном виде, но все их основные свойства, относящиеся к механизму вызова функций, уже перечислены.
expression
Детали зависят от реализации, но в принципе к ним относится информация о том, что функция должна вернуть управление и некое значение в точку вызова. Такую структуру данных называют
Теперь функция expression
term
, поэтому компилятор создает активационную запись для вызова функции term
.