Локальные и глобальные переменные




Переменные объявленные внутри функций, являются локальными по отношению к этим функциям; переменные объявленные вне функций, включая main(), - глобальными.

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

Локальные переменные также называют автоматическими переменными, так как они создаются и разрушаются программами автоматически.

В начале работы функция выделяет память в стеке для запоминания своих локальных переменных. Эта память существует только до тех пор, пока функция активна. После возврата из функции стековая память удаляется, отбрасывая все запомненные в ней переменные. Этим обеспечивается:

1) Большее по сравнению с доступной памятью суммарное пространство, занимаемое всеми локальными переменными;

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

 

Я языке С++ допустимо объявление локальных переменных непосредственно в операторном блоке:

if (условие)

{

int i;

----------------

}

Переменная i будет занимать место в памяти только в том случае, если условие оператора if окажется истинным, а область ее действия будет распространяться до закрывающей фигурной скобки оператора if.

Чаще всего объявляют локальные переменные цикла с параметром:

for (int cnt=0; cnt<100; cnt++)

{ // тело цикла

}

 

Локальная переменная цикла cnt существует в памяти ЭВМ, пока выполняется тело цикла.

Глобальные переменные существуют в течение всего времени работы программы. Они запоминаются в сегменте данных программы и занимают память независимо от того, нужны они или нет.

В Borland C++ существуют регистровые переменные, которые запоминаются непосредственно в регистрах процессора. Для объявления регистровых переменных используют спецификатор памяти register. Регистровые переменные бывают только локальными. Регистровые переменные ускоряют работу программы, поэтому чаще всего их используют для объявления управляющих переменных циклов. Объявление регистровой переменной имеет вид:

register int Var_Reg;

 

Строки, массивы и структуры в качестве параметров функций

Рассмотрим передачу строк и массивов в качестве параметров функций.

Можно передавать массивы параметрам функции, а функции могут возвращать массивы в программу:

Void Print_String(char string[])

{ printf(string):

}

main()

{------------------

char MyStr[] = "C++ - язык для профессиналов";

--------------------

Print_String(MyStr);

--------------------

}

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

При передаче символьных строк функциям следует помнить о том, что в случае объявления

#define MAXLEN 128

могут запоминаться только (MAXLEN-1) символов и завершающий нулевой байт.

Пример 9.

#define MAXLEN 128

void A_Func(char s[],int len); // объявление прототипа функции,

// обрабатывающей строку длины len

--------------------

char author[MAXLEN] = "Tom Swan";

A_Func(author, MAXLEN);

--------------------

Здесь A_Func – любая функция обработки строк, при этом строки рассматриваются как элементы символьного масива.

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

В языке С++ (как и в С) допустима передача любых массивов функциям.

Пример 10.

#define M 100

int data[M];

-------------

теперь можно объявить функцию, принимающую в качестве аргумента массив:

int Summa(int mas[M]);

Но лучше поступить следующим образом:

int Summa(int mas[], int n);

-----------------------

Тогда вызов функции будет:

printf("Summa=%d\n",Summa(data,M));

Сама функция суммирования может при этом выглядеть следующим образом:

int Summa(int mas[], int n);

{ int sum = 0;

for (int i=0; i<n; i++)

sum += mas[i];

return sum;

}

Другой пример реализации функции суммирования:

int Summa(int mas[], int n);

{ int sum = 0;

while (n>0)

sum += mas[- – n];

return sum;

}

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

Можно объявить функцию:

void SomeFunc (int arr[rows][cols]);

или

void SomeFunc (int arr[][cols]);

причем второй вариант более предпочтителен.

Но объявление

void SomeFunc (int arr[rows][]);

скомпилировано не будет, как и объявление void SomeFunc (int arr[][]);

 

Пример 11.

#include <stdio.h>

#include <conio.h>

 

const int row = 4;

const int col = 5;

// Объявляем прототипы функций

void form (int arr[][col], int r, int c);

void print_arr (int arr[][col], int r, int c);

int sum_less (int arr[][col], int r, int c, int dig);

 

main()

{ int matr[row][col];

int a, summa;

clrscr();

form (matr,row,col);

print_arr (matr,row,col);

printf("Введите число");

scanf("%d",&a);

summa=sum_less (matr,row,col,a);

printf("Summa = %d", summa);

return 0;

}

 

void form (int arr[][col], int r, int c); // формирование матрицы

{ for (int i = 0; i < r; i++)

for (int j = 0; j < c; j++)

arr[i][j] = random(100) –50;

}

 

void print_arr (int arr[][col], int r, int c); // вывод матрицы на экран

{ for (int i = 0; i < r; i++)

{for (int j = 0; j < c; j++)

printf("%4d",arr[i][j]);

printf("\n");

}

}

 

