При выходе из функции, соответствующий участок стека освобождается, поэтому значения локальных переменных между вызовами одной и той же функции не сохраняются. В том случае, когда этого требуется избежать, при объявлении локальных переменных используется модификатор static.
Пример 2. Использование модификатора static. #include<iostream.h> voidf(inta){ intm = 0; cout<< "n m p\n"; while(a--){ static intn = 0; intp = 0; cout << n++ <<' ' << m++ << ' ' << p++ <<'\n'; } } intmain(){ f(3); f(2); return0; } |
В результате выполнения этой программы на экран будет выведено следующее:
n | m | p |
0 | 0 | 0 |
1 | 1 | 0 |
2 | 2 | 0 |
n | m | p |
3 | 0 | 0 |
4 | 1 | 0 |
Такой результат выполнения программы объясняется тем, что статическая переменная n помещается в сегмент данных, а её инициализация выполняется всего один раз при первом появлении оператора, содержащего её определение. В тот же момент автоматические переменные m и p инициализируются при каждом новом входе в функцию.
Модификатор inline
Функцию можно определить как встроенную с помощью модификатора inline. В таком случае компилятор вместо обращения к функции будет помещать её код непосредственно в каждую точку вызова. Сама по себе директива inline носит, по большей степени, рекомендательный характер и выполняется компилятором по мере возможности. Важно иметь ввиду то, что использование inline-функций может повлечь за собой увеличение размера исполняемого файла программы.
Определение inline-функции всегда должно предшествовать её вызовам, иначе вместо inline-расширения компилятор выполнит обычный вызов функции.
Функции. Возвращаемое значение.
Механизм возврата из функции в вызвавшую её функции реализуется с помощью оператора return[выражение]. Функция может содержать в себе несколько операторов return и если она описана как void, выражение после этого оператора не указывается.
Также оператор return можно опускать для функции типа void, если возврат из неё происходит перед закрывающей фигурной скобкой, и для функции.
Выражение после return всегда неявно преобразуется к типу возвращаемого функцией значения и передаётся в точку вызова функции.
Пример 3. Использование return. int fl(){ return 1; // правильно } void f2(){ return 1; /* неправильно, f2 не должна возвращать значение */ } double f3(){ return 1; /* правильно, 1 преобразуется к типу double */ } |
Нельзя возвращать из функции указатели на локальную переменную, так как память, выделенная под локальные переменные при входе в эту функцию, освобождается после выхода из неё.
Параметры функций.
Механизм параметров является основным способом обмена информацией между вызываемой и вызывающей функциями. Параметры, описанные в заголовке описания функции, называются формальными параметрами, а параметры, описанные в операторе вызова функции – фактическими параметрами (или аргументами).
При вызове функции последовательно выполняются следующие действия:
- вычисляются выражения, стоящие на месте аргументов;
- в стеке выделяется память под формальные параметры функции в соответствии с их типом;
- каждому из параметров присваивается значение соответствующего аргумента;
- проверяется соответствие типов и при необходимости выполняются их преобразования;
- при несоответствии типов выдается диагностическое сообщение
Существует два способа передачи параметров в функцию: передача по значению и по адресу.
При использовании первого способа передачи в стек заносятся копии значений аргументов. Соответственно, операторы функции работаю именно с этими копиями, а доступ к исходным значениям параметров функция не имеет, следовательно, она не может их изменить.
При использовании второго способа передачи в стек заносятся копии адресов аргументов. Функция осуществляет доступ к ячейкам памяти по этим адресам и может изменить исходные значения аргументов.
Пример 4. Использование передачи по ссылке и по значению. #include <iostream.h> void f(int i, int* j, int &k); int main(){ int i = 1, j = 2, k = 3; cout << "i j k\n"; cout << i <<' '<< j <<' '<< k <<'\n'; f(i, &j, k); cout << i <<' '<< j <<' '<< k; return 0; } void f(int i, int* j, int &k){ i++; (*j)++; k++; } |
Первый параметр (i) передается по значению, следовательно, его изменение в функции не влияет на исходное значение. Второй параметр (j) передается по адресу с помощью указателя, при этом для передачи в функцию адреса фактического параметра используется операция взятия адреса, а для получения его значения в функции требуется операция разыменования. Третий параметр (k) передается по адресу с помощью ссылки.
При передаче по ссылке в функцию передаётся адрес указанного при вызове параметра, а внутри функции все обращения к параметру неявно разыменовываются. Поэтому использование ссылок в качестве параметров, вместо указателей, повышает читабельность программного кода, избавляя от необходимости применения операций получения адреса и разыменовывания.
Передача массивов в качестве параметров
При использовании в качестве параметра массива в функцию передаётся указатель на его первый элемент, т.е. начало непрерывного блока памяти выделенного под массив. Массив всегда передаётся по адресу. В свою очередь размерность массива следует передавать с помощью отдельного параметра.
Пример 5. Передача массивов в качестве параметра. #include<iostream.h> intsum(const int* mas, const intn); int constn = 10; intmain(){ intmas[n] = {3, 4, 5, 4, 4}; cout <<"Сумма элементов массива: " <<sum(mas, n); return0; } intsum(const int* mas, const int n){ /* варианты: int sum(int mas[], int n) или int sum(int mas[n], int n) */ // (величина n должна быть константой) ints = 0; for(inti = 0; i<n; i++) s+=mas[i]; returns; } |
При передаче многомерных массивов все размерности, если они не известны на этапе компиляции, должны передаваться в качестве параметров.
Передача имён функций в качестве параметров
Указатели на функции передаются в подпрограмму таким же образом, как и параметры других типов. Тип указателя и тип функции, которая вызывается посредством этого указателя, должны совпадать в точности.
Пример 6. Передача имён функций в качестве параметра. #include <iostream.h> typedef void (*PF)(int); void f1(PF pf){ /* функция f1 получает в качестве параметра указатель типа PF */ pf(5); /*вызов функции, переданной через указатель */ } void f(int i){ cout << i; } int main(){ f1(f); return 0; } |