logo
Metodichka_Modul_6_red

Синтаксис та основні оператори мови с.

Складні програмні проекти можна більш компактно описувати (в порівнянні з мовою Assembler) за допомогою мови програмування С, якому присутні можливості мов низького та високого рівнів, а також великою бібліотекою функцій.

Алфавіт мови С складається з букв латинського алфавіту, цифр, та спеціальних символів. При заданні ідентифікаторів sysreg та Sysreg не є однаковими. Усі ключові слова повинні бути набрані строковими літерами. Між операторами повинен стояти символ «пробіл». Закоментовані строки починаються з двох символів //.

Константи у мові С декларуються за допомогою директиви #define згідно з синтаксисом:

#define ім’я константи значення;

При роботі з апаратними засобами зручно записувати константи у двійковій та 16-ичній формах. Перед значенням констант ставляться символи 0b та 0x для двійкового та 16-ичного представлення відповідно. Регістр символів при задаванні 16-их констант на має значення.

Базовими цілими типами даних в мові С являються сhar (розмір 1 байт, діапазон значень - 128  127) та int (розмір 2 байта, діапазон значень – 32768  32767). Модифікатор unsigned, що задається перед іменем базового типу, дозволяє вказувати значення заданих вище типів даних як числа без знаку: unsigned сhar (розмір 1 байт, діапазон значень 0  255), unsigned int (розмір 2 байта, діапазон значень 0  65535). При цьому старший розряд є бітом даних, а не знаковим бітом числа.

Усі змінні повинні бути декларовані до їх використання у програмі. При запису імен змінних необхідно враховувати регістр символів. Формат задання змінних:

тип даних ім’я змінної [= початкове значення];

Якщо кілька змінних мають однаковий тип даних, то при заданні їх ідентифікатори можна перераховувати через кому. Початкове значення змінної можна не вказувати. Приклади:

char A=10;

int B, C, D;

unsigned int E, F.

Розглянемо основні оператори мови С.

Оператор задавання значення має наступний синтаксис:

Ідентифікатор = вираз;

В виразах над операндами можуть використовуватись наступні арифметичні та логічні операції мови С:

Арифметичні операції:

+ додавання;

– віднімання;

* множення,

/ ділення.

Логічні операції:

|| логічне АБО;

&& логічне ТА;

! логічне НІ;

| побітова операція АБО;

& побітова операція ТА;

^ побітова операція виключне АБО;

 побітова операція НІ;

<< логічний зсув ліворуч;

>> логічний зсув праворуч.

Мова С відноситься до суворо типізованих мов програмування: змінним одного типу не можна безпосередньо призначати значення іншого типу даних. Для однозначного визначення пріоритету операцій у виразах необхідно використовувати дужки ( ). Приклад оператора призначення:

A = (B+C)*D;

Оператор умови if/else дозволяє виконувати одну з двох дій в залежності від умови. Синтаксис оператора:

if (умова) вираз1

[else вираз2];

Умова це вираз, що задається за допомогою операцій відношення:

== дорівнює;

!= не дорівнює;

< менше;

<= менше або дорівнює;

> більше;

>= більше або дорівнює.

Якщо ця умова є істиною, то виконується вираз1, у іншому разі – вираз2. Частина else не обов’язкова. Якщо, в залежності вид умови необхідно виконати фрагмент програми, що складається з декількох операторів, то їх необхідно помістити між фігурними дужками { }. Приклад використання умовного оператору:

if (A==B) {C=D+E; I=N+5}

else {C=D–E; I=N–5;}

Оператор вибору switch/case дозволяє вибірково виконати фрагмент програмного коду, в залежності від значення виразу. Формат оператору:

switch (вираз з цілими числами) {

case константа1: вираз1;

break;

case константа2: вираз2;

break;

case константа N: вираз N;

break;

[ default: дія за умовчанням;] }

Оператор break повинен знаходитись у всіх гілках, інакше не буде мати місце вибіркове виконання команд у гілках після case. Гілку default можна не вказувати. Приклад використання оператора вибору:

switch (num) {

case 0: A=B+C; C=D+2;

break;

case 1: A=B–C; C=D+10;

break;

case 5: A=B*C; C=D+15;

break; }

Оператор циклу з параметром for використовується у тих випадках, коли нам відома до виклику циклу кількість його ітерацій. Синтаксис оператора циклу for:

for (початкове значення ітератора; умовний вираз; модифікація ітератора)

{

оператори в тілі циклу;

}

Розглянемо роботу цикала for: початкове значення ітератора визначає значення лічильника при вході у цикл; після цього аналізується умовний вираз (цикл виконується доки умова є істиною). Кожен раз після усіх строчок тіла циклу виконується модифікація ітератора, тобто змінюється значення лічильника. Вихід з циклу станеться, як тільки умовний вираз отримає значення false. Приклад оператору циклу:

s=0;

m = 1;

for (i=1; i<=10; i++)

{ s=s+i;

m=m*i; }

Оператор циклу з передумовою while застосовується, коли число повторень невідомо, та необхідно здійснити певну умову. Формат оператору наведений нижче:

while (умовний вираз)

{

Оператори тіла циклу;

}

Оператор починається з ключового слова while, за яким слідує логічний вираз, що повертає значення false або true. Оператори, що у фігурних дужках, утворюють тіло циклу. Приклад використовування оператору циклу while:

a=0;

while (a<10)

{ b=(c+d)*a-f;

a=a+2; }

Підпрограми на мові С оформлюються у вигляді функцій. Опис функції має наступний синтаксис:

[Тип виразу, що повертаємо] ім’я тіла ([список параметрів])

{

[декларації локальних змінних]

оператори тіла функції;

[return вираз;]

}

Якщо функція не повертає значення, то тип значення, що повертається не вказується і секція return не використовується. За відсутності параметрів після імені функції обов`язково вказують порожні круглі дужки ( ). Тіло функції заключається в фігурні дужки { }. Змінні, що об`явлені в тілі функції, є локальними (які видно тільки в межах тіла функції). Функції, що повертають значення, можна повертати в правій частині операторів присвоювання.

Розглянемо приклад декларації функції, що повертає, в залежності від значенния аргументу С, суму чи різність двох цілих чисел:

int sum (int A, int B, char C)

{

if (C>=0) return A+B;

else return A–B;

}

Приклад функції, що не має аргументів і неповертає значення:

init_data ( ) {

A=10;

B=100+А; }

При програмуванні мікроконтролера часто виникає необхідність використовувати асемблерний код в програмі, що написана на мові С. В цьому випадку фрагмент програми, що складений з асамблерних операторів, розміщується в операторні дужки:

#asm

оператори мови асемблер

#endasm ;

Приклад фрагменту програми на мові Assembler:

#asm

mov r1,r5

ldi r17,0xf5

#endasm ;

Одиничний асемблерний оператор в тілі С – програми, може бути записаний у вигляді директиви #asm ("Оператор мови Assembler”);.

Приклад:

#asm("out 0x12,r16"); //виконати команду out 0x12,r16.

Функції, написані на асемблері, повертають значення через регістр R30 для типів char і unsigned char, і регіструють пару (R31, R30) для типів int і unsigned intR31 – старший, а в R30 – молодший байти). Параметри функції передаються через стек, на вершину котрого вказують регістр Y, причому старший байт слова записує за старшою адресою. Механізми виклику та повернення з функції виконується засобами компілятору С. Директива #pragma warn- забороняє компілятору генерувати попередження про те, що функція не повертає результат стандартним засобом (за допомогою оператору return).

В якості приклада розглянемо асемблерну функцію, що виконує додавання двох цілих чисел і повертає значення типу int (пояснення до ассемблерного коду надаються):

int summa (int A, int B)

{

#asm

ldd R27, Y+3 завантажити старший байт параметру А;

ldd R26, Y+2 завантажити молодший байт параметру А;

ldd R25, Y+1 завантажити старший байт параметру В;

ld R24, Y; завантажити молодший байт параметру В;

add R24, R26 виконати додавання молодших байтів А і В;

adc R25, R27 виконати додавання старших байтів А і В з урахуванням прапору переносу С;

mov R30, R24 записати молодший байт додатка до регістру R30;

mov R31, R25 записати старший байт додатка до регістру R31;

#endasm

}

Виклик даної процедури виконується наступним чином:

int С;

C=summa (10, 15);

Часто у асемблерних підпрограмах виникає необхідність отримати доступ до значень змінних, що визначені в С–програмі. Розміщення змінних (у регістрах процесора чи пам’яті даних) можна визначити з файлу з розширенням *.map, ім’я якого збігається з іменем файлу, в якому зберігається код програми. Цей файл генерується компілятором та знаходиться в одному каталозі з основним модулем програми.

Розглянемо структуру програми на С. Текст основного модуля програми починається з директив препроцесора #include <ім’я файлу.h>, за допомогою яких у програмний код проекту підвантажуються файли заголовків, в яких зберігається значення різних констант, ідентифікаторів та прототипи функцій. Приклад використання даної директиви, що завантажує файл заголовку mega2561.h:

#include <mega2561.h>

Після цього визначаються значення констант (за допомогою директив #define) та глобальних змінних.

Наступним кроком є декларація функцій, що використовуються у головній програмі, що розміщена в тілі функції main(), з операторів якої починається виконання програми. Тіло функції main() розміщено між двома фігурними дужками {}.