Ez a dokumentum egy előző változata!
A C nyelv egy általános célú programozási nyelv. Rendszerprogramozási nyelvként is emlegetik, mivel hatékonyan használható operációs rendszerek írására. Természetesen használható más alkalmazói programok írására is. A nyelvet a BCPL és B nyelvből eredeztetik:
BCPL | → | B | → | C |
A C nyelvet Dennis Ritchie az 1970-es évek elején fejlesztette ki Ken Thompson segítségével. A nyelvet UNIX operációs rendszerre tervezte, amely egy DEC PDF-7 rendszeren futott. Volt egy B nevű nyelv, amely nem lett ismert. A C sok tekintetben örökölte a B nyelv tulajdonságait, azért is lett a neve az ábécé következő betűje. Az 1970es években a személyi számítógépeken BASIC nyelvet lassan felváltja a C nyelv. 1978-ban Dennis Ritchie és Brian Kerninghan nevével fémjelzett „A C programozási nyelv” című könyv első kiadása megjelenik. 1983 – 1989 az Amerikai Nemzeti Szabványügyi Hivatal (angolul: American National Standards Institute, röviden ANSI) szabványnak fogadják el. 1990-ben megjelenik a szabványnak egy átdolgozott kiadása C99 névvel.
Az ANSI C az eredeti K&R nyelvnek egy kibővített változata. A megvalósítások ehhez még plusz kiterjesztéseket tesznek hozzá.
#include <stdio.h> int main() { printf("Helló Világ\n"); }
A C nyelv több előre megírt függvénnyel rendelkezik, amit csak meg kell hívni. Ezen előre megírt függvénygyűjteményeket hívjuk sztenderd könyvtáraknak. A sztenderd könyvtárak is több csoportja ismert, amelyeket elneveztünk a rájuk jellemző névvel. Ilyenek a stdio, stdlib, math, stb.
A fenti program első sora amely #include-al kezdődik egy szabványos könyvtár használatát teszi lehetővé, amelynek neve stdio. Az stdio programozói könyvtárnevet kötelező kisebb mint és nagyobb mint karakterek közé tenni:
#include <stdio.h>
A programban szereplő printf() utasítás a stdio.h programozói könyvtárban található. Ha használni akarjuk a printf() függvényt, mindig szükség van a #include <stdio.h> sorra.
A C nyelvben, minden utasítást függvényekbe rendezünk. Egy C programban mindig lenni kell egy main nevű függvénynek, a programnak ugyanis ez lesz a belépési pontja. A függvények neve után egy nyitó és egy bezáró zárójelet teszünk. Ezzel is jelezzük, hogy a main valójában függvény:
main()
A main() tehát egy függvény fejrésze, amely egy névből (esetünkben a „main”) és az azt követő zárójelekből áll.
Egy függvénynek mindig van törzs része is. A függvény törzsét kapcsos zárójelek írjuk. A függvény törzsében írjuk le mit történjen a programban. A mi programunkban egyeteln utasítás van:
printf("Helló Világ\n");
A printf() függvény, illetve utasítás a képernyőre írja a paraméterként megkapott értékeket. Esetünkben ez a „Helló Világ” szöveg. A szöveg végén a „\n” gondoskodik a sortörésről.
Ha leírok egy karaktersorozatot, például alma, a printf() függvény a képernyőre írja:
#include <stdio.h> int main() { printf("alma"); }
#include <stdio.h> int main() { puts("alma"); }
A karaktersorozat persze lehet számok vagy akár más karakterek sorozata is:
printf("35");
A számokat csak a formátumuk meghatározásával lehet kiíratni.
printf("%d", 35);
Előbb írok egy formázó szöveget, majd jöhet a szám. Egész számok esetén a formázó szöveg: „%d”.
A printf() kiíratandó paramétere, lehet akár egy kifejezés is:
printf("%d", 35*2);
A kifejezés kiértékelődik, és az eredményt kapjuk vissza.
Fentebb láttuk, ha egy karaktersorozatot a képernyőre iratok, annak minden karaktere változtatás nélkül megjelenik a képernyőn. Egyes karaktereknek azonban lehet speciális jelentése is.
Fentebb már láttuk, ha az „n” betű elé egy visszaperjelet (\) írunk, akkor annak különleges jelentése lesz. Ahelyett, hogy magát a karaktert kapnánk a képernyőn egy sortörést küld a képernyőre.
Ehhez hasonló, például a "\t", amely egy tabulátorjelet küld a képernyőre.
Az ilyen karaktereket hívjuk Escape szekvenciáknak.
Mivel az idézőjelnek (") is speciális jelentése van, ezért azt is csak escape szekvenciaként tudjuk megjeleníteni:
\"
A következő táblázat az escape szekvenciák listáját tartalmazza:
Escape szekvencia | Leírás |
---|---|
\n | Sortörés |
\t | Tabulátor |
\" | Idézőjel |
A C nyelvbe változókat hozhatunk létre, értékek tárolására. A változó deklarálása során meg kell adni milyen típus fogunk benne tárlni, majd megadom a változó nevét. Például egy egész típusú változót szeretnék tárolni, egy szam nevű változóban, akkor:
int szam = 25;
Ebben a példában rögtön definiáltam a szam nevű változó értékét.
A típusok előtt különféle módosítókat adhatunk meg. Ilyen módosító a signed vagy az unsigned. Magyarul előjeles vagy előjel nélküli. A típusok alapértelmezetten előjelesek. Ha nem szeretnék negatív számokat használni, akkor használhatom az unsigned módosítót, így a pozitív számokból nagyobb intervallummal dolgozhatok.
Példaként, adjuk meg az int számára unsigned módosítót. Ez azt jelenti, hogy csak előjel nélküli, pozitív számokat adhatunk meg.
unsigned int szam = 25;
Egy int típusban, a géptől függően 2 vagy 4 bájt nagyságú számot tárolhatunk. Az int számra megadhatunk egy short módosítót. Ekkor maximum 32767 a legnagyobb megadható szám. A legkisebb -32768.
short int szam = 25;
A legnagyobb megadható szám tárolása:
short int szam = 32767;
Használhatjuk egyszerre a unsigned és a short módosítót is. Ekkor a legkisebb tárolható szám 0, a legnagyobb, 65535. Például:
unsigned short int szam = 63000;
A következő táblázat tovább módosítókat tartalmaz, és méreteket tartalmaz.
Típus | Bájt | Minimális érték | Maximális érték |
---|---|---|---|
char, signed char | 1 | -128 | 127 |
unsigned char | 1 | 0 | 255 |
short, short int, signed short int | 2 | -32768 | +32767 |
unsigned short, unsigned short int | 2 | 0 | 65535 |
int, signed int | 2 vagy 4 | short vagy long | short vagy long |
unsigned unsigned int | ugyanígy, de unsigned | ugyanígy, de unsigned | |
long, long int, signed long int | 4 | -2147483648 | +2147483647 |
unsigned long, unsigned long int | 4 | 0 | 4294967295 |
char, unsigned char | 1 | 0 | 255 |
signed char | 1 | -128 | 127 |
A szabványos limits.h fejállományban megtalálhatók a maximum és minimum értékek.
CHAR_MIN |
CHAR_MAX |
UCHAR_MAX |
SHRT_MIN |
SHRT_MAX |
USHRT_MAX |
INT_MAX |
INT_MIN |
UINT_MAX |
LONG_MAX |
LONG_MIN |
ULONG_MAX |
Ha szeretnénk kiíratni a legnagyobb tárolható int típust, írjuk meg a következő programot:
#include <stdio.h> #include <limits.h> int main() { printf("%d\n", INT_MAX); }
Az állandó olyan érték, amelyet a továbbiak során nem akarunk megváltoztatni. A program futási ideje alatt ezek az állandók nem változtathatók. Kétfajta állandót használunk: literális és nevesített.
Lehet szám, karakter, karaktersorozat, stb.
1234 formában leírt szám egész szám. Ha long típusú állandót akarok leírni akkor utána kell tennem egy kis l vagy L betűt: 123456789L
Egy vagy több aposztrófok (') közzé írt karakter. Pl.: 'a' vagy 'b' Karakterként nem szerepelhet a ' vagy az újsor. Ezért ezek helyettesítésére ún. escapesorozatokat (escape szekvencia) használunk:
új sor NL \n vízszintes tabulátor HT \t függőleges tabulátor VT \v visszalépés (backspace) BS \b kocsivissza CR \r lapemelés (formfeed) FF \f hangjelzés (bell) BEL \a backslash \ \\ kérdőjel ? \? aposztróf ' \' idézőjel " \" oktális szám ooo \ooo hexadecimális szám hh \xhh
Szokásos elnevezése: „karaktersorozat”. A szöveget úgy adjuk meg, hogy maga a program azon változtatni nem tud a végrehajtás során, vagyis állandó. A C nyelvben a szöveges állandót idézőjelek között adjuk meg. Pl.:
"Alma a fa alatt"
Vegyük észre! A magyar nyelvtől eltérően a C nyelvben a nyitóidézőjel felül van.
Az állandó számára egy azonosító nevet adunk meg. Egy szövegkonstans pl. a „Nagy Péter”. Ennek a konstansnak nevet is adhatunk, pl.: TELJESNEV. Innentől kezdve a TELJESNEVet bárhova írom oda behelyettesítődik a „Nagy Péter”. C nyelvben ez a következő módon adható meg: #define TELJESNEV ”Nagy Peter” A C nyelvben lehetőség van még egy módon állandó megadására a const módosítóval. Pl.: const int a; Azt vállaljuk,hogy a változó tartalmát sosem változtatjuk meg.
Általánosan:
#define azonosító helyettesítő–szöveg
Egy konkrét példa:
#define MAX 5 #define FELSOHATAR 100 #define ALSOHATAR 50
Egy nem módosítható objektumot deklarál. Az azonosítót nem használhatjuk egy egyenlőségjel baloldalán.
const int a = 5;
#include <stdio.h> #define MERET 3 typedef double Ttomb[MERET]; main() { Ttomb tomb = {3.7, 5.2, 2.8}; printf("%f\n", tomb[0]); }
Láthattuk, hogy nevesített állandót két módon hozhatok létre. A define előfordítói utasítással, és a const módosítóval.
A const egy módosító. A módosító után áll az állandó típusa. A típus lehet char*, ezzel szöveges állandó adható meg.
A felhasználása előtt minden változót deklarálni kell, bár bizonyos deklarációk a programkörnyezet alapján is létrejöhetnek. A deklaráció egy típust határoz meg, és utána egy vagy több adott típusú változó felsorolása (listája) áll.
int also, felso, lepes; char c, sor[1000];
A változók a deklarációk közt tetszőleges módon szétoszthatók, pl. a bal oldali deklarációs listákkal teljesen egyenértékű az
int also; int felso; int lepes; char c; char sor[1000];
A változók a deklaráció során kezdeti értéket is kaphatnak:
int i = 0; char esc = '\\'; int hatar = MAXSOR+1; float eps = 1.0e-5
Bármely változó deklarációjában alkalmazható a const minősítő. Pl.:
const double e = 2.71828182845905; const char uzenet [ ] = ”figyelem:”; int strlen(const char[ ]);
Kétfajta operátort különböztetünk meg:
Egy operandus esetén az operátor lehet prefix és postfix:
operátor operandus operandus operátor
Két operandusú operátor esetén:
operandus1 operátor operandus2
+ összeadás - kivonás * szorzás / osztás % maradék képzés
Példák:
int c = 3 + 5;
int a = 3; int b = 5; int osszeg = a + b;
Maradékképzés:
int maradek = 9 % 2;
A maradékot a „maradek” nevű változóban tároljuk.
> < >= <= == Egyenlő != Nem egyenlő
Az eredményük vagy 0 (hamis) vagy 1 (igaz)
&& ÉS || VAGY ! NEM
Kiértékelés balról jobbra. Ha a kifejezés értéke meghatározható, akkor a kiértékelés leáll.
Például:
(a < 10) && (b > 5)
a = a + 1 -> a += 1 a = a - 1 -> a -= 1 a = a * 1 -> a *= 1 a = a / 1 -> a /= 1
a = a * (b + 1) -> a *= b + 1
++ --
Az operátorok lehetnek prefix vagy postfix.
a++ -> a = a + 1; ++a -> a = a + 1;
Ha postfixként használjuk:
a = 15; b = a++; // -> b = a; a = a + 1;
Ha prefixként használjuk:
a = 15; b = ++a; // -> a = a + 1; a = b;
& | bitenkénti ÉS |
| | bitenkénti VAGY |
^ | bitenkénti kizáró vagy |
<< | bitléptetés balra |
>> | bitléptetés jobbra |
~ | egyeskomplemens |
Csak egész számokra alkalmazhatók
000011 | nem számít mennyit jelent |
000010 | 3-at jelent, ennyivel léptetünk |
001100 | eredmény |
a = 6 | 0000 0110 | |
~a | 1111 1001 | 249 |
() | zárójel |
! ++ – - | negálás, növelés, csökkentés, előjel |
* / % | szorzás, osztás, maradékképzés |
+ - | összeadás, kivonás |
« » | bitenkénti eltolás balra és jobbra |
< <= >= > | kisebb mint, kisebb vagy egyenlő, nagyobb vagy egyenlő, nagyobb mint |
== =! | egyenlő, nem egyenlő |
& | bitenkénti megengedő (inkluzív) és |
^ | kizáró (exkluzív) vagy |
| | bitenkénti vagy |
&& | és |
|| | vagy |
Ha a egy művelet azonos szinten van, akkor az operátorok balról jobbra lesznek kiértékelve.
Példa a balról jobbra kiértékelésre:
12/2*3 = 18
A zárójel mindig módosítja a kiértékelés sorrendjét.
A printf() függvény formátumozott kivitelt tesz lehetővé számunkra. A formátum kétféle karaktert tartalmazhat: amely kiíródik változás nélkül a kimenetre, és amely a soron következő argumentum konverzióját írja elő. Minden konverziós szakasz a % jellel kezdődik.
A % jel és a konverziós karakter között a sorrendben a következők lehetnek:
Kiíratás 2 tizedesjegy pontossággal:
Kiíratás 20 szélesen:
Balra igazítás:
formátum karakter | típus |
---|---|
ld | long |
d | int, short, char |
f | float |
c | char, int |
Lg | long double |
A matematikai függvények a math.h nevű fejállományban vannak.
PI értéke
M_PI 3.14159265358979323846
double sin (double); double cos (double); double tan (double); double sinh (double); double cosh (double); double tanh (double); double asin (double); double acos (double); double atan (double); double atan2 (double, double); double exp (double); double log (double); double log10 (double); double pow (double, double); double sqrt (double); double ceil (double); // Felfele kerekít double floor (double); // Felfele kerekít double fabs (double); // Abszolút érték double ldexp (double, int); double frexp (double, int*); double modf (double, double*); double fmod (double, double);
int abs(int) //Abszolút érték
Az stdlib.h fájlban találhatók a következő függvények.
int rand(void) void srand(unsigned int)
A két függvény az ANSI szabvány része.
Ha több véletlen számot kell generálnunk egymás után, a rand() függvény nem ad mindig megfelelő eredményt. BSD és Linux rendszereken a valódi véletlenszámhoz használjuk a /dev/urandom állományt.
char *s = (char*) malloc(5 * sizeof(char)); strcpy(s, "15"); int a = atoi(s);
Az atoi() függvény a stdlib.h fejállományból érhető el.
char *s = (char*) malloc(5 * sizeof(char)); strcpy(s, "15"); long a = atol(s);
Az atol() függvény a stdlib.h fejállományból érhető el.
char *s = (char*) malloc(5 * sizeof(char)); strcpy(s, "15"); float a = atof(s);
Az atof() függvény a stdlib.h fejállományból érhető el.
#include <stdio.h> main() { int a; printf("Szam: "); scanf("%d", &a); printf("Ezt írtad: %d\n", a); }
Az fgets állományok olvasására való, azonban az stdio.h fejlécállományban deklarálva van egy stdin nevű fájl, amely billentyűzetet szimbolizálja. Így egy valódi állomány helyett a billentyűzetet is olvashatjuk.
#include <stdio.h> main() { char nev[100]; printf("Név: "); fgets(nev, 100, stdin); printf("Ezt írtad: %s\n", nev); }
Az fgets() függvény csak adott számú karaktert kér be, amit a második paraméterben adhatunk meg. Az fgets() függvény beolvassa az Enter billentyűt is.
Ha szeretnénk sortörés elhagyni, akkor a következő egyszerű példa erre mutat egy lehetőséget. A karaktersorozat első elemének címét az „s” mutató tartalmazza:
if (s[strlen(s)-1]=='\n') s[strlen(s)-1] = '\0';
A fenti utasítás csak akkor tünteti el az utolsó karaktert, ha az valóban egy sortörés. Működése: megnézzük , hogy az utolsó karakter sortörése. Ha igen, akkor lecseréljük karakter sorozat végét jelölő null karakterre.
#include <stdio.h> #include <stdlib.h> main() { char *nev; nev = (char*) malloc(100 * sizeof(char)); printf("Név: "); fgets(nev, 100, stdin); printf("Ezt írtad: %s\n", nev); }
#include <stdio.h> #include <stdlib.h> main() { char *nev; nev = (char*) malloc(100 * sizeof(char)); printf("Név: "); scanf("%s", nev); printf("Ezt írtad: %s\n", nev); }
#include <stdio.h> main() { int a = 5; if(a > 0) printf("Pozitív szám\n"); }
#include <stdio.h> main() { int a = 5; if(a > 0) printf("Pozitív szám\n"); else printf("Nulla vagy negatív\n"); }
A switch() utasítás paramétereként a C nyelvben csak egész számokat megvalósító típusok adhatok meg. Például int, char.
#include <stdio.h> main() { int szam; printf("Szám: "); scanf("%d", &szam); switch(szam) { case 1 : printf("Egy\n"); break; case 2 : printf("Kettő\n"); break; default: printf("Más szám\n"); } }
Minden case részt meg kell szakítani egy break utasítással, ha nem szeretnénk a következő case utáni rész is végrehajtani.
#include <stdio.h> main() { int i; for(i=0; i<10;i++) printf("%d\n", i); }
#include <stdio.h> main() { int i=0; while(i<10) { printf("%d\n", i); i++; } }
#include <stdio.h> main() { int i=0; do { printf("%d\n", i); i++; }while(i<10); }
Felsorolt vagy enum típus. Az enum a felsorolt értékeket egészként tárolja.
enum {hetfo, kedd, szerda, csutortok pentek};
A hetfo 0 értékkel egyenlő, a kedd 1 értékkel, a szerda 2, stb.
#include <stdio.h> main() { enum het {hetfo, kedd, szerda, csutortok, pentek, szombat, vasarnap}; enum het a; a = hetfo; if(a==hetfo) printf("hétfő\n"); }
A fenti példában deklarálunk egy a változót, amely a het napjai értéket veheti fel:
enum het a;
Az a változóban ez után értékként megadhatjuk a hetfo, kedd, szerda, stb. értékeket. A példában a hetfo értéket adjuk meg:
a = hetfo;
Ezek után megvizsgálhatjuk, hogy az a változó milyen értéket tartalmaz. A példában az vizsgáljuk értéke egyenlő-e hetfo-vel:
if(a==hetfo)
Az a változóval persze többet is tehetünk. Például növelhetjük az értéket:
a++
Egy ilyen növelés után a tartalma már kedd lesz.
Megjegyzés: A későbbiekben még veszünk összetett adattípusokat. Ilyen lesz a tömb meg a struktúra.
A karaktersorozat (angolul string) típus nem létezik a C nyelvben. Ha karakter sorozatot szeretnénk tárolni, akkor egy karakteres mutató típusú változót hozok létre, ahol a mutatótól kezdve a memóriában több karaktert is tárolhatok. A mutató típusú változó a karaktersorozat elejére mutat a memóriában. A sorozat végét egy null karakter adja (nem az ASCII 0-ás karaktere, hanem egy olyan byte aminek az értéke 0. Escape szekvenciával jelölve:
'\0'
betűk | 'H' | 'e' | 'l' | 'l' | 'o' | 0 |
---|---|---|---|---|---|---|
memóriacím | fe1011 | fe1012 | fe1013 | fe1014 | fe1015 | fe1016 |
Az utolsó tehát nem számjegy, hanem egy 0 értékű bájt.
Ezt persze nem kell nekünk a karakter sorozat végére tenni, mert az a fordító automatikusan megteszi. Egy ilyen mutató létrehozása a következő képen néz ki:
char *s;
A működéshez le kell még foglalni annyi karakter helyet a memóriában, amennyit szeretnénk eltárolni. A helyfoglaláshoz két függvényt szoktunk használni:
Mind a kettő az stdlib.h programozói könyvtárakban van. Mi most a malloc() használatát nézzük meg.
A malloc() függvénynek egyetlen paramétere van, mégpedig a lefoglalandó byte-ok száma. Ha például 30 karaktert akarunk tárolni, akkor így írhatjuk:
malloc(30);
Ezzel még nincs kész. Meg kell adni, hogy melyik mutatóhoz rendeljük ezt a 30 byte-ot:
s = malloc(30);
A malloc() azonban void típus ad vissza, amit át kell alakítanunk char* típusúvá:
s = (char*) malloc(30);
Ez már egy működő helyfoglalás.
A deklarálás és a helyfoglalás együtt ezek után:
#include <stdlib.h> main() { char *s; s = (char*) malloc(30); }
A C nyelven egy karakter eltárolására nem biztos, hogy 1 byte-ot használunk. Ez függhet az adott rendszertől. A helyfoglaló függvényt ezért szokás kibővíteni egy vizsgálattal, amely megmondja mekkora egy char típus az adott rendszerben. Erre a sizeof() operátort használhatjuk. Az általa visszaadott értéket szorozzuk be 30-al:
#include <stdlib.h> main() { char *s; s = (char*) malloc(30 * sizeof(char)); }
Ha szeretnénk megváltoztatni a lefoglalt hely mérettét az következő utasítással tehetjük meg:
A lefoglalt hely felszabadítása:
Most már használhatjuk a „s” változót. A C nyelvben egy karakter sorozatot nem adhatunk meg egy egyenlőség jel jobboldalán. Ha egy változóban egy karaktersorozatot szeretnénk elhelyezni, akkor arra az strcpy() függvény használható, amely a string.h könyvtárakban találhatók:
#include <stdlib> #include <string.h> main() { char *gyumolcs; gyumolcs = (char*) malloc(30 * sizeof(char)); strcpy(gyumolcs, "szilva"); }
Ha egy változóban lévő karaktersorozatot akarunk a képernyőre íratni, akkor az „s” formátumkódot kell használnunk. A fenti gyumolcs változó tartalmának képernyőre íratása:
#include <stdio.h> #include <stdlib> #include <string.h> main() { char *gyumolcs; gyumolcs = (char*) malloc(30 * sizeof(char)); strcpy(gyumolcs, "szilva"); printf("%s\n", gyumolcs); }
#include <stdio.h> #include <stdlib> main() { char *gyumolcs; gyumolcs = (char*) malloc(30 * sizeof(char)); scanf("%s" gyumolcs); printf("%s\n", gyumolcs); }
Ha egy változóba egy értéket teszünk a scanf() függvénnyel, akkor sosem magát a mutató, hanem annak címét kell megadnunk. Ezt úgy érjük el, hogy a változó név elé egy „&” karaktert teszünk. A gyumolcs változó elé mégsem tettünk, mert az eleve mutató típus, vagyis itt eleve a változó címét kapjuk meg.
A scanf() függvény azonban csak egyetlen szó bekérésére alkalmas, mert whitespace karakterig olvas. A szóközök után írt karaktereket már nem tárolja el. Ezért mondatokat nem tudunk vele beolvasni. Mondatokat a gets vagy fgets függvények alkalmasak.
A C nyelvben az strtok() függvényt használhatjuk egy karaktersorozat darabolására.
Szintaktikája:
#include <string.h> char *strtok( char *str1, const char *str2 );
Az strtok() függvény visszaad egy következő egységet (tokent), a karaktersorozatból. A választott karaktersorozat az első paraméter. Második paraméter az elválasztó.
strcpy(str, "alma:körte:szilva:barack"); strtok(str, ":");
Az strtok() NULL értékkel tér vissza ha nem talál több tokent. Az strtok() függvényt úgy használjuk, hogy az első részt kiolvassuk első paraméternek megadva az str változót:
strtok(str, ":");
Minden további hívást a NULL értékkel kell végrehajtani:
strtok(NULL, ":");
A teljes megvalósítás így nézhet ki:
#include <stdio.h> #include <stdlib.h> main() { char *str = (char*) malloc(30 * sizeof(char)); char *token = NULL; strcpy(str, "alma:körte:szilva:barack"); token = (char*) strtok(str, ":"); while(token != NULL) { printf("%s\n", token); token = (char*) strtok(NULL, ":"); } }
Másik példa:
#include <stdio.h> #include <stdlib.h> main() { char *jarmu = (char*) malloc(30 * sizeof(char)); char elvalaszto[] = ":"; char *resz = NULL; strcpy(jarmu, "opel:ford:citroen:mazda"); resz = (char*) strtok(jarmu, elvalaszto); while(resz != NULL) { printf("%s\n", resz); resz = (char*) strtok(NULL, elvalaszto); } }
Ugyanaz más szeparátorral:
#include <stdio.h> #include <stdlib.h> main() { char *jarmu = (char*) malloc(30 * sizeof(char)); char elvalaszto[] = "#"; char *resz = NULL; strcpy(jarmu, "opel#ford#citroen#mazda"); resz = (char*) strtok(jarmu, elvalaszto); while(resz != NULL) { printf("%s\n", resz); resz = (char*) strtok(NULL, elvalaszto); } }
A szeparátorokaból lehet több is:
#include <stdio.h> #include <stdlib.h> main() { char *jarmu = (char*) malloc(30 * sizeof(char)); char elvalaszto[] = "#:;"; char *resz = NULL; strcpy(jarmu, "opel#ford:citroen;mazda"); resz = (char*) strtok(jarmu, elvalaszto); while(resz != NULL) { printf("%s\n", resz); resz = (char*) strtok(NULL, elvalaszto); } }
Lásd még:
A tömbök azonos típusú elemek tárolása kitalált adatszerkezet. Például több egész számot is szeretnék eltárolni. A változó deklaráció során jeleznünk kell, hogy most nem egy értékű változóról van szó. ezt a változó neve után tett szögletes zárójelek jelzik.
int tomb[4];
Meg kell adnunk azt is hány darab értéket szeretnénk tárolni. A fenti példában négy darab érték tárolásához foglaltunk memóriát, amely mindegyik „int” azaz egész típusú lehet.
A tömbnek ez után 4 eleme lehet. Minden elemet egy indexel tudunk azonosítani. Az első index a nulla: 0, majd a többi szám. Ebből következik, hogy egy négy elemű tömbnél a legfelső index 3.
A tömb elemeinek úgy adhatunk értéket, hogy leírjuk a tömb nevét, utána szögletes zárójelben megadom, melyik indexű elemet akarom beállítani.
int tomb[4]; tomb[0] = 3; tomb[1] = 8; tomb[2] = 5; tomb[3] = 2;
Felhasználni ugyanígy tudjuk. Ha szeretném például kiíratni a tömb egyik elemét, azt így tehetem meg:
printf("%d\n", tomb[0]);
A tömböknek rögtön kezdőértéket adhatunk a deklarációban. Az alábbi példában erre látunk példát.
Megadható a tömb nagysága:
int tomb[3] = {3, 5, 6};
De ez nem kötelező. Ha viszont megadjuk akkor annak értéke nem lehet kisebb a tömb kezdőértékeinek a számánál.
Ez még helyes.
int tomb[5] = {3, 5, 6};
Ez már helytelen:
int tomb[2] = {3, 5, 6};
A tömböknek lehet két kiterjedése is. Ekkor mátrixról beszélünk. A tömb felépítése ekkor hasonlít a matematikában tanult mátrixhoz, amelynek kiterjedése kétirányú.
Lássunk egy példát egy mátrixra:
3 | 12 | 8 | 9 |
2 | 15 | 17 | 7 |
11 | 4 | 3 | 18 |
Az alábbi egyszerű mátrixot szeretnénk megvalósítani:
3 | 12 | 8 | 9 |
2 | 15 | 17 | 7 |
int tomb[2][4];
Ez két sort és 4 oszlopot jelent.
Értékadás:
int tomb[2][4]; //Első sor: tomb[0][0] = 3; tomb[0][1] = 12; tomb[0][2] = 8; tomb[0][3] = 9; //Második sor: tomb[1][0] = 2; tomb[1][1] = 15; tomb[1][2] = 17; tomb[1][3] = 7;
int tomb2[2][4] = { {3, 13, 8, 9}, {2, 15, 17, 7} };
GCC-ben:
int tomb[10] = {[0 ... 9] = 5};
Egyébként meg egy for ciklussal.
Tömb átadása paraméterként:
#include <stdio.h> void rendez(int *tomb) { if(tomb[0] > tomb[1]) { int tmp = tomb[0]; tomb[0] = tomb[1]; tomb[1] = tmp; } } main() { int tomb[2]; tomb[0] = 3; tomb[1] = 2; rendez(tomb); printf("%d %d\n", tomb[0], tomb[1]); }
Az átadott tömb cím szerint adódik át.
Vannak olyan esetek amelyekben célszerű saját típust létrehozni. Először nézzük meg, hogyan kell saját típust létrehozni.
Általánosan:
typedef típus újtipusnév;
Legyen a példa kedvéért egy új típus amely megegyezik az egészeket tárolni képes int típussal:
typedef int TEgesz;
Ezek után, például így használhatom:
TEgesz szam = 5;
A „TEgesz” azonosítót mint típust használtam fel.
Ennek persze kevés értelme lehet, de annál több haszna van, tömbtípusok létrehozásánál, különösen ha a tömböket paraméterként szeretném átadni. Lássunk egy tömbtípus létrehozását:
typedef int TEgeszTomb[100];
Ezek után így használhatom:
TEgeszTomb tomb; tomb[0] = 5; tomb[1] = 8; //...
Még hasznosabb, amikor a saját típust paraméterlistában használjuk.
Az állományok írása és olvasása a stdio.h könyvtárak függvényei teszik lehetővé. A C nyelvben egy a fájlváltozó típusa FILE. Mutatóként kell létrehoznunk, ezért fájlmutatóról fogunk beszélni. A fájlokat először mindig meg kell nyitni, használat után pedig bezárni. A megnyitásnak több módja is van, amelyek a következő táblázatban találhatók.
Mód | Leírás |
---|---|
„r” | Megnyitás olvasásra. |
„w” | Megnyitás írásra. |
„a” | Megnyitás hozzáfűzésre. |
„r+” | Olvasható-írható mód. Írás a fájl elejére. |
„w+” | Olvasható-írható mód. Az fájl tartalma elvész. |
„a+” | Olvasható-írhatód mód. Kezdetben a fájlmutató elöl van. Az írás viszont a fájl végére történik |
A megnyitott állományt csatorna névvel is szokás illetni. A megnyitás az fopen() függvénnyel történik. Az fopen() szintaxisa:
fájlmutató = fopen(char *állománynév, char *mód)
Például:
FILE *fp; fp = fopen("adat.txt", "w");
alma szilva barack
#include <stdio.h> #include <stdlib.h> main() { FILE *fp = fopen("adat.txt", "r"); char *str = (char*) malloc(30 * sizeof(char)); while(!feof(fp)) { fgets(str, 30, fp); if(!feof(fp)) printf("%s", str); } fclose(fp); }
A fenti program képes az összes sort gond nélkül kiírni, akár van az utolsó sor végén sortörés, akár nincs.
A következő megoldás működik, de csak akkor, ha az utolsó sor után van sortörés:
#include <stdio.h> #include <stdlib.h> main() { FILE *fp = fopen("adat.txt", "r"); char *str = (char*) malloc(30 * sizeof(char)); fgets(str, 30, fp); while(!feof(fp)) { printf("%s", str); fgets(str, 30, fp); } fclose(fp); }
Az adat.txt állományhoz írunk áradatokat.
alma 250 szilva 300 barack 450
#include <stdio.h> #include <stdlib.h> #include <string.h> main() { FILE *fp = fopen("adat2.txt", "r"); char *str = (char*) malloc(30 * sizeof(char)); char *nev = (char*) malloc(30 * sizeof(char)); int ar; while(!feof(fp)) { fgets(str, 30, fp); if(!feof(fp)) { sscanf(str, "%s %d\n", nev,&ar); printf("%s %d\n", nev, ar); } } fclose(fp); }
#include <stdio.h> #include <stdlib.h> #include <string.h> main() { FILE *fp = fopen("adat2.txt", "r"); char *str = (char*) malloc(30 * sizeof(char)); char *nev = (char*) malloc(30 * sizeof(char)); int ar; while(!feof(fp)) { fgets(str, 30, fp); if(!feof(fp)) { sscanf(str, "%s", nev); sscanf(str + strlen(nev), "%d", &ar); printf("%s %d\n", nev, ar); } } fclose(fp); }
#include <stdio.h> #include <stdlib.h> main() { FILE *fp = fopen("adat.txt", "r"); char *str = (char*) malloc(30 * sizeof(char)); char ch; while((ch = fgetc(fp)) != -1) printf("%c", ch); }
A következő példa megegyezik az előzővel, kivéve, hogy a -1 értéket egy állandóból vesszük, melynek neve EOF.
#include <stdio.h> #include <stdlib.h> main() { FILE *fp = fopen("adat.txt", "r"); char *str = (char*) malloc(30 * sizeof(char)); char ch; while((ch = fgetc(fp)) != EOF) printf("%c", ch); }
Ha az állomány megnyitása során probléma van, akkor NULL értékkel tér vissza. Ennek segítségével vizsgálhatjuk, hogy fájl megnyitás sikeres volt-e.
#include <stdio.h> #include <stdlib.h> #include <errno.h> main() { FILE *fp; if((fp= fopen("adat.txta", "r"))== NULL) { perror("Hiba a fájl megnyitása során\n"); printf("Hibaszám: %d.\n", errno); exit(-1); } char *str = (char*) malloc(30 * sizeof(char)); fgets(str, 30, fp); while(!feof(fp)) { printf("%s", str); fgets(str, 30, fp); } fclose(fp); }
Érték szerint átadott paraméterek:
int novel(int szam) { return szam + 1; }
Globális paraméterek
int szam = 0; void novel() { szam = szam + 1; }
Cím szerint átadott paraméterek
void novel(int *szam) { *szam = *szam + 1; } main() { int szam = 0; novel(&szam); }
void* calloc(size_t, size_t) void* malloc(size_t) void* realloc(void*, size_t) void free(void*) void abor(void) // Program vége void exit(int)
int system(const char*) // Egy külső program futtatása char* getenv(const char*) // Környezeti változó lekérdezése void _beep(unsigned int, unsigned int) // hang: frekvencia, másodperc void _sleep(unsigned long) // Várakozás ezredmásodpercig
Szintaxis:
#include <time.h> struct tm *localtime( const time_t *time );
Leírás: A localtime() függvény átkonvertálja a naptári időt helyi időbe.
Szintaxis
#include <time.h> time_t time( time_t *time );
Leírás:
A time() függvény visszatér az aktuális idővel, vagy -1-gyel ha hiba történik. Ha van megadunk egy time paramétert, akkor az aktuális idő eltárolódik ebben a változóban.
Az strftime() függvény dátum és időt formáz a time kimenetből egy formátum sztring alapján. A visszatérési érték szintén egy sztring. Az egyes formátumokhoz tartozó formátum kódok a következő táblázat mutatja:
Kód | Jelentés |
---|---|
%a | a hét napja, rövidítve |
%A | a hét napja, kiírva |
%b | a hónap neve, rövidítve |
%B | a hónap neve, kiírva |
%c | az alapértelmezett dátumformátum |
%d | nap, 2 számjeggyel (01 .. 31) |
%H | óra, 24-órás formátumban, 2 számjeggyel (00 .. 23) |
%I | óra, 12-órás formátumban, 2 számjeggyel (01 .. 12) |
%j | év napja, 3 számjegy (001 .. 366) |
%m | hónap, számmal (01 .. 12) /van kitöltő szóköz/ |
%M | perc, 2 számjegy |
%p | „am” vagy „pm”, az adott időpont délelőtt, vagy délután |
%S | másodperc, 2 számjegy |
%U | az aktuális év hányadik hetében járunk, a hét vasárnappal kezdődik, vagyis az „01” hét január első vasárnapján kezdődik. /Az első hét előtti napokat „00” hétnek jelzi/ |
%w | hét napja számmal, a vasárnap a 0 |
%W | év hete, az év első hétfőjén kezdődik az „01” hét |
%x | alapértelmezett dátumformátum idő nélkül |
%X | alapértelmezett időpontformátum dátum nélkül |
%y | év 2 számjeggyel (00 .. 99) |
%Y | év, évszázaddal együtt |
%Z | időzóna neve, 3 betű |
%% | a „%” karakter |
time_t datum = time(NULL); strftime(sz, sizeof(sz), "%c", &datum); printf("%s\n", sz);
A C nyelvben ha utasításról beszélek, gyakran azt mondjuk helyette, hogy függvény. Ez azért van mert minden utasítás valójában egy függvényként van megvalósítva.
Mivel számítógépről beszélünk, ezért ez monitor, nyomtató vagy más ehhez hasonló eszköz.
A Standard Output az alapértelmezett kimeneti eszközt jelenti. Szokásos módon ez a monitor képernyője. Tehát ha azt mondom „A Standard Outputra írok”, ez azt jelenti, hogy a képernyőre írok.
A Standard Input az alapértelmezett bemeneti eszköz. Szokásos módon ez a billentyűzet. A Standard Inputról olvasás így billentyűzetről bevitt adatokat jelenti.
A Standard Outputot és a Standard Inputot együtt röviden stdio néven rövidítjük.
Amikor írunk egy „saját” függvényt, előfordulhat, hogy a hívó programból adatokat akarunk átadni. Ezeket az adatokat a függvény neve után zárójelbe írjuk és paramétereknek nevezzük.
Megmondjuk a gépnek mit tegyen. Ezt úgy tesszük, hogy leírjuk a nevét. Valójában ez egy függvény neve. A C nyelvben ha nincs egy utasításnak paramétere akkor is kötelező a zárójelek kitétele. Minden utasítást pontosvessző (;) zár le! Létezik üres utasítás is, ami egy pontosvessző önmagában.
A C fordító a forráskód lefordítása előtt elvégez egy ún. előfordítást. Az előfordítás az előfordítói utasításokon történik. Az előfordító utasítások „#” jellel kezdődnek. Előfordítással fordított utasítás típusok:
Példák az előfordítói utasításokra:
#include <stdio.h> #define A 3