Что такое переменные данные
Что такое переменная в программировании
Переменная (от англ. variable) — поименованная или адресуемая иным способом область памяти, которую можно использовать для доступа к данным. Звучит сложно и пугающе, не правда ли? Однако на практике такое определение вашему ребенку не потребуется. По крайней мере, на начальных этапах изучения программирования.
Переменная простыми словами — это хранилище данных. Сюда можно положить какое-то значение (например, число, строку или другой тип данных). Еще более простой вариант представить себе переменную — подумать о том, что нас окружает. Например, переменной может быть какой-то небольшой мешочек, куда можно положить, к примеру, яблоко. Оно будет там находиться до тех пор, пока мы не решим произвести с яблоком какие-то действия.
Переменную в любой программе необходимо объявлять. То есть достать этот мешочек перед тем, как положить в него яблоко. В современных приложениях переменных может быть сколько угодно много. Например, в одном мешочке хранится яблоко, в другом — конфета.
В переменных хранятся определенные данные, которые можно впоследствии использовать в программе. Для того, чтобы переменная появилась, необходимо ее объявить (зарезервировать ячейку памяти под определенные данные). В разных языках переменные объявляются по-разному в зависимости от синтаксиса. Где-то это может быть слово «var», где-то «let». Существуют также неизменяемые переменные, которые задаются только раз и объявляются словом «const».
Переменные в программе нужны не только для хранения данных, но и для различных операций с ними. Например, можно создать простой калькулятор, используя всего три переменные — a, b и c. Как это будет работать? Не углубляясь в тонкости синтаксиса каждого отдельно взятого языка программирования, покажем простой пример:
То есть у нас есть три переменные. Первые две используются для вычисления, а третья служит для того, чтобы в нее записывалось значение суммы. Причем первые две переменные могут быть с заданными исходными значениями, а могут быть и пустыми (то есть переменная объявляется, но ей не присваивается никакое значение). Во втором случае, можно самим подставлять значения и на их основе будут производиться расчеты.
Кстати, данные можно записывать сразу в программе. Если взять наш пример выше, вместо латинских букв мы можем сразу же использоваться цифры. Например, 1+2 = 3. Однако в этом случае программа, которую вы напишете, не будет обладать должной гибкостью. То есть можно посчитать в ней только заданные значения и для того, чтобы что-то изменить, каждый раз придется в коде писать новые числа и только тогда мы получим другой результат.
Переменные позволяют добавить гибкости приложению. То есть мы сами можем менять их значения и каждый раз получать необходимые результаты без вмешательства в код программы.
При работе с средой разработки Scratch также используются переменные. Здесь есть отдельный раздел, где может их создавать. При нажатии на специальную кнопку, у нас появится окно, где можно задавать имя и значение переменной. Также, в Scratch есть возможность определения области видимости, то есть будет ли переменная доступна только одному спрайту или всем.
Важно помнить о том, что если переменная доступна всем спрайтам, она должна иметь уникальное имя. Две переменные с одним из тем же именем в одной и той же области видимости существовать не могут.
А если планируется использование переменных в разных областях видимости (спрайтах), здесь допустимо использование одинаковых имен.
Как называть переменные? Можно просто латинскими буквами. Однако наиболее популярный и эффективный вариант — описательное именование, чтобы мы, работая с кодом в будущем, могли знать, какое именно значение в переменной и что туда «положить» в случае, если планируются такие действия. При обучении через скретч лучше сразу пользоваться английскими названиями переменных. Дело в том, что во взрослом программировании весь код пишется исключительно на английском и в будущем, если ребенок в будущем будет работать с иностранными компаниями, ему придется изучить этот язык.
Например, у нас есть переменная, в которую заносятся какие-то животные. Ее можно назвать «Zhivotnye», но лучше сразу писать «Animals».
Для чего можно использовать переменные в Scratch? В целом, для любой программы, которую мы здесь пишем. Например, у нас есть два персонажа, и они должны двигаться по экрану. Мы можем задать им скорость вручную, а можем внести эти данные в переменную и затем менять скорость одновременно для обоих персонажей. Это будет более эффективно.
Помимо скорости, в качестве переменных могут выступать и другие динамические параметры, например, количество шагов. И это далеко не все возможности.
Типы данных, переменные
Начнём с типов памяти микроконтроллера, их целых три:
В ближайшее время нас будет интересовать только SRAM память, в которой хранятся переменные, именно о них дальше и пойдёт речь.
Двоичная система
В цифровом мире, к которому относится также микроконтроллер, информация хранится, преобразуется и передается в цифровом виде, то есть в виде нулей и единиц. Соответственно элементарная ячейка памяти, которая может запомнить 0 или 1, называется бит (bit). Таким образом мы плавно переходим к двоичной системе исчисления. Ну же, вспоминайте школьную информатику! Не вдаваясь в подробности “как это работает”, просто попробуем рассмотреть закономерность
Двоичная | Десятичная |
0000 | 0 |
0001 | 1 |
0010 | 2 |
0011 | 3 |
0100 | 4 |
0101 | 5 |
0110 | 6 |
0111 | 7 |
1000 | 8 |
1001 | 9 |
… | … |
10000 | 16 |
И так далее. Помимо закономерности увеличения разрядов и чисел есть ещё одна: приглядитесь к числам в двоичной системе со всеми нулями справа от единицы:
10 | 2 |
100 | 4 |
1000 | 8 |
10000 | 16 |
Именно, степень двойки! Именно на степенях двойки в цифровом мире завязано очень много. Чтобы получить количество десятичных чисел, которые могут быть закодированы заданным количеством бит, нужно возвести 2 в степень количества бит. Смотрим на таблицу выше и продолжаем:
И так далее. Сразу нужно запомнить, что в программировании счёт начинается с нуля, то есть 5ю битами мы можем закодировать десятичное число от 0 до 31, 8-ю битами – от 0 до 255, 10-ю битами – от 0 до 1023. Очень важно понять и запомнить это, дальше очень пригодится. Следующая по величине единица измерения в цифровом мире – байт (byte), состоит из 8 бит. Почему 8? Исторически сложилось, что шины первых микропроцессоров имели разрядность 8 бит, возможно поэтому это количество приняли за более старшую единицу памяти. Также 8 это 2 в степени 3, что очень символично и удобно. А ещё, для кодирования всех латинских букв, знаков препинания, математических знаков и просто символов (всех что на клавиатуре) раньше хватало 7-ми бит (128 символов), но потом их стало мало, и ввели дополнительный бит, восьмой. То есть 8 бит это также размер таблицы символов, которая называется ASCII. К ней мы вернёмся уже в этой главе. Так что вопрос почему в одном байте 8 бит четкого ответа не имеет, ведь бывает и 6-ти битный байт, и 9-ти битный… Но это исключения старых процессоров, в современных цифровых устройствах в одном байте обычно содержится 8 бит (на отличных от AVR архитектурах может быть иначе), что позволяет закодировать 256 десятичных чисел от 0 до 255 соответственно. Дальше вы уже точно знаете:
Двоичная система является родной для микроконтроллера, и для работы с отдельными битами существует целый ряд инструментов, о них мы поговорим в уроке о битовых операциях из раздела продвинутых уроков.
Другие системы исчисления
Данные в памяти микроконтроллера хранятся в двоичном представлении, но помимо него существуют и другие системы исчисления, в которых мы можем работать. Постарайтесь сразу запомнить и понять, что переводить числа из одной системы исчисления в другую не нужно, Ардуино абсолютно всё равно, в каком формате вы скармливаете значение переменной, они автоматически будут интерпретированы в двоичный вид. Разные системы исчисления введены в первую очередь для удобства программиста. Теперь по сути: ардуино поддерживает (да в целом другого и не нужно) четыре классических системы исчисления: двоичную, восьмеричную, десятичную и шестнадцатеричную. Да, и до неё добрались. Краткая напоминалка: 16-ричная система имеет 16 значений на один разряд, первые 10 как у десятичной, остальные – первые буквы латинского алфавита: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f. С десятичной системой всё просто, пишем числа так, как они выглядят. 10 это десять, 25 это двадцать пять, и так далее. Двоичная (Binary) имеет префикс 0b (ноль бэ) или B, то есть двоичное число 101 запишется как 0b101 ИЛИ B101. Восьмеричная (Octal) имеет префикс 0 (ноль), например 012. Шестнадцатеричная (hexadecimal) имеет префикс 0x (ноль икс), FF19 запишется как 0xFF19.
Базис | Префикс | Пример | Особенности |
2 (двоичная) | B или 0b (ноль бэ) | B1101001 | цифры 0 и 1 |
8 (восьмеричная) | 0 (ноль) | 0175 | цифры 0 – 7 |
10 (десятичная) | нет | 100500 | цифры 0 – 9 |
16 (шестнадцатеричная) | 0x (ноль икс) | 0xFF21A | цифры 0-9, буквы A-F |
Основная фишка 16-ричной системы в том, что она позволяет записывать длинные десятиричные числа короче, например один байт (255) запишется как 0xFF, два байта (65 535) как 0xFFFF, а жуткие три байта (16 777 215) как 0xFFFFFF. Вы не представляете (или уже имеете представление), насколько удобно и понятно это позволяет работать с цветами и оттенками. Двоичная же система обычно используется для наглядного представления данных и низкоуровневых конфигураций различного железа. Например конфиг кодируется одним байтом, каждый бит в нём отвечает за отдельную настройку (вкл/выкл), и передав один байт вида 0b10110100 можно сразу кучу всего настроить, к этому мы вернёмся в уроке работа с регистрами из раздела продвинутых уроков. В документации по этому поводу пишут в стиле “первый бит отвечает за это, второй за то” и так далее.
Переменные
Переменная – это ячейка SRAM памяти, которая имеет своё уникальное название и хранит числа соответственно своему размеру. К переменной мы можем обратиться по её имени и получить значение, либо изменить его. Степень двойки преследует нас и дальше, ведь объём одной ячейки памяти в микроконтроллере тоже ей кратен:
Да, больше четырёх байт в ардуино (точнее в МК от AVR) уже не влезет, при использовании обычных типов данных. Для работы с разными диапазонами значений используются разные типы данных (переменных). По сути можно использовать 4 байта для хранения чего угодно, но это не оптимально. Это как знать, что вам нужно будет унести максимум 200 мл воды (меньше 1 байта), но вы всё равно берёте 19 литровую бутыль (2 байта). Или железнодорожную цистерну на 120 тонн (4 байта). Если хотите писать красивый и оптимальный код, используйте соответствующие типы данных. Кстати, вот они:
Типы данных
*Не встречал упоминания об этом в официальных источниках, но Ардуино (точнее компилятор) также поддерживает 64 битные числа, соответственно тип данных int64_t и uint64_t Максимальный размер всех типов данных хранится в константах, и его можно использовать в коде по надобности:
Есть ещё несколько нестандартных типов, которые иногда встречаются в чужом коде:
Объявление и инициализация переменных
Преобразование типов
Преобразование _cast (Pro)
Иногда можно встретить преобразование типов через оператор cast. Отличную статью можно глянуть на Хабре, а я кратко опишу 4 основных каста:
Как пользоваться: на примере предыдущего примера
Константы
Что такое константа понятно из её названия – что-то, значение чего мы можем только прочитать и не можем изменить. Задать (объявить) константу можно двумя способами:
Ещё пару слов о константах и переменных: если обычная переменная нигде не изменяется в процессе выполнения программы – компилятор может самостоятельно сделать её константой и она не займёт места в оперативной памяти, т.е. будет помещена во Flash.
Область видимости
Переменные, константы и другие типы данных (структуры и перечисления) имеют такое важное понятие, как область видимости. Она бывает
Глобальная
Глобальная переменная объявляется вне функций и доступна для чтения и записи в любом месте программы, в любой её функции.
Локальная
Локальная переменная живёт внутри функции или внутри любого блока кода, заключённого в < фигурные скобки >, доступна для чтения и записи только внутри него. При попытке обратиться к локальной переменной из другой функции (за пределами её < блока >) вы получите ошибку, потому что локальная переменная создаётся заново при выполнении содержащего её блока кода (или функции) и удаляется из памяти при завершении выполнения этого блока (или функции):
Важный момент: если имя локальной переменной совпадает с глобальной, то приоритет обращения по имени в функции отдаётся локальной переменной:
Формальная (параметр)
Формальная переменная, она же параметр, передаваемый в функцию, ведёт себя как обыкновенная локальная переменная, но появляется при немного других условиях: при вызове функции. Эту переменную можно читать и менять внутри её функции. Также читайте отдельный урок про функции.
Структуры (Pro)
Структура struct – очень интересный тип данных: это совокупность разнотипных переменных, объединённых одним именем. В некоторых случаях структуры позволяют очень сильно упростить написание кода, сделать его более логичным и легко модифицируемым. Тип данных структура объявляется вот по такой схеме:
Ярлык будет являться новым типом данных, и, используя этот ярлык, можно объявлять уже непосредственно саму структуру:
Также есть вариант объявления структуры без создания ярлыка, т.е. создаём структуру, не объявляя её как тип данных со своим именем.
Рассмотрим большой пример, где показано всё вышеописанное
Размер элемента структуры
Структуры позволяют делать одну очень интересную вещь для оптимизации памяти: указывать максимальный вес элемента в битах. Таким образом можно делать даже однобитные флаги (обычный bool / boolean занимает в памяти 8 бит). Делается это при помощи оператора двоеточие :
Вложенные структуры
Структуры также могут быть вложенными друг в друга, доступ к нужному элементу осуществляется так же при помощи оператора “точка”, смотрите простой пример:
Перечисления (Pro)
Перечисления ( enum – enumeration) – тип данных, представляющий собой набор именованных констант, нужен в первую очередь для удобства программиста. Сразу пример из опыта: допустим у нас есть переменная mode, отвечающая за номер режима работы устройства. Мы для себя запоминаем, какому значению переменной какой режим будет соответствовать, и где-нибудь себе записываем, например 0 – обычный режим, 1 – режим ожидания, 2 – режим настройки_1, 3 – режим настройки_2, 4 – калибровка, 5 – аварийный режим, ошибка. При написании или чтении программы часто придётся обращаться к этому списку, чтобы не запутаться. Можно сделать первый шаг по оптимизации: обозвать каждый режим при помощи дефайна:
Таким образом вместо цифры можно будет использовать понятные слова и ориентироваться в коде будет гораздо проще. Использование enum ещё немного упрощает эту конструкцию: перечисление позволяет создать переменную (по умолчанию типа int ), которая может принимать только те “названия”, которые для неё указаны. Это удобно тем, что в одной программе могут находиться разные хранители режимов с одинаковыми названиями, и в отличие от #define это не будет приводить к ошибкам. Объявление перечисления чем-то похоже на объявление структуры:
Таким образом мы объявили ярлык. Теперь, используя этот ярлык, можно объявить само перечисление:
Также как и у структур, можно объявить перечисление без создания ярлыка (зачем нам лишняя строчка?):
Созданное таким образом перечисление является переменной, которая может принимать указанные для неё имена, также с этими именами её можно сравнивать. Теперь самое главное: имена для программы являются числами, начиная с 0 и далее по порядку увеличиваясь на 1. В абстрактном примере выше имя1 равно 0, имя2 равно 1, имя3 равно 2, и так далее. Помимо указанных имён, перечислению можно приравнять и число напрямую, но как бы зачем. Рассмотрим пример!
Таким образом SET1 имеет значение 1, SET2 будет 2 и так далее по порядку.
Пользовательские типы (Pro)
В С++ (и на Ардуино) этого делать не нужно! Наоборот, typedef в этом применении может приводить к ошибкам. Например:
Пространство имён (Pro)
Пространство имён – очень удобная возможность языка, с её помощью можно разделить функции или переменные с одинаковыми именами друг от друга, то есть защитить свой набор данных инструментов от конфликтов имён с другими именами. “Именная область” определяется при помощи оператора namespace :
Чтобы использовать содержимое из пространства имён, нужно обратиться через его название и оператор разрешения области видимости ::
Более подробный пример:
И ниже по коду можно будет пользоваться содержимым пространства имён без обращения через имя::
Спецификаторы (Pro)
Помимо возможности сделать переменную константой при помощи спецификатора const у нас есть ещё несколько интересных инструментов по работе с переменной.
static
static – делает переменную (или константу) статичной. Что это значит? Статичная локальная Для начала вспомним, как работает обычная локальная переменная: при вызове функции локальная переменная создаётся заново и получает нулевое значение, если не указано иначе. Если локальная переменная объявлена как static – она будет хранить своё значение от вызова к вызову функции, то есть станет грубо говоря глобально-локальной. Пример:
Статическая локальная:
extern
extern – указывает компилятору, что переменная объявлена где-то в другом файле программы, и при компиляции он её найдёт и будет использовать. А если не найдёт – ошибки не будет. Например при помощи данного кода можно сбросить счётчик millis()
volatile
volatile – данный спецификатор указывает компилятору, что данную переменную не нужно оптимизировать и её значение может быть изменено откуда-то извне. Обычно переменные с таким спецификатором используются в обработчиках прерываний. Вычисления с такими переменными также не оптимизируются и занимают больше процессорного времени.
Видео
Java Core для самых маленьких. Часть 3. Переменные
В предыдущей статье мы говорили о типах данных, а теперь поговорим о вытекающей из этого теме, о переменных.
На скриншоте выше продемонстрирован пример создания переменных.
Давайте сразу научимся давать имена переменным правильно. Существует документ Java Code Conventions. В нем указаны правила к написанию кода на Java. Нужно это для того, что бы Java код в разных проектах был написан в едином стиле. Таким образом, новоприбывший на проект программист не будет отвлекаться на новый стиль кода, ведь он будет оформлен так же, как и на других проектах. Эти правила работают и при нейминге переменных.
Итак, переменные принято называть в lowerCamelCase стиле. Сейчас покажу как хорошо, а как плохо:
Вот было значение 1998, а на следующей строке стало 2000. А вот с константой так не получится. Константа гарантирует, что ее значение не изменится на протяжении всего времени выполнения программы. Как создать константу? Очень просто:
Нужно всего лишь дописать ключевое слово final перед типом данных переменной. Для простоты понимания, мы как бы «финализируем» значение переменной и говорим, что это значение конечное.
Обратим внимание на имя константы. Когда мы даем имя константе, то должны делать это в стиле SCREAMING_SNAKE_CASE. Давайте исправим мой код:
В целом, вас никогда не погладят по головке за правильный нейминг в коде. Подразумевается, что это нечто естественное при написании кода. Как правила этикета у людей. А вот если вы будете называть ваши переменные и константы абы как, получите незамедлительного леща.
Инициализация переменных
У всех переменных из моих примеров уже было присвоено начальное значение. Процесс присвоения начального значения называется инициализацией переменной. И не обязательно инициализировать переменную во время ее объявления. Java позволяет сделать это и позже.
Во-первых, можно объявлять переменные через запятую (если они одинакового типа):
При этом, смотрите, мы можем некоторые из них инициализировать прямо во время объявления. А теперь инициализируем оставшиеся:
Для инициализации переменных типа char я использовал цепочку присваиваний. Да, и так тоже можно. Хотя используется очень редко.
Если мы попытаемся провести какую-то операция с переменной, у которой нет значения, то получим ошибку.
Оператор присваивания
Литералы
В Java постоянные значения задаются литеральным представлением. Простым языком, при помощи определенных символов мы можем указывать тип данных. Вот например, при работе с целочисленными литералами нам не нужны никакие литералы. Мы просто пишем число 1998; и Java понимает, что это целочисленное значение.
Так, что еще за символы подчеркивания в значении переменной? Это всего-навсего декор. С JDK 7 допустимо указывать знаки подчеркивания, для разбиения больших чисел на разряды, для удобства чтения.
В вышеописанных примерах были литералы для целочисленных десятичных значений. А еще мы можем присвоить целому числу литерал в виде восьмеричного значения. Для этого вам нужно добавить в самом начале значения 0. Но в таком случае, логично, что это значение не сможет содержать цифры 8 и 9, поскольку они выходят за пределы восьмеричной системы счисления.
Мало того, еще мы можем использовать литерал шестнадцатеричных значений. Такие значения обозначают с начальным 0x или 0X, а цифры в пределах от 10 до 15 заменяются символами ABCDEF английского алфавита.
С JDK 7 есть возможность указывать целочисленные литералы в двоичном виде. Для этого вначале значения нужно указать 0b или 0B и соответственно в значениях можно использовать только цифры 0 и 1. В реальности, вам навряд ли понадобятся литералы шестнадцатеричных, восьмеричного или двоичных значений. Однако, знать о них нужно.
Литералы дробных чисел
Но существует и экспоненциальная форма записи этих чисел в виде 2.34e12;
Где число после символа ‘e’ обозначает степень 10-ти. Простыми словами, мы записали 2 целых и 34 сотых умноженное на 10 в 12 степени.
Как и в случае с целочисленными значениями, литералы с плавающей точкой поддерживают нижнее подчеркивание начиная с JDK 7.
Логические литералы
Символьные литералы
Для символьных литералов, которые присутствуют на вашей клавиатуре вы можете использовать одинарные кавычки, обернув ими необходимый вам символ введенный с клавиатуры.
Я не просто так обратил ваше внимание на символы с клавиатуры. Ведь существуют и такие символы, которых на клавиатуре нет, да и на символы они не похожи. Сейчас разберемся.
А вот если символ нельзя ввести непосредственно с клавиатуры, то для ввода таких символов используют ряд управляющих последовательностей. Например, символ переноса строки ‘\n’. Существуют последовательности, которые существуют для ввода значений в восьмеричной и шестнадцатеричной форме. Например, мы захотели ввести букву tu катаканы смайлик: ツ. Берем и пишем:
В данной таблице вы можете посмотреть наиболее используемые последовательности символов:
Когда-то давно, я писал статью про экранирование символом. Настоятельно рекомендую прочитать, как дополнительный материал. Узнаете много интересного.
Строковый литерал
Можно заключить последовательность символов в двойные кавычки и получить так называемую строку.
Динамическая инициализация
Пару слов о таком термине как динамическая инициализация. Ничего сложного, только звучит страшно.
Переменную можно инициализировать другой переменной. Также, допускается инициализация переменной результатом какого-то выражения.
Главное, чтобы тип данных выражения / инициализирующей переменной совпадал с типом данных целевой переменной.
Преобразование и приведение типов
Знаю, вы уже подустали, сделали массу перерывов и подходов. Обещаю, это последняя тема в этой статье.
Часто программисты сталкиваются с ситуацией, когда переменной одного типа приходится присваивать значение другого типа. Если оба типа данных совместимы, их преобразование будет выполнено автоматически. Для автоматического преобразования должно выполняться 2 условия:
Чтобы выполнить преобразование двух несовместимых типов данных, нужно воспользоваться приведением типов. Это явное преобразование одного типа в другой. И выглядит это вот так:
Нужно понимать, что преобразование вы проводите на свой страх и риск. Вы должны быть уверенным в том, что преобразуемое значение помещается в диапазон целевого типа. В противном случае вы получите некорректные данные.
Подводим итоги
В этой статье мы поговорили о многих важных вещах. Узнали, как создавать переменные и константы в Java, и чем они отличаются. Узнали о способах инициализации переменных. Разобрались с тем, что такое литералы и какие они бывают. А так же выполнили преобразование и приведение типов данных.
Скажем спасибо Егору за предоставленный материал.