Какую память используют переменные arduino
Переменная - это способ сохранения и дальнейшего использование данных в программе. Значение переменной может быть многократно изменено, в отличии от констант.
Для создания переменной, ее нужно объявить - задать ее имя и тип данных. Также можно сразу присвоить ей некоторое значение:
Имена переменных могут состоять из латинских букв, цифр и знака подчеркивания "_", но не могу совпадать с ключевыми словами языка ( false , else , long и тд). При этом заглавные и строчные буквы различаются.
Существуют соглашения по именованию переменных. Имена переменных обычно начинаются со строчной буквы. Если имя состоит из одного слова, то оно целиком пишется строчными буквами (имена полностью из заглавных букв обычно используются для констант). Если же имя переменной состоит из нескольких слов, то принято либо разделять их символом подчеркивания, либо начинать каждое новое слово, кроме первого, с заглавной буквы:
Первый вариант обычно используют при объектно-ориентированном подходе, а второй - при процедурном.
Область видимости
От того, в какой части кода объявлена переменная, будет зависть то, в какой части кода эта переменная будет доступна. переменные делят на:
Глобальные переменные объявляются вне функций, обычно перед функцией setup() . Доступ к таким переменным возможен из любой функции файла.
Локальные переменные - это переменные, объявленные в функции или в теле блоков if , for , while и др. Доступ к таким переменные возможен только из тоже же функции/блока, где они объявлены.
Рекомендуется как можно реже использовать глобальные переменные, так как их использование чаще приводит к ошибкам: значение переменной может быть случайно изменено в любой функции. Также при использовании локальных переменных код становится более понятным для восприятия.
static
Ключевое слово static используется для объявление локальных переменных, значение которых будет сохраняться между вызовами функции. Этим они отличаются от обычных локальных переменных, которые создаются при каждом вызове функции и уничтожаются при ее завершении.
В этом примере переменная a будет создана только при первом запуске функции loop() . После этого при каждом вызове функции ее значением будет увеличено на единицу и мы увидим следующий вывод:
Обратите внимание, что значением 10 будет также присвоено переменной только при ее инициализации.
volatile
Ключевое слово volatile используется для переменных, значение которых может быть изменено чем-то, находящимся вне секции кода. Например параллельным потоком. При использовании ключевого слова volatile компилятор будет брать значение переменной из ОЗУ, а не из регистра (временной ячейки памяти).
Переменная – это ячейка в оперативной памяти микроконтроллера, которая имеет своё уникальное название (а также адрес в памяти) и хранит значение соответственно своему размеру. К переменной мы можем обратиться по её имени или адресу и получить это значение, либо изменить его. Зачем это нужно? В переменной могут храниться п ромежуточные результаты вычислений, полученные “снаружи” данные (с датчиков, Интернета, интерфейсов связи) и так далее.
Объявление и инициализация
- Объявление переменной – резервирование ячейки памяти указанного типа на имя: тип_данных имя;
- Присваивание – задание переменной значения при помощи оператора = (равно): имя = значение;
- Инициализация переменной – объявление и присваивание начального значения: тип_данных имя = значение;
Можно объявить и инициализировать несколько переменных через запятую:
- Переменная должна быть объявлена до использования, буквально выше по коду. Иначе вы получите ошибку Not declared in this scope – переменная не объявлена.
- Нельзя объявить две и более переменных с одинаковым именем в одной области определения.
Типы данных
Переменные разных типов имеют разные особенности и позволяют хранить числа в разных диапазонах.
- (*) – да, bool занимает 1 байт (8 бит), так как это минимальная адресуемая ячейка памяти. Есть способы запаковать логические переменные в 1 бит, о них поговорим в другом уроке.
- (**) – на esp8266 int и unsigned int имеет вес 4 байта, то есть является аналогом типа long !
- (***) – не встречал упоминания в официальных источниках, но Arduino также поддерживает 64 битные числа. Arduino-библиотеки с переменными этого типа не работают, поэтому можно использовать только в своём коде.
Глобальная
- Объявляется вне функций, например просто в начале программы.
- Доступна для чтения и записи в любом месте программы.
- Находится в оперативной памяти на всём протяжении работы программы, то есть не теряет своё значение.
- При объявлении имеет нулевое значение.
Логический тип
bool – логический, он же булевый (придуман Джорджем Булем) тип данных, принимает значения 0 и 1 или false и true – ложь и правда. Используется для хранения состояний, например включено/выключено, а также для работы в условных конструкциях.
Также переменная типа bool принимает значение true , если присвоить ей любое отличное от нуля число.
Локальная
- Объявляется внутри любого блока кода, заключённого в < фигурные скобки >.
- Доступна для чтения и записи только внутри своего блока кода (и во всех вложенных в него).
- Находится в оперативной памяти с момента объявления и до закрывающей фигурной скобки, то есть удаляется из памяти и её значение стирается.
- При объявлении имеет случайное значение.
Важный момент: если имя локальной переменной совпадает с одной из глобальных, то приоритет обращения отдаётся локальной переменной (в её области определения).
Константы
Что такое константа понятно из её названия – что-то, значение чего мы можем только прочитать и не можем изменить: при попытке изменить получим ошибку компиляции. Задать константу можно двумя способами:
Как переменную, указав перед типом данных слово const: const тип_данных имя = значение; . Пример: const byte myConst = 10; . Фактически это будет обычная переменная, но её значение нельзя поменять. Особенности:
- Занимает место в оперативной памяти МК.
- Имеет адрес в памяти, по которому к ней можно обратиться.
- Вычисления с ней не оптимизируются и чаще всего выполняются точно так же, как с обычными переменными.
- Компилятор выдаст ошибку, если имя константы совпадает с именем другой переменной в программе.
- Не занимает места в оперативной памяти, а хранится во Flash памяти как часть кода программы.
- Не имеет адреса в оперативной памяти.
- Вычисления с такими константами оптимизируются и выполняются быстрее, так как это просто цифры.
- Если имя дефайн-константы совпадёт с именем другого “объекта” в программе или даже в библиотеке – работа может быть непредсказуемой: можно получить невнятную ошибку компиляции, либо программа может просто работать некорректно! Дефайн буквально заменяет текст в коде программы, это довольно опасная штука.
Во избежание проблем нужно называть дефайн-константы максимально уникальными именами. Можно добавлять к ним префиксы, например вместо PERIOD сделать MY_PERIOD и так далее.
Преобразование типов
Иногда требуется преобразовать один тип данных в другой: например, функция принимает int , а вы хотите передать ей byte . В большинстве случаев компилятор сам разберётся и преобразует byte в int , но иногда вылетает ошибка в стиле “попытка передать byte туда, где ждут int“. В таком случае можно преобразовать тип данных, для этого достаточно указать нужный тип данных в скобках перед преобразуемой переменной (тип_данных)переменная , иногда можно встретить запись тип_данных(переменная) . Результат вернёт переменную с новым типом данных, сам же тип данной у переменной не изменится. Например:
И всё! val будет обрабатываться как int , а не как byte .
Измерение информации
Прежде чем перейти к переменным и их типам, нужно вспомнить школьный курс информатики, а именно – как хранятся данные в “цифровом” мире. Любая память состоит из элементарных ячеек, которые имеют всего два состояния: 0 и 1. Эта единица информации называется бит (bit). Минимальным блоком памяти, к которому можно обратиться из программы по имени или адресу, является байт (byte), который в Arduino (и в большинстве других платформ и процессоров) состоит из 8 бит, таким образом любой тип данных будет кратен 1 байту.
Максимальное количество значений, которое можно записать в один байт, составляет 2^8 = 256. В программировании счёт всегда начинается с нуля, поэтому один байт может хранить число от 0 до 255. Более подробно о двоичном представлении информации и битовых операциях мы поговорим в отдельном уроке.
Стандартные типы переменных в Arduino по своему размеру кратны степени двойки, давайте их распишем:
- 1 байт = 8 бит = 256
- 2 байта = 16 бит = 65 536
- 4 байта = 32 бита = 4 294 967 296
Статические переменные
Вспомним, как работает обычная локальная переменная: при входе в свой блок кода локальная переменная создаётся заново, а при выходе – удаляется из памяти и теряет своё значение. Если локальная переменная объявлена как static – она будет сохранять своё значение на всём протяжении работы программы, но область видимости останется локальной: взаимодействовать с переменной можно будет только внутри блока кода, где она создана (и во всех вложенных в него).
Статические переменные позволяют более красиво организовывать свой код, избавляясь от лишних глобальных переменных.
Символьный тип
char – тип данных для хранения символов, символ указывается в одинарных кавычках: char var = 'a'; . По факту это целочисленный тип данных, а переменная хранит номер (код) символа в таблице ASCII:
Отдельный символьный тип данных нужен для удобства работы, чтобы программа могла понять разницу между числом и символом, например для вывода на дисплей (чтобы вывести именно букву A, а не число 65). Из символов можно составлять строки, об этом более подробно поговорим в уроках про символьные строки и String-строки.
Область видимости
Переменные, константы const и другие создаваемые пользователем данные имеют такое важное понятие, как область видимости. Она бывает глобальной и локальной.
Дробные числа
float (англ. float – плавающий) – тип данных для чисел с плавающей точкой, т.е. десятичных дробей. Arduino поддерживает три типа ввода чисел с плавающей точкой:
Тип записи | Пример | Чему равно |
Десятичная дробь | 20.5 | 20.5 |
Научный | 2.34E5 | 2.34*10^5 или 234000 |
Инженерный | 67e-12 | 67*10^-12 или 0.000000000067 |
Особенности float чисел и работу с ними мы рассмотрим в уроках про математические операции и условия.
Видео
There are three pools of memory in the microcontroller used on avr-based Arduino boards :
Flash memory (program space), is where the Arduino sketch is stored.
SRAM (static random access memory) is where the sketch creates and manipulates variables when it runs.
EEPROM is memory space that programmers can use to store long-term information.
Flash memory and EEPROM memory are non-volatile (the information persists after the power is turned off). SRAM is volatile and will be lost when the power is cycled.
The ATmega328P chip found on the Uno has the following amounts of memory:
The ATmega2560 in the Mega2560 has larger memory space :
Notice that there's not much SRAM available in the Uno. It's easy to use it all up by having lots of strings in your program. For example, a declaration like:
char message[] = "I support the Cape Wind project.";
puts 33 bytes into SRAM (each character takes a byte, plus the '\0' terminator). This might not seem like a lot, but it doesn't take long to get to 2048, especially if you have a large amount of text to send to a display, or a large lookup table, for example.
If you run out of SRAM, your program may fail in unexpected ways; it will appear to upload successfully, but not run, or run strangely. To check if this is happening, you can try commenting out or shortening the strings or other data structures in your sketch (without changing the code). If it then runs successfully, you're probably running out of SRAM. There are a few things you can do to address this problem:
If your sketch talks to a program running on a (desktop/laptop) computer, you can try shifting data or calculations to the computer, reducing the load on the Arduino.
If you have lookup tables or other large arrays, use the smallest data type necessary to store the values you need; for example, an int takes up two bytes, while a byte uses only one (but can store a smaller range of values).
If you don't need to modify the strings or data while your sketch is running, you can store them in flash (program) memory instead of SRAM; to do this, use the PROGMEM keyword.
Логический тип, может принимать только 2 значения - true (правда) и false (ложь). В памяти занимает 1 байт.
Тип позволяет хранить 1 алфавитно-цифровой символ и занимае 1 байт. Для записи символа используются одинарные кавычки.
В памяти хранится число, соответствующее записанному символу в таблице ASCII, поэтому над переменной можно производить арифметические действия.
Переменная это типа - знаковая, диапазон допустимых значений - от -128 до 127.
Тип для хранения однобайтового целого беззнакового числа. Соответственно диапазон значений от 0 до 255.
Пожалуй самый частоиспользуемый тип для хранения целых чисел со знаком - integer (целое число). Занимает 2 байта и может хранить цисла от -32768 до 32767.
На платформе Arduino также присутствует тип , который ничем не отличается от типа int .
unsigned int
Беззнаковое целое число, занимает так же, как и int , 2 байта. Диапазон значений - от 0 до 65535.
Тип long служит для хранение больших целых знаковых чисел. Диапазон его значений от -2147483648 до 2147483647, а занимает в памяти он 4 байта.
unsigned long
Беззнаковое целое число расширенного диапазона может хранить значения от 0 до 4294967295 и занимает 4 байта.
float
Тип данных чисел с плавающей точкой (или с плавающей запятой). Используется для нецелочисленных расчетов. В Arduino используется например для считывания значений с аналоговых пинов. Диапазон значений от -3.4028235E+38 до 3.4028235E+38,а занимает такая переменная 4 байта.
Точность - 6-7 знаков после запятой.
double
Тип ничем не отличается от типа float и введен для обратной совместимости. На многих других платформах он имеет большую чем у float точность.
string
Тип для хранение текстовых строк. Является массивом символов типа char и символа конца строки '\0' в последнем его элементе. Не путать с классами string и String .
Строка может быть создана и инициализирована несколькими способами:
Если забыть указать символ конца строки при посимвольной инициализации или не отвести под него место, то функции работы со строками будут работать некорректно.
массив
Массив - это набор элементов одного типа с доступом к каждому элементу по индексу.
Нумерация индексов массива начинается с 0.
Ключевое слово void используется при объявлении функции, которая не возвращает значения.
Преобразование типов
Преобразование типов - это приведение значение переменной к другому типа, отличному от типа данной переменной.
Приведение типов делится на явное и неявное.
Пример явного приведения типа:
Пример неявного приведения типа:
Условная конструкция if принимает на вход значение типа boolean , поэтому целочисленное значение переменной a будет приведено к типа boolean .
A variable is a place to store a piece of data. It has a name, a value, and a type. For example, this statement (called a declaration):
creates a variable whose name is , whose value is , and whose type is . Later on in the program, you can refer to this variable by its name, at which point its value will be looked up and used. For example, in this statement:
it is the value of pin (13) that will be passed to the pinMode() function. In this case, you don't actually need to use a variable, this statement would work just as well:
The advantage of a variable in this case is that you only need to specify the actual number of the pin once, but you can use it lots of times. So if you later decide to change from pin 13 to pin 12, you only need to change one spot in the code. Also, you can use a descriptive name to make the significance of the variable clear (e.g. a program controlling an RGB LED might have variables called redPin, greenPin, and bluePin).
A variable has other advantages over a value like a number. Most importantly, you can change the value of a variable using an assignment (indicated by an equals sign). For example:
will change the value of the variable to 12. Notice that we don't specify the type of the variable: it's not changed by the assignment. That is, the name of the variable is permanently associated with a type; only its value changes. [1] Note that you have to declare a variable before you can assign a value to it. If you include the preceding statement in a program without the first statement above, you'll get a message like: "error: pin was not declared in this scope".
When you assign one variable to another, you're making a copy of its value and storing that copy in the location in memory associated with the other variable. Changing one has no effect on the other. For example, after:
only pin has the value 12; pin2 is still 13.
Now what, you might be wondering, did the word "scope" in that error message above mean? It refers to the part of your program in which the variable can be used. This is determined by where you declare it. For example, if you want to be able to use a variable anywhere in your program, you can declare at the top of your code. This is called a global variable; here's an example:
As you can see, is used in both the setup() and loop() functions. Both functions are referring to the same variable, so that changing it one will affect the value it has in the other, as in:
Here, the digitalWrite() function called from loop() will be passed a value of 12, since that's the value that was assigned to the variable in the setup() function.
If you only need to use a variable in a single function, you can declare it there, in which case its scope will be limited to that function. For example:
In this case, the variable pin can only be used inside the setup() function. If you try to do something like this:
Why, you might be wondering, wouldn't you make all your variables global? After all, if I don't know where I might need a variable, why should I limit its scope to just one function? The answer is that it can make it easier to figure out what happens to it. If a variable is global, its value could be changed anywhere in the code, meaning that you need to understand the whole program to know what will happen to the variable. For example, if your variable has a value you didn't expect, it can be much easier to figure out where the value came from if the variable has a limited scope.
[block scope][size of variables]
[1] In some languages, like Python, types are associated with values, not variable names, and you can assign values of any type to a variable. This is referred to as dynamic typing.
Целочисленные типы
Переменные целочисленных типов нужны для хранения целых чисел. В своей программе рекомендуется использовать альтернативное название типов (второй столбец в таблице выше), потому что:
- Проще ориентироваться в максимальных значениях
- Легче запомнить
- Название более короткое
- Проще изменить один тип на другой
- Размер переменной задан жёстко и не зависит от платформы (например int на AVR это 2 байта, а на esp8266 – 4 байта)
Максимальные значения хранятся в константах, которые можно использовать в коде. Иногда это помогает избавиться от лишних вычислений:
- UINT8_MAX – 255
- INT8_MAX – 127
- UINT16_MAX – 65 535
- INT16_MAX – 32 767
- UINT32_MAX – 4 294 967 295
- INT32_MAX – 2 147 483 647
- UINT64_MAX – 18 446 744 073 709 551 615
- INT64_MAX – 9 223 372 036 854 775 807
Читайте также: