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.
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.
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;
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: | |
& |
Logický součin bit po bitu (and) |
Nižší priorita - zleva doprava: | |
^ | 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 |
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.
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);
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)
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;
/* Násobí se jako celá čísla,
i = 1;
x=2*i; 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;
/* Dělí se celé číslo celým, výsledek dělení je proto 0 (typu
z=i/3; 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 1.
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í.
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)
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<b) m=n=100;
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.
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ů:
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.
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.
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ů:
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.
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.
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).
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 (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.
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á.
#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))
#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
).
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
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:
int 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.
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.
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<RADKY; i++) for(j=0; j<SLOUPCE; j++) { ... ... table[i][j] ... } ...
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.
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.
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]); }
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ší:
0
čili znak '\0'.
X
a Y
.
Textové konstanty zapíšeme prostě do uvozovek. Taková konstanta už automaticky
obsahuje i závěrečný nulový znak. <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í. %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.
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 C raději: vymen(int *p, int *q) */
{ int pom;
pom = *p; *p = *q; *q = pom;
}
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:
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émemI 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)
int main() int argc; char **argv;
- v ANSI C int main(int argc, char **argv)
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:
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.
Pomocí bitových polí je možno rozdělit strukturu nebo union na položky typu int
,
unsigned int
(doporučeno) nebo signed int
o délce měřené
pouze na bity. V jazyce C nelze použít jiné celočíselné typy, jako short
/long
,
char
nebo enum
(to lze až v C++). Délka jednotlivých
komponent se zadává (jakožto počet bitů) za jejich názvy po dvojtečce:
struct barva { unsigned int red:6; unsigned int green:6; unsigned int blue:4; };
Většinou tak však paměť neušetříme - zkrátí se data, ale prodlouží přeložený program.
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
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 */
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;
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;
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;
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
.
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
.
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*i= 1 i=2 i*i= 4 i=3 i*i= 9 i=4 i*i=16
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 | Poznámka |
---|---|---|---|
d |
int |
dekadicky | Pro long int se před formátovací písmeno přidá l ,
pro short int se přidá h |
o |
oktalově | ||
x |
hexadecimálně | ||
f |
float nebo double |
běžný tvar | Pro long double se před formátovací písmeno přidá
L; písmena f e g slouží pro float i double |
e |
mantisa e exponent |
||
g |
podle hodnoty jako f nebo jako e |
||
c |
char |
||
s |
char * (tj. textový řetězec) |
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.
Č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 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 Do rodiny funkcí 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 Funkce 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: jejichž parametry mají následující typ a význam: Obě funkce vracejí funkční hodnotu typu 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 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
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
%
a mezery, ne další text. 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
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)
formátovací_řetězec
sscanf(buffer, ,
ukazatele)
Další vstupní/výstupní funkce
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:
Význam:
ch = getc(fp);
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
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ší.)fread(buffer, size, n, fp)
fwrite(buffer, size, n, fp)
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
int
, která udává, kolik
datových prvků bylo přeneseno. Její hodnotu by program opět měl kontrolovat.Standardní funkce
Matematické funkce
#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
e
x
double sqrt(x)
double x
Druhá odmocnina
double floor(x)
double x
Zaokrouhlení na celočíselnou hodnotu dolů
double ceil(x)
double x
Zaokrouhlení na celočíselnou hodnotu nahoru
int abs(i)
int i
Absolutní hodnota
double fabs(x)
double x
double acos(x)
double x
Arkuskosinus
double asin(x)
double x
Arkussinus
double atan(x)
double x
Arkustangens
Manipulace s textovými řetězci
#include <string.h>
Funkce:
Parametry:
Popis:
char * strcpy(a,b)
char *a
char *bZkopíruje řetězec
b
do řetězce a
; vrací ukazatel na
začátek a
char * strcat(a,b)
char *a
char *bPřidá řetězec
b
na konec řetězce a
; vrací ukazatel na
začátek a
int strcmp(a,b)
char *a
char *bPorovná ř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 cHledá, 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 *bHledá, 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