int sum_less (int arr[][col], int r, int c, int dig); // вычисление суммы

{ int s_l = 0;

for (int i = 0; i < r; i++)

for (int j = 0; j < c; j++)

if (arr[i][j] < dig) s_l += arr[i][j];

return s_l;

}

 

Рассмотрим передачу структур в качестве параметров функций.

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

Пример 12. Объявим структуру, определяющую окружность путем указания координат ее центра и радиуса:

#typedef struct circle { int coord_x;

int coord_y;

int radius;

} Circle;

Можно написать функцию, которая возвращает инициализированную структуру типа Circle:

Circle C;

C = Def_Circle(25,43,15);

 

При этом Def_Circle()может иметь вид:

Circle Def_Circle(int x, int y, int r)

{

Circle rtemp;

rtemp.cood_x=x;

rtemp.cood_y=y;

rtemp.radius=r;

return rtemp;

}

Несмотря на то, что переменная rtemp локальна в функции и не существует вне области ее действия, функция может возвращать значение переменной rtemp в качестве своего результата. При выполнении оператора return байты переменной rtemp копируются временно в стековую память, а оттуда – в переменную, которой присваивается результат функции. После этого временно записанные в стек байты удаляются.

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

 

Рекурсия

 

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

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

Каждый вызов рекурсивной функции называется рекурсивным вызовом, или шагом рекурсии.

Существует много задач, рекурсивных по своей природе: n!, an, числа Фибоначчи и др.

Пример 13. Рекурсивное вычисление факториала.

n!=n*(n-1)*(n-2)*…*2*1, причем 0!=1, 1!=1 – по определению факториала.

Любая рекурсивная функция может быть вычислена и без применения рекурсии:

fact=1;

for (cnt=n; cnt>=1; cnt--)

fact=fact*cnt; // или fact*=cnt;

 

Рекурсивное решение заключается в многократном вызове функции вычисления факториала из самой этой функции. Рассмотрим для примера вычисление 5!

5!=5*4!

4!=4*3!

3!=3*2!

2!=2*1!

1!=1

Вычисление 1! – базовая задача, а также признак завершения рекурсии. Таким образом:

1!=1 – возвращает 1;

2!=2*1!=2*1=2 – возвращает 2;

3!=3*2!=3*2=6 – возвращает 6;

4!=4*3!=4*6=24 – возвращает 24;

5!=5*4!=5*24=120 – возвращает 120;

// Рекурсивная функция факториала:

unsigned long fact(unsigned long);

main()

{

printf("5!=%d",fact(5));

return 0;

}

// Рекурсивное описание функции факториала:

unsigned long fact(unsigned long i_dat)

{ if (i_dat<=1)

return 1;

else

return i_dat*fact(i_dat-1);

}

Пример 14. Рекурсивное вычисление степенной функции.

xn=x*x*x*…*x

xn=x*x*x*…*x

xn=xn-1*x

xn-1=xn-2*x

x0=1 – признак завершения рекурсии.

long power(int number, int x);

main()

{ printf("4 в 5-й степени = %d",зщцук(5б4));

return 0;

}

// Рекурсивное описание степенной функции:

long power(int number, int x);

{ if (number = 0)

return 1;

else

return x*power(number-1,x);

}

Если рекурсивная функция объявляет какие-либо локальные переменные, то эти переменные повторно создаются на каждом уровне рекурсии.

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

Обычно рекурсивные решения используются лишь в тех случаях, когда они поддаются отладке легче, чем не рекурсивные.

 

Встраиваемые функции

 

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

Но С++ позволяет задавать функции, которые не вызываются, а встраиваются в программу непосредственно в месте ее вызова. В результате этого в программе создается столько копий встраиваемой функции, сколько раз к ней обращалась вызывающая программа.

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

Для объявления встраиваемой функции указывают спецификатор inline перед определением функции.

Пример 15. Встраиваемая функция, определяющая четность передаваемого ей аргумента.

#include <stdio.h>

#include <conio.h>

inline int parity(int x)

{ return!(x%2);}

main()

{ int dig;

printf("Введите число");

scanf("%d",&dig);

if (parity(dig)) printf("%d – четное\n",dig);

else printf("%d – нечетное\n",dig);

return 0;

}

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

Некоторые компиляторы не могут сделать функцию встраиваемой, если:

w функция содержит операторы цикла,

w функция содержит операторы switch или goto,

w функция рекурсивная.

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

 

 

Использованная литература:

Ашарина, И.В. Основы программирования на языках С и С++ / И.В. Ашарина – М.: Горячая линия-Телеком, –2002. – 207 с.

 



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2016-04-27 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: