Základy programovacího jazyka C



  1. Úvod,charakteristika jazyka

  2. Způsob zpracování programu

  3. Konstrukce a struktura programu v jazyce C

  4. Proměnné, datové typy, rozsahy platnosti a hodnot

    1. Základní typy: int, char, float, double

    2. Konstanty, příkaz: #define

    3. Automatická změna typu proměnné

    4. Paměťové třídy auto, extern, static a registr

    5. Pole

    6. Vlastní datové typy: typedef

  5. Operátory

    1. Přiřazení a aritmetické operátory: =, *, /, %, +, -, ++, --

    2. Porovnávací a logické operátory: ==, !=, <=, =>, <, >, !, &&, ||

    3. Bitové operátory: &, |, <<, >>, ^, ~

    4. Funkce jazyka C

    5. Podmíněný výraz : ?

    6. Přednost zpracování výrazů

  6. Řízení programu: příkazy: if, else, while, switch, case, for

    1. Příkazy jazyka C

    2. Blok: { }

    3. Příkaz: if – else

    4. Smyčky : while

    5. Příkaz: switch – case

    6. Příkaz: for

    7. Nepodmíněné větvení programu: break, continue, goto

  7. Definice, deklarace

  8. Direktivy preprocesoru

    1. Zahrnutí souboru: include

    2. Přepínače kompilátoru: #define name, #if, #ifdef, #elif

    3. Makro: #define jmeno text_makra

    4. Konstanta: define jmeno hodnota

  9. Zdrojové soubory a soubory include















1.Úvod, charakteristika jazyka

Vznik jazyka C se datuje do 70. let minulého století, kdy byly položeny jeho základy Brianem W. Kenrnighanem a Dennisem M. Ritchiem. V roce 1983 byla v Americkém národním úřadu pro normy (American National Standard Institute - ANSI) zřízena komise pro vypracování jasné definice jazyka C nezávislé na konkrétním procesoru. V roce 1988 byl touto komisí stanoven dnes známý standard (norma) ANSI – C. Od této doby se jazyk C osvědčuje jako velmi příjemný, výkonný a mnohostranný jazyk pro libovolné aplikace na libovolných mikrokontrolérech. Proto existují překladače z jazyka C pro téměř všechny typy současných mikrokontrolérů.

Základy jazyka C jsou jednoduché a lze se je rychle naučit a aplikovat. Syntaxe (pravidla pro zápis jazyka) umožňuje velmi kompaktní a srozumitelný zápis.


2. Způsob zpracování programu

Zpracování programu je znázorněno následujícím schématem:

Schema je ve skutečnosti složitější, před překladem se vkládají do textu tzv. „hlavičkové soubory“ (.h), vynechají se komentáře, rozvinou se makra atd. Po překladu vznikne tzv. „relativní kód“, což je téměř hotový program, kde ale nejsou ještě známy adresy proměnných a funkcí (protože jsou ještě uloženy v knihovně funkcí). Následuje spuštění tzv. „linkeru“, který přidělí relativnímu kódu absolutní adresy a provede všechny odkazy na volané knihovní funkce. Výsledkem je spustitelný soubor (.exe).

Po spuštění programu se provádí obvykle jeho „ladění“ (debugging), které slouží k odstranění chyb v programu. K ladění se používá program debugger. Po nalezení chyby a odstranění se celý postup opakuje, dokud náš program žádnou chybu neobsahuje.



3. Konstrukce a struktura programu v jazyce C

Konstrukce programu C je velmi jednoduchá, protože C je vyšší programovací jazyk strukturovaný do přehledných bloků. Základní blok je funkční blok, označovaný jako „main“. Celý program se skládá z množiny funkcí, které obsahují a zpracovávají data a proměnné a jejich prostřednictvím vzájemně komunikují.

Použití funkcí není nic nového a provádí se v jiných programovacích jazycích jako podprogramy nebo procedury.


Běh programu v C začíná vždy funkcí s názvem main(), která je vyvolána startup modulem. Proto je založení funkce main() povinné. Po spuštění programu převezme funkce main() řízení, zpracovává postupně příkazy a volá další funkce . Pokud se program skládá z více modulů, funkce main() smí být pouze v jednom modulu a v tomto modulu pouze jednou!


C funkce

Je to skupina příkazů jazyka C. Funkce musí mít jednoznačné jméno, jehož prostřednictvím může být volána. Pro jména funkcí, konstant a proměnných se nesmí použít následující rezervovaná slova jazyka C:




auto

do

if

struct

break

double

int

switch

case

else

long

typedef

char

enum

register

union

const

extern

return

unsigned

continue

float

short

void

data

for

sizeof

while

default

goto

static



Dva důležité pojmy, deklarace a definice funkce:


deklarace – ohlášení, popř. popis vlastností proměnných nebo funkcí

