4.6: визначити
- Page ID
- 29740
Дуже часто бажано замість фактичних значень використовувати символьні константи. Наприклад, ви, ймовірно, вважаєте за краще використовувати символ, такий як PI замість числа 3.14159. Ви можете зробити це за допомогою директиви препроцесора #define. Зазвичай вони знаходяться у файлах заголовків (наприклад, stdio.h або math.h) або у верхній частині модуля вихідного коду C. Ви можете побачити щось на кшталт:
#define PI 3.14159
Як тільки компілятор побачить це, кожен раз, коли він потрапляє на маркер PI, він замінить його значенням 3.14159. Ця директива використовує просту заміну, але ви можете зробити набагато складніші речі, ніж це. Наприклад, ви також можете створити щось, схоже на функцію:
#define parallel((x),(y)) ((x)*(y))/((x)+(y))
X і y служать заповнювачами. Таким чином, лінія
a = parallel( b, c );
розширюється до:
a = (a*b)/(a+b);
Навіщо це робити? Тому що це вбудоване розширення або макрос. Це означає, що немає функції виклику накладних витрат і операція працює швидше. При цьому читається як функція, тому програмісту простіше слідувати. Гаразд, але чому всі додаткові дужки? Причина полягає в тому, що x і y є заповнювачами, і ці елементи можуть бути виразами, а не простими змінними. Якщо ви зробили це таким чином, ви можете потрапити в біду:
#define parallel(x,y) x*y/(x+y)
Що робити, якщо x є виразом, як у наступному прикладі?
a = parallel(2+b,c);
Це буде розширюватися до:
a = 2+b*c/(2+b+c);
Оскільки множення виконується перед додаванням, ви закінчуєте, що 2 додається до добутку b разів c після ділення, що не збігається з сумою 2 і b множиться на c, і ця кількість потім будучи розділеним. За допомогою додаткових дужок зберігається порядок виконання.
Повертаючись до операцій з бітовим полем, ось деякі корисні визначення для того, що здаються функціями, але які насправді є побітовими операціями, розширеними в рядку:
#define bitRead(value, bit) (((value) >> (bit)) & 0x01) #define bitSet(value, bit) ((value) |= (1UL << (bit))) #define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
1UL просто означає 1 виражений як непідписаний довгий. Нарешті, біт також може бути визначений як символ, який призводить до деякого приємного вигляду самодокументуючого коду:
#define LEDBIT 7 // more code here... bitSet( DDR, LEDBIT );
Розширення #define можуть бути досить складними, оскільки вони можуть мати вкладені посилання. Це означає, що один #define може містити в собі символ, який сам по собі є #define. Слідкуйте за ними часом може бути трохи нудним, але в кінцевому підсумку варто зусиль. Ми подивимося на кілька вниз по дорозі. Пам'ятайте, що це робиться для того, щоб полегшити щоденне програмування, а не заплутати код. Наразі почніть з простих математичних постійних замін. Вони надзвичайно корисні і прості у використанні. Просто майте на увазі, що за допомогою мікроконтролерів конкретні регістри та порти часто дають символічні імена, такі як PORTB, щоб вам не довелося запам'ятовувати точні числові адреси з них. Норма полягає в розміщенні цих символічних констант у ВСІХ CAPS.
