Функции и процедуры. Рекурсия
Внутри функции могут быть объявлены переменные, как, например, переменные p и i в функции power . Такие переменные называются и они определены только внутри этой функции. Переменные, определенные вне тела любой функции (ранее мы такие переменные вообще не рассматривали) называются и они определены всюду начиная с места определения и до конца файла. Глобальными переменными можно пользоваться во всех функциях, но с современной точки зрения использовать глобальные переменные рекомендуется как можно реже.
Локальные переменные создаются каждый раз при входе в функцию и уничтожаются при выходе из нее. Таким образом, значения, сохраненные в локальных переменных, пропадут после завершения работы функции.
В различных функциях можно определять переменные с одним и тем же именем. Это будут различные переменные. Также можно объявлять в функциях переменные, имена которых совпадают с именами глобальных переменных. Тогда будет создана новая локальная переменная, а изменить значение глобальной переменной с таким именем будет невозможно. Пример:
#include
using namespace std;
int i; // i - глобальная переменная
void f1() int i; // Определена локальная переменная i
i=5; // Изменяется значение локальной переменной
cout >
void f2() i=3; // Изменяется значение глобальной переменной
>
int main() i=1; // Изменяется значение глобальной переменной
f1(); // f1() не меняет значение глобальной переменной
cout f2(); // f2() меняет значение глобальной переменной
cout return 0; >
Вопрос: как будет работать программа, если добавить в функцию main определение переменной i , как локальной? А если при этом убрать объявление глобальной переменной i?
Передача параметров по значению и по ссылке
Переменные, в которых сохраняются параметры, передаваемые функции, также являются локальными для этой функции. Эти переменные создаются при вызове функции и в них копируются значения, передаваемые функции в качестве параметров. Эти переменные можно изменять, но все изменения этих переменных будут «забыты» после выхода из функции. Рассмотрим это на примере следующей функции, «меняющей» значения двух переданных ей переменных:
#include
using namespace std;
void swap(int a, int b)
int t;
t=b;
b=a;
a=t;
>
int main()
int p=3,q=5;
swap(p,q);
cout return 0;
>
При вызове функции swap создаются новые переменные a и b , им присваиваются значения 3 и 5. Эти переменные никак не связаны с переменными p и q и их изменение не изменяет значения p и q . Такой способ передачи параметров называется .
Чтобы функция могла изменять значения переменных, объявленных в других функциях, необходимо указать, что передаваемый параметр является не просто константной величиной, а переменной, необходимо передавать значения . Для этого функцию swap следовало бы объявить следующим образом:
void swap(int & a, int & b)
Амперсанды перед именем переменной означают, что эта переменная является не локальной переменной, а ссылкой на переменную, указанную в качестве параметра при вызове функции. Теперь при вызове swap(p,q) переменные a и b являются синонимами для переменных p и q , и изменение их значений влечет изменение значений p и q . А вот вызывать функцию в виде swap(3,5) уже нельзя, поскольку 3 и 5 — это константы, и сделать переменные синонимами констант нельзя.
Однако в языке C (не C++) вообще не было такого понятия, как передача параметров по ссылке. Для того, чтобы реализовать функцию, аналогичную swap в рассмотренном примере, необходимо было передавать адреса переменных p и q , а сама функция при этом должна быть объявлена, как
void swap(int * a, int * b)
Поскольку в этом случае функция swap знает физические адреса в оперативной памяти переменных p и q , то разыменовав эти адреса функция swap сможет изменить значения самих переменных p и q .
Теперь вспомним, что в C++ массивы и указатели — это почти одно и то же. А поскольку передача массива по значению, то есть создание копии всего массива является трудоемкой операцией (особенно для больших массивов), то вместо массива всегда передается указатель на его начало. То есть два объявления функции void f(int A[10]) и void f(int * A) абсолютно идентичны. В любом случае функция f получит указатель на начало массива, а, значит, будет способна изменять значения его элементов.
Упражнения
Функции — удобный способ структурирования программы. Существует мнение, что если фрагмент программы реализует некоторую законченную алгоритмическую идею и состоит более чем из 10 строк, то его обязательно надо оформить в виде отдельной функции.
Упражнения к этому листку объединены темой генерирования комбинаторных объектов (перестановок, подмножеств и т.д.) Такие объекты следует хранить в массивах, например, перестановка чисел от 1 до n — это массив типа int[n] , заполненный числами от 1 до n .
В программе должны быть следующие функции ( p — массив для хранения комбинаторного объекта, n — число элементов в нем):
int * Init (int n); // Создание и инициализация массива
bool Next (int * p, int n); // Построение следующего комбинаторного объекта
void Print(int * p, int n); // Печать комбинаторного объекта
void Done (int * p); // Освобождение памяти
а функция main должна иметь следующую структуру:
cin>>n;
p=Init(n);
Print(p,n);
while( Next(p,n) )
Print(p,n);
Done(p);
Кроме того, программа должны работать за оптимальное время.
- По данным числам n и k выведите на экран все строки длины n, состоящие из чисел 1, . k в лексикографическом (алфавитном) порядке.
- По данному n выведите на экран все подмножества множества .
- По данным n и k выведите на экран все двоичные строки длины n, содержащие ровно k единиц в лексикографическом порядке.
- По данному n напечатайте все перестановки чисел от 1 до n в лексикографическом порядке.
- По данному n напечатайте все способы размещения из n элементов k элементов (количество способов выбрать из чисел от 1 до n ровно k чисел с учетом порядка выбора).
- По данному числу n напечатайте всевозможные его представления в виде суммы натуральных слагаемых. Представления, различающиеся порядком слагаемых, считать за одно.
- По данному слову (или последовательности цифр) напечатайте всевозможные перестановки его букв. Учтите, что буквы исходного слова могут совпадать.
Глобальные переменные
В противоположность локальным переменным глобальные переменные видны всей программе и могут использоваться любым участком кода. Они хранят свои значения на протяжении всей работы программы. Глобальные переменные создаются путем объявления вне функции. К ним можно получить доступ в любом выражении, независимо от того, в какой функции находится данное выражение.
В следующей программе можно увидеть, что переменная count объявлена вне функций. Она объявляется перед функцией main(). Тем не менее, она может быть помещена в любое место до первого использования, но не внутри функции. Общепринятым является объявление глобальных переменных в начале программы.
void func1(void) , func2(void);
int count; /* count является глобальной переменной */
int main(void)
count = 100;
func1 ();
return 0; /* сообщение об удачном завершении работы */
>
void func1 (void)
func2 ();
printf(«счетчик %d», count); /* выведет 100 */
>
Рассмотрим поближе данный фрагмент программы. Следует понимать, что хотя ни main(), ни func1() не объявляют переменную count, но они оба могут ее использовать. func2() объявляет локальную переменную count. Когда func2() обращается к count, она обращается только к локальной переменной, а не к глобальной. Надо помнить, что если глобальная и локальная переменные имеют одно и то же имя, все ссылки на имя внутри функции, где объявлена локальная переменная, будут относиться к локальной переменной и не будут иметь никакого влияния на глобальную,. это очень удобно. Если забыть об этом, то может показаться, что программа работает странно, даже если все выглядит корректно.
Глобальные переменные хранятся в фиксированной области памяти, устанавливаемой компилятором. Глобальные переменные чрезвычайно полезны, когда одни и те же данные используются в нескольких функциях программы. Следует избегать ненужного использования глобальных переменных по трем причинам:
- Они используют память в течение всего времени работы программы, а не тогда, когда они необходимы.
- Использование глобальных переменных вместо локальных приводит к тому, что функции становятся более частными, поскольку они зависят от переменных, определяемых снаружи.
- Использование большого числа глобальных переменных может вызвать ошибки в программе из-за неизвестных и нежелательных эффектов.
Одним из краеугольных камней структурных языков является разделение кода и данных. В С разделение достигается благодаря использованию локальных переменных и функций. Например, ниже показаны два способа написания mul() — простой функции, вычисляющей произведение двух целых чисел.
Общий | Частный |
---|---|
int mul(int х, int у) return(x*y); > |
int х, у; int mui(void) return(x*y); > |
Обе функции возвращают произведение переменных х и у. Тем не менее общая или параметризированная версия может использоваться для вычисления произведения любых двух чисел, в то время как частная версия может использоваться для вычисления произведения только глобальных переменных х и у.
Глобальные и локальные переменные в си
Не могу разобраться. Допустим у меня есть 2 файла в проекте. Один main.c, другой fo.c. В файле fo.c есть переменная, допустим float a, которая объявлена не внутри функций. Делает ли это эту переменную глобальной? Т.е на неё будет выделена память на всё время выделение программы? ( в файле main, я не пишу никакой extern)
Отслеживать
задан 14 окт 2019 в 12:36
user309591 user309591
13 3 3 бронзовых знака
Или же переменная «а» , будет локальной внутри файла fo ?
14 окт 2019 в 12:54
чтобы она стала локальной внутри файла надо static писать, иначе глобальная
14 окт 2019 в 13:21
если я поставлю «static» переменная не станет локальной, а область видимости будет только в переделах файла. Вопрос был конкретно о том, будет ли память выделяться только в области видимости этого файла, а потом удаляться, или на всём рабочем процессе программы. Надеюсь, получил правильный ответ. Спасибо.
14 окт 2019 в 14:19
Что такое «память выделяться только в области видимости этого файла». Область видимости — чисто пространственная характеристика, а выделение и освобождение памяти — временнАя характеристика. Они никак не совместимы.
14 окт 2019 в 15:37
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
Термин «глобальная переменная» недостаточно четко определен.
Ваша переменная float a; будет обладать статическим классом хранения, т.е. она будет существовать все время выполнения программы. Ваша переменная будет иметь внешнее связывание, т.е. к ней, при желании, можно будет получить прямой доступ из любого места программы.
Но это не означает, что имя вашей переменной будет иметь некую глобальную область видимости (в С нет такой области видимости вообще). Чтобы получить доступ к вашей переменной из других единиц трансляции, вам придется дополнительно выполнить объявление этой переменной в этих других единицах трансляции
extern float a;
Обычно именно для глобальных переменных, т.е. переменных, которые потенциально «нужны везде», такое объявление помещают в заголовочный файл.
Область видимости переменных в C++: локальные и глобальные переменные
Всем привет! Сегодня мы затронем тему, которую должны были изучить еще в самом начале пути изучения C++ — область видимости переменных. Мы разберем, что такое локальные и глобальные переменные.
Область видимости переменных в C++
Область видимости переменных — это те части программы, в которой пользователь может изменять или использовать переменные в своих нуждах.
В C++ существуют отдельные блоки, которые начинаются с открывающей скобки ( < ) и заканчиваются соответственно закрывающей скобкой ( >). Такими блоками являются циклы (for, while, do while) и функции.
int func () // блок (функция - func) > int main() // блок (функция - main) for (int i = 0; i 10; i++) // блок (цикл - for), также является дочерним блоком функции main for (int j = 0; j 5; j++) // блок (цикл - for), но он еще является и дочерним блоком для первого цикла > > system("pause"); return 0; >
Если переменная была создана в таком блоке, то ее областью видимости будет являться этот блок от его начала (от открывающей скобки — < ) и до его конца (до закрывающей скобки - >) включая все дочерние блоки созданные в этом блоке.
В примере ниже, программист ошибся с областью видимости:
- Он создал переменную j во втором цикле.
- Использовав ее в первом цикле for он вынудил компилятор сообщить об ошибке (переменной j больше нет, поскольку второй цикл закончил свою работу).
int main() for (int i = 0; i 10; i++) int b = i; for (int j = 0; j 5; j++) cout + j; > cout ; // ошибка: так как переменная j была создана в другом блоке > system("pause"); return 0; >
А вот ошибки в строке 6 нет, поскольку второй цикл находится в первом цикле (является дочерним блоком первого цикла) и поэтому переменная b может спокойно там использоваться.
Глобальные переменные в C++
Глобальными переменными называются те переменные, которые были созданы вне тела какого-то блока. Их можно всегда использовать во всей вашей программе, вплоть до ее окончания работы. В примере ниже мы создали две глобальные переменные global и global_too и использовали их в функции summa :
int global = 5; // глобальные int global_too = 10; // переменные int summa() cout + global_too; // суммируем числа > int main() summa(); // вызываем функцию summa system("pause"); return 0; >
Вот, что выведет данная программа:
15 Process returned 0 (0x0) execution time : 0.010 s Press any key to continue.
Как видите глобальные переменные видны везде. В нашем примере внутри функции summa мы не создавали ни какие переменные, мы лишь использовали две глобальные переменные, которые были созданы раньше.
Локальные переменные — это переменные созданные в блоках. Областью видимости таких переменных является блоки ( и все их дочерние ), а также их область видимости не распространяется на другие блоки. Как ни как, но эти переменные созданы в отдельных блоках.
Из этого можно сделать вывод: у нас есть возможность создавать переменные с одинаковыми именами, но в разных блоках (или другими словами, чтобы их область видимости не совпадала друг с другом).
int main() for (int i = 0; i 2; i++) int b = i; // локальная переменная (она находится в блоке for) cout ; > system("pause"); return 0; >
В примере выше блоком где была создана локальная переменная b является цикл for (2 — 5). А вот если бы мы захотели вывести переменную b вне блока for , компилятор сообщил бы нам об ошибке, подробнее об этом говорится ниже.
Распространенной ошибкой среди начинающих программистов является использование локальных переменных в других блоках. Например ниже мы решили использовать переменную cost в функции summa , хотя мы ее создали в совершенно другой функции — main .
int summa () cout ; // ошибка > int main() int cost = 10; // переменная созданная в блоке main summa(); system("pause"); return 0; >
Нужно запомнить! Если вы создали локальную переменную, то вы должны понимать, что использование ее в других блоках будет невозможным.
Глобальная переменная уступает локальной
Если мы создадим глобальную переменную и с таким же именем локальную, то получится, что там где была создана локальная переменная будет использоваться именно локальная переменная, а не глобальная. Так как локальная переменная считается по приоритету выше глобальной. Давайте разберем, как это работает на примере ниже:
string str = "You lucky!"; void message() string str = "You very lucky man!"; cout ; > void sait_message() cout ; >
Мы создали глобальную переменную str со значением “You lucky!” и локальную переменную с таким же названием в функции message , но с другим значением — “You very lucky man!“. Если мы вызовем функцию message , то результатом будет:
You very lucky man! Process returned 0 (0x0) execution time : 0.010 s Press any key to continue.
А вот, если мы вызовем функцию sait_message то результатом будет:
You lucky! Process returned 0 (0x0) execution time : 0.010 s Press any key to continue.
Вот так глобальная переменная уступает локальной!
Мы советуем вам не создавать переменные с одинаковыми именами, поскольку в будущем вам будет тяжело разобраться в коде, если там будут присутствовать одинаковые переменные.
Глобальный оператор разрешения
В случае создания двух переменных с одинаковым именем (одна из которых является глобальной, а другая локальной) при использовании в блоке, в котором была объявлена локальная переменная, можно использовать и глобальную переменную. Для ее использования нужно всего лишь применить глобальный оператор разрешения.
Глобальный оператор разрешения — это два подряд поставленные двоеточия, с помощью которых мы говорим компилятору, что хотим использовать глобальную переменную, а не локальную.
Чтобы использовать глобальный оператор разрешения нужно применять данную конструкцию:
:: имя глобальной переменной>
В примере ниже вы можете увидеть, как работает глобальный оператор разрешения:
string str = "You lucky!"; void message() string str = "You very lucky man!"; cout <:: str; // использовали глобальный оператор разрешения >
Получится вот такой результат, если мы вызовем функцию message :
You lucky! Process returned 0 (0x0) execution time : 0.010 s Press any key to continue.
Какие виды переменных использовать
Сегодня мы разобрали, что такое область видимости в C++. Также мы рассмотрели, что такое локальные переменные и глобальные переменные.
У многих может возникнуть соответствующий вопрос:“Какие переменные нам лучше использовать в своих программах?“. Мы считаем, что разницы нет, какие переменные вы будете использовать, глобальные и локальные виды переменных нужно использовать вместе. Тяжело будет использовать только один вид переменных!
Мы рады, если вам понравилась данный урок. Если вы хотите оставить свой вопрос, то пишите его в комментариях. Нам будет приятно, если вы поделитесь данной статьей со своими друзьями. Удачи!
Читайте также
Цикл do while в C++
В очередном уроке по C++ мы пройдем цикл do while. В этом уроке вы узнаете как его просто реализовать и закрепим пройденные знания на примере. Удачи!
Динамические массивы и переменные в C++
Как пользоваться динамическими переменными и массивами в C++. Мы разберем как их создать и узнаем их плюсы перед использованием обычных массивов.
Инкремент и декремент в C++
Инкременты и декременты позволяют увеличивать и уменьшать значения переменных на единицу. Также мы научимся использовать постфиксные и префиксные операторы.
Мы рассмотрим создание программы, ее структуру, а также главные правила синтаксиса язык