Stručný úvod do programovacího jazyka C Tento text není kompletním popisem jazyka. Je určen těm, kteří jsou podrobně seznámeni s některým jiným programovacím jazykem a potřebují získat stručný přehled prvků jazyka C. Jazyk C existuje v několika verzích; nejrozšířenější je původní verze Kernighana a Ritchieho (K-R) a normalizovaná verze ANSI C. Prvky specifické pro ANSI C nebo rozšíření nad rámec této normy jsou vyznačeny menším písmem na žlutém pozadí; téměř všechny takové prvky jsou použitelné i v jazyce C++.
Nástupcem "Céčka" se stal jazyk C++, tím se však v tomto materiálu nebudeme zabývat.
Obsah • • • • • • • • • • •
Základní datové typy Konstanty Operátory a výrazy Řídicí struktury Lexikální prvky a struktura programu Pole a ukazatele Funkce Struktury, uniony, výčty a uživatelem definované typy Dynamická alokace paměti Vstup a výstup Standardní funkce
Základní datové typy Předtím než můžeme použít jakoukoli proměnnou, musíme specifikovat její typ čili ji deklarovat. Např. float xA,yA,p,q,r=5.0,epsilon=1e-6; double dd; int i,j; short int si; long int li; char ch='A';
V deklaraci může být proměnná současně inicializována, tj. můžeme jí přiřadit počáteční hodnotu. V uvedeném příkladu jsou inicializovány proměnné r, epsilon a ch. Typy float a double (v některých verzích jazyka i long double) se používají pro reálná čísla. Typ int a z něj odvozené typy short int a long int pracují s celými čísly. Poněkud překvapivě k celočíselným typům patří i char. Je sice primárně určen pro uložení jednoho textového znaku, může být však použit i pro malá celá čísla. Všechny celočíselné typy mohou být prefixem unsigned deklarovány jen pro čísla bez znaménka. Vnitřní zobrazení čísel v počítači (a tedy i jejich velikost v bitech) závisí na hardwaru a překladači. Obvykle (ne však povinně) je char uložen v jednom bajtu, short int ve 2 bajtech a long int ve 4 bajtech; int je buď dvoubajtový nebo 4bajtový.
Jazyk C nezná logický typ (boolean či logical). Místo něj lze použít kterýkoli celočíselný typ (z úsporných důvodů obvykle char) s tím, že nulová hodnota má význam false, jakákoli nenulová hodnota se interpretuje jako true.
Konstanty Celočíselné konstanty zapisujeme v přirozeném tvaru, např. 0 15 -1000. Podle velikosti jsou typu int nebo long int. Potřebujeme-li explicitně zapsat konstantu typu long int, zapíšeme za ni písmeno L, podobně písmeno U označuje konstantu typu unsigned int. Například: -99L 40000U. Celočíselné konstanty můžeme vyjadřovat i v osmičkové soustavě, přidáme-li před ně "nadbytečnou" nulu, nebo v soustavě šestnáctkové po znacích 0x. Následující konstanty jsou tudíž ekvivalentní: 30 036 0x1e. Reálné konstanty obsahují desetinnou tečku nebo dekadický exponent: 2.0 -0.33 1e-5; implicitně jsou typu double. Potřebujeme-li zapsat konstantu typu float, zapíšeme za ni písmeno f, např. 0.5f. Podobně přípona L určuje, že konstanta je typu long double. Znakové konstanty (typu char) zapisujeme do apostrofů: 'A' '+'. Některé speciální znaky lze kódovat tzv. escape sekvencí začínající obráceným lomítkem. Nejčastěji se setkáváme s kódováním znaku "nový řádek": '\n'; podobně musíme zakódovat i samotné obrácené lomítko jako '\\', apostrof jako '\'' a uvozovky jako '\"'. I když jsou k zápisu takové konstanty použity dva znaky, její hodnotou je znak jediný. Znaková konstanta obsahuje vždy jediný znak Nezaměňujte s řetězcovými (textovými) konstantami zapisovanými do uvozovek, o nichž bude řeč dále. Pomocí escape sekvence můžeme kódovat jakýkoli znak, napíšeme-li za obrácené lomítko jeho pořadové číslo v osmičkové soustavě. Písmeno 'A' lze tedy zapsat i jako '\101' Protože typ char je počítán mezi celočíselné, můžeme na místě znakové konstanty použít i celé číslo. Používá-li počítač kód ASCII, je konstanta 'A' ekvivalentní konstantě 65 apod. Konstanty lze definovat i symbolicky. Klasické C k tomu používá direktivu #define, např. #define PI 3.14159265 ANSI C dovoluje definovat konstantu pomocí klíčového slova const, např. const double pi=3.14159265;
Operátory a výrazy Přehled operátorů Jazyk C má velmi bohatý sortiment operátorů. Každý z nich se aplikuje na 1, 2 nebo 3 operandy (operátory unární, binární a ternární); operátory + - * a & se používají jako unární i binární (s různým významem). Operátory mají různou prioritu, operátory se stejnou prioritou se provádějí některé zleva doprava a některé zprava doleva, jak ukazuje následující tabulka:
Operátor
Význam
Nejvyšší priorita - zleva doprava: ()
Volání funkce
[]
Prvek pole
.
Výběr prvku struktury
->
Výběr prvku struktury zadané ukazatelem
Nižší priorita - ZPRAVA DOLEVA: !
Logická negace (viz dále)
~
Jednotkový doplněk (viz dále)
+
Potvrzení znaménka (unární plus)
-
Změna znaménka (unární minus)
++
Inkrementace (přičtení 1)
--
Dekrementace (odečtení 1)
&
Adresa (vytvoření ukazatele)
*
Dereference ukazatele
(typ)
Přetypování (konverze typu)
sizeof
Velikost objektu (počet bajtů)
Nižší priorita - zleva doprava: *
Násobení (binární *)
/
Dělení
%
Modulo (zbytek po celočíselném dělení)
Nižší priorita - zleva doprava: +
Sčítání
-
Odčítání
Nižší priorita - zleva doprava: <<
Bitový posun vlevo (viz dále)
>>
Bitový posun vpravo (viz dále)
Nižší priorita - zleva doprava: <
Menší než
<=
Menší nebo rovno
>
Větší než
>=
Větší nebo rovno
Nižší priorita - zleva doprava: ==
Test na rovnost
!=
Test na nerovnost
Nižší priorita - zleva doprava: &
Nižší priorita - zleva doprava:
Logický součin bit po bitu (and)
^
Logické vylučovací nebo bit po bitu (xor)
Nižší priorita - zleva doprava: |
Logický součet bit po bitu (or)
Nižší priorita - zleva doprava: &&
Logický součin (and) - viz dále
Nižší priorita - zleva doprava: ||
Logický součet (or) - viz dále
Nižší priorita - ZPRAVA DOLEVA: ?:
Podmíněný výraz - viz dále
Nižší priorita - ZPRAVA DOLEVA: = += -= *= /= %= <<= >>= &= ^= |=
Přiřazení - viz dále
Nejnižší priorita - zleva doprava: ,
Sériové provedení - viz dále
Přiřazování a inkrementace Běžné operátory mají stejný smysl jako v jiných jazycích, běžným způsobem se používají i závorky. Pozor však na to, že = je přiřazovací operátor a jako test na rovnost je nutno zapsat dvě rovnítka ==. V řadě případů je však používání výrazů v C unikátní. Některé příkazy mají vedlejší efekt, tedy jednak provedou nějakou akci, která mění okolí, a současně vracejí hodnotu, díky čemuž mohou být součástí složitějšího výrazu. Například následující řádek i=j=k=30
je korektní výraz. Protože operátory = se vyhodnocují (poněkud neobvykle) zprava doleva, chápe se tento výraz jako i=(j=(k=30))
a všem třem proměnným se přiřadí hodnota 30. Podobně i i=i+1
je tzv. přiřazovací výraz, který zvýší hodnotu i o jedničku. Zároveň však tento výraz má hodnotu, která je rovna nové hodnotě i. Má proto smysl psát např. j=5*(i=i+1)+2
Zvýšení hodnoty celočíselné proměnné o jedničku je možno provést i jinými výrazy: i+=1 i++ ++i
takže výše uvedený složitější výraz mohl být zapsán také např. ve tvaru
j=5*(++i)+2
Jak i++ tak ++i způsobují, že hodnota proměnné se zvýší o jedničku. Rozdíl je v tom, že ++i znamená napřed zvětšit i o jedničku a použít tuto novou hodnotu, zatímco i++ sice rovněž i zvětší, ale do dalšího vyhodnocení obklopujícího výrazu použije ještě starou hodnotu. Tedy pro provedení i=5; j=++i; m=5; n=m++;
bude mít i, j, m hodnotu 6, ale n hodnotu 5. Operátor -- znamená samozřejmě analogicky odečtení jedničky.
Výrazy versus příkazy Zapíšeme-li za přiřazovací výraz středník, stane se z něj přiřazovací příkaz, např. i=i+j; Podobně lze psát např. i++;
Středník opět udělal z výrazu příkaz. Poznamenejme, že středník má v jazyce C jiný význam než v Pascalu a řadě dalších jazyků. Neslouží k oddělení dvou příkazů od sebe, ale je integrální částí příkazu. Proto musí být zapsán i za posledním příkazem. Nepíše se však za příkazy řídicích struktur, leda jako součást vloženého příkazu - viz dále. Někdy bychom potřebovali provést několik akcí na místě, kde je syntaxí povolen jen jediný výraz. V tom případě použijeme operátor čárka (,). Mějme například cyklus: while (i+=j, j--, k=i+2*j, k>0) j*=2;
(Kombinované přiřazovací příkazy tvaru operátor= mají následující význam: j*=2 je zkráceným zápisem místo j=j*2, i+=j znamená i=i+j apod.) Jediný ternární operátor ?: je dalším neobvyklým, ale užitečným operátorem. Tzv. podmíněný výraz podmínka?výraz1:výraz2 se zpracuje tak, že se nejprve vyhodnotí podmínka. Je-li splněna (tj. má nenulovou hodnotu), je výsledkem výraz1; při nesplnění (nulová hodnota) vrací podmíněný výraz hodnotu výraz2. Tedy místo if (x>y) max=x; else max=y;
lze psát kompaktněji
max=(x>y ? x : y);
Logické výrazy Začátečníci si často pletou logické a bitově orientované operátory. Pravé logické operátory spojují logické výrazy (připomeňme, že celočíselná hodnota 0 v logických výrazech znamená false a "nenula" true). Tedy (x<0 || y>0 && y<1) && !(z<0)
znamená "x je záporné nebo y je mezi 0 a 1, a současně z není záporné".
Při použití logických operátorů || a && překladač zajišťuje, že druhý operand se vyčísluje pouze tehdy, jestliže první operand ještě neurčuje jednoznačně výsledek. Nehrozí tedy nebezpečí, že by ve výrazu i>0 && j/i>k
mohlo dojít k dělení nulou. Výsledek logických výrazů je vždy 1 (true) nebo 0 (false). Něco jiného jsou operátory pracující s čísly bit po bitu. Zde se musíme na číslo díval prostě jako na kombinaci bitů, i když formálně jsou čísla deklarovaná jako některý z celočíselných typů. Například: i=5; j=i<<2; k=i|j;
Zde jsou použity operátory bitového posunu vlevo a nebo po bitech. Ve dvojkové soustavě proto platí: i = 0...000101 (=5) j = 0...010100 (=20) k = 0...010101 (=21)
Přetypování V mnoha případech jazyk C podle potřeby automaticky konvertuje veličiny jednoho typu na jiný typ: int i,j; float x; double z; i = 1; x=2*i; /* Násobí se jako celá čísla, výsledek se převede na 2.0f (typu float) */ z=x*x; /* z bude 4.0 (typu double) */ x=z/3; /* x je teď 1.333333f (typu float, tedy s menší přesností) */ j=x; /* j bude 1 (typu int) */
Občas je však nutné provést změnu typu (přetypování) explicitně. Například výsledek následujícího výpočtu začátečníka překvapí:
int i=1; double z; z=i/3; /* Dělí se celé číslo celým, výsledek dělení je proto 0 (typu int). Teprve před uložením do z se zkonvertuje na 0.0 (typu double) */
Chceme-li, aby výsledek byl 1.333...3, musí být alespoň jeden operand dělení typu double. Proto buď dělíme reálnou trojkou: z=i/3.0;
nebo na i aplikujeme operátor přetypování: z=(double)i/3;
Přetypování obvykle mění vnitřní zobrazení veličiny tak, aby hodnota byla co nejbližší původní hodnotě (až na potřebné zaokrouhlení). Jsou však i případy, kdy přetypování nemůže zachovat hodnotu (například přetypování čísla na ukazatel). V takovém případě je výsledkem tatáž kombinace bitů, ovšem se zcela jinou interpretací. Přetypování může být nutné i ve spojení s operátorem zbytku po dělení %, který může být aplikován pouze na celočíselné operandy. Místo int i; double d=37.9; i=d%2;
musíme proto psát int i; double d=37.9; i=(int)d%2;
Operátor přetypování má vyšší prioritu než %, takže přetypování se týká pouze proměnné d. Proměnná i bude mít hodnotu 2. Přiřazuje-li se reálná hodnota celočíselné proměnné, desetinná místa se useknou (bez zaokrouhlení). Stejně funguje i přetypování.
Operátor sizeof Tento operátor se používá dvojím způsobem. Operandem je buď datový objekt (např. proměnná nebo pole), nebo název typu uzavřený do závorek. Výsledkem je délka příslušného objektu v bajtech. Příklad: sizeof i sizeof(unsigned long int)
Řídicí struktury Příkaz
if
Podívejme se na následující úsek programu: if (x>=0.5) {i=10; j=20;} else {k=30; l=40;} if (u<3.0) {v=1.8; w=3.4;} if (a
Význam těchto podmíněných příkazů je intuitivně zřejmý. Všimněte si, že if je vždy následováno výrazem v závorkách (podmínkou). Část else je nepovinná. Za podmínkou následuje příkaz, který se má provést, je-li podmínka splněna; za else příkaz, který se provede při nesplnění podmínky. Část else je nepovinná. Pokud bychom druhý z uvedených příkazů zapsali bez složených závorek jako if (u<3.0) v=1.8; w=3.4;
závisel by na splnění podmínky pouze příkaz v=1.8; (a w=3.4; by se provedlo vždy). Složené závorky totiž umožňují zapsat více příkazů na místo, kam syntakticky patří příkaz jediný. { } je tedy céčkovským ekvivalentem pascalského begin end.
Cykly Mějme spočítat součet přirozených čísel od 1 do n. Mohli bychom to provést následujícími příkazy: s=0; i=1; znovu: if (i<=n) {s+=i; i++; goto znovu;}
(Připomeňme, že s+=i a i++ jsou zkrácené zápisy pro s=s+i a i=i+1.) Z hlediska strukturovaného programování je však vhodnější pro cyklus použít některý z příkazů specificky určených pro programování cyklů: Příkaz while s=0; i=1; while (i<=n) {s+=i; i++;}
Příkaz while testuje podmínku v závorkách a dokud je splněna, opakovaně provádí příkaz za závorkami. Příkaz do s=0; i=1; do {s+=i; i++;} while (i<=n);
Příkaz do while testuje podmínku až po provedení příkazu; pokud podmínka už na začátku není splněna, provede se tělo cyklu jedenkrát. Příkaz for s=0; for (i=1;i<=n;i++) s+=i;
V závorce za slovem for jsou 3 výrazy oddělené středníky. První se provede pouze jednou na začátku (inicializace cyklu). Pak se opakovaně provádí cyklus sestávající ze 3 kroků: • •
Testuje se podmínka (mezi oběma středníky). Je-li splněna, provede se příkaz za uzavírající závorkou (tělo cyklu).
•
Provede se výraz za druhým středníkem.
Tento postup se opakuje, dokud se podmínka nestane nesplněnou. S využitím operátoru čárka je možno do kterékoli části v závorce "nacpat" několik výrazů. Výše uvedený příkaz by mohl být zapsán také takto: for (i=1,s=0;i<=n;i++) s+=i;
Kterákoli z částí v závorce (až na středníky) může chybět. Chybějí-li všechny, jedná se o "nekonečný" cyklus: for (;;) příkaz;
Takový cyklus musí být ukončen v rámci příkazu v těle cyklu. Předčasné ukončení cyklu Z cyklu lze kdykoli vyskočit příkazem goto nebo lépe příkazem break. Podobně příkazem continue skočíme na konec těla cyklu, tj. na testování podmínky.
Přepínač Ke složitějšímu větvení výpočtu lze s výhodou použít konstrukci s příkazem switch: switch (n) { case 1: a+=b; break; case 2: case 3: a*=b; break; default: puts("Chybné n"); exit(0); }
Tento úsek programu zvětší hodnotu a o b, pokud n=1; vynásobí ji b, pokud n=2 nebo 3, ohlásí chybu a ukončí celý program při jiné hodnotě n. Výraz, podle něhož se rozhoduje (zde n) musí být celočíselný; za slovem case rovněž musí být celočíselná konstanta (připomeňme, že char a znakové konstanty se rovněž považují za celočíselné). Všimněte si, že jednotlivé větve (case) jsou ukončeny příkazem break. Pokud tento příkaz vynecháme, bude se po provedené jedné větve pokračovat ještě následující větví (případ prázdné větve case 2 výše).
Lexikální prvky a struktura programu Komentáře Mezery a dělení programu do řádků nemají ve většině případů na smysl programu vliv. Stejně tak překladač ignoruje i komentáře, které se zapisují ve tvaru /* Komentář */
/* Dlouhý komentář zapsaný na více řádcích */ Některé verze jazyka C dovolují podle vzoru jazyka C++ zapisovat i komentáře ve tvaru // Komentář Tyto komentáře končí vždy koncem řádku (je však možno jich zapsat více za sebou, pokud na každém řádku zopakujeme úvodní //).
Identifikátory Identifikátory (například názvy proměnných) se skládají z písmen a číslic, začínat musí vždy písmenem. Malá a velká písmena se rozlišují, takže index Index INDEX jsou 3 různé názvy. Podtržítko _ je považováno rovněž za písmeno. Nedoporučuje se, aby programátor používal názvy začínající podtržítkem - mnoho takových názvů je definováno překladačem a mají proto zvláštní význam.
Direktivy Na začátku překladu se program zpracuje tzv. preprocesorem. Příkazy pro preprocesor (zvané též direktivy) začínají znakem #; nejčastěji se setkáváme s direktivami #include a #define. Teprve text zpracovaný preprocesorem se překládá. Direktiva #define Direktiva #define definuje makro. Pokud na začátek programu napíšeme #define MAXIMUM 1000
nahradí se každý výskyt identifikátoru MAXIMUM číslovkou 1000. Poznamenejme, že je zvykem názvy maker psát velkými písmeny. Povšimněte si, že na rozdíl od příkazů jazyka C se za direktivami nedělá středník; pokud bychom jej zde napsali, stal by se součástí nahrazujícího textu. Zajímavé možnosti poskytují makra s parametry. Definujeme-li např. makro #define MAX(x,y) x>y?x:y
bude řetězec MAX(a-b,5)
nahrazen řetězcem a-b>5?a-b:5
Makra se nahrazují zcela mechanicky, což může vést k nepříjemným důsledkům. Definujme např. makro: #define KVADRAT(r) r*r
Příkaz
w=1.5/KVADRAT(d-5);
se rozvine do tvaru w=1.5/d-5*d-5;
což jistě není to, co měl programátor v úmyslu. Takovým nepříjemným překvapením většinou zabráníme tím, že každý výskyt parametru v rozvoji makra uzávorkujeme a celý nahrazující text dáme opět do závorek: #define KVADRAT(r) ((r)*(r))
Direktiva #include Direktiva #include vloží do programu soubor, jehož název bezprostředně následuje. Prakticky každý program musí jednu nebo více direktiv #include obsahovat. Pokud například program obsahuje vstup či výstup (což je asi vždy), musíme na jeho začátek zapsat #include <stdio.h>
Soubory s příponou .h jsou tzv. hlavičkové soubory. Obsahují zejména informace o standardních funkcích jazyka C, makra definující některé konstanty apod. Kromě zmíněného souboru stdio.h se nejčastěji setkáváme s následujícími hlavičkovými soubory: stdlib.h standardní knihovna funkcí jazyka C string.h prostředky pro manipulaci s textovými řetězci math.h matematické funkce a konstanty
Direktivou #include můžeme do programu vložit i vlastní soubory (nemusejí ani mít příponu .h). Název vlastního souboru však zapisujeme do uvozovek, např. #include "spolecna_cast". Ve vkládaném souboru mohou byt obsaženy další direktivy (včetně #include). Další direktivy Pomocí dalších direktiv (#ifdef #ifndef #if #else #elseif #endif) můžeme vázat překlad části zdrojového textu na splnění nějaké podmínky. Například v následujícím fragmentu se příkazy printf přeloží pouze tehdy, pokud v předchozí části programu bylo definováno makro s názvem TISKY (bez ohledu na to, jakým textem je nahrazováno, ten může být i prázdný): #ifdef TISKY printf("Mezivýsledky:\n"); printf("Iterace č. %d x=%f r=%f\n",i,x,r); #endif
Celková skladba programu Program sestává z jedné nebo více funkcí. Jazyk C nerozlišuje funkce a procedury, procedura v pascalském smyslu je v C prostě funkce, která nevrací žádnou hodnotu. Dokonce i hlavní program je funkce, která má povinně název main. Funkce může, ale nemusí mít parametry.
Pokud je bez parametrů, je přesto nutné za jejím názvem psát závorky. Jako příklad uveďme úplný program, který přečte souřadnice dvou bodů (P, Q) a spočítá jejich vzdálenost. /* Tento program počítá a tiskne vzdálenost mezi dvěma body */ #include <stdio.h> #include <math.h> void print_distance(x1, y1, x2, y2) float x1, y1, x2, y2; { float delta_x, delta_y, distance; delta_x = x2 - x1; delta_y = y2 - y1; distance = sqrt(delta_x*delta_x+delta_y*delta_y); printf("Vzdálenost: %f\n", distance); } main() { float xP, yP, xQ, yQ; printf("Zadej xP, yP, xQ, yQ:"); scanf("%f %f %f %f", &xP, &yP, &xQ, &yQ); print_distance(xP, yP, xQ, yQ); } Uvedený program je napsán v klasickém C podle Kernighana a Ritchieho. V ANSI C by bylo vhodnější první řádky obou funkcí napsat ve tvaru: void main(void) void print_distance(float x1, float y1, float x2, float y2)
V programu jsou použity standardní funkce printf (tisk), scanf (čtení dat) a sqrt (odmocnina). Funkce pro vstup a výstup a matematické funkce jsou vysvětleny dále.
Alokace paměti Běžným proměnným je přidělena paměť na začátku funkce, kde jsou deklarovány; při ukončení funkce je paměť uvolněna a hodnota proměnné se ztrácí. Tomu říkáme automatická alokace paměti. Pokud chceme, aby paměť byla alokována po celou dobu práce programu (staticky), přidáme na začátek deklarace slovo static, např. static int pocitadlo;
V tom případě je na začátku programu proměnná automaticky vynulována (jinak by její hodnota nebyla definována) a při opětovném vyvolání funkce má proměnná svou předešlou hodnotu. Proměnné je možno deklarovat i mimo funkce (např. na začátku programu). Takové proměnné jsou vždy statické (i bez klíčového slova static) a nazýváme je globální. Jsou dostupné v celém programu. Kromě automatické a statické alokace existuje v jazyce C ještě třetí způsob alokace - alokace dynamická neboli řízená. O ní bude řeč později.
Pole a ukazatele Pole a indexy
Po deklaraci pole příkazem float a[5];
je k dispozici následujících 5 proměnných typu float: a[0], a[1], a[2], a[3], a[4]
Můžeme také psát a[i], kde index můžete být libovolný celočíselný výraz, jehož hodnota není záporná nebo větší než 4. Indexy se vždy počítají od nuly. Při deklaraci pole se v hranatých závorkách smí objevit pouze celočíselná konstanta. Místo čísel často používáme konstanty pojmenované prostřednictvím makra, jako v následujícím příkladu. Příklad rovněž dokumentuje, že pole mohou mít více než jeden index: #define RADKY 10 #define SLOUPCE 8 ... int table[RADKY][SLOUPCE]; ... for(i=0; i
Povšimněte si toho, že každý index vícerozměrného pole se píše do vlastních závorek, zápis [i,j] obvyklý v jiných programovacích jazycích nelze v C použít.
Ukazatele Je-li v proměnná, pak &v je adresa této proměnné, nebo odborně řečeno ukazatel (pointer) na tuto proměnnou. Naopak, je-li p ukazatel na nějaký objekt, je objekt sám možno získat zápisem *p (operátor & tedy vytváří z objektu ukazatel, operátor * z ukazatele objekt). Následující program ukazuje, jak mohou být deklarovány a použity proměnné typu ukazatel: main() { int i, *p; i=123; p=&i; *p=789; }
Toto není zářný vzor, jak by se v C mělo programovat, ale ukázka ilustruje, že přístup k proměnné lze uskutečnit také bez použití jejího názvu. Zde p je ukazatel na i, takže *p je ekvivalentní i. To znamená, že hodnota proměnné i bude nakonec 789 místo původního 123.
Vztah polí a ukazatelů Název pole (bez následujícího indexu) funguje jako ukazatel na jeho první prvek. Místo &A[0] můžeme proto psát prostě A. Máme-li ukazatel na nějaký prvek pole a přičteme k němu 1, dostaneme ukazatel na následující prvek téhož pole bez ohledu na to, kolik bajtů zabírá jeden prvek v paměti. Místo &A[i] můžeme proto psát A+i. Naopak odečtením dvou
ukazatelů na prvky téhož pole dostaneme celé číslo udávající, o kolik prvků (nikoli bajtů) se ukazatele liší. Pole mohou být podobně jako jednoduché proměnné inicializována (tj. může jim být přiřazena počáteční hodnota). Platí to však jen o globálních a statických polích, na rozdíl od jednoduchých proměnných automaticky alokovaná pole inicializovat nelze. V následujícím programu jsou inicializována pole X a Y; program vytiskne hodnotu 5.75: #include <stdio.h> float X[4] = {6.0, 6.0, 5.9, 6.1}, Y[4] = {-0.25, 0.25, 0.0, 0.0}; main() { printf("%f\n",X[0]+Y[0]); }
V uvedeném příkladu byla použita globální pole. Úplně stejně by pracoval i následující program používající statická pole uvnitř funkce main: #include <stdio.h> main() { static float X[4] = {6.0, 6.0, 5.9, 6.1}, Y[4] = {-0.25, 0.25, 0.0, 0.0}; printf("%f\n",X[0]+Y[0]); }
Textové řetězce Speciálním případem pole je textový řetězec. Řetězce deklarujeme jako pole typu char (ať už unsigned char nebo signed char). I když s jednotlivými znaky můžeme pracovat běžným způsobem jako s prvky pole, přesto se textové řetězce od jiných typů polí v několika věcech liší: •
•
•
•
Textový řetězec může být kratší než je délka pole. Konec textu se označuje tím, že za jeho poslední znak ze zapíše speciální koncový znak, jehož numerická hodnota je 0 čili znak '\0'. Existují textové konstanty, které se zapisují podstatně jednodušeji, než výše uvedené konstrukce definující počáteční hodnoty polí X a Y. Textové konstanty zapíšeme prostě do uvozovek. Taková konstanta už automaticky obsahuje i závěrečný nulový znak. Jazyk C obsahuje řadu standardních funkcí pro manipulaci s textovými řetězci; tyto funkce zpřístupní hlavičkový soubor <string.h>. Nejčastěji se setkáváme s funkcí strcpy(a,b) kopírující řetězec b do řetězce a, a s funkcí strlen(a), která vrací počet znaků v řetězci (bez započítání závěrečné nuly). Přehled nejdůležitějších funkcí je uveden na konci tohoto pojednání. Textový řetězec můžeme číst a tisknout jako celek použijeme-li ve formátovacím řetězci %s.
Uveďme několik příkladů: #include <string.h> char txt[30] /* Může obsahovat maximálně 29 znaků */ strcpy(txt, "Fakulta informatiky");
txt[7]=0; /* Řetězec se zkrátí na prvních 7 znaků */ printf("Text \"%s\" ma delku %d znaku.\n",txt,strlen(txt);
Poslední příkaz vytiskne řádek: Text "Fakulta" ma delku 7 znaku.
Funkce Jak již bylo řečeno dříve, funkce může (ale nemusí) vracet hodnotu a může (ale nemusí) mít parametry. Pokud má hodnotu, vrací ji volající funkci (nebo operačnímu systému v případě funkce main) příkaz return. Příkaz return současně ukončuje práci funkce. Následující funkce například vrací hodnotu 1 nebo -1 podle znaménka svého parametru: int f(x) float x; /* resp. int f(float x) v ANSI C */ { if (x<0) return -1; else return 1; }
Mimochodem, s použitím podmíněného výrazu můžeme zapsat tělo funkce úsporněji (hlavička zůstane stejná): { return x<0?-1:1; }
Všimněte si, jak jsme vyjádřili, že funkce má argument typu float a vrací hodnotu typu int. V ANSI C u funkce, která hodnotu nevrací, uvádíme jako typ návratové hodnoty void, void uvádíme rovněž místo parametrů u funkce, která parametry nemá. Není-li specifikováno, hodnotu kterého typu funkce vrací, pak vrací int. Nedoporučuje se však na to spoléhat - explicitní deklarace návratového typu přispívá ke srozumitelnosti programu, a navíc zmíněné pravidlo neplatí v jazyce C++. Je na programátorovi, aby funkci volal se správným počtem a typem parametrů. Bylo by chybou volat naši funkci f jako f(5), tj. s parametrem int, nebo dokonce bez parametru jako f(). Je-li však hlavička funkce je zapsána podle ANSI C, překladač odpovídající této normě správnost typu parametru zkontroluje a pokud je to možné, zajistí potřebné přetypování.
Pokud funkce nevrací hodnotu, nemusí obsahovat příkaz return. Použít tento příkaz však i v tomto případě můžeme (bez následujícího výrazu určujícího návratovou hodnotu), například tehdy, potřebujeme-li provádění funkce ukončit jinde než na jejím fyzickém konci). Funkce může používat hodnoty svých parametrů, nemůže je však v žádném případě změnit. Předpokládejme, že máme následující program: #include <stdio.h> fce(i) int i; { i=999; printf("%d ",i); } main() { int k=1; fce(k); printf("%d\n",k); }
Program vytiskne 999 1, tedy hodnota parametru i uvnitř funkce se sice změní, ale s ním svázaná proměnná k mimo funkci zůstane nezměněna. Někdy však potřebujeme vytvořit funkci, která by hodnotu některého parametru změnila (typickým případem je třeba funkce pro čtení dat). V takovém případě jako parametr
použijeme místo příslušné proměnné ukazatel na ni. Následující funkce vymění navzájem hodnoty svých parametrů: vymen(p,q) int *p, *q; /* V ANSI { int pom; pom = *p; *p = *q; *q = pom; }
C raději: vymen(int *p, int *q) */
Po provedení příkazů i = 1; j = 2; vymen(&i, &j);
bude mít i hodnotu 2 a j hodnotu 1. Jak to jde dohromady s uvedeným tvrzením, že funkce nemůže změnit hodnotu svého parametru? Docela dobře, parametrem jsou ukazatele, a jejich hodnota se opravdu nezměnila! Změnil se pouze obsah proměnných, na něž ukazují. Stejnou funkcí můžeme vyměnit i obsah prvků pole, stačí napsat např. vymen(A+k, A+m);
Připomeňme si, že A+k je totéž, co &A[k]. Jestliže je funkce použita dříve, než je definována, je vhodné (někdy i nutné) ji před použitím deklarovat. V klasickém C je možno deklarovat pouze typ vracené hodnoty, např. float prepona(); V ANSI C je však zvykem v deklaraci uvádět i typy parametrů, např. float prepona(float, float); popř. float prepona(float x, float y); U deklarace mají názvy parametrů pouze význam komentáře, takže oba uvedené zápisy jsou ekvivalentní.
Poznamenejme ještě, že taková deklarace se zapisuje do funkce nebo zdrojového programu, z níž se taková funkce volá, v citovaném příkladě tedy do funkce main() společně s deklaracemi proměnných xP, yP atd. V rozsáhlejších programech se takové deklarace zapisují do hlavičkového souboru, a ten se vkopíruje direktivou #include do všech volajících funkcí. Deklarovaná funkce se samozřejmě musí někde také definovat, tedy zapsat její tělo (jinak by nebylo jasné, co má funkce vlastně dělat). Záhlaví definice je podobné deklaraci až na dvě odchylky: • •
v definici nesmí být za záhlavím středník názvy parametrů se nemohou v definici vynechat, aby se ně mohlo tělo funkce odvolávat.
Za záhlavím definice potom následuje tělo funkce, obvykle zapsané do složených závorek. main
jakožto funkce volaná operačním systémem
I funkce main vrací hodnotu (typu int) a může být volána s parametry. Parametry jí předává a návratovou hodnotu přebírá shell operačního systému. Pokud přeložený program vyvoláme z příkazového řádku, můžeme mu zadat parametry podobně jako u kteréhokoli jiného příkazu. (Parametry často obsahují názvy souborů, avšak není to podmínkou.) Pokud funkci main definujeme jako funkci s parametry, může hodnoty z příkazového řádku použít. Parametry funkce main nemohou mít libovolný počet a typ. První parametr je typu int, v programu se mu dává obvykle název argc (argument count). Hodnotou tohoto parametru je číslo o jedničku větší než počet parametrů zapsaných v příkazovém řádku (pokud je program volán bez parametrů, je tedy argc rovno jedné). Druhý parametr (obvykle nazývaný argv - argument values) je typu char **, je tvořen polem ukazatelů na textové řetězce. Každým prvkem tohoto pole je tedy ukazatel na char čili textový řetězec. Prvním z těchto řetězců, tj. argv[0] je název programu (tak, jak byl na příkazovém řádku napsán), dalšími řetězci argv[1] až argv[argc-1] jsou jednotlivé parametry. Program nemůže hodnoty parametrů změnit. Hodnota, kterou main vrací, musí být typu int. Je zvykem, že pokud program pracoval bez problémů, vrací nulu, v případě nějaké chyby vrací číslo, na základě něhož může shell (skript či dávkový soubor) na chybu reagovat. Záhlaví funkce main tedy může být například (pokud se funkce nezajímá o případné parametry): int main() - v ANSI C int main(void)
nebo (pokud používá parametry): int main() int argc; char **argv; - v ANSI C int main(int argc, char **argv)
Struktury, uniony, výčty a uživatelem definované typy Struktury Několik proměnných může být seskupeno do tzv. struktury, tj. celku vyššího řádu. Předpokládejme, že chceme ukládat informace o bodech v rovině. Pro každý bod se informace skládá ze souřadnic x, y a z údaje o barvě. Údaje o jednom bodu je z více důvodů vhodné seskupit dohromady. Předpokládejme, že chceme definovat 2 takové body P a Q. Můžeme deklarovat struct {float x,y; int color;} P, Q;
S jednotlivými proměnnými objektu definovaného jako struktura pracujeme pomocí operátoru "tečka", např. P.x = 1.5; P.y = -0.8; P.color = 45; Q.x = 2*P.x;
apod. Každé struktuře můžeme dát název, mohli jsme například napsat:
struct point {float x,y; int color;} P, Q;
Povšimněme si, že uvedená deklarace vlastně plní dva účely: • •
popisuje, z čeho se struktura skládá (případně jí dává název) deklaruje dva objekty P a Q
Tyto dvě akce můžeme od sebe oddělit. Mohli jsme nejprve definovat složení struktury a potom deklarovat dva objekty s danou strukturou: struct point {float x,y; int color;}; struct point P, Q;
Povšimněte si, že v tomto případě struktuře dát název musíme, abychom se na něj mohli později odvolat.
Struktury a ukazatele Definujeme-li ukazatel na strukturu, např. Point *p
a zařídíme, aby ukazoval na bod P: p = &Q;
můžeme se na x-vou souřadnici odkázat běžným zápisem: (*p).x
(Závorky jsou zde nutné, protože operátor . má vyšší prioritu než * a my potřebujeme nejprve dereferencovat ukazatel a teprve potom vybrat součást struktury.) Protože s částmi struktury, na niž ukazuje ukazatel, se v praxi pracuje velmi často, je pro tuto operaci vyhrazen zvláštní operátor: místo (*p).x můžeme psát stručněji p->x
Uniony Union se velmi podobá struktuře, avšak místo toho, aby jeho komponenty následovaly za sebou, překrývají se. Samozřejmě to znamená, že pouze jedna ze složek unionu může mít v jednom okamžiku přiřazenou hodnotu. Příklad: union {int nr; char op; double beta;} u1; u1.nr = 8; u1.op = '-'; /* u1.nr ztratilo svou hodnotu */
Výčty (jen v ANSI C) Proměnná výčtového typu se podobá celočíselné proměnné, jejíž jednotlivé hodnoty dostávají mnemonický název. Např. enum r_obdobi { jaro, leto, podzim, zima } obdobi,*p_obd;
Lze oddělit deklaraci typu od deklarace proměnných: enum r_obdobi { jaro, leto, podzim, zima }; enum r_obdobi obdobi,*p_obd; Implicitně jaro==0 leto==1 atd. Lze však změnit: enum r_obdobi { jaro=0, leto=1, podzim=0, zima=-1 }; Kombinace výčtových a celočíselných veličin: možno int i=podzim; nelze obdobi=1;
Uživatelské typy Ještě pohodlnější pro programování je deklarovat pro strukturu popisující bod zvláštní uživatelský datový typ: typedef struct {float x,y; int color;} Point;
S typem Point můřeme dále zacházet podobně jako se standardními typy. Naše dva body můžeme deklarovat příkazem: Point P, Q;
a podobně můžeme deklarovat celé pole bodů: Point A[1000];
Každý prvek pole A je teď struktura; A[i].color je barva i-tého bodu. Uživatelské typy lze ovšem vytvářet nejenom ze struktur. Mnoho programátorů například používá "zkrácené" názvy standardních typů: typedef unsigned long int ULI;
nebo dává témuž typu různé názvy podle použití (například pro logické proměnné zavede pro lepší srozumitelnost uživatelský typ): typedef char bool;
Dynamická alokace paměti Předpokládejme, že bychom potřebovali pracovat s polem, jehož velikost bude známa až za běhu programu (nebo je sice známa předem, ale pole je zapotřebí pouze po krátkou dobu a nechceme zbytečně plýtvat pamětí v době, kdy to není nutné). Pro takové účely slouží dynamická alokace paměti. Provádí se standardní funkcí malloc. Příklad: #include <stdlib> char *p; p=malloc(n);
kde n je celočíselný výraz udávající počet alokovaných bajtů. Funkce malloc vyhradí potřebný souvislý úsek paměti a vrátí ukazatel na jeho začátek. S jednotlivými znaky můžeme
potom pracovat běžným způsobem pomocí indexu. Alokace paměti nemusí být úspěšná (např. jsme požadovali příliš velký úsek), v tom případě funkce malloc vrátí speciální "nulový" ukazatel NULL. Po každém pokusu o alokaci paměti musíme proto testovat, zda nebyla vrácena hodnota NULL. V novějších verzích jazyka C (a samozřejmě v ANSI C) vrací funkce malloc ukazatel na nespecifikovaný typ, tj. void *, musíme potom použít přetypování: p=(char *)malloc(n); V ANSI C se toto přetypování sice provede automaticky, i tak je však pro srozumitelnost vhodné zapsat je explicitně.
Když alokovanou paměť přestaneme potřebovat, uvolníme ji funkcí free; jako parametr jí předáme ukazatel, který předtím vrátila funkce malloc. Může se někdy stát, že se během výpočtu ukáže, že jsme alokovali příliš malý (nebo naopak zbytečně velký) úsek paměti. V tom případě můžeme jeho velikost změnit funkcí realloc(původní_ukazatel, nová_velikost), např. p = realloc(p, 2000);
Pokud p nenabude hodnoty NULL, bude nyní p ukazovat na paměťový blok nově požadované délky. V rámci společné délky původního a nového bloku bude tento blok mít stejný obsah jako původní. Pomocí malloc můžeme alokovat prostor pro data libovolného typu. Následujícím příkazem například alokujeme paměť pro 64 objektů typu Point deklarovaného výše: Point *bod bod = (Point *)malloc(64*sizeof(Point));
Barvu posledního bodu můžeme potom změnit příkazem: bod[63].color=5;
Vstup a výstup Součástí C nejsou zvláštní jazykové konstrukce pro vstup a výstup. Místo toho používáme množství funkcí ve spojení s předdefinovaným typem struktury nazvaným FILE. Detaily s tím spojené zahrneme do programu pomocí hlavičkového souboru: #include <stdio.h>
Soubor stdio.h obsahuje deklaraci typedef ve tvaru typedef struct { ... } FILE;
Napíšeme-li do programu FILE *fp;
proměnná fp je ukazatel na FILE a kompilátor zná detaily struktury tohoto typu. Pro konkrétní soubor je tato struktura přístupná pomocí ukazatele fp poté, co soubor otevřeme příkazem: fp = fopen(název_souboru, režim);
Název_souboru může být zadán včetně cesty, jako režim uvedeme "r" (chceme-li soubor číst) nebo "w" (chceme-li do něj zapisovat). Otevřený soubor na konci práce zavřeme funkcí fclose.
Standardní zařízení Existují dva "soubory", s nimiž se pracuje jednodušším způsobem. Není nutno je otevírat a zavírat, a pro práci s nimi se používají odlišné funkce. Těmto souborům se říká standardní vstup stdin (obvykle čte z klávesnice) a standardní výstup stdout (obvykle píše na monitor). stdin a stdout jsou rovněž ukazatele na strukturu FILE, a můžeme je proto uvést na místě prvního parametru v příkaze fprintf a dalších. Výhodnější však je pro standardní vstup a výstup používat funkce bez počátečního písmene f.
Výstup dat Pro výstup do diskového souboru používáme funkci fprintf volanou ve tvaru fprintf(fp, formátovací_řetězec, výrazy)
pro standardní výstup podobnou funkci printf (té chybí první parametr fp, protože je už názvem dáno, kam má výstup směrovat). printf(formátovací_řetězec, výrazy)
Počet výrazů následujících za formátovacím řetězcem musí být v souladu s obsahem formátovacího řetězce. Pokud tedy formátovací řetězec neobsahuje žádnou formátovací položku, nenásleduje za ním už nic. Programátor musí rovněž dbát na to, aby typ každého výrazu odpovídal příslušné formátovací položce; zde se žádná automatická konverze neprovádí. Následující příklad ukazuje, jak můžeme zapisovat výsledky do souboru EXAMPLE. Pokud takový soubor neexistuje, vytvoří se. #include <stdio.h> main(); { FILE *out; int i; out = fopen("EXAMPLE", "w"); for (i=1; i<=4; i++) fprintf(out,"i=%1d i*i=%2d\n", i, i*i); fclose(out); }
Po provedení tohoto programu bude soubor EXAMPLE obsahovat:
i=1 i=2 i=3 i=4
i*i= 1 i*i= 4 i*i= 9 i*i=16
Formátovací řetězce První parametr funkce printf (druhý parametr fprintf) je tzv. formátovací řetězec. Ten se skládá z úseků textu, které se mají objevit ve výstupu, a z formátovacích položek, které přikazují do výstupu vložit hodnoty určitých proměnných nebo výrazů. Každá formátovací položka začíná znakem % a končí písmenem, které musí odpovídat typu vypisované hodnoty: Písmeno
Typ
Způsob výpisu dekadicky
d int
o
oktalově
x
hexadecimálně
f
běžný tvar float nebo double
e g c
mantisa e exponent podle hodnoty jako f nebo jako e
Poznámka Pro long int se před formátovací písmeno přidá l, pro short int se přidá h
Pro long double se před formátovací písmeno přidá L; písmena f e g slouží pro float i double
char char * (tj.
textový řetězec)
s
proměnný počet znaků podle délky řetězce
Mezi znak % a formátovací písmeno lze ještě vložit číslo udávající počet míst, resp. číslo.číslo, kde číslo za tečkou udává počet desetinných míst.
Vstup dat Čtení dat se provádí podobně jako jejich výstup. Čteme-li z diskového souboru, použijeme funkci fscanf(fp, formátovací_řetězec, ukazatele)
Pro standardní vstup je podobná funkce scanf(formátovací_řetězec, ukazatele)
Formátovací řetězec je podobný jako u výstupu, obsahuje však obvykle pouze formátovací položky bez dalšího textu. Avšak pozor: • •
Pro čtení hodnoty typu double se používá (na rozdíl od printf) kombinace %lf, zatímco %f je vyhrazeno pro typ float %s končí na první mezeře, neboť scanf a sscanf považují mezeru za oddělovač vstupních dat. Pro čtení textů z mezerami je nutno použít jiné funkce
•
Součástí formátovacího řetězce jsou obvykle pouze kombinace začínající % a mezery, ne další text.
Obě funkce vracejí celočíselnou hodnotu - ta udává, kolik položek se úspěšně přečetlo. Tuto hodnotu by program měl vždy prověřovat, aby se ujistil o tom, že se všechny požadované údaje byly přečteny. Protože vstupní funkce musejí měnit obsah proměnných, nemůžeme v nich uvádět proměnné, ale výhradně ukazatele. Nedodržení tohoto pravidla není kontrolováno. Je-li i proměnná typu int, musíme proto místo scanf("%d",i) psát scanf("%d",&i). Avšak pozor: je-li t textový řetězec deklarovaný např. příkazem char t[81]; nesmíme psát scanf("%s",&t), ale naopak pouze scanf("%s",t). Proč tato "nelogičnost"? Protože t je pole, a název pole je už sám ukazatelem!
Formátování bez vstupu a výstupu Do rodiny funkcí printf, fprintf, scanf a fscanf patří ještě další dvě funkce: sprintf a sscanf. Striktně vzato nejde o funkce vstupu a výstupu, protože pracují pouze s daty v paměti počítače, místo souboru či standardního V/V zapisuje sprintf výsledná data prostě do textového pole (kterému říkáme obvykle buffer) a sscanf naopak data z takového pole čte. Samotné formátování probíhá ovšem naprosto stejně jako u zbývajících funkcí. Použití je: sprintf(buffer, formátovací_řetězec, výrazy) sscanf(buffer, formátovací_řetězec, ukazatele)
Další vstupní/výstupní funkce Někdy je pro vstup a výstup textu vhodnější místo výše popsaných funkcí použít funkce jiné. Přečtení jednoho znaku ch či textového řetězce t a jejich výpis můžeme provést následujícími funkcemi (fp je ukazatel na odpovídající strukturu FILE, ch musí u vstupních funkcí být typu int, protože při dočtení souboru do konce funkce vracejí hodnotu EOF, která není typu char.): Příkaz: ch = getc(fp);
Význam: Přečte 1 znak ze souboru
ch = getchar(); Přečte 1 znak ze standardního vstupu (klávesnice) ungetc(ch, fp);
Vrátí znak ch do vstupního bufferu, takže následující vstupní funkce jej přečte znovu
putc(ch, fp);
Zapíše 1 znak do souboru
putchar(ch);
Zapíše 1 znak na standardní výstup (monitor)
gets(t);
Přečte ze standardního vstupu (klávesnice) řetězec až do konce řádku
fgets(t, n, fp);
Přečte ze souboru řetězec až do konce řádku (nejvýše však n-1 znaků)
puts(t);
Zapíše řetězec do standardního výstupu (monitor) a přidá za něj znak "nový řádek"
fputs(t, fp);
Zapíše řetězec do souboru
Bezformátový vstup a výstup Funkce printf, fprintf, scanf a fscanf uskutečňují tzv. formátovaný vstup/výstup (jak také naznačuje písmeno f na konci jejich názvů). Formátované soubory (i standardní V/V zařízení) mají řádkovou strukturu a čísla jsou v nich reprezentována posloupnostmi alfanumerických znaků, zatímco v proměnných jsou uložena ve vnitřním (strojovém, binárním) tvaru. Funkce musejí proto data mezi těmito dvěma tvary konvertovat, což u velkého objemu dat představuje pro počítač nezanebdatelnou zátěž. (Navíc data ve vnitřním zobrazení jsou obvykle paměťově úspornější.) Pokud zapisujeme data na disk pouze proto, abychom je uchovali a později přečetli zpět, a diskový soubor nemusí být pro člověka srozumitelný, je efektivnější používat bezformátový V/V. To znamená, že data se na disk uloží bez konverze ve vnitřním zobrazení. Pro bezformátový V/V používáme následující funkce: fread(buffer, size, n, fp) fwrite(buffer, size, n, fp)
jejichž parametry mají následující typ a význam: buffer
char *
Ukazatel na blok paměti, který se má zapsat nebo přečíst
size
int
Velikost jednoho prvku v bajtech
n
int
Počet datových prvků v bufferu
fp
FILE *
Ukazatel na soubor
Obě funkce vracejí funkční hodnotu typu int, která udává, kolik datových prvků bylo přeneseno. Její hodnotu by program opět měl kontrolovat.
Standardní funkce Matematické funkce V tabulce jsou uvedeny často používané matematické funkce. Pro jejich úspěšné použití musí být v záhlaví uveden hlavičkový soubor #include <math.h> Funkce:
Parametr:
Popis:
double cos(x)
double x
Kosinus
double sin(x)
double x
Sinus
double tan(x)
double x
Tangens
double log(x)
double x
Přirozený logaritmus
double exp(x)
double x
ex
double sqrt(x)
double x
Druhá odmocnina
double floor(x)
double x
Zaokrouhlení na celočíselnou hodnotu dolů
Zaokrouhlení na celočíselnou hodnotu nahoru
double ceil(x)
double x
int abs(i)
int i
double fabs(x)
double x
double acos(x)
double x
Arkuskosinus
double asin(x)
double x
Arkussinus
double atan(x)
double x
Arkustangens
Absolutní hodnota
Manipulace s textovými řetězci Funkcí pro manipulaci s textovými řetězci je velmi mnoho a různé překladače podporují různé množiny těchto funkcí. Proto zde uvádíme pouze ty nejdůležitější. Pro jejich úspěšné použití musí být v záhlaví uveden hlavičkový soubor #include <string.h> Funkce:
Parametry:
Popis:
char * strcpy(a,b)
char *a char *b
Zkopíruje řetězec b do řetězce a; vrací ukazatel na začátek a
char * strcat(a,b)
char *a char *b
a
int strcmp(a,b)
char *a char *b
Porovná řetězce a a b; vrací číslo <0 / 0 / >0, je-li a lexikograficky menší / rovno / větší než b
int strlen(a) char *a
Vrací délku řetězce a (počet znaků před ukončujícím nulovým znakem)
char * strchr(a,c)
char *a int c
Hledá, zda je v řetězci a obsažen znak c. Pokud ano, vrátí ukazatel na první výskyt znaku; pokud ne, vrátí NULL
char * strstr(a,b)
char *a char *b
Hledá, zda je v řetězci a obsažen podřetězec b. Pokud ano, vrátí ukazatal na začátek prvního výskytu podřetězce; pokud ne, vrátí NULL
Přidá řetězec b na konec řetězce a; vrací ukazatel na začátek