В предыдущих занятиях мы немного стали понимать смысл раздельной компиляции.
Также для того, чтобы грамотнее конструировать наши программы, мы должны знать такое понятие, как область видимости переменных. Хотя данное понятие, думаю, мы можем применить не только к переменным, также мы можем всё это применить и к указателям, и к структурам, и к массивам и к другим видам данных, с которыми мы работаем в нашей программе. Поэтому данный урок будет актуален в очень широком диапазоне данных. В рамках области видимости переменные условно делятся на локальные и глобальные. Глобальные переменные видны во всей программе, но для того чтобы они были также видны в других модулях, то их туда надо будет соответствующим образом подключить, а локальные — только в рамках тела отдельно взятой функции, в которой они объявлены.
Также кроме области видимости у переменных существует время жизни, определяемое классами памяти, но об этом мы будем говорить уже не в данном уроке.
Чтобы лучше прочувствовать концепцию области видимости переменных и других видов данных, давайте поработаем с этим практически.
Проект был сделан из проекта прошлого урока с именем MYPROG20 и получил соответственно имя MYPROG21.
Откроем файл main.c. Особо мы править наш проект не будем, по своему содержимому он вполне подходит под тему нашего урока.
Раскомментированный последний код в функции main() мы снова закомментируем
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/* char str1[35] = {}; int a = 0b00111000, b = 0b10000010, c = 0b01000001; int_to_binary(a, str1); printf ("Value is %s\r\n", str1); int_to_binary(b, str1); printf ("Value is %s\r\n", str1); int_to_binary(c, str1); printf ("Value is %s\r\n", str1); printf("==========\r\n"); int res = a | b ^ c; int_to_binary(res, str1); printf ("Value is %s\r\n", str1); */ |
А первый раскомментируем
1 2 3 4 5 6 7 |
float xf = 8; float yf = 3; float zf = 2; float res = xf + yf + zf; printf ("Value is %.5f\n", res); res = xf + yf - zf; printf ("Value is %.5f\n", res); |
Здесь у нас объявлены три локальные переменные, с которыми мы работает далее в теле функции main(). Соберём наш код и запустим его на выполенение
Код прекрасно собрался и выполнился, потому что в теле функции данные переменные видятся и мы с ними спокойно можем работать, изменять их значение, проводить над ними операции и т.д.
Ниже функции main() добавим ещё одну функцию
1 2 3 4 5 6 |
//-------------------------------------------------------- void print_res(char str[]) { printf ("%s %.5f\n", str, res); } //-------------------------------------------------------- |
Добавим на данную функцию прототип, чтобы она была видна в main()
1 2 3 4 |
#include "utils.h" //---------------------------------------------- void print_res(char str[]); //---------------------------------------------- |
Как видим, что в некотором роде область видимости также относится и к функциям, но пользоваться ею здесь приходится немного иначе — с помощью прототипов. Это мы уже знаем.
Вернёмся в фукнцию main() и добавим локальный строковый массив
1 2 |
float zf = 2; char str1[30] = {}; |
В двух местах в раскомментированном участке закомментируем вызов функции вывода результата в консоль
1 |
//printf ("Value is %.5f\n", res); |
Вместо этого в этих местах мы сначала подготовим начало строки для вывода в консоль результата и потом попробуем его вывести с помощью нашей функции
1 2 3 |
//printf ("Value is %.5f\n", res); sprintf (str1, "Value is "); print_res(str1); |
Попробуем собрать наш код
Мы получили ошибку, говорящую нам о том что о существовании переменной res функция print_res ничего не знает. А не знает она о ней потому, что данная переменная локальная и областью её видимости является только тело функции main().
Удалим объявление переменной из функции main(), оставив лишь только её инициализацию
1 |
res = xf + yf + zf; |
А объявим мы теперь эту переменную глобально, но попробуем объявить глобально после тела функции main()
1 2 3 4 |
return 0; //Return an integer from a function } //-------------------------------------------------------- float res; |
Сохраним теперь изменения и соберём код
Мы получили подобную ошибку, только теперь о существовании переменной res ничего не знает функция main().
Почему так произошло, ведь переменная глобальная и должна быть видна везде?
А произошло так потому, что компилятор читает код сверху вниз, а переменная res находится ниже функции main(). Поэтому область видимости глобальных переменных начинается в файле с исходным кодом после их объявления. Это бывает удобно тогда, чтобы о некоторых переменных знали только определённые функции в коде.
Удалим объявление переменной и объявим её теперь после подключения заголовочных файлов
1 2 3 4 |
#include "utils.h" //---------------------------------------------- float res; //---------------------------------------------- |
Соберём теперь наш код
Теперь всё отлично. Проверим работу нашего кода, запустив программу
Всё работает.
Теперь закомментируем наш код в функции main() и раскомментируем вот этот участок кода
1 2 3 4 5 6 7 |
float xf = 8; float yf = 3; float zf = 2; float res = xf + my_div(yf, zf); printf ("Value is %.5f\n", res); res = xf / my_sum(yf, zf); printf ("Value is %.5f\n", res); |
Уберём тип данных у переменных yf и zf, так как мы объявим их в другом месте
1 2 |
yf = 3; zf = 2; |
Объявим данные переменные глобально, а также глобально объявим строковый массив, его удалять ниоткуда не надо, так как он был в начальном коде и попал уже в область действия комментариев
1 2 3 |
float res; char str1[30] = {}; float yf, zf; |
Попробуем собрать наш код и выполнить его
Всё выполнилось.
Вернёмся в main() и из вызовов функций my_div и my_sum уберём аргументы, так как данные аргументы мы передавать не будем, а будем использовать их область видимости
1 |
float res = xf + my_div(); |
1 |
res = xf / my_sum(); |
Поэтому перейдём в файл ariph.c и уберём аргументы и в заголовках данных функций, а также изменим имена переменных в возвратах функций
1 2 3 4 5 6 7 8 9 10 11 12 |
//---------------------------------------------- float my_div(void) { return yf/zf; } //---------------------------------------------- float my_sum(void) { return yf+zf; } //---------------------------------------------- |
Уберём аргументы также и в прототипах в одноимённом заголовочном файле
1 2 |
float my_div(void); float my_sum(void); |
Сохраним все изменения и попробуем собрать проект
И у нас опять ошибка.
А ошибка потому, что глобальные переменные, хоть они и видны во всей программе, но их надо соответствующим образом подключить. Всё это сделано для раздельной компиляции, иначе утилита сборки make не узнает в других модулях об этом, так как тогда придётся ставить много зависимостей.
А подключаются глобальные переменные, объявленные в одном модуле, в другие путём использования ключевого слова extern. Если переменная (массив, структура и т.д.) при объявлении была инициализирована, то инициализацию при подключении мы не повторяем. Поэтому вернёмся в файл ariph.c и подключим наши переменные
1 2 3 4 |
#include "ariph.h" //---------------------------------------------- extern float yf, zf; //---------------------------------------------- |
сохраним изменения и попробуем теперь собрать наш проект
Проект прекрасно собирается, то есть теперь у нас видны глобальные переменные, объявленные в совершенно другом модуле нашего проекта.
Проверим работу нашего кода
Всё работает.
Итак, сегодня мы познакомились с немаловажной темой — областью видимости переменных, благодаря чему мы теперь знаем, какие данные где видны и где не видны. Узнали, что существуют переменные глобальные, которые видны во всей программе, а также локальные, которые видны только в рамках той функции, где они объявлены.
Всем спасибо за внимание!
Предыдущий урок Программирование на C Следующий урок
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Добрый день. У меня к Вам огромная просьба. Не могли бы Вы выпустить урок по управлению с помощью МК AVR и STM симистором. Например спецэффекты для елочных гирлянд на 220v. Плавное включение ламп, плавное затухание, моргание не затушенных полностью ламп итд. Очень интересная тема. Для Нас, новичков в этом деле.
Уроки по ШИМ уже были и для AVR, и для STM.
Доработки делайте сами. Ничего из уже пройденной теории нового для этого не надо. Соответственно смысла в уроке нету тоже.
По симисторам почитайте что нибудь из классической теории .
Там важно для такой задачи — детекция перехода через ноль.
Ну или вообще, забейте на семистор и отвяжитесь твердотельным реле. А его напрямую можете ШИМить.
По факту это тот же семистор с оптопарой.