При использовании функций часто возникает проблема, как вернуть из функции больше одного значения. Дело в том, что из функции с помощью функции return можно вернуть только значение одной переменной. А если нам нужно вернуть из функции больше одного значения? Например, результат деления двух чисел или невозможность выполнить деление, если делитель равен нулю. Другой пример: извлечение корня из отрицательного числа. Если бы в С++ не было предусмотрено решения данной проблемы, он вряд ли стал бы любимым языком профессиональных программистов. Но сначала я хочу рассказать о правилах видимости переменных в функциях, и передаче аргументов по значению и по ссылке. И так начнем правил видимости переменных.
Как мы знаем все переменные делятся на локальные и глобальные. Глобальные переменные видны в любом месте программы. Локальные переменные видны в пределах нескольких инструкций отделенных фигурными скобками. Инструкции, находящиеся между фигурными скобками еще называют блоком.
Если в функции main() объявить переменную int test и точно такую же переменную, с тем же именем внутри функции void local(), которую мы вызываем из функции main(), то обе переменные никак не будут между собой связаны. Это значит, что если мы в функции local() изменим, значение переменной test, то в функции оно останется прежним. Каждый раз, когда мы вызываем функцию local() выделяется место для переменной int test, а после того как функция завершила свою работу переменная разрушается. При повторном вызове функции local() она создается заново. В этом вы можете убедиться сами, выполнив следующую программу:
#include <iostream>
#include <windows.h>
using namespace std;
void local();
int main()
{
int test = 10;
cout << "Значение переменной test в функции main()\n"
<< "до вызова функции local: " << test << "\n\n";
local();
cout << "Значение переменной test в функции main()\n"
<< "после вызова функции local: " << test;
cout << "\n\n";
system("PAUSE");
return 0;
}
void local()
{
int test = 20;
cout << "Значение переменной test в функции"
<< " local: " << test << "\n\n";
}
Результат будет следующим:
Значение переменной test в функции main()
до вызова функции local: 10
Значение переменной test в функции local: 20
Значение переменной test в функции main()
после вызова функции local: 10
Для продолжения нажмите любую клавишу . . .
Как видим изменение переменной test в функции local() никак не отразилось на переменной test объявленной в функции main(). Дело в том, для компилятора это не одна переменная, а две у которых совпадают имя и тип.
При вызове функции можно передать данные (профессиональный программист сказал бы, передать имя переменной функции качестве ее аргумента), поместив имя переменной в скобках. Изменим предыдущую программу:
#include <iostream>
#include <windows.h>
using namespace std;
void local(int test);
int main()
{
int test = 10;
cout << "Значение переменной test в функции main()\n"
<< "до вызова функции local: " << test << "\n\n";
local(test);
cout << "Значение переменной test в функции main()\n"
<< "после вызова функции local: " << test;
cout << "\n\n";
system("PAUSE");
return 0;
}
void local(int test)
{
test = 20;
cout << "Значение переменной test в функции"
<< " local: " << test << "\n\n";
}
Результат выполнения этой программы будет таким же, как и предыдущей. В чем тут дело? Для начала важно запомнить то, что находиться внутри скобок в прототипе функции и в ее реализации называется параметрами, а то, что находиться внутри скобок при ее вызове называется аргументами функции. Когда мы передаем в качестве аргумента имя переменной, ее значение копируется в параметр функции. То, что происходит внутри функции, никак не отражается на значении переменной, используемой при вызове функции. Это называется передачей аргументов по значению (передается только значение, а не сама переменная). Но можно в качестве аргумента передать функции указатель на переменную. Переделаем нашу программу, передав в качестве аргумента указатель на переменную test:
#include <iostream>
#include <windows.h>
using namespace std;
void local(int* test);
int main()
{
int test = 10;
cout << "Значение переменной test в функции main()\n"
<< "до вызова функции local: " << test << "\n\n";
local(&test);
cout << "Значение переменной test в функции main()\n"
<< "после вызова функции local: " << test;
cout << "\n\n";
system("PAUSE");
return 0;
}
void local(int* test)
{
*test = 20;
cout << "Значение переменной test в функции"
<< " local: " << *test << "\n\n";
}
Обратите внимания что, используя в качестве параметра функции указателя, в качестве аргумента мы должны передать функции не имя переменной, а ссылку на переменную. Иначе компилятор выдаст ошибку. Еще этот способ называют передачей функции аргумента по ссылке.
Можно автоматизировать этот процесс с помощью ссылочных параметров. И так вот как будет выглядеть та же самая программа с использованием ссылочных параметров:
#include <iostream>
#include <windows.h>
using namespace std;
void local(int &test);
int main()
{
int test = 10;
cout << "Значение переменной test в функции main()\n"
<< "до вызова функции local: " << test << "\n\n";
local(test);
cout << "Значение переменной test в функции main()\n"
<< "после вызова функции local: " << test;
cout << "\n\n";
system("PAUSE");
return 0;
}
void local(int &test)
{
test = 20;
cout << "Значение переменной test в функции"
<< " local: " << test << "\n\n";
}
Ссылочный параметр объявляется с помощью знака, & который должен предшествовать параметру в объявлении функции. При этом при вызове функции знак & перед именем переменной ставить не надо. Иначе компилятор выдаст ошибку. Теперь если изменять значение переменной в функции, то оригинал, то же измениться.
Еще один важный момент. Как я уже говорил переменная объявленная внутри функции, создается при вызове функции, а после завершения разрушается. А что делать, если мы хотим сохранить значения переменной между вызовами? Для этого можно использовать ключевое слово static.
#include <iostream>
#include <windows.h>
using namespace std;
void local();
int main()
{
cout << "Первый вызов функции local().\n\n";
local();
cout << "Второй вызов функции local().\n\n";
local();
cout << "Третий вызов функции local().\n\n";
local();
cout << "\n\n";
system("PAUSE");
return 0;
}
void local()
{
static int test = 0;
for(int i=0; i<=4; i++)
{
test++;
cout << test << "\n";
}
cout << "\n\n";
}
Теперь мы можем написать программу, которой возвращаем больше двух значений. Сначала программа, в которой используется указатель в качестве параметра функции. Так сказать передача аргументов по ссылке вручную:
#include <iostream>
using namespace std;
double division(double num1, double num2, bool* success);
int main()
{
bool exit = 1;
bool success;
double num1, num2, total;
char choice[1];
while(exit)
{
cout << "Введите первое число: \n\n";
cin >> num1;
cout << "\n";
cout << "Введите второе число: \n\n";
cin >> num2;
cout << "\n";
total = division(num1, num2, &success);
if(success)
{
cout << "Результат: " << total << "\n\n";
cout << "Хотите попробовать еще раз? (Y/N)";
cin >> choice;
cout << "\n";
if(!strcmp(choice, "y") || !strcmp(choice, "Y"))
exit = 1;
else if(!strcmp(choice, "n") || !strcmp(choice, "N"))
return 0;
}
else
{
cout << "На ноль делить нельзя!\n\n"
<< "Хотите попробовать ввести заново? (Y/N)";
cin >> choice;
cout << "\n";
if(!strcmp(choice, "y") || !strcmp(choice, "Y"))
exit = 1;
else if(!strcmp(choice, "n") || !strcmp(choice, "N"))
return 0;
}
}
return 0;
}
double division(double num1, double num2, bool* success)
{
double total;
if(num2 == 0)
*success = 0;
else
{
total = num1/num2;
*success = 1;
}
return total;
}
Обратите внимание на то, что инструкция return 0; в нашей программе написана несколько раз. Дело в том, что функция main() то же возвращает значение типа int. Строчка return 0; означает, что функция успешно выполнена и программа может завершить свою работу. Поэтому, как только программа встречает в функции main() эту инструкцию она завершает свою работу. Теперь напишем ту же самую программу с использованием ссылочных параметров:
#include <iostream>
using namespace std;
double division(double num1, double num2, bool &success);
int main()
{
bool exit = 1;
bool success;
double num1, num2, total;
char choice[1];
while(exit)
{
cout << "Введите первое число: \n\n";
cin >> num1;
cout << "\n";
cout << "Введите второе число: \n\n";
cin >> num2;
cout << "\n";
total = division(num1, num2, success);
if(success)
{
cout << "Результат: " << total << "\n\n";
cout << "Хотите попробовать еще раз? (Y/N)";
cin >> choice;
cout << "\n";
if(!strcmp(choice, "y") || !strcmp(choice, "Y"))
exit = 1;
else if(!strcmp(choice, "n") || !strcmp(choice, "N"))
return 0;
}
else
{
cout << "На ноль делить нельзя!\n\n"
<< "Хотите попробовать ввести заново? (Y/N)";
cin >> choice;
cout << "\n";
if(!strcmp(choice, "y") || !strcmp(choice, "Y"))
exit = 1;
else if(!strcmp(choice, "n") || !strcmp(choice, "N"))
return 0;
}
}
return 0;
}
double division(double num1, double num2, bool &success)
{
double total;
if(num2 == 0)
success = 0;
else
{
total = num1/num2;
success = 1;
}
return total;
}
На следующем уроке мы продолжим изучение функций.