programozási nyelv From Wikipedia, the free encyclopedia
A C egy általános célú programozási nyelv, melyet Dennis Ritchie fejlesztett ki Ken Thompson segítségével 1969 és 1973 között a UNIX rendszerekre az AT&T Bell Labs-nál.[2] Idővel jóformán minden operációs rendszerre készítettek C fordítóprogramot, és a legnépszerűbb programozási nyelvek egyikévé vált. Rendszerprogramozáshoz és felhasználói programok készítéséhez egyaránt jól használható. Az oktatásban és a számítógép-tudományban is jelentős szerepe van.
C | |
![]() | |
Paradigma | imperatív (procedurális), strukturált |
Jellemző kiterjesztés | .h, .c |
Megjelent | 1972[1] |
Tervező | Dennis Ritchie |
Fejlesztő | Dennis Ritchie & Bell Labs |
Típusosság | statikus, gyenge |
Fordítóprogram | GCC, MSVC, Borland C, Watcom C |
Megvalósítások | Clang, GCC, Intel C, MSVC, Turbo C, Watcom C |
Hatással volt rá | B (BCPL,CPL), ALGOL 68, Assembly, Pascal |
Befolyásolt nyelvek | awk, csh, C++, C#, ObjC, BitC, D, Concurrent C, Java, Javascript, Rust |
Operációs rendszer | |
Weboldal |
A C minden idők legszélesebb körben használt programozási nyelve,[3][4] és a C fordítók elérhetők a ma elérhető számítógép-architektúrák és operációs rendszerek többségére. Elterjedésében fontos szerepet játszott a RISC technológia. A sokféle processzorhoz operációs rendszerekre volt szükség, és az eleve C-ben írt Unix volt a legkönnyebben portolható.
A kezdeti fejlesztések az AT&T berkein belül történtek 1969 és 1973 között. A legkreatívabb időszak, Ritchie-nek köszönhetően 1972-ben volt. Azért lett „C” a nyelv neve, mert egy korábbi, „B” nevű programozási nyelv sok tulajdonságát „örökölte”. A leírások különböznek a „B” név forrását illetően: Ken Thompson írt egy programozási nyelvet, a BCPL-t, de írt egy Bon nevűt is, a feleségéről (Bonnie-ról) elnevezve.
Az 1973-as évben a C nyelv elég hatékonnyá vált, így a UNIX rendszermag legnagyobb részét, melyek PDP-11/20 assembly nyelven íródtak, újraírták C-ben. Ez volt az egyik első operációs rendszer rendszermag, mely nem assembly nyelven íródott, korábbiak, a Multics PL/I-ben íródott, a TRIPOS BCPL-ben.
1978-ban megjelent a Dennis Ritchie és Brian Kernighan nevével fémjelzett A C programozási nyelv c. könyv első kiadása. Ez a könyv, melyet a C programozók csak K&R néven emlegettek, sokáig szolgált a nyelv formai leírásának forrásaként. A C nyelvnek az a verziója, melyet leírt, az a „K&R C” nyelv. (A könyv második kiadása az „ANSI C” szabványt írta le, lásd alább.)
A K&R a nyelv következő tulajdonságait vezette be:
struct
adattípuslong int
adattípusunsigned int
adattípus=+
típusú értékadó operátorokat a +=
formára változtatták. (A 'var =- érték
' túlságosan hasonlított a 'var = -érték
'-hez, bár hatásuk egészen más.)A K&R C a nyelv legalapvetőbb részének tekinthető, melyet egy C fordítónak mindenképpen ismernie kell. Sok éven keresztül, még az ANSI C bevezetése után is, a „legnagyobb közös osztó” volt a K&R, melyet a C programozók használtak, ha a legnagyobb mértékű (forrás szintű) kompatibilitásra volt szükség, hiszen nem minden C fordító támogatta a teljes ANSI C-t és a megfelelően megírt K&R C (forrás)kód megfelelt az ANSI C szabványnak is.
A K&R C megjelenése utáni években, sok „nem hivatalos” kiegészítés látott napvilágot, melyet az AT&T és néhány másik cég fordítói is támogattak.
Ilyen változtatások voltak többek közt:
void
típusú függvény és void *
adattípusstruct
vagy union
típusokat voltak képesek visszaadni (return)memcpy
függvénnyel lehetett megtenni)const
definíció, az érték írásvédettségéhezenum
)float
adattípusAz 1970-es évek vége felé, a C kezdte felváltani a BASIC nyelvet a személyi számítógépeken. Személyi számítógépekre is átültették az 1980-as években, így a C nyelv népszerűsége ugrásszerűen emelkedni kezdett. Ugyanebben az időben Bjarne Stroustrup és társai a Bell Labs-nél elkezdtek dolgozni objektumorientált nyelvi elemek hozzáadásán a C nyelvhez. A nyelv, amit készítettek a C++ nevet kapta, ez ma a legelterjedtebb programozási nyelv a Microsoft Windows operációs rendszereken, míg a C a UNIX világban megőrizte népszerűségét.
1983-ban az Amerikai Nemzeti Szabványügyi Hivatal (angolul: American National Standards Institute, röviden ANSI) megalakította az X3J11 bizottságot, hogy létrehozzanak egy egységes (szabvány) C definíciót. A hosszú és fáradságos folyamat végén 1989-ben elkészült a szabvány (egy évvel az első C++ ANSI szabvány után!) és jóváhagyták mint: ANSI X3.159–1989 „A C programozási nyelv”. A nyelvnek ezt a verzióját nevezik ANSI C-nek. 1990-ben az ANSI C szabványt (néhány apróbb módosítással) átvette a Nemzetközi Szabványügyi Szervezet (angolul: International Organization for Standardization, röviden ISO) mint ISO/EC 9899:1990.
Az ANSI C szabványosítás egyik célja az volt, hogy a K&R C-ből és a nem hivatalos bővítésekből egy egységeset alakítson ki. Belevettek azonban számos új megoldást is, mint például függvény prototípust (a C++ nyelvből) valamint egy jobban alkalmazható (fejlettebb) előfordítót (preprocesszor).
ANSI C-t szinte minden fordító támogat. A legtöbb C kód, mely manapság íródott, az ANSI C-n alapul. Bármilyen program, amely a szabvány C-ben íródott, helyesen működik bármely platformon, amelyen szabványos C létezik. Vannak azonban programok, melyek csak adott platformon vagy adott fordítóval fordíthatók le, a használt nem szabvány függvénygyűjtemények miatt (például grafikus függvények) és vannak olyan fordítók, melyek nem támogatják alapértelmezésben az ANSI C szabványt.
Az ANSI szabványosítási folyamatot követően, a C nyelv viszonylag állandó maradt, míg a C++ fejlődött. Új C verzió, 1995-ben az első normatív kiegészítéssel jött létre, de ezt a változatot ritkán használják. A szabványt átdolgozták az 1990-es években és ebből lett az ISO 9899:1999 1999-ben. Ez a szabvány „C99” néven vált ismertté, majd 2000 márciusában bekerült az ANSI szabványok közé is.
C99 új tulajdonságai, többek közt:
long long int
, hogy a 32bitről a 64bitre való átállást megkönnyítsék, explicit bool
(stdbool.h
) és a complex
(complex.h
) típus.//
(a C++-ból)snprintf()
inttypes.h
, amely rögzített méretű integer típusokat definiál: int8_t, int16_t, int32_t, int64_t
, illetve ezek előjel nélküli változatait.Az érdeklődés a C99 új tulajdonságainak támogatásával kapcsolatban eléggé vegyes. Míg GCC (GNU Compiler Collection, korábban GNU C Compiler) és más fordítók támogatják a C99 újdonságait, addig a Microsoft és Borland által forgalmazottak nem, és ez a két cég nem is foglalkozik a C99 jövőbeli támogatásának lehetőségével jelenleg.
2007-ben kezdődött a munka a C sztenderd egy másik revíziójával kapcsolatban, amit informálisan "C1X"-nek hívtak egészen addig, míg hivatalosan is nem publikálták 2011. december 8-án. A C sztenderdek tanácsa elfogadta az ajánlásokat az új lehetőségek limitált beépítésére, amelyeket még nem kezdtek el tesztelni létező implementáción.
A C11 sztenderd számos új lehetőséget adott hozzá a C és könyvtárakhoz, beleértve a típus generikus makrókat, anonim struktúrákat, javított Unicode támogatást, atomi operációkat, többszálúságot és határ ellenőrző függvényeket. Továbbá elkészítették a létező C99 könyvtár néhány portolását, és javították a kompatibilitást a C++-szal.
A C18-at 2018 júniusában adták ki, ami a C programozási nyelv aktuális szabványa. Nem vezetett be új nyelvi elemeket, csak technikai korrekciókat, pontosításokat tartalmaz a C11-hez képest. Az __STDC_VERSION__ macro 201710L-nek van definiálva.
Rendszerint a beágyazott rendszerekhez nem szabványosított kiterjesztéseket használnak, hogy lehetővé tegyék az egzotikusabb funkciók használatát, mint pl. fix pontos aritmetikát, különböző memória bankok használatát és alap I/O műveleteket.
2008-ban a C szabványügyi bizottság publikált egy technikai beszámolót, hogy kiterjessze a C programozási nyelvet ezekkel a lehetőségekkel, az által, hogy közös szabványt biztosít. Ez rengeteg funkciót foglal magába ami nem része a normál C-nek, mint pl. fix pontos aritmetika, nevesített címtartományok és alapvető I/O hardver címzések.
A nyelv makrónyelv abban az értelemben, hogy a C-fordító assembly nyelvre fordít, a programozónak azonban egyetlen assembly sort sem kell leírnia (sőt, nem is kell tudnia erről).
A C strukturált programnyelv: bármelyik utasítás helyén állhat blokk, mely {
és }
jelek közé zárt tetszőleges típusú és számú utasításból állhat. A blokkok egymásba skatulyázhatók. A függvények utasításai blokkban helyezkednek el. A C-program belépési pontja a main
nevű függvény, mely az operációs rendszertől kapja a híváskor megadott paramétereket, és annak adja vissza az (egész típusú) visszatérési értékét.
A nyelv utasításai a preprocesszor-utasítások kivételével szabad formátumúak: ahol egy helyköz megengedett, ott akárhány helyköz, tabulátor, új sor lehet. A nyelv szavai (utasításnevek, változónevek, számok, műveleti jelek stb.) között lehet helyköz, de nem kötelező. Az utasítások pontosvesszővel végződnek. Az üres utasítás az előző utasítás vége után tett pontosvessző. A folytatósor – a sor végi \
– a szabad formátum miatt csak preprocesszor-utasításokban használatos.
A megjegyzéseket (kommenteket) /*
és */
közé kell zárni, és szabvány szerint nem ágyazhatók egymásba, bár sok fordítóprogram mégis megengedi. Az ANSI C-től kezdve használható a //
, mely a sor végéig tartó megjegyzést vezet be (a C++-hoz hasonlóan). Hosszabb megjegyzéseket a #if 0
...#endif
közé is lehet tenni; ezek – lévén preprocesszor-utasítások – egymásba ágyazhatók.
C-ben a nevek kis- és nagybetűkből, számjegyekből és aláhúzásból állhatnak, számjegy nem lehet az első karakter. A kis- és nagybetűk különbözőnek számítanak. A kialakult szokás szerint a változó- és függvénynevekben kisbetűket használunk, a preprocesszor-utasításokban rendszerint nagybetűket.
A preprocesszor utasítások az assembly nyelvek makróihoz hasonlítanak: a fordítás első menetében „normál” C-utasításokká fordulnak le.
Az aritmetikai utasítások nagyon különböznek a többi programozási nyelvben megszokott értékadó utasításoktól. Ezt az aritmetikai utasítást vette át a C++ és a Java.
A nyelvnek nincs input/output utasítása, ezt szabványos könyvtári függvények végzik.
A végrehajtható utasítások (aritmetikai és vezérlő utasítások) függvényen belül, blokkban helyezkednek el. A C-program preprocesszor-utasításokból, deklarációkból és függvényekből áll.
#include <stdio.h> // preprocesszor utasítás
int main() // függvénydefiníció, egyúttal a program belépési pontja, ezúttal nincs paramétere
{ // blokk kezdete
int i; // deklaráció
for (i=1; i <= 3; i++) // vezérlő (ciklus-) utasítás. A ++ egyváltozós értékadó művelet: eggyel növeli i-t.
{ // újabb blokk-kezdet
printf("Haho\n"); // I/O műveletet végző könyvtári függvény. A konzolra ír.
// A stringkonstansot <code>"</code>-k közé kell zárni. A <code>\n</code> az új sor jele a stringben.
} // a belső blokk vége
return 0; // vezérlő utasítás: kilépés a függvényből. A <code>main</code> értékét az operációs rendszer kapja meg
// Windows-ban az <code>errorlevel</code>, Unixban a <code>$?</code> változóban.
} // main blokkjának vége
A program fordítása linuxban (ha a fenti kódot a haho.c
file-ba tettük):
gcc -o haho haho.c
Futtatás:
./haho
Kimenet:
Haho Haho Haho
A C-programozók a fenti ciklusutasítást for (i=0; i < 3; i++)
alakban szokták leírni, mert a tömbök indexelése 0-tól kezdődik a C-ben. A példában a kettő teljesen azonos.
char | 8 |
short | 16 |
int | 16 |
long | 32 |
long long | 64 |
float | 32 |
double | 64 |
long double | 80 |
'A'
, 65
, \x41
, 0101
(az utóbbi oktális, melyet a kezdő 0 jelez). A legfontosabb speciális karakterkonstansok:
65L
.65LL
.3.14
, 8.3e11
, 8.3d-11
. Float típusú konstans 3.14F
, long double 3.14L
alakban adható meg. Ha nincs típusjelzés, a konstans double típusú.A char, short, int, long és long long fixpontos, a float, double és long double lebegőpontos típus. Fixpontos adattípuson nincs túlcsordulás-ellenőrzés: az hibás működést eredményez.
A C-ben nincsen string típus (bár string konstans van, a példaprogramban: "Haho\n"). A stringet karaktertömbben tartja, a string végét bináris nulla ('\0'
) jelzi.
A C-ben nincs logikai típus (igaz vagy hamis). A nem 0 értékű fixpontos kifejezés a logikai igaz, a 0 értékű a hamis.[5] A relációk (melyek szintén aritmetikai műveletek) igaz értékként 1-et adnak vissza.
A char
típusú változóval ugyanazok a műveletek elvégezhetők, mint az int
-tel. Ilyenkor a karakter egésszé konvertálódik.
A char, int, long és long long típus előtt használható a signed
ill. unsigned
típusmódosító. A nyelv nem definiálja, hogy a char
típus egész számként használva előjeles-e, ezért ha az érték 127-nél nagyobb, mindenképpen meg kell adni, hogy hordozható legyen a kód. Az int, long és long long előjeles, ha az unsigned
-et nem adjuk meg.
Az előjeltelen konstansot az utána írt U
jelzi, pl. 15U, 15UL, 15ULL. A hexadecimális alakú konstans (0xF) előjeltelen (az utána írt S
betűvel tehető előjelessé), a többi alak előjeles, ha nincs utána U.
A C nyelv az alábbi típusokkal tud műveletet végezni:
Minden más típus csak tárolásra való, aritmetikai műveletben azonnal átkonvertálódik a nála nagyobb, előjelben megfelelő típusra.
A deklaráció a fordítóprogramnak szóló utasítás. Kódot nem generál, a fordítóprogram szimbólumtáblájában okoz változást.
A C-ben háromféle deklaráció van:
Használat előtt a változókat és típusokat deklarálni kell. A függvényeket nem kötelező, de nyomatékosan ajánlott.
A deklaráció hatására foglalja le a fordítóprogram a memóriaterületet a változó számára, és megadja a memóriaterület nevét, amivel hivatkozni lehet a tartalmára.
Négy dolgot lehet/kell megadni a változó nevén felül:
A C-ben – meglehetősen szerencsétlen módon – az első kettőt nagyjából ugyanazokkal a kulcsszavakkal kell megadni.
Az adat láthatósága C-ben háromféle lehet:
A blokkon belül deklarált változók csak a blokkon belül láthatók (beleértve a blokk által tartalmazott blokkokat is). Ha a blokk egy külső blokkbeli vagy blokkon kívüli változónevet használ, akkor saját példányt definiál belőle, és (névvel) nem tudja elérni a feljebb levő azonos nevű változót.
C-ben függvényen belül nem lehet függvényt definiálni, ezért a függvényen (blokkon) kívüli adatok mindig statikusak, azaz a program indulásától kezdve ugyanazon a memóriaterületen vannak, így ezt a tényt nem kell külön megadni. A blokkon kívüli static
kulcsszó az adat vagy függvény láthatóságát a forrásfájlon belülre korlátozza. A blokkon kívül deklarált, static
nélküli változó és a static
nélküli függvény globális.
Globális változóra vagy függvényre a program többi forrásfájljából az extern
kulcsszóval hivatkozhatunk, melyben meg kell adni a változó nevét, típusát és a tárolási osztályt. Hogy ne kelljen mindezt többször leírni, általában saját header-fájlokat használunk, melyeket minden forrásfájl betölt a #include
preprocesszor-utasítással. extern
változónak nem lehet kezdőértéke. A program valamelyik forrásfájljában (általában a főprogramban) a változót extern
nélkül kell deklarálni, és itt kaphat kezdőértéket.
Betöltéskor létrejövő adatok |
Verem | Változó memóriatartalom |
Kezdőértéket nem kapott adatok | ||
Programfájlban tárolt adatok |
Kezdőértéket kapott adatok | |
Konstansok | Konstans memóriatartalom | |
Programkód |
A tárolási osztály adja meg, hogy az adat a program melyik memóriaterületére kerül (lásd jobb oldali táblázat, középső oszlop).
A blokkon (függvényen) kívül deklarált adat mindig statikus, a blokkon belüli – ha mást nem adunk meg – dinamikus. Blokkon belüli adat a static
kulcsszóval tehető statikussá, és az extern
jelzi, hogy másik forrásprogramban van deklarálva. Az extern
kulcsszót blokkon kívül szokás használni, és mindig statikus adatra vonatkozik.
A statikus adatnak állandó helye (memóriacíme) van. A dinamikus adat a veremben tárolódik, a blokkba belépéskor foglalódik le a helye, kilépéskor felszabadul, kezdőértéke definiálatlan.
A register
kulcsszóval javasolhatjuk a fordítóprogramnak, hogy a dinamikus adatot ne veremben, hanem a CPU regiszterében tartsa. Az ilyen változóknak nincs memóriacímük, így a &
művelet nem használható rájuk. Kezdőértékük definiálatlan. Ha nincs elég regiszter, akkor a deklaráció ellenére verembe kerül az adat. A jelenlegi igen jól optimalizáló fordítók mellett a register
használata idejétmúlt.
A programban kezdőértéket nem kapott statikus adatok 0 értéket kapnak, amikor az operációs rendszer a memóriába tölti a programot.
Konstans változót a const
kulcsszóval lehet megadni, és kötelezően kezdőértéket kell kapjon, mely a program végrehajtása során nem változik, és a fordítóprogram ellenőrzi is, hogy ne szerepelhessen olyan utasításban, ahol értéket kaphatna. A konstans memóriaterületre kerülnek azok a konstansok is, melyeknek nincs nevük ("Haho\n"
a mintapéldában).
Háromféle lehet:
Kezdőérték a változónév utáni =
jelet követő konstanssal adható meg. Kezdőérték adható dinamikus változónak is, de az érték beállításához a fordítóprogram kódot generál, és nem teszi a kezdőértékeket a konstansok memóriaterületére.[6]
Tömbök és összetett változók kezdőértékeit {
és }
közé kell tenni, a zárójelbeli értékeket vesszővel elválasztva. Nem hiba az utolsó érték után is kitenni a vesszőt.
Ha egy változónak nincs kezdőértéke, akkor az dinamikus változó esetén definiálatlan, statikus változó esetén 0 (lásd: tárolási osztály).
int i;
int a, b=2;
static const unsigned short alfa = 88;
extern int globalis;
A struktúra különböző típusú adatokból álló adat. A struktúra szerkezetét és a változókat lehet együtt vagy külön-külön deklarálni. Az alábbi két példa egyenértékű:
struct datstr {
short ev;
short ho;
short nap;
};
struct datstr ma, holnap;
|
struct {
short ev;
short ho;
short nap;
} ma, holnap;
|
Az első példában az első utasítás az adatszerkezetet definiálja (melyet gyakran header-fájlba teszünk, ha több forrásfáljból is használni akarjuk), a második deklarálja a változókat.
A második esetben a struktúrának nem kell kell neve legyen, bár ilyenkor nem használhatjuk a definiált adatszerkezetet később, más változó deklarálásához.
Kezdőértékadás a deklarációban:
struct datstr ma = { 2015, 12, 4 };
Értékadás aritmetikai utasítással:
holnap = ma;
holnap.nap = 5;
A struktúrák egymásba ágyazása:
struct {
struct datstr dat;
short ora;
} pelda;
Az évre pelda.dat.ev
néven hivatkozhatunk, pelda.ev
néven nem.
Mutatóval adott struktúra tagjaira a ->
művelettel lehet hivatkozni.
Az unió (union
) formailag megegyezik a struktúrával, de a tagjai (melyek rendszerint struktúrák) azonos memóriaterületen helyezkednek el. Az unió mérete a legnagyobb tag mérete lesz. Arra szolgál, hogy ugyanazt a memóriaterületet a program különböző időpontokban különböző célokra használhassa. Rendszerprogramokban fordul elő, felhasználói programban ritka.
Akkor használatos, ha egy egész változó csak néhány értéket vehet fel, és ezekre az értékekre (tipikusan kódokra) névvel akarunk hivatkozni a könnyebb megjegyezhetőség érdekében. Alakja a struktúrához hasonló, pl.:
enum httpkod { VAN=200, TILTOTT=403, NINCS=404 } htkod;
httpkod
a struktúranév megfelelője, htkod
a változó neve. A struktúrához hasonlóan külön is megadható a kettő. A kapcsos zárójelben nem kötelező értékeket megadni, ilyenkor a fordítóprogram 0-tól egyesével növekvő értékeket rendel a felsorolt nevekhez.
C-ben az enum – a C++-tól eltérően – nem definiál külön adattípust, egyszerűen hozzárendeli a felsorolt nevekhez az egész értékeket. A nevek ezután bármilyen aritmetikai kifejezésben szerepelhetnek, mintha egész típusú konstansok lennének, de a program hordozhatósága érdekében ezt a tulajdonságot nem ajánlatos kihasználni.
A programozásban tömbnek olyan változókat neveznek, melyek több azonos típusú adatból állnak. A deklaráció formája azonos a skalár (nem tömb) típusú változóval. Az elemek számát C-ben a változónév után szögletes zárójelben kell megadni (csak egész típusú érték lehet), a kezdőértékeket pedig a struktúráknál megismert módon. Pl:
int egesztomb[4];
const int allando[3] = { 1, 2, 3 };
static struct datstr { int ev; int ho; int nap; } dat[2] = { { 1954, 10, 19 }, { 2015, 12, 06 } };
A tömbök indexei 0-val kezdődnek. allando
elemeire allando[0], allando[1], allando[2]-vel lehet hivatkozni. C-ben nincs tömbindex-ellenőrzés: allando[3]-ra hivatkozás hibás működést fog eredményezni. A tömbindex-túlcsordulás az egyik leggyakoribb programozási hiba C-ben.
Bármilyen típusú változó lehet tömbben, beleértve a tömböt is: C-ben a kétdimenziós tömb az egydimenziós tömb tömbje:
double matrix[3][14];
A többdimenziós tömbök elemei sorfolytonosan tárolódnak. Nem hiba egydimenziós tömbként hivatkozni rájuk, pl. matrix[0][40]
.
A tömb elemszáma megadható fordítási időben kiértékelhető (konstans) kifejezéssel:
int tomb[3+8*4];
Ennek preprocesszor-változók használatakor van jelentősége.
Más forrásfájlban deklarált tömbnek nem kell méretet adni. Pl.:
extern int tomb[];
Akkor sem kell megadni a tömb méretét, ha az kezdőértéket kapott. Ilyenkor a méret a kezdőértékek száma lesz.
A mutató memóriacímet tároló változó. Leggyakrabban függvények paramétereiben és tömbelem-hivatkozásokban fordul elő.
A mutató típusa a memóriacímen tárolt adat típusát jelzi. A mutatót a változódeklarációban a név elé tett csillag jelzi:
int *mut;
Értéket a címképző operátorral adhatunk a mutatónak:
int egesz = 3, *mut;
mut = &egesz;
A mutatóval megadott adat értékére a *
operátorral hivatkozunk. A fenti két sor után egesz
és *mut
értéke egyaránt 3.
A tömb neve a tömb memóriacímét jelenti; ilyenkor nem kell kitenni az &
-et:
int tomb[8], *mut;
mut = tomb;
A mutatóhoz 1-et hozzáadva nem eggyel, hanem a fenti példában sizeof(int)
-tel nő a mutató értéke, vagyis a következő egészre (a tömb következő elemére) mutat. Ez azt jelenti, hogy *(mut+1)
és tomb[1]
a fenti példában ugyanazt az értéket adja, és *(mut+1)
is leírható mut[1]
alakban.
Különbség csak két azonos típusú mutató között értelmezett, és a két cím különbsége elosztódik a típus hosszával. A fenti példában mut-tomb
azt mondja meg, hogy mut
tomb
hányadik elemére mutat.
A mutató lehet void
típusú; ilyenkor a fordítóprogram nem ellenőrzi a mutató típusát. Pl. a malloc
nevű könyvtári függvény, mely memóriát kér a program számára, void
mutatót ad vissza, melyet a program a kívánt típusú mutatóba tesz. A void típus miatt az értéket eltároló utasítás nem jelez típuskonverziós hibát.
A mutató típusa a memóriacímen tárolt adat típusa. Ebbe a const
is beleértendő:
int egy;
const int *mut;
mut = &egy; // helyes
*mut = 5; // hibas: konstansnak nem adhato ertek
Struktúramutatók számára külön műveletet vezettek be. Pl.:
struct { int ev; int ho; int nap; } dat, *datmut;
datmut = &dat;
után dat.ev
-re (*datmut).ev
alakban kellene hivatkozni. (A zárójelre szükség van, mert a .
-nak nagyobb a prioritása, mint a *
-nak.) Ezt könnyíti meg a datmut->ev
alak. A kettő hatásában teljesen azonos.
A függvénymutatók használatát lásd a függvényeknél.
A mutatót visszaadó könyvtári függvények NULL
értéket adnak vissza sikertelenség esetén (pl. a memóriafoglalás nem sikerült). A NULL
az stdio.h
header-fájlban definiált konstans.
Mutató mutatóra is mutathat:
int mut=3,*mut1,**mut2,***mut3;
mut1 = &mut;
mut2 = &mut1;
mut3 = &mut2;
A fentiek után a mut
, *mut1
, **mut2
vagy ***mut3
kifejezések mindegyikének 3 az értéke.
A típusdeklaráció nevet ad egy adattípusnak. A típusnév a deklaráció után úgy használható, mint a beépített típusok, de – a C++-szal ellentétben – nem hoz létre új típust: a fordítóprogram úgy tekinti, mintha a típusnév helyett a típusdeklarációt írtuk volna le.
A típusdeklaráció alakja formailag azonos az adattípusokéval, de a tárolási osztályt megadó static
vagy extern
kulcsszó helyére a typedef
kerül. Az adat neve lesz a típusnév:
typedef unsigned int UINT24;
UINT24 valt;
Ezután valt
előjeltelen egész változóként használható.
Aritmetikai utasításnak egy pontosvesszővel lezárt aritmetikai kifejezést nevezünk. Az aritmetikai kifejezés változók vagy konstansok és műveletek kombinációja. (A C-ben csak aritmetikai művelet létezik, ezért a jelzőt el lehet hagyni.)
Az aritmetikai utasításban nem okvetlenül van értékadás: aritmetikai utasítás lehet egy függvényhívás, de szintaktikusan helyes a 1;
utasítás is, bár a fordítóprogram ilyenkor figyelmeztet, hogy az utasításnak nincs semmilyen hatása.
A C-ben az értékadás ugyanolyan aritmetikai művelet, mint pl. a szorzás. Pl. megengedett az
x = (a = b) * (c = d);
utasítás, melyet a többi programnyelvben
a = b;
c = d;
x = a * c;
alakban írnánk (természetesen C-ben is írható így). Az értékadás művelet (szükség esetén) automatikus konverziót végez, és ez a konvertált érték a művelet eredménye. „Mellékhatásként” az érték az =
bal oldalán levő változóba[7] is eltárolódik. (E mellékhatás miatt használjuk a műveletet, hiszen konverziós művelet is létezik.)
Miután az aritmetikai utasításoknak nincs külön nevük (mint pl. az if
vagy a többi programnyelv értékadó utasításának =
karaktere), ezért a fordító minden utasítást aritmetikai utasításnak tekint, ami nem fenntartott szóval kezdődik. Ebből az is következik, hogy C-ben nem szabad a fenntartott szavakat változónévnek használni.
A másik érdekesség, hogy a nyelvben nincs eljárás, csak függvény, de ez a tulajdonság is az aritmetikai utasítások jellegzetességéből adódik.[8]
Precedencia | Operátor | Leírás | Asszociativitás |
---|---|---|---|
1 legmagasabb |
:: |
Látókör meghatározás (csak C++) | Nincs |
2 | ++ |
Postfix növelés | Balról jobbra |
-- |
Postfix csökkentés | ||
() |
Függvényhívás | ||
[] |
Tömbindexelés | ||
. |
Adattag kiválasztás referencia szerint | ||
-> |
Adattag kiválasztás pointeren keresztül | ||
typeid() |
Futásidejű típusinformáció (csak C++) | ||
const_cast |
Típuskonverzió (csak C++) | ||
dynamic_cast |
Típuskonverzió (csak C++) | ||
reinterpret_cast |
Típuskonverzió (csak C++) | ||
static_cast |
Típuskonverzió (csak C++) | ||
3 | ++ |
Prefix növelés | Jobbról balra |
-- |
Prefix csökkentés | ||
+ |
Unáris plusz | ||
- |
Unáris mínusz | ||
! |
Logikai NEM | ||
~ |
Bitenkénti NEM | ||
(type) |
Típuskonverzió | ||
* |
Indirekció (dereferencia) | ||
& |
Memóriacím lekérése | ||
sizeof |
Méret lekérése | ||
_Alignof |
Igazítási követelmény (C11 óta) | ||
new , new[] |
Dinamikus memóriafoglalás (csak C++) | ||
delete , delete[] |
Dinamikus memóriafelszabadítás (csak C++) | ||
4 | .* |
Pointerből adattag (csak C++) | Balról jobbra |
->* |
Pointerből adattag (csak C++) | ||
5 | * |
Szorzás | Balról jobbra |
/ |
Osztás | ||
% |
Maradék | ||
6 | + |
Összeadás | Balról jobbra |
- |
Kivonás | ||
7 | << |
Bitenkénti balra tolás | Balról jobbra |
>> |
Bitenkénti jobbra tolás | ||
8 | <=> |
Háromirányú összehasonlítás (nagyobb: 1, kisebb: -1, egyenlő: 0) (C++20 óta, csak C++) |
Balról jobbra |
9 | < |
Kisebb mint | Balról jobbra |
<= |
Kisebb-egyenlő | ||
> |
Nagyobb mint | ||
>= |
Nagyobb-egyenlő | ||
10 | == |
Egyenlő | Balról jobbra |
!= |
Nem egyenlő | ||
11 | & |
Bitenkénti ÉS | Balról jobbra |
12 | ^ |
Bitenkénti XOR (kizáró vagy) | Balról jobbra |
13 | | |
Bitenkénti VAGY (megengedő vagy) | Balról jobbra |
14 | && |
Logikai ÉS | Balról jobbra |
15 | || |
Logikai VAGY | Balról jobbra |
16 | co_await |
Párhuzamos folyamat feldolgozása (csak C++) | Jobbról balra |
co_yield | |||
17 | ?: |
Hármas feltételes elágazás (ha-akkor-különben) |
Jobbról balra |
= |
Értékadás | ||
+= |
Értékadás és összeg | ||
-= |
Értékadás és különbség | ||
*= |
Értékadás és szorzás | ||
/= |
Értékadás és osztás | ||
%= |
Értékadás és maradék | ||
<<= |
Értékadás és bitenkénti balra tolás | ||
>>= |
Értékadás és bitenkénti jobbra tolás | ||
&= |
Értékadás és bitenkénti ÉS | ||
^= |
Értékadás és bitenkénti XOR | ||
|= |
Értékadás és bitenkénti VAGY | ||
throw |
Dobás operátor (kivételdobás, csak C++) | ||
18 legalacsonyabb |
, |
Vessző | Balról jobbra |
Egy több műveletet tartalmazó kifejezésben a műveletek prioritás szerinti sorrendben hajtódnak végre (lásd a jobb oldali táblázatot). Az azonos prioritású műveletek közötti sorrendet az asszociativitás dönti el. Miután az asszociativitásnak csak a prioritási szinten belül van szerepe, az egyes prioritási szintek asszociativitása eltérhet egymástól. A C nyelv – a „szokásos” programnyelvekkel ellentétben – használja ezt a lehetőséget.[9] A műveletek végrehajtási sorrendjét zárójelekkel téríthetjük el a prioritás szerintitől.
Ha a művelet két operandusa nem azonos típusú, akkor a „kisebb” típus automatikusan a „nagyobbra” konvertálódik a művelet előtt. Ez alól kivételek a nem szimmetrikus műveletek (pl. struktúra tagjára hivatkozás, tömbindexelés, léptetés).
.
esetén a struktúra változóval, ->
esetén memóriacímmel (mutatóval) adott.!c
értéke 1, ha c == 0, egyébként 0.++n
esetén n+1, n++
esetén n (vagyis utóbbi esetben a növelés előtti érték).--n
esetén n-1, n--
esetén n (vagyis utóbbi esetben a csökkentés előtti érték).x = +a;
)(unsigned long)c
kifejezés a c változó értékét előjeltelen hosszú egésszé alakítja.int tomb[6];
, akkor a sizeof(tomb)/sizeof(int)
kifejezés értéke tomb elemszáma (ez esetben 6) lesz. A változó vagy típus összetett is lehet (struktúra vagy unió).[12]1<<5
értéke 32.==
nem tévesztendő össze az értékadó =
művelettel.pow
.)a != 0 && 1000/a < 2
kifejezés sohasem vezet 0-val osztáshoz.5 < 3? 1 : 2
kifejezés értéke 2. A példabeli számok helyén tetszőleges kifejezés állhat.a += 2;
a értékéhez 2-t ad, az eredményt a-ba teszi, egyúttal ez az érték a kifejezés értéke. A többi művelet teljesen hasonló.A bitműveletek (~, <<, >>, &, ^, |, <<= és >>=) és a maradékképzés (%) csak fixpontos típusokon értelmezettek.
Három típusuk van:
Alakja:
if(feltétel) utasítás; else utasítás2;
A többi programnyelvtől eltérően C-ben nincs then
kulcsszó (ezért kell a feltételt zárójelbe tenni). Az else
elmaradhat.
Az if
és else
után egy utasítás állhat, ami blokk is lehet (és majdnem mindig az is). Ha utasítás is if
, és a két if
-nek egy else
-e van, az a belsőhöz tartozik. Az ilyen helyzeteket a {
}
használatával célszerű elkerülni.
C-ben nincs logikai változó vagy kifejezés (a relációs műveletek is egész típusú értéket adnak vissza), ezért a feltétel egész[5] típusú aritmetikai kifejezés, melynek 0 értéke esetén a feltétel nem teljesül (else
ág), nem 0 érték esetén teljesül.
Példa: az alábbi kód:
if(a > 5)
{
x = 3;
y = 2;
}
else
{
x = 8;
y = 9;
}
így is írható (bár a könnyű téveszthetőség miatt nem javasolt):
if(a > 5)
x = 3, y = 2;
else x = 8, y = 9;
Többirányú elágazás egy egész típusú aritmetikai kifejezés lehetséges értékei szerint. A lehetséges értékek egész típusú konstansok. Alakja:
switch(kifejezés) { case érték1: ... break; case érték2: ... break; default: ... }
A case
utáni egész konstansok különbözőek kell legyenek. Arra a case
-ra kerül a vezérlés, melynek értékével egyezik a kifejezés értéke. Ha egyikkel sem, a default
ágra kerül a vezérlés. A default
elhagyható, ilyenkor a switch
semmit sem csinál, ha a kifejezés értéke mindegyik case
-tól különbözik.
A break
kilép a switch
utasításból. Nem kötelező minden ág végére kitenni: ilyenkor az ág „átcsurog” a következőbe. Miután ez rendszerint programhiba, ha szándékos, célszerű megjegyzésben jelezni.
Példa (HTTP-státuszokkal):
enum { VAN=200, NINCS=404, TILTOTT=403 } kod;
...
switch(kod) {
case VAN: puts("Van ilyen lap");
break;
case NINCS: puts("Nincs ilyen lap");
break;
case TILTOTT: puts("Nincs engedély a lap olvasására");
break;
default: printf("Ismeretlen kód: %d\n",kod);
break;
}
Alakja:
while(feltétel) utasítás;
A feltétel az if utasításhoz hasonlóan egész kifejezés, az utasítás – a ciklusmag – szinte mindig blokk.
Először feltétel értékelődik ki. Ha ez nem 0, végrehajtódik az utasítás, majd újra feltétel kiértékelése következik mindaddig, amíg feltétel a 0 értéket nem veszi fel.
A break
utasítás kilép a ciklusból. A continue
kilép a ciklusmagból, és feltétel kiértékelésével folytatja.
Végtelen ciklus:
while(1);
A pontosvessző az üres utasítást jelenti.
Alakja:
for(előkifejezés; feltétel; utókifejezés) utasítás;
Hatása ugyanaz, mintha ezt írtuk volna:
előkifejezés; while(feltétel) { utasítás; utókifejezés; }
Ha a feltétel elmarad, igaznak számít. A két kifejezés bármelyike elhagyható; a for(;feltétel;)
azonos a while(feltétel)
utasítással. utasítás is elmaradhat, de a pontosvessző nem (üres utasítás). Ez elég gyakori: a ciklus feladatát a ciklus fejléce látja el.
A break
utasítás kilép a ciklusból. A continue
kilép a ciklusmagból, és utókifejezés végrehajtásával folytatja.
Példa: megszámoljuk, hogy tomb
-ben hány 1-es érték van, és a szaml
változóba tesszük.
int tomb[1000],szaml,*mut;
...
for(mut=tomb,szaml=0; mut-tomb < sizeof(tomb)/sizeof(int); mut++)
{
if(*mut == 1)
{
szaml++;
}
}
sizeof(tomb)
a tomb
mérete byte-ban. sizeof(int)
az egész típus hossza byte-ban. A kettő hányadosa megadja tomb
elemszámát, így azt egyszer kellett csak leírni. mut-tomb
azt adja meg, hogy hányadik tömbelemnél jár a ciklus.
A ciklus lefutása után a mut
változó értéke tomb+1000
lesz. A C-ben nincs igazi ciklusváltozó; a for
utasításban szereplő változók értékét – a legtöbb nyelvtől eltérően – a ciklus lefutása vagy a break
-kel történő kilépés után is megkötés nélkül használhatjuk.
Végtelen ciklus:
for(;;);
A ciklusutasítások harmadik formája. Abban különbözik while
-tól, hogy először hajtja végre a ciklusmagot, utána értékeli ki a feltételt (Fortran-típusú ciklus). C-ben ritkán használatos. Alakja:
do utasítás; while feltétel;
A strukturált programozást a goto utasítás elkerülésére, a program olvashatóbbá tételére találták ki, ezért a goto használata egyáltalán nem javasolt.
Alakja: goto címke;
A címke egy név, melyet kettőspont követ. A goto hatására a program a címkével megjelölt utasítással folytatódik.
Példa:
goto cimke;
...
cimke: ...
A függvények a hívó programtól kapott paraméterek alapján kiszámítanak egy értéket, és visszaadják a hívónak. A paraméterek és a visszatérési értékek helyes értelmezéséről a függvény deklarációja gondoskodik. A deklaráció nyomatékosan ajánlott, de nem kötelező.
A C-ben nincs eljárás, de a hívó program – az aritmetikai utasítások szabályai miatt – nem köteles felhasználni a függvény visszatérési értékét, ezért a függvények használhatók eljárásként. A gyakorlatban így is zavart okozott a függvények eljárásként történő használata, ezért az ANSI C-ben bevezették a void típust, mellyel semmilyen művelet nem végezhető. Ez azt jelenti, hogy a void típust visszaadó függvény gyakorlatilag eljárásnak tekinthető.
Két módja van:
A függvényérték típusa bármilyen C-típus lehet, beleértve az egyszerű és összetett típusokat, a mutatót (a tömböt mutató formájában adja vissza a függvény) és a már említett void
típust.
Példa: a strcmp
könyvtári függvény két stringet hasonlít össze. Ha a kettő azonos, 0-t ad vissza, ha az első előbb van az abc-ben, -1-et, ha hátrébb, 1-et. A típusdeklaráció:
int strcmp();
A függvény prototípusa a string.h
header-fájlban van:
int strcmp(const char *string1, const char *string2);
Ha a deklaráció elmarad, a függvényt egész típusúnak tekinti a fordítóprogram. Ha a paraméterek deklarációja marad el, azok típusát az első hívás alapján állapítja meg.
Ha a függvénynek nincs paramétere, a prototípusban – a típusdeklarációtól való megkülönböztetés érdekében – a void
kulcsszót kell megadni: int fv(void);
.
A függvény utasításait adja meg. Két részből áll: a fej adja meg a paramétereket és az érték típusát, az azt követő blokk az utasításokat.
A fej kétféle formában írható a kétféle függvénydefinícióhoz hasonlóan.[13] Az eredeti, típusdefiníció-szerű alak:
int memcmp(string1,string2)
const char *string1,*string2;
{...}
A másik alak ugyanolyan, mint a prototípus, csak a ;
helyén áll a blokk. Ma már kizárólag ez az alak használatos:
int strcmp(const char *string1, const char *string2)
{...}
A függvény a return
utasítás utáni kifejezéssel adja meg a visszatérési értékét. A kifejezés automatikusan átkonvertálódik a függvény típusára. void
típusú függvény esetén a return
utáni kifejezés elmarad, és a return
-t sem kell kiírni, ha az a függvény utolsó utasítása a záró }
előtt.
Ha a függvény mutatót ad vissza, az nem mutathat dinamikus adatra (verembeli területre). A függvényből való visszatéréskor ui. a dinamikus változók memóriaterülete felszabadul, és a terület bármikor megváltozhat.
A paramétereket a függvény neve után kell írni zárójelbe, vesszővel elválasztva. A zárójelet akkor is ki kell írni, ha a függvénynek nincs paramétere,[10] ui. a zárójel a függvényhívó művelet.
A paraméterek átadása érték szerint történik, azaz a függvény az értékek másolatát kapja meg. Ebből az is következik, hogy nem tudja megváltoztatni a hívó változóinak értékét. Ezt úgy hidalják át, hogy a változó címét (mutatóját) adják át a függvénynek.[14]
Függvényhívásra példák a könyvtári függvények fejezetben találhatók.
A függvénymutató egy függvény memóriacímét tároló változó, melyen a függvényhívás művelete (()
) hajtható végre.
Függvénymutató is kétféleképpen deklarálható:
int (*comp)(); // típusdeklarációs forma
int (*comp)(const char *string1,const char *string2); // prototípus forma
Mindkettő azt jelenti, hogy a comp
nevű változó egy egész típusú függvény címét tartalmazza.[15] Érték így adható neki:
comp = strcmp;
Ezután pl. a comp("egyik",valt)
és strcmp("egyik",valt)
kifejezés teljesen azonos hatású.
Függvénymutatót kap paraméterként a rendezést végző qsort
és a szimbóltáblában kereső lsearch
, lfind
és bsearch
könyvtári függvény.
A preprocesszor-utasítások hatására a fordítás első menete a forrásprogramon hajt végre módosításokat, melynek eredménye a preprocesszor-utasítás nélküli C-program.
A preprocesszor-utasítások nem szabad formátumúak: a sor eleji #
-jellel kezdődnek, és a sor végével végződnek. Folytatósor a sor végi \
-sel írható.
Alakja:
#include <fájlnév> #include "fájlnév"
Hatására a preprocesszor az include
utasítást a megadott nevű fájl (header-fájl) tartalmával helyettesíti (mintha beírtuk volna a programba). A két alak között az a különbség, hogy az első a fordítóprogram fájljai között keresi a megadott fájlt (linuxban pl. a /usr/include
könyvtárban), míg a második a C-programmal azonos könyvtárban (saját header-fájl).
A fájlnév tartalmazhat path-t, kiterjesztése a kialakult szokások szerint .h
Ugyancsak kialakult az a szokás, hogy a header-fájl végrehajtható utasítást nem, csak adat-, függvény- és típusdeklarációkat tartalmaz (a preprocesszor-utasításokon kívül).
A header-fájlok használata lehetővé teszi, hogy a deklarációkat egy helyen lehessen leírni. A forrásprogramoknak csak hivatkozniuk kell rá az #include
utasítással.
Preprocesszor-változóhoz rendel értéket. A preprocesszor a változó helyére szövegszerűen behelyettesíti az értéket. Például ha egy tömb méretére több helyen hivatkozunk a programban:
#define TOMBMERET 100
int tomb[TOMBMERET],i;
for(i=0; i < TOMBMERET; i++)
...
Ezzel a tömb méretét egy helyen lehet változtatni a programban.
A #define
értéke bármilyen szöveg lehet. Ha C-kifejezésnek akarunk így nevet adni, nyomatékosan ajánlott a kifejezést zárójelbe tenni. A használatkor ui. szöveges másolás történik, nincs prioritásellenőrzés.
A legtöbb fordítóprogram lehetővé teszi, hogy a fordítóprogram hívásakor adhassunk meg preprocesszor-változókat. Pl. linuxban a fenti változó a
gcc -DTOMBMERET=100 ...
kapcsolóval adható meg. Több -D
kapcsoló írható. A #ifndef
utasítással meg lehet vizsgálni, hogy a változó létezik-e (és default érték is rendelhető hozzá, ha a fordításkor nem adtunk meg értéket).
Megvizsgálja, hogy létezik-e egy preprocesszor-változó. Alakja:
#ifdef preproc-változó ... #else ... #endif
A #else
ág elmaradhat. Több utasítás skatulyázható egymásba. A #ifdef
akkor teljesül, ha a változó létezik, #ifndef
akkor, ha nem.
Az utasítással forráskódot hagyhatunk ki a programból. A #else
-ig (ha elmarad, #endif
-ig) leírt szöveg (program) csak akkor kelül a programba, ha a feltétel teljesül. Ha nem teljesül, a #else
után leírt; ha nincs #else
, akkor semmi.
Példa. Gyakran okoz furcsa hibákat az, ha egy header-fájlt többször hívunk be a programba (esetleg nem is közvetlenül .c-ből, hanem másik headerfájlból). Ezért a headerfájlt így célszerű megírni (legyen a neve pelda.h
):
#ifndef PROBA_H
#define PROBA_H // létrehozzuk a változót, hogy a következő híváskor ne teljesüljön a feltétel
// Ide jön a header-fájl tartalma
#endif
Abban különbözik #ifdef
-től, hogy a #if
után tetszőleges konstans (konstansokból és értéket kapott preprocesszor-változókból) álló fixpontos kifejezés írható. Ha a kifejezés értéke nem 0, a #if
utáni, ha 0, a #else
utáni kód kerül a programba.
Példa: szükségünk van egy legalább 24 bites előjeltelen egész típusra. A szabvány szerint az unsigned
típus legalább 16, az unsigned long
legalább 32 bites. A legnagyobb előjeltelen egész értékét a limits.h
header-fájl definiálja az UINT_MAX
nevű preprocesszor-változóban.
#include <limits.h>
#if 1L << 24 < UINT_MAX
typedef unsigned INT24;
#else
typedef unsigned long INT24;
#endif
Adat | stdout | fájl |
---|---|---|
karakter | putchar | fputc |
string | puts | fputs |
formátumozott | printf | fprintf |
Szinte minden program használja az input-output függvények valamelyikét. Ezek fájlból olvasnak vagy fájlba írnak, és a stdio.h
header-fájlban vannak definiálva.
A fájlt meg kell nyitni. A fopen
függvény FILE
típusú mutatót ad vissza, és ugyancsak stdio.h
-ban van definiálva (fordítóprogramtól függően általában typedef struct ... FILE;
alakban). A többi fájlkezelő függvény erre az ún. fájlleíróra hivatkozik.
A program az induláskor az operációs rendszertől kap három nyitott fájlt (az alábbi globális nevek ugyancsak stdio.h
-ban vannak):
stdin
: standard bemenetstdout
: standard kimenetstderr
: standard hibakimenetAdat | stdin | fájl |
---|---|---|
karakter | getchar | fgetc |
string | -[16] | fgets |
formátumozott | scanf | fscanf |
Ezeket nem kell megnyitni, de le lehet zárni, ha a program nem használja őket. Néhány I/O függvénynek nem kell fájleírót adni: ezek stdout
-ra írnak vagy stdin
-ről olvasnak.
A függvények pufferelnek: a kiírt adatok a memóriába kerülnek, és csak bizonyos mennyiség után, a fájl lezárásakor (fclose
) vagy a fflush
függvény meghívására íródnak ki.
A printf és scanf függvénycsaládnak a formátumot stringben kell megadni. A formátum %
-jellel kezdődik, és az adat típusára utaló betűvel végződik. A kettő között további információkat lehet megadni. A formátumstring utáni első paraméter az első %-hoz tartozó adat stb. A paraméterek száma tetszőleges, de a %-ok és a paraméterek párban kell legyenek.
Az sprintf
függvény fájl helyett karaktertömbbe írja a kimenetet. A sscanf
karaktertömbből veszi a bemenetet. A két családnak további függvényei is vannak.
Az alábbi programrészlet a meretek.txt
nevű fájlba írja, hány bájtos a gépen a short
, int
, long
és long long
típus.
FILE *fp;
static const char fnev[] = "meretek.txt";
...
if( (fp = fopen(fnev,"w")) == NULL)
{
fprintf(stderr,"Nem tudom írásra megnyitni a %s fájlt\n",fnev);
exit(2); // 2-es hibakóddal kilép a programból
}
fprintf(fp,"short = %d\nint = %d\nlong = %d\nlong long = %d\n",
sizeof(short),sizeof(int),sizeof(long),sizeof(long long));
if(ferror(fp) | fclose(fp)) // || nem jó, mert hiba esetén nem zárja be a fájlt
{
fprintf(stderr,"I/O hiba a %s fájlban\n",fnev);
exit(2); // 2-es hibakóddal kilép a programból
}
A meretek.txt
tartalma linuxban, x86_64 architektúrában:
short = 2 int = 4 long = 8 long long = 8
headerfájl | feladat | példák |
---|---|---|
stdlib.h | kilépés a programból | exit, abort |
memóriakérés futás közben | malloc, calloc, realloc, free | |
rendezés, szimbóltábla kezelés | qsort, lsearch, lfind, bsearch | |
string konverziója C-típussá | atoi, atol, atof | |
string.h | stringkezelés | strlen, strcmp, strcat, strchr, strstr, strspn, strcspn |
ctype.h | karakterosztályozás | isalpha, isalnum, isupper, toupper, islower, tolower, isspace |
math.h | matematikai függvények | sin, asin, cos, acos, tan, atan, atan2, pow, sqrt |
limits.h | értékhatárok | preprocesszor-változók a különböző típusok minimális és maximális értékeire |
errno.h | hibakódok | az errno globális változó definíciója és lehetséges értékei az utolsó művelet sikerességéről |
unistd.h | paranccsori paraméterek átvétele | getopt |
setjmp.h | fatális hiba kezelése | setjmp, longjmp |