Локальные переменные в функциях 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Локальные переменные в функциях



 

Методы структурного программирования способствуют утверждению идеи, что функция должна быть как независимой, так и, по возможности, многократно используемой. Следовательно, функции могут иметь собственные типы данных, константы и переменные с целью придания им независимости.

Переменные, которые определяются внутри функции, называются локальными переменными. Они существуют только тогда, когда вызывается функция, в которой они описаны. Как только функция завершит выполнение, специальный код удаляет локальные переменные. Следовательно, после выполнения функции локальные переменные теряют свои значения. Код функции может создавать локальные переменные только тогда, когда исполняется функция, в которой они определены.

Несколько рекомендаций при работе с локальными перемеными:

1) используйте локальные переменные для сохранения и изменения значений параметров, которые объявляются с модификатором const;

2) не объявляйте локальную переменную с тем же именем, что и у глобальной переменной, к которой необходимо иметь доступ в функции.

 

Статические переменные в функциях

 

Язык С++ позволяет объявлять переменную как статическую, поместив ключевое слово static слева от ее типа данных. Статические переменные обычно инициализируются. Это происходит при первом вызове функции, в которой они определены. Существует ряд методов программирования, которые требуют сохранения локальных переменных между вызовами функций. Эти специальные локальные переменные называются статическими переменными.

Когда функция, в которой определены статаческие переменные, завершает выполнение, эти переменные сохраняют свои значения. Компилятор поддерживает эту особенность языка, сохраняя статические переменные в специальном сегменте памяти, который не изменяется в процессе выполнения программы.

С++ программа, которая показывает применение статистических локальных переменных в функциях

 

//Prim6_.cpp

include<iostream.h>

double mean (double x)

{

static double sum=0;

static double sumx=0;

sum = sum+1;

sumx = sumx+x;

return sumx / sum;

}

main()

{

cout<<"среднее = "<<mean(2)<<endl;

cout<<"среднее = "<<mean(1)<<endl;

cout<<"среднее = "<<mean(4)<<endl;

cout<<"среднее = "<<mean(10)<<endl;

cout<<"среднее = "<<mean(11)<<endl;

return 0;

}

 

 

Выход из функций

 

Преждевременный выход из тела функции обычно происходит из-за того, что некоторые условия не позволяют выполнить операторы этой функции. В языке С++ предусмотрен оператор return для выхода из функции. Если функция имеет тип void, то можно использовать оператор return, не включая после никаких выражений. Если нужно осуществить выход из непустой функции, то оператор return должен породить значение, которое укажет результат выхода из функции.

 

Передача параметров функции

 

Обрабатывая вызов функции, компилятор вставляет в код программы последовательность машинных команд, выполняющих следующие действия:

1) запись в стек копий переменных или констант, перечисленных в списке аргументов (если поле «список_аргументов» в прототипе функции не void);

2) вызов процедуры с возвратом (либо «близкий», либо «далекий» в зависимости от заданного явно или неявно типа функции).

Функция в процессе исполнения извлекает копии аргументов из стека. Таким образом, формальные и фактические аргументы изолируются друг от друга.

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

В общем случае существуют два стиля передачи параметров функции:

1) вызов функции с передачей значений (Call-By-Value);

2) вызов функции с передачей адресов переменных (Call-By-Reference).

Вызов с передачей значений – это простая передача копий переменных в функцию, не оставляющая никаких возможностей для воздействия функции на переменные в точке вызова. Например:

/* Prim6_2.cpp */

#include <stdio.h>

int sum(int, int);

void main(void)

{int a = 5, b = 4, c;

c = sum(a, b);

printf("%d\n", c);

}

int sum(int k, int l)

{int m;

m = k + l;

return m;

}

Функции sum() передаются копии переменных a и b. Эти копии используются функцией в качестве значений для аргументов k и l.

Вызов с передачей адресов предполагает, что в качестве параметров функции передаются не копии переменных, а копии адресов переменных. Используя указатель, функция осуществляет доступ к нужным ячейкам памяти. Так как известен адрес в памяти, можно изменить его значение. Поэтому при вызове с передачей адресов функция может изменить значение переменных в точке вызова. Приведенная ранее программа при вызове с передачей адресов выглядит так:

/* Prim6_3.cpp */

#include <stdio.h>

int sum(int*, int*);

void main(void)

{int a = 5, b = 4, c;

c = sum(&a, &b);

printf("%d\n", c);

}

int sum(int* k, int* l)

{int m;

m = *k + *l;

return m;

}

В приведенном примере в функцию передаются копии указателей на ячейки памяти (другими словами, адреса в памяти). Эти адреса используются для доступа к памяти.

Вызов функции с передачей адресов позволяет разрабатывать функции, имеющие доступ к массивам и другим протяженным объектам данных. Если в список переменных включено имя массива, то в функцию передается только адрес начала массива. Например, так выглядит функция, выполняющая копирование строки из одного места в памяти в другое (т.е. действия, аналогичные библиотечной функции strcpy()):

/* Prim6_4.cpp */

char* my_strcpy(char* destination, char* source)

{char* ret_ptr = destination;

while(*source)

*destination ++= *source++;

*destination = ‘\0’; /* перенос символа ‘\0’ */

return ret_ptr;

}

Если программисту привычнее манипулировать с элементами массива, предыдущую программу можно записать так:

/* Prim6_5.cpp */

char* my_strcpy(char destination[], char source[])

{char* ret_ptr = destination;

while(*source)

*destination ++= *source++;

*destination = ‘\0’; /* перенос символа ‘\0’ */

return ret_ptr;

}

Первую строку функции my_strcpy() можно записать и так:

char* my_strcpy(char* destination, char* source)

а для доступа к элементам по-прежнему использовать индекс.

Язык программирования С++ допускает использование переменного числа аргументов. Признаком функции с переменным числом аргументов является многоточие … в списке параметров прототипа функции. Встретив (…), компилятор прекращает контроль соответствия типов параметров для такой функции. Функция с переменным числом параметров должна имет способ определения точного их числа при каждом вызове.

Ниже приводится пример функции example, которая принимает переменное число аргментов и выводит на печать их количество и принятые значения. Число действительно переданных через стек значений задает первый аргумент:

/* Prim6_6.cpp */

#include <stdio.h>

void example(int, …);

void main(void)

{int var1 = 5, var2 = 6, var3 = 7, var4 = 8, var5 = 9;

example(2, var1); /* вызов функции с двумя аргументами */

example(3, var1, var2); /* та же функция имеет три аргумента */

example(6, var1, var2, var3, var4, var5); /* а теперь – шесть */

}

void example(int arg1, …)

{int* ptr = &arg1; /* установка указателя на первый аргумент */

printf("\n Функции EXAMPLE передано всего %d аргумента(ов):\n", arg1); /* печать всех переданных в функцию фактических аргументов */

for(; arg1; arg1--) {printf("%d", *ptr); ptr++;}

 

Запуск программы на выполнение дает такие результаты:

Функции EXAMPLE передано всего 2 аргумента(ов):

2 5

Функции EXAMPLE передано всего 3 аргумента(ов):

3 5 6

Функции EXAMPLE передано всего 6 аргумента(ов):

6 5 6 7 8 9

В данном примере первый аргумент используется, как счетчик количества аргументов.

 

 

Модификаторы cdecl и pascal

 

Обратите внимание на то, что для доступа к фактическим аргументам в функции example() используется продвижение указателя вперед. Именно в таком порядке расположатся в стеке копии аргументов при так называемом Си-порядке передачи аргументов в функцию: последний аргумент записывается в стек самым первым, затем в стек помещается предпоследний аргумент и так далее. Запись в стек выполняется по адресу, задаваемому парой регистров SS:SP. Но так как при выполнении команды PUSH содержимое регистра SP физически уменьшается, то в оперативной памяти копия первого аргумента в списке параметров расположена по меньшему адресу памяти, следующий аргумент – по большему адресу и т.д.

На Си-порядок передачи параметров указывает модификатор типа функции cdecl. Он устанавливается специальным параметром IDE (Main Menu – Options – Compile – Entry/Exit Code в Borland C++) или задается опцией командной строки(-p-). По умолчанию выбирается cdecl. Альтернативой С++-порядку передачи параметров является последовательность передачи параметров, принятая в языке Pascal: копии аргументов записываются в стек, начиная с первого. Как результат, копия первого аргумента имеет в оперативной памяти больший адрес, чем копия последнего. На такой порядок передачи параметров указывает модификатор типа функции pascal. Явное задание порядка передачи отменяет порядок передачи, принимаемый компилятором по умолчанию.

Модификаторы типа функции должны совпадать как в прототипе, так и в определении функции. В языке Pascal не поддерживается переменное число аргументов и функция с модификатором pascal должна иметь фиксированное число аргументов. Модификатор типа функции pascal требуется при разработке С++-функций, которые будут вызываться из пределов написанных на языке Pascal программ.

Функции с переменным числом аргументов должны иметь как минимум один фиксированный аргумент для того, чтобы была возможность «привязаться» к адресу копии этого аргумента в стеке.

 



Поделиться:


Последнее изменение этой страницы: 2017-02-05; просмотров: 850; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.147.104.120 (0.014 с.)