definice funkce – je to část programu, která tvoří tuto funkci

definice proměnných - rezervuje pro tuto proměnnou místo v paměti


Podrobněji o deklaracích a definicích bude pojednáno v kapitole 7.


Je-li funkcí vracena hodnota, je před jménem deklarace a definice uveden typ návratové hodnoty. Za jménem funkce jsou v kulatých závorkách uvedeny formální parametry předávané při vyvolání funkce a jejich typ. Jde-li o funkci, která nevrací žádnou hodnotu a při volání nepředává žádný parametr, je nutné toto vyznačit na příslušné pozici slovem void (prázdný, neobsazený). Funkcí main() se u mikrokontrolérů nepředávají žádné parametry (proto je v závorkách void), ani se při ukončení funkce nevrací žádná hodnota (též před jménem main se uvádí void).


Každá definice funkce obsahuje funkční blok, vymezený složenými závorkami { a }. Blok je seznam definicí následovaný seznamem příkazů.

Složené závorky { a } se též používají pro ohraničení složeného výrazu, který obsahuje též příkazy, neobsahuje však definice proměnných (tím se liší od funkce).



Příklad:

{ /* toto je blok, protože obsahuje */

int i; /* definici proměnné i */

i = 5;

j = 10;

}




{ // toto je složený příkaz

i = 5; // protože neobsahuje

j = 10; //definice proměnných

}



Předcházející text obsahuje dvojici znaků //, /* a */, které uvozují komentáře a poznámky.

Dobrý program v C obsahuje mnoho komentářů, např. pro záhlaví popisující činnost funkce, souborů, historii změn ale také poznámek k jednotlivým řádkům programu, vysvětlujícím souvislosti. Tyto komentáře mohou být jednořádkové a víceřádkové, viz příklady:


/* toto je jednořádkový komentář */

/* toto je víceřádkový

……………………………..

……………………………..

……………….komentář */


// toto jsou jednořádkové komentáře

// na konci komentáře není již žádný znak


Pozn.: Překladač tyto poznámky ignoruje, neobjeví se v programu mikrokontroléru.


4. Proměnné, datové typy, rozsahy platnosti a hodnot

Proměnné slouží k ukládání dat do paměti a k jejich opětovnému vyzvednutí. Používají se pro výpočty, vstupy, výstupy, řízení chodu programu apod. Proměnné vyjadřují symbolicky určité paměťové místo, které může během provádění programu měnit svou hodnotu.

Pro úsporné zacházení s paměťovými místy a pro efektivní činnost programu v mikrokontroléru je třeba zvolit vhodný typ proměnné.

Zvolený typ a jméno proměnné musí být překladači (kompilátoru) známo před jejím prvním použitím.


4.1. Základní typy:



Podle ANSI-C existují jen čtyři základní typy: int, char, float a double. Kromě těchto typů se u mikrokontrolérů definují ještě typy short int (2 bajty) a long int (4 bajty). Proměnné int a char mohou ještě být typu signed char/signed int (pro kladná i záporná čísla) a unsigned char/unsigned int (pro kladná čísla). Přehled typů a rozsahy čísel jsou v následující tabulce:




V následující tabulce je seznam typů proměnných s uvedením rozsahu hodnot čísel a obsazeným místem v paměti:



Typ

Počet bajtů

Rozsah hodnot

Vysvětlení

char

1

´ -128 až +127

Celá čísla s rozsahem hodnot jednoho bajtu

unsigned char

1

´ 0 až 255

Celá kladná čísla s rozsahem hodnot jednoho bajtu

int

2

´ - 32768 až 32767

Celá čísla s rozsahem hodnot dvou bajtů

short int

2

´ - 32768 až 32767

Celá čísla s rozsahem hodnot dvou bajtů

unsigned int

2

´ 0 až 65535

Celá kladná čísla s rozsahem hodnot dvou bajtů

long int

4

´ -2147483648 až 2147483647

Celá čísla s rozsahem hodnot čtyř bajtů

float

4

´ +-3,402e-38 až +-3,402e38

Čísla s pohyblivou řádovou čárkou s jednoduchou přesností

double

8

´ +-1,75e-308 až +-1,75e308

Čísla s pohyblivou řádovou čárkou s dvojnásobnou přesností


Pravidla C pro jména proměnných:


Globální a lokální proměnná


Globální proměnná:

Pokud se proměnná založí mimo nějakou funkci, doporučuje se hned na začátku souboru C, pak je viditelná a použitelná i pro všechny ostatní funkce tohoto i jiných souborů C. K této proměnné je možno přistupovat globálně, je platná po celou dobu běhu programu, její paměťové místo je obsazeno navždy.

Zdá se, že použití globálních proměnných je velmi vhodné, jelikož umožňují jednoduchou formu přenosu dat mezi jednotlivými funkcemi programu. Toto má však ale i nevýhody: U mikrokontrolérů AVR se lokální proměnné uchovávají v registrovém zásobníku, kde každý registr může být akumulátorem. S těmito proměnnými je možné zacházet podstatně efektivněji, než s globálními proměnnými, které jsou umístěny v paměti RAM. Navíc tyto globální proměnné může používat libovolná funkce , čímž se ztrácí čitelnost a přehlednost programu.


Lokální proměnná:

Je-li proměnná založena uvnitř funkce, může být použita pouze lokálně, tj. uvnitř této funkce. Po opuštění funkce ztrácí proměnná platnost a jí obsazená paměť může být použita k jiným účelům.

Výjimkou jsou proměnné založené s výrazem static, které si uchovají svoji hodnotu i po opuštění funkce, při novém volání funkce je pak možno používat původní uložené hodnoty. Proměnné static obsazují  trvale paměťové místo, stejně jako globální proměnné.

Lokální proměnné se zakládají zpravidla na začátku funkce, před prvním příkazem C. Je však možné je zakládat v novém otevřeném bloku funkce, tyto jsou však použitelné, popř. viditelné pouze v tomto bloku. Po opuštění bloku se uvolní jimi obsazené paměťové místo.


4.2 Konstanty, příkaz: #define

Jsou to neměnné číselné nebo znakové hodnoty dosazené do jazyka C, např. 3, 17, 25.4, 0.386, ‘A’, “Nazdar C svete”. Konstanty mohou být celočíselné (integer), v pohyblivé řádové čárce (float), znakové (char), znakové řetězce a výčtové konstanty. Konstanty se mohou použít přímo svou číselnou hodnotou nebo, což je lepší, prostřednictvím symbolického jména přiřazeného pomocí #define.

Pravidlo: Pro jména konstant se používají velká písmena, na rozdíl od proměnných, kde se používají malá nebo smíšeně malá i velká písmena.

V následující tabulce jsou příklady různých typů konstant:



Konstanta

Příklady

 

 

Integer

Jako číselné hodnoty:

100

// int

 

 

561247

// long int

 

Symbolickým jménem:

 

 

 

#define MAX

150

// hodnota 150

 

#define POČET

100

// desítkové číslo

 

#define ROZSAH

0x2450

// hexadecimální číslo

 

#define PRVEK

045

// oktalové číslo

 

#define NOVY_RADEK

'\n'

// '\n' má ASCII hodnotu 0x0D

Float

Jako číselné hodnoty:

153.8e-5

// 153.8*10-5

 

 

1E-3

// 0.001

 

 

0.25

 

 

Symbolickým jménem:

 

 

 

#define PI

3.1415926

// typ double

 

#define KONST_1

215E-2B

 

Znak

#define CISLICE_5

'5'

// použití znaku ASCII jako // takového

 

#define CISLICE_5H

0x35

// nebo číselné hodnoty znaku 5

Znakový řetězec

Přímo použitý text:

"Nazdar C"

 

Výčtový typ

enum BOOLEAN {FALSE,TRUE} error;

 

 

 

enum SWITCH {OFF,ON} motor;

 

 


Výčtový typ enum:

Enum jsou zvláštním druhem konstant (enumeration constants). Jsou to konstanty, které spolu funkčně souvisí a sjednocují se do seznamu. Proměnná příslušného typu enum může nabývat jen hodnot z tohoto seznamu konstant. Všechny konstanty typu enum jsou typu int. Konstantám seznamu se přiřazují symbolická jména a nabývají jen diskrétních hodnot, např.:VYP/ZAP, OFF/ON, PONDELI/UTERY/STREDA/ … /NEDELE.

Pokud při deklaraci konstant typu enum není přidělena hodnota, přiřadí se prvnímu symbolu 0 a následujícím symbolům vždy o 1 větší hodnota.


4.3. Automatická změna typu proměnné

Je provedena automaticky překladačem, pokud jsou u operací operandy odlišného typu nebo když jsou při operaci překročeny rozsahy hodnot. Platí pravidlo, že operandy s menším rozsahem se mění na typ s větším rozsahem hodnot.

Nejprve se mění char a short na int, popř. unsigned char a unsigned short na unsigned int, podobně je tomu i u proměnných s větším rozsahem, které se mění na double.

Příklad:


void main(void)

{

unsigned char i = 20;

unsigned int k,,j = 1654;

k = i * j; // i se automaticky převede na 16bitovou hodnotu

}



4.4. Paměťové třídy auto, extern, static a registr

Při deklaraci proměnných je možno zadávat paměťovou třídu, která určuje, ve které části paměti bude proměnná překladačem umístěna a kde všude bude proměnná viditelná. Rozšiřují tak možnosti viditelnosti proměnných, které dosud byly jen globální a lokální.


Třída auto

Je to implicitní (automatická) paměťová třída pro lokální proměnné. Je-li proměnná definována uvnitř funkce bez určení typu paměťové třídy, je její implicitní typ auto a je uložena ve stacku. Proměnná existuje od vstupu do funkce a zaniká při výstupu z funkce. Při vstupu do funkce má náhodnou hodnotu a neponechává si svoji hodnotu mezi dvěma voláními funkce.


Příklad:

void main(void) void main(void)

{ {

auto int i; je stejné jako int i;

auto int j = 5; je stejné jako int j = 5;

… …

} }




Třída extern

Je to implicitní paměťová třída pro globální proměnné. Tyto proměnné jsou uloženy v datové oblasti.

Klíčové slovo extern se používá při odděleném překladu souborů, kdy dva nebo více souborů sdílejí tutéž proměnnou. Tato globální proměnná je v jednom souboru definována zásadně bez klíčového slova extern a ve všech ostatních musí být deklarována s použitím extern.

Příklad:

soubor DIS.C soubor KEY.C

int maska; // definice extern int maska; // deklarace



Třída static

Paměťovou třídu static využívají nejčastěji lokální proměnné (definované uvnitř funkce), které si ponechávají svoji hodnotu i mezi jednotlivými voláními této funkce. Tato proměnná existuje od prvního volání příslušné funkce až do doby ukončení programu, není však přístupná z vnějšku funkce. Tyto proměnné jsou uloženy v datové paměti.



Třída register

Používá se při požadavku uložení proměnné v registru a nikoliv v datové paměti. Výhodou je mnohem rychlejší přístup k proměnné a tudíž rychlejší zpracování programu. Registrové proměnné se používají výhradně jako lokální proměnné. Následující definice proměnné :

register int i;

znamená, že proměnná může být uložena do registru, pokud je nějaký volný. Označíme-li všechny proměnné jako register, uloží se do registrů pouze některé. Jako register je vhodné označit například proměnnou jednoduchého cyklu.



4.5. Pole

Pole umožňuje označit několik položek (prvků) stejného typu jediným jménem. S jednotlivými prvky pole pracujeme prostřednictvím indexů, které se zapisují do hranatých závorek za identifikátor pole. První prvek pole má vždy index 0, následující 1, atd. Pole s 10 prvky má indexy 0 až 9. Pole jsou pouze jednorozměrná, vícerozměrná pole lze vytvořit pouze jako pole, které má prvky typu pole (pole polí).


Definice pole:


<typ> <identifikátor> [< velikost>];


int x[10];


Inicializace prvků pole:


C dovoluje provést inicializaci prvků pole již při definici. Inicializační hodnoty se zapisují do složených závorek.

Proměnné typu pole nelze kopírovat jednoduše pomocí operátoru přiřazení. Dvě pole lze kopírovat pouze po jednotlivých prvcích.



Jsou dvě možnosti inicializace prvků:


  1. klasický zápis:


int a [3];

.

.

a [0] = 1;

a [1] = 7;

a [2] = 3;


  1. definice spojená s inicializací


int a [3] = {1,7,3};


Pozn.: Zde je možné vynechat počet prvků pole, překladač jej určí sám. Proto je možný tento zápis:


int a [ ] = {1,7,3};



Příklad inicializace dvourozměrného pole:


int b [2][3] = { {0,1} , { 10,11,12 } };

nebo:

int b [ ][ ] = { {0,1} , {10,11,12} };


Pozor!: C nezabraňuje překročení indexu pole, proto je nutné pracovat s indexy velmi opatrně!



Pole konstant v paměti programu

Jde o pole, ze kterého se při běhu programu prvky pole pouze čtou. Pak je výhodné umístit toto pole do paměti programu, výsledný program se tím zrychlí. Pro tento účel se používá klíčové slovo code. Následující příklad ukazuje definici spojenou s inicializací:


code int a[ ] = {1,2,4,8,16,32,64,128};


Znakové řetězce

Znakové řetězce (strings) jsou zvláštním druhem polí (arrays). Jsou typu char a ukončeny jsou nulou ( \0 ). Proto musí být velikost pole o jeden prvek větší, než je počet znaků. Při inicializaci je zakončovací znak automaticky přidán překladačem. Následující příklad ukazuje definici řetězcového pole s inicializací:


#define DELKA_TEXTU 9 // v pameti bude 8 bajtu


char TEXT[DELKA_TEXTU ] = “NAZDAR-C”; // poslední prvek ma index 8


4.6. Vlastní datove typy: typedef

Klíčové slovo typedef slouží k definování uživatelských typů. Dosáhne se tím zlepšení přehlednosti a srozumitelnosti programu , přenositelnosti programu na jiný typ procesoru, zvýšení informačního obsahu použitím vlastních datových typů (např. typ BOOLEAN, který v C neexistuje, v programech se však hojně používá ).

Příklad:


typedef unsigned char bajt; // založení vlastního 8bitového typu

// místo "unsigned char" lze psát "bajt"

typedef short int word //založení vlastního 16bitového typu


5. Operátory

5.1. Operátory přiřazení a aritmetické operátory


Přiřazení se provádí přiřazovacím operátorem ` = `, viz příklad:

x = 5;

Je-li proměnná výsledku obsažena v matematickém výrazu, např.:

x = x + y;

je možné použít zkrácený způsob zápisu výrazu:

x + = y;


V následující tabulce je uveden seznam aritmetických operátorů a operátorů přiřazení:


Symbol

Příklad

Vysvětlení

=

i = 10; i = j;

Přiřazení hodnoty: proměnné i je přiřazena hodnota 10, popř. j

*=

i *= 10; i *= j;

Násobení s přiřazením: stará hodnota i se vynásobí 10, popř. j, výsledek je proměnné i přiřazen jako nová hodnota

/=

i /= 10; i /= j;

Dělení s přiřazením: stará hodnota i se vydělí 10, popř. j, výsledek je proměnné i přiřazen jako nová hodnota

%=

i %= 2; i %= j;

Dělení modulo s přiřazením: pro i se provede dělení modulo (celočíselné dělení) dvěma, popř. j, výsledek je proměnné i přiřazen jako nová hodnota

+=

i += 10; i += j;

Sčítání s přiřazením: k proměnné i je přičteno 10, popř. j, výsledek je proměnné i přiřazen jako nová hodnota

-=

i -= 10; i -= j;

Odečítání s přiřazením: od proměnné i je odečteno 10, popř. j, výsledek je proměnné i přiřazen jako nová hodnota

* / + -

i = 10*j; i = j*k; i = 10/j; i = j/k; i = 10+j; i = j+k; i = 10-j; i = j-k;

Násobení, dělení, sčítání, odčítání: dva operandy se vzájemně vynásobí, vydělí, sečtou, odečtou. Ve větších aritmetických výrazech se při stejné prioritě operací zpracovávají operandy zleva doprava.

++

i ++; ++ i;

Inkrement (přírůstek) a dekrement (úbytek).

--

i --; --i;

Proměnná i se zvětší popř. zmenší o 1.

 

Příklad pro i=6:

Prefix operátor, operátor před proměnnou (++i, --i).

 

x = i++; //x=6

Nejprve se provede operace a pak se použije proměnná.

 

//i =7

 

 

x = ++i; //x=7

Postfix operátor, operátor za proměnnou (i++, i--).

 

//i=7

Nejprve se použije proměnná a pak se provede operace ++ nebo --.

 

 

 

 

 

Pokud proměnná není v aritmetickém výrazu, ale stojí samostatně, jako i++, ++i nebo i--, --i, je postfix nebo prefix bezvýznamný.

%

x = 3;

Modulo: Pro operand vlevo od operátoru % se provede dělení modulo (celočíselné dělení) proměnnou nebo číslem vpravo od operandu %. Výsledkem je celočíselný zbytek po dělení.

y = 12;

i = 19%x; //=1

i = y%x; //=0

i = y%5; //=2


5.2. Porovnávací a logické operatory


Porovnávací operatory se používají v dotazech typu if, while, nebo ve smyčkách for k rozhodování o dalším průběhu programu, popřípadě k větvení program. Pomocí operátorů AND a OR se spojuje několik porovnávacích výrazů do jednoho.

Následující tabulka obsahuje seznam logických operátorů a operátorů porovnání:



Symbol

Příklad

Vysvětlení

==

if(i==10) {…..}

Rovnost: jsou-li i a 10 rovny, proveď {…..}

!=

if(i!=10) {…..}

Nerovnost: nejsou-li i a 10 rovny, proveď {…..}

<=

if(i<=10) {…..}

Menší nebo rovno: je-li i menší nebo rovno 10, proveď {…..}

>=

if(i>=10) {…..}

Větší nebo rovno: je-li i větší nebo rovno 10, proveď {…..}

<

if(i<10) {…..}

Menší: je-li i menší než 10, proveď {…..}

>

if(i>10) {…..}

Větší : je-li i větší než 10, proveď {…..}

&&

if(i>6 && i<9) {…..}

Logické A (AND): je- li levé porovnání A (a zároveň) pravé porovnání TRUE, je celý test TRUE

||

if(i>6 || i<9) {…..}

Logické NEBO (OR): je- li levé porovnání NEBO pravé porovnání TRUE, je celý test TRUE

!

if (!i) {…. // i má hodnotu 0

Logická negace: dává hodnotu 1, když má operand numerickou hodnotu 0, jinak dává hodnotu 1


Pozn.:




















5.3. Bitové operátory

Tyto operatory jsou povoleny pouze pro celočíselné (integer) datové typy char, int, short, long.

Následující tabulka obsahuje seznam bitových operátorů a operátorů posunutí bitů:


Symbol

Vysvětlení

Příklad

&

bitové A (AND):

00010101

Dva operandy se porovnávají po bitech,

& 00010001

jsou- li oba bity 1, je výsledek na tomto

00010001

bitovém místě roven 1

 

|

bitové NEBO (OR):

00010101

 

Dva operandy se porovnávají po bitech,

| 00010001

 

je- li alespoň jeden z obou bitů 1, je

00010101

 

výsledek na tomto bitovém místě roven 1

 

^

bitové výlučné NEBO (XOR):

00010101

 

Dva operandy se porovnávají po bitech,

^ 00010001

 

jsou-li oba bity rozdílné, je výsledek na

00000100

 

tomto místě roven 1

 

~

bitový doplněk:

~ 00010101

 

Každý bit operandu je negován

11101010

<<N

bitový posun vlevo:

00010101 << 2

 

všechny bity se posunou o N míst vlevo ,

01010100

 

zprava se doplňují nuly

(N = 2)

>>N

bitový posun vpravo:

00010101 >>1

 

všechny bity se posunou o N míst vpravo ,

00001010

 

zleva se doplňují nuly

(N = 1)





5.4. Funkce jazyka C

Pomocí funkcí je program rozložen na dílčí komponenty, čímž se program stává přehlednější, modulárnější, nedochází k redundandnímu (nadbytečnému) kódu a program lze snáze modifikovat. Funkce umožňují návrh programu metodou “shora dolů”, t.j. celek se rozdělí na dílčí problémy, které se pak řeší samostatně.


Deklarace a definice funkce:

Deklarace funkce – jejím smyslem je ohlásit jméno a typy předávaných a vracených parametrů. V deklaraci neexistuje žádný blok příkazů! Struktura deklarace vypadá následovně:


[paměťová třída]<typ vracené hodnoty>jméno_funkce(typy parametrů)


Definice funkce – je to část programu, která tvoří tuto funkci. Obsahuje blok příkazů, uzavřený mezi složené závorky { a }. Při definici funkce musí být před jménem funkce uveden typ vracené hodnoty. Pokud funkce nevrací žádnou hodnotu, uvede se místo typu slovo void.

Identifikátor

Vysvětlení

Paměťová třída

Možné paměťové třídy funkce: extern nebo static

 

static: Funkce může být volána jen uvnitř souboru, ve kterém byla definována

 

extern: Nemusí se zadávat, není-li zadáno, je funkce automaticky typu extern a může ji volat každá jiná funkce.

Typ vracené hodnoty

Vracení hodnot:pomocí return může být při opuštění funkce vracena jedna hodnota libovolného typu.

 

Při definici:

 

int ObsahObdelnika(int a,int b)

 

{ …

 

return c;

 

}

 

Při volání:

 

x=ObsahObdelniku(3,5);

Jméno funkce

Je to libovolná posloupnost ASCII znaků (max. 128) následovaná levou a pravou kulatou závorkou.

Seznam parametrů

Předávání perametrů:

 

Předávané parametry se uzavírají do kulatých závorek. Může se předávat jeden nebo více parametrů, které jsou odděleny čárkami. Typ a jméno parametrů se předávají při založení (definici) funkce. Nejsou- li použity žádné parametry, zůstávají závorky při vyvolání funkce prázdné a při definici a deklaraci se uvádí slovo void. Při volání funkce se předává přímo číselná hodnota nebo jméno proměnné.

 

Příklad:

 

int ObsahObdelniku(int a,int b)

Příkaz C

Funkční blok:

 

Mezi složenými závorkami { a } jsou všechny příkazy funkce. Mohou zde být definovány proměnné, které mají paměťovou třídu auto, není-li uvedeno jinak.

 

Každý příkaz je zakončen ;



5.5 Podmíněný výraz ? :


Tvar podmíněného výrazu vypadá následovně:


<testovaný_výraz> ? příkaz1:příkaz2;


Je-li výsledek testu <testovaný_výraz> roven TRUE, bude se zpracovávat “příkaz1”, v případě FALSE se bude zpracovávat “příkaz2”. Výsledkem podmíněného výrazu je příkaz1 nebo příkaz2 a lze jej použít přímo k přiřazení, viz následující příklad:


x = (y>z) ? y:z;


Operátor ? je tedy zkrácená forma příkazu if-else, se kterým by měl uvedený příklad následující tvar:

if(testovaný_výraz) příkaz1;

else příkaz2;

v našem příkladu:


if(y>z) x=y;

else x=z;



5.6. Přednost zpracování výrazů

Obsahuje-li výraz několik operátorů, je třeba uvažovat prioritu (pořadí) zpracování výrazů a postup zpracování, zleva či zprava. Vše je přehledně uvedeno v následující tabulce, priorita 1 má nejvyšší úroveň zpracování:


Priorita

Operátor

Postup zpracování

1

( ) [ ] . ->

Zleva doprava

2

* ~ ! & ++ -- + - sizeof(type)

Zprava doleva

3

* / %

Zleva doprava

4

+ -

Zleva doprava

5

<< >>

Zleva doprava

6

< <= > >=

Zleva doprava

7

== !=

Zleva doprava

8

&

Zleva doprava

9

^

Zleva doprava

10

|

Zleva doprava

11

&&

Zleva doprava

12

||

Zleva doprava

13

?:

Zleva doprava

14

*= /= %= += -= <<= >>= &= |= ^=

Zprava doleva

15

,

Zleva doprava




















6. Řízení programu: if, else, while, switch, case, for


6.1. Příkazy jazyka C


<příkaz C>;

Příkaz jazyka C se skládá z výrazu (expression, statement), za nímž následuje středník (semi colon).


6.2. Blok { }

Blok začíná levou složenou závorkou { a končí pravou složenou závorkou }. Za ukončovací závorkou nenásleduje středník. Uvnitř bloku jsou umístěny deklarace a příkazy.

Navenek působí blok jako jediný příkaz, proto jej používáme na místech, kde nemůže být více než jeden příkaz, např. za if, else, for, while atd. Každá funkce obsahuje nejméně jeden blok, tzv. funkčí blok.

Na začátku bloku před prvním příkazem jazyka C je možná deklarace proměnných. Tyto proměnné zakrývají všechny stejnojmenné proměnné z bloků ležících nad nimi a jsou samotné viditelné jen uvnitř tohoto bloku a v hlouběji ležících blocích, nejsou-li v nich deklarovány stejnojmenné proměnné. Na tyto proměnné není přístup z vnějšku, z bloků ležících nad nimi.


6.3. Příkaz: if – else


if(testovací výraz) příkaz 1; else příkaz 2


Příkaz if-else slouží k vyhodnocení porovnávacího výrazu uzavřeného v kulatých závorkách. Je-li výsledek porovnání TRUE, provede se příkaz 1 nebo blok příkazů stojící na tomto místě (ve složených závorkách). Při výsledku FALSE se provede příkaz 2, který ale může i chybět, pak vypadá příkaz if – else takto:


if(testovací výraz) příkaz 1;


Hodnota testovacího výrazu TRUE je libovolná hodnota různá od nuly, FALSE je hodnota přesně nula! Následující dvě porovnání jsou identická:


if(x!=0) y = x*x; if(x) y = x*x;


Je možné použít několikanásobně vnořenou konstrukci if - else – if, viz příklad:


if(x!=0) y = x*x;

else if(z!=0) y = z*z;

else if(u!=0) y = u*u;

else y = 0;








6.4. Smyčky: while


Smyčky slouží k cyklickému provádění bloku příkazů, pokud testovací výraz dává hodnotu TRUE (nerovno nule). Rozlišují se dva druhy smyček while:



U první varianty se narozdíl od druhé nejprve vyhodnocuje <testovací_výraz> a potom se zpracovává blok <příkazy>. To znamená, že když toto první vyhodnocení dá hodnotu FALSE, blok příkazů se vůbec nezpracuje.

U druhé varianty (do … while) by se před testem jednou zpracoval blok příkazů <příkazy>. Teprve potom následuje vyhodnocení výrazu <testovací_výraz>.

Příkaz while se používá často pro vytvoření tzv. „nekonečné smyčky“, což je obvyklé právě u aplikací mikrokontrolérů, jejichž programy se cyklicky opakují. V těchto případech vypadá tvar příkazu následovně:


while(1)

{

příkazy C;

}


V závorce za while uvedená 1 namísto testovacího výrazu znamená vždy hodnotu TRUE.




6.5. Příkaz: switch-case


switch(výraz)

{ case Konstanta_1: <příkaz>; break;

case Konstanta_2: <příkaz>; break;


case Konstanta_N: <příkaz>; break;

default: <příkaz>; break;

}


Příkaz switch (přepínač) se používá pro mnohonásobné větvení programu. Příkaz zjišťuje hodnotu výrazu a porovnává ji se seznamem case, který k ní náleží. Při souhlasu s konstantou, která je umístěna mezi case a dvojtečkou se zpracuje příkaz mezi dvojtečkou a break. Pokud příkaz switch nezjistí shodu výrazu se žádnou konstantou uvedenou za case, provede se příkaz uvedený za default. Řádek s default příkazem nemusí být uveden, potom příkaz switch neprovádí nic, pokud nedošlo k žádné shodě mezi výrazem a konstantou.





Příklad:


switch (SPINACE)

{

case 255-0 : DISPLEJ = 0xfe;break; //rozsvit des.tecku

case 255-1 : DISPLEJ = 0xf3;break; //rozsvit "1"

case 255-2 : DISPLEJ = 0x49;break; //rozsvit "2"

case 255-3 : DISPLEJ = 0x61;break; //rozsvit "3"

case 255-4 : DISPLEJ = 0x33;break; //rozsvit "4"

case 255-5 : DISPLEJ = 0x25;break; //rozsvit "5"

case 255-6 : DISPLEJ = 0x05;break; //rozsvit "6"

case 255-7 : DISPLEJ = 0xf1;break; //rozsvit "7"

case 255-8 : DISPLEJ = 0x01;break; //rozsvit "8"

case 255-9 : DISPLEJ = 0x21;break; //rozsvit "9"

case 255-10 : DISPLEJ = 0x11;break; //rozsvit "a"

case 255-11 : DISPLEJ = 0x07;break; //rozsvit "b"

case 255-12 : DISPLEJ = 0x8d;break; //rozsvit "C"

case 255-13 : DISPLEJ = 0x43;break; //rozsvit "d"

case 255-14 : DISPLEJ = 0x0d;break; //rozsvit "E"

case 255-15 : DISPLEJ = 0x1d;break; //rozsvit "F"

default : DISPLEJ = 0xff;break; //zhasni displej

}



6.6. Příkaz: for


Příkaz for se používá k realizaci programových smyček se zadaným počtem opakování. Tvar příkazu for je následující:


for(výraz_start;výraz_stop;výraz _iterace)

{ …

příkazy C;

}


Příklad:


for(i=0; i<100; i++)

{

vytiskni(i); //vytiskne cisla od 0 do 99

// vytiskni () je funkce pro tisk

}


nebo:


for(i=10; i<100; i+=2)

{

vytiskni(i); //vytiskne suda cisla od 10 do 98

}


Tvar následujícího příkazu se používá často pro nekonečnou smyčku:


for( ; ; ;)

{ …

příkazy C;

}


Následující tvar příkazu se používá u mikrokontrolérů často pro vytvoření krátkého časového zpoždění, např. pro odstranění zákmitů spínačů:


for(i=0; i<100; i++);


Pozor!: Používat jen pro velmi malá zpoždění (řádově mikrosekundy), v době vykonávání této prázdné smyčky nemůže mikrokontrolér vykonávat nic jiného.



6.7. Nepodmíněné větvení programu: break, continue, goto


break

- je možno použít k přerušení smyčky switch-case, while, do-while nebo for. Po tomto příkazu opouští program okamžitě cyklus.


continue

-vyvolává uvnitř smyčky while, do-while nebo for zavedení následující iterace (opakování) tím, že skáče na konec smyčky, smyčku však neopouští.


goto

-tento příkaz se v dobře napsaných programech používá málokdy, vždy se mu lze vyhnout. Provádí nepodmíněný skok na libovolné místo v programu. Tyto „divoké skoky“ narušují strukturu programu, činí program nesrozumitelným, měly by se používat, jen pokud existují k tomu pádné důvody.


7. Definice, deklarace


Deklarace:

Pod pojmem deklarace se rozumí ohlášení, popř. popis vlastností proměnných nebo funkcí. U proměnných jsou to paměťová třída, atribut, typ a jméno a u funkcí jsou to paměťová třída, atribut a typ návratové hodnoty a předávaných parametrů a též jméno funkce. Každá proměnná nebo funkce musí být před prvním použitím v programu deklarována.

Zpravidla se lokální proměnné deklarují hned na začátku funkce a externí a globální proměnné na začátku zdrojového souboru C.


Příklady:


int i,j;

float x;

char Text[] = “Ahoj C“;

extern int k;


Interní funkce modulu je nejlépe deklarovat též na začátku souboru modulu, v němž jsou definovány a použity. Globální funkce, které mohou být používány i jinými moduly (soubory *.c) by měly být raději zaváděny ve speciálních deklaračních souborech *.h s deklaracemi funkcí.

Pomocí #include „jméno_souboru.h“ jsou zahrnuty i do těch modulů, v nichž jsou tyto funkce volány, takže i tam jsou tyto funkce známy.

Pro předávací parametry funkce stačí při deklaraci uvedení typu. Jméno parametru je možno na tomto místě vynechat, navadí však, je-li uvedeno. Naopak, někdy jména připomínají aplikaci a použití proměnné, takže by se měla ponechávat u deklarace.


Definice:

Jako definice je označována část programu, která pro tuto funkci tvoří kód C.

O definici proměnné hovoříme, jakmile je pro tuto proměnnou rezervováno paměťové místo. Definice vždy zahrnuje i deklaraci.


8. Direktivy preprocesoru





8.3. Makro: #define jmeno text_makra