Макросы. Директива #define . Примеры
В программах могут встречаться повторяющиеся фрагменты кода. Для того, чтобы вызвать часто используемые фрагменты кода, в программах на C++ можно использовать:
- функции;
- макросы.
Для объявления макроса используется директива #define . Общая форма объявления макроса следующая:
- MacroName – имя макроса;
- parameters – параметры, которые получает макрос;
- expression – выражение, которое вычисляет макрос. Выражение размещается той же строке, что и директива #define .
После объявления макроса его можно использовать как вызов обычной функции.
2. Реализация макросов в программах. Пример, который демонстрирует использование 3-х макросов в программах
Задача 1. Разработать программу, которая реализует 3 макросы:
- SQR(x) – возвращает значение x 2 ;
- POWER_4(x) – возвращает x 4 ;
- SIGN(x) – определяет знак числа.
Текст программы на языке C++
3. Пример, в котором в одном макросе используется результат другого макроса. Вычисление площади треугольника
В данном примере демонстрируется вызов макроса из другого макроса. Решается задача вычисления площади треугольника по сторонам a , b , c . Если из сторон a , b , c невозможно образовать треугольник, то макрос возвращает 0.
_______________ сдвиг влево << _______________
Выражения, в которых над переменной производится какая-либо операция, а потом результат операции присваивается этой же переменной, можно записывать короче, используя составные операторы.
tmp = 7; //0b00000111
tmp <<= 2; //сокращенный вариант записи
//теперь в переменной tmp число 28 или 0b00011100
Операция сдвига влево на n разрядов эквивалентна умножению переменной на 2 n .
_______________ сдвиг вправо >> _______________
Пример для беззнаковой переменной
unsigned char tmp = 255; //0b11111111
tmp = tmp >> 1;
//теперь в переменной tmp число 127 или 0b01111111
tmp >>= 3; //сокращенный вариант записи
//теперь в переменной tmp число 15 или 0b00001111
Пример для переменной знакового типа
int tmp = 3400; //0b0000110101001000
tmp >>= 2;
//теперь в переменной число 850 или 0b0000001101010010
tmp = -1200; //0b1111101101010000
tmp >>= 2;
//теперь в tmp число -300 или 0b1111111011010100
//видите — два старших разряда заполнились единицами
Операция сдвига вправо на n разрядов эквивалентна делению на 2 n . При этом есть некоторые нюансы. Если потерянные младшие разряды содержали единицы, то результат подобного “деления” получается грубоватым.
Например 9/4 = 2,5 а 9>>2 (1001>>2) равно 2
11/4 = 2,75 а 11>>2 (1011>>2) равно 2
28/4 = 7 а 28>>2 (11100>>2) равно 7
_______________поразрядная инверсия
tmp;
//теперь в переменной tmp число 161 или 0b10100001
tmp;
//теперь в tmp снова число 94 или 0b01011110
_______________ поразрядное ИЛИ | ______________
Оператор | обычно используют для установки заданных битов переменной в единицу.
tmp = 155
tmp = tmp | 4; //устанавливаем в единицу второй бит переменной tmp
155 0b10011 0 11
|
4 0b00000 1 00
159 0b10011 1 11
tmp = tmp | (1<<4); //устанавливаем в единицу четвертый бит переменной tmp
Установить несколько битов в единицу можно так
tmp = tmp | (1<<7)|(1<<5)|(1<<0);
//устанавливаем в единицу седьмой, пятый и нулевой биты переменной tmp
С помощью составного оператора присваивания |= можно сделать запись компактней.
_______________ побитовое И & _______________
Оператор & обычно применяют, чтобы обнулить один или несколько битов.
tmp = 155;
tmp = tmp & 247; //обнуляем третий бит переменной tmp
155 0b1001 1 011
&
247 0b1111 0 111
147 0b1001 0 011
Видите, третий бит стал равен 0, а остальные биты не изменились.
Обнулять биты, используя десятичные цифры, неудобно. Но можно облегчить себе жизнь, воспользовавшись операторами << и
tmp = 155;
tmp = tmp & (
(1<<3)); //обнуляем третий бит
(1<<3)) 0b1001 1 011 & 0b1111 0 111
результат 0b1001 0 011
Обнулить несколько битов можно так
((1<<3)|(1<<5)|(1<<6))); //обнуляем третий, пятый и шестой биты
Используя составной оператор присваивания &= ,можно записать выражение более компактно
((1<<3)|(1<<5)|(1<<6)));
Как проверить установлен ли бит в переменной? Нужно обнулить все биты, кроме проверочного, а потом сравнить полученное значение с нулем
if ((tmp & (1<<2)) != 0 ) <
// блок будет выполняться, только если установлен
// второй бит переменной tmp
>
if ((tmp & (1<<2)) == 0 ) <
// блок будет выполняться, только если не установлен
// второй бит переменной tmp
>
_______________побитовое исключающее ИЛИ ^ _______________
Оператор ^ осуществляет операцию логического исключающего ИЛИ между соответствующими битами двух операндов. Результатом операции логического исключающего ИЛИ будет 0 в случае равенства битов. Во всех остальных случаях результат будет 1. Это проиллюстрировано в табице истинности.
tmp = 155;
tmp = tmp ^ 8; // инвертируем четвертый бит переменой tmp
155 0b1001 1 011
^
8 0b0000 1 000
147 0b1001 0 011
Четвертый бит изменил свое значение на противоположное, а остальные биты остались без изменений.
tmp = tmp ^ 8; // опять инвертируем четвертый бит переменой tmp
147 0b1001 0 011
^
8 0b000 0 1 000
155 0b1001 1 011
Видите, четвертый бит снова изменил свое значение на противоположное.
Так записывать выражение намного удобнее
tmp = tmp ^ (1<<3); / / инвертируем третий бит переменой tmp
А так и удобно и компактно
tmp ^= (1<<4); //инвертируем четверый бит
Можно инвертировать несколько битов одновременно
tmp = var1;
var1 = var2;
var2 = tmp;
Но используя оператор ^ переставить значения можно так:
var1 ^= var 2;
var 2 ^= var 1;
var 1 ^= var 2;
Чистая магия, хотя, честно говоря, я ни разу не пользовался таким приемом.
________________Директива #define__________________
Пример:
//порт, к которому подключены кнопки
#define PORT_BUTTON PORTA
#define PIN_BUTTON PINA
#define DDRX_BUTTON DDRA
//выводы, к которым подключены кнопки
#define DOWN 3
#define CANCEL 4
#define UP 5
#define ENTER 6
int main()
<
//конфигурируем порт на вход,
//и включаем подтягивающие резисторы
DDRX_BUTTON = 0;
PORT_BUTTON = 0xff;
При задании символического имени можно использовать и выражения
пример использования:
tmp = PORTB & MASK_BUTTONS;
Используя #define не жалейте скобок чтобы четко задать последовательность вычисления выражений!
Некоторые выражения можно замаскировать под «функции».
#define ADC_OFF() ADCSRA = 0
Можно использовать многострочные определения, используя в конце каждой строки символ \
#define INIT_Timer() TIMSK = (1<<OCIE0);\
TCCR0 = (1<<WGM01)|(0<<WGM00)|(1<<CS02);\
TCNT0 = 0;\
OCR0 = 0x7d
Ну и самое мощное применение директивы #define – это задание макроопределений (или просто макросов). Вот как с помощью #define можно задать макросы для рассмотренных ранее операций с битами
#define SetBit(reg, bit) reg |= (1<<bit)
#define ClearBit(reg, bit) reg &= (
(1<<bit))
#define InvBit(reg, bit) reg ^= (1<<bit)
#define BitIsSet(reg, bit) ((reg & (1<<bit)) != 0)
#define BitIsClear(reg, bit) ((reg & (1<<bit)) == 0)
пример использования:
…
SetBit(PORTB, 0); //установить нулевой бит порта B
InvBit(tmp,6); //инвертировать шестой бит переменной tmp
Определим макрос, вычисляющий квадрат числа:
#define SQUARE(x) x*x
выражение
tmp = SQUARE(my_var);
даст корректный результат.
А что будет если в качестве аргумента макроопределения использовать выражение my_var+1
tmp = SQUARE(my_var +1);
Препроцессор заменит эту строчку на
tmp = my_var + 1 * my_var +1;
а это вовсе не тот результат, который мы ожидаем.
Чтобы избежать таких ошибок не скупитесь на скобки при объявлении макросов!
Если объявить макрос так
#define SQUARE(x) ((x)*(x))
выражение
tmp = SQUARE(my_var +1);
даст корректный результат, потому что препроцессор заменит эту строчку на
tmp = ((my_var + 1) * (my_var +1));
Арифметические операции в VBA: сложение, вычитание, умножение, деление и возведение в степень
Подробно рассмотрим то, как VBA выполняет такие арифметические операции, как сложение, вычитание, умножение, деление и возведение в степень, а также особые операции, такие как целочисленное деление и деление по модулю. Ниже, в таблице, представлены знаки операций, используемые при написании арифметических VBA-выражений.

Выражение — это значение либо группа значений, выражающая отдельное значение. Результат выражения — одно значение определенного типа данных. Знаки (обозначения) операций используются для действий над определенными значениями в выражениях. Для присваивания результата выражения переменной используется оператор присваивания (=), который сохраняет любое значение, представленное выражением справа от оператора присваивания в ячейке памяти, на которую ссылается переменная слева от этого оператора.
Во всех операциях приведенных ниже, оба операнда должны быть численными выражениями или строками, которые VBA может преобразовать в число.
Сложение
Знак (+) используется для выполнения операции сложения. Слагаемые должны быть численными выражениями, строками, которые VBA может преобразовать в числа либо датами, с которыми также возможны арифметические действия.
Тип данных результата выражения сложения обычно тот же, что и наиболее точный тип в этом выражении за некоторыми исключениями. Все исключения из этого правила наглядно представлены в примерах.
Вычитание
Знак (-) используется для выполнения операции вычитания, а также для обозначения отрицательных чисел (когда ставится перед переменной или выражением и означает тоже самое, что и умножение на -1). Знак минуса, который помещают перед числом для обозначения того, что число отрицательное, называют унарным минусом.
Для определения типа данных результата выражения вычитания VBA следует тем же правилам, что и для выражений, использующих знак операции сложения, но имеются два дополнительные правила.
Правило 1. Если в выражении вычитания один из операндов является типом Date, то и результат выражения будет иметь тип Date.
Правило 2. Если в выражении вычитания оба операнда являются типом Date, то результат выражения будет иметь тип Double.
Умножение
Знак (*) используется для выполнения операции умножения, результатом этой операции является произведение операндов. Для определения типа данных результата выражения умножения VBA использует те же правила, что и для выражений, использующих сложение. В выражениях умножения все переменные Variant, содержащие значения типа Date, преобразуются в численные значения.
Деление
Знак (/) используется для выполнения операции деления, этот знак называют знаком деления действительных чисел. В выражениях деления один операнд делится на другой, а результатом деления является частное.
Если любой операнд в выражении деления имеет значение Null, то результатом выражения также будет Null. Тип данных в выражениях деления действительных чисел обычно Double, но есть и исключение.
Если в выражении деления оба операнда имеют тип Integer или Single, то результат выражения деления имеет тип Single. Если результат переполняет диапазон для типа Single, то VBA преобразует его в тип Double.
Целочисленное деление
Знак (\) используется для выполнения операции целочисленного деления, при котором результатом деления всегда является целое число без дробной части. VBA не округляет частное целочисленного деления, а просто укорачивает его до целого числа, отбрасывая дробную часть.
Тип данных результата выражения целочисленного деления — либо Integer, либо Long. VBA использует наименьший тип данных, который соответствует результату выражения.
Деление по модулю
Знак (Mod) используется для выполнения операции деления по модулю. При делении по модулю выражение возвращает только остаток от деления как целое.
Доступное для понимания объяснение этой математической операции приведено на одном из форумов программистов. Приведу цитату оттуда: «представь, что есть полная 50л канистра и 3л банка. И ты начинаешь вычерпывать из канистры банкой воду (набирать можно только полную банку). 48л вычерпал, осталось 2 литра. Это и есть остаток от деления 50 на 3 по модулю.» Другими словами 50 Mod 3 возвращает 2.
Тип данных результата выражения деления по модулю — это Integer или Long. VBA использует наименьший тип, который подходит для результата выражения.
Возведение в степень
Знак (^) используется для выполнения операции возведения в степень числа или выражения. Показатель степени показывает, сколько раз число или выражение должно быть умножено на само себя.
VBA Excel. Арифметические операторы
Перед вычислением целочисленного результата или остатка от деления двух чисел делимое и делитель округляются. Причем, используется бухгалтерское округление:
- -3.5 => -4
- -2.5 => -2
- -1.5 => -2
- -0.5 => 0
- 0.5 => 0
- 1.5 => 2
- 2.5 => 2
- 3.5 => 4
Следующие строки вызовут ошибку «Division by zero» («Деление на ноль»):
Чтобы избежать ошибок, когда требуется общепринятое математическое округление, округляйте делитель и делимое с помощью оператора WorksheetFunction.Round.
Приоритет арифметических операторов
Приоритет определяет очередность выполнения математических операторов в одном выражении. Очередность выполнения арифметических операторов в VBA Excel следующая:
- «^» – возведение в степень;
- «—» – отрицание;
- «*» и «/» – умножение и деление; 1
- «\» – целочисленное деление;
- «Mod» – остаток от деления двух чисел;
- «+» и «—» – сложение и вычитание. 2
1 Если умножение и деление выполняются в одном выражении, то каждая такая операция выполняется слева направо в порядке их следования.
2 Если сложение и вычитание выполняются в одном выражении, то каждая такая операция выполняется слева направо в порядке их следования.
Для переопределения приоритета выполнения математических операторов в VBA Excel используются круглые скобки. Сначала выполняются арифметические операторы внутри скобок, затем — операторы вне скобок. Внутри скобок приоритет операторов сохраняется.