Felhasználói eszközök

Eszközök a webhelyen


oktatas:programozas:c:c_nyelv

Tartalomjegyzék

< C

C programozási nyelv

Bevezetés

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 1970­es é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á.

Gyakorlat 001

  • Válaszoljon a következő kérdésekre:
    • Ki kezdte fejleszteni a C nyelvet?
    • Milyen operációs rendszerre tervezték a C nyelvet eredetileg?
    • Milyen programozási nyelvből örökölt tulajdonságokat a C nyelv?
    • Mikor fogadták el a C nyelvet ANSI szabványnak?

Helló Világ

#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 fejállományban vagy 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.

Megjegyzés

A C nyelv lehetővé teszi egy és többsoros megjegyzés elhelyezését.

// egy soros megjegyzés
/* több
   soros
   megjegyzés
*/

Gyakorlat 002

  • Írjon programot, ami kiírja a településének a nevét, és irányító számot egy újabb sorba.
  • Az előző programban, írja többsoros megjegyzésbe saját nevét.

Kivitel

Ha leírok egy karaktersorozatot, például alma, a printf() függvény a képernyőre írja:

Program01.c
#include <stdio.h>
 
int main() {
    printf("alma");
}
Program01.c
#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.

Escape szekvenciák

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
\% Százalékjel

Gyakorlat 003

  • Írjon programot, ami „Kék János” nevet.
    • A következő sorba, tabulátorral tagolva a fizetését írja ki, ami 2856000.
    • A program írja ki a saját (szerző) nevét is, a „szerző: ” szöveg után.

Változók és adattípusok

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:

nagyegesz.c
#include <stdio.h>
#include <limits.h>
 
int main() {
    printf("%d\n", INT_MAX);
}

Valós típus

Legnagyobb tárolható double típus:

valos.c
#include <stdio.h>
#include <float.h>
int main() {
    printf("double: %g\n", DBL_MAX);
}
printf("float : %g\n", FLT_MAX);
FLT_MAX
DBL_MAX

Gyakorlat 004

  • Válaszoljon a következő kérdésekre:
    • Mi a legnagyobb tárolható szám, ha short típust adok meg?
    • Mi a legnagyobb tárolható szám, ha char típust adok meg?
    • Mi annak az állandónak a neve, amiből kideríthető, mi legnagyobb tárolható int típus?

Állandók vagy konstansok

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.

Literális állandó

Lehet szám, karakter, karaktersorozat, stb.

Számállandó

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

Karakteres állandó

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. escape­sorozatokat (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

Szöveges állandó

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.

Nevesített állandó

#define

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 TELJESNEV­et 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

const

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;
konst.c
#include <stdio.h>
int main() {
    const int a = 5;
    printf("%d\n", a);
}
konst2.c
#include <stdio.h>
#define MERET 3
typedef double Ttomb[MERET];
int 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 const módosító mondja meg, hogy nevesített állandót szeretnénk, létrehozni. Amikor a programozó állandót hoz létre, azt vállalja, hogy ezt a memória területet a program tovább része alatt nem szeretné megváltoztatni.

Gyakorlat 005

  • Írjon programot, amely két állandót tartalmaz:
    • a programban használja a #define utasítást
    • az egyik szám, egy iskolában adható legkisebb osztályzatot tartalmazza
    • a másik szám, az iskolában adható legnagyobb osztályzatot tartalmazza
    • az állandók neveit válassza meg tetszőlegesen

Deklarációk

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[ ]);

Gyakorlat 006

  • Válaszoljon a következő kérdésekre:
    • Milyen nyelvet tanulunk most?
    • Ki találta ki ezt a nyelvet amit tanulunk?
    • Mikor let a nyelv kifejlesztve?
    • Mire használjuk?
    • Van-e szabványosított verziója?
    • Mi az az előfordítás?
    • Mivel kezdjük az előfordítói utasításokat?
    • Mi az a konstans?
    • Hogyan hozunk létre C nyelvben konstanst?
    • Milyen az írásmódja a változóknak és a konstansoknak?
    • A C nyelv kis és nagybetű érzékeny?
    • A C nyelv kiírató utasítása?
    • Hol nem szükséges a C nyelvben az utasítás végére (;) pontosvessző?

Operátorok

Kétfajta operátort különböztetünk meg:

  • egy operandusú
  • két operandusú

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

Aritmetikai műveletek

+  ö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.

Relációs műveletek

>
<
>=
<=
==  Egyenlő
!=  Nem egyenlő

Az eredményük vagy 0 (hamis) vagy 1 (igaz)

main.c
#include <stdio.h>
int main() {
	int a = 3;
	int b = 5;
	int e = a < b;
	printf("%d\n", e);
}

Logikai műveletek

&&    É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)

Értékadás másként

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

Növelő és csökkentő operátorok

++
--

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 logikai operátorok

& 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

Példa a bitenkénti balra léptetésre

000011 nem számít mennyit jelent
000010 3-at jelent, ennyivel léptetünk
001100 eredmény

Komplemens képzés

a = 6 0000 0110
~a 1111 1001 249

Precedencia

() 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.

Gyakorlat 007

  • Válaszoljon a következő kérdésekre:
    • Mire való a % operátor?
    • Mire való a && operátor?
    • Mire való a || operátor?
    • Mire való a ++ operátor?

Formázott kivitel

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:

  • mínusz jel, ami konvertált argumentumot balra igazítja
  • egy szám, ami megadja a minimális mezőszélességet
  • egy pont, ami elválasztja a mezőszélességet a pontosságot megadó számtól
  • egy szám (pontosság)
    • karaktersorozatnál a kiírandó karakterek maximális számát
    • lebegőpontos számok esetén a tizedespont után kiírt számjegyek számát
    • egész karakterek esetén a kiírt számjegyek maximális számát
  • egy h betű, ha egy egész számot short típusként vagy egy l betű, ha long típusként írun ki

A leggyakrabban használt konverziós karakterek, a d, f, c és s.

A d konverziós karaktert egész számok kiíratására használjuk.

Az f konverziós karaktert valós számok kiíratására használjuk.

A c konverziós karaktert, karakterek kiíratására használjuk.

Az s konverziós karaktert, karaktersorozatok, vagyis szövegek kiíratására használjuk.

Kiíratás 2 tizedesjegy pontossággal:

main.c
#include <stdio.h>
int main() {
	double a = 31.123456;
	printf("%.2f\n", a);
}

Kiíratás 20 szélesen:

main.c
#include <stdio.h>
int main() {
	double a = 31.123456;
	printf("%20.2f\n", a);
 
}

Balra igazítás:

main.c
#include <stdio.h>
int main() {
	double a = 31.123456;
	printf("|%-20.2f|\n", a);
}
formátum karakter típus Használatra példa
ld long
d int, short, char
f float
c char, int
Lg long double
% százalék jelet ír a képernyőre printf("%%\n");

Gyakorlat 008

  • Válaszoljon a következő kérdésekre:
    • Milyen formátum karakter szükséges egy egész érték kiíratásához?
    • Milyen formátum karakter szükséges egy valós érték kiíratásához?
    • Milyen formátum karakter szükséges egy karakter kiíratásához?
    • Milyen formátum karakter szükséges egy karaktersorozat kiíratásához?

Matematikai függvények

A matematikai függvények a math.h nevű fejállományban vannak.

Állandók

PI értéke

M_PI		3.14159265358979323846
main.c
#include <stdio.h>
#include <math.h>
int main() {
	printf("%f\n", M_PI);
}

Szinusz

A trigonometriai függvények szög helyett radiánban várják az értéküket. A feladatokban általában szögben adják meg az értékeket, amit át kell számolnunk radiánban.

Ha például van 30 fokunk, akkor meg kell szorozni PI értékével, majd az eredményt el kell osztani 180-nal.

main.c
#include <stdio.h>
#include <math.h>
 
int main() {
	printf("%f\n", sin(30 * M_PI / 180));
 
}

Készítsük el példaprogramot, majd fordítsuk és futtassuk.

A matematikai függvények használata esetén szükség van egy új kapcsolóra, a -l. A -l értéke egy „m” betű, utalva a math-ra. Egy matematikai függvény használata esetén, tehát így fordítunk egy C forráskódot:

cc -l m -o main main.c

Függvények

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);          // Lelfele 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);
main.c
#include <stdio.h>
#include <math.h>
int main() {
	printf("%f\n", sqrt(9.5));
}
trig.c
#include <stdio.h>
#include <math.h>
int main() {
        printf("%f\n", pow(2,8));
}

Az abszolút érték egész számokkal

Érdekesség, hogy az abs() függvény másik programozói könyvtárban van, a stdlib.h könyvtárban.

Szintaxis:

int abs(int)      //Abszolút érték

Használata, például:

main.c
#include <stdlib.h>
 
int main() {
    int szam = -4;
    int ered = abs(szam);
}

Ha fordításnál kihagyjuk az stdlib.h programozói könyvtárat, akkor figyelmeztetést kapunk:

warning: implicit declaration of function 'abs' [-Wimplicit-function-declaration]
  int ered = abs(szam);

Az újabb GCC 5-s verzióban az alapértelmezetten C szabvány a C11. Ez megköveteli, hogy szerepeltessük az #include fordítási direktívával, a stdlib.h könyvtárat.

A fordító rávehető, hogy C89/C90 szabványt használjon, ami nem találja hibának, ha nem fordítjuk az stdlib.h könyvtárat hozzá.

cc -std=gnu89 -o main main.c

Az alapértelmezett C11 szabványt nem kell megadni, de megadható:

cc -std=gnu11 -o main main.c

Egész osztás

Az stdlib.h fejállományban van egy div() függvény, amellyel egész osztást végezhetünk. A div() függvény egy div_t struktúrát ad vissza.

oszt.c
#include <stdio.h>
#include <stdlib.h>
 
int main() {
    div_t a = div(5, 2);
    printf("%d\n", a);
}

A struktúra így néz ki:

typedef struct
  {
    int quot;
    int rem;
  } div_t;

Mivel az eredményt struktúrában kapjuk, kiírathatjuk külön az osztás eredményét, és a maradékot is:

oszt.c
#include <stdio.h>
#include <stdlib.h>
 
int main() {
    div_t a = div(5, 2);
    printf("%d\n", a.quot);
    printf("%d\n", a.rem);
}

Gyakorlat 009

  • Válaszoljon a következő kérdésekre:
    • Mi a math.h?
    • A maradék képzést milyen operátorral valósítjuk meg?
    • Mondjon két egy operandusú operátort.
    • Mondjon négy bitenkénti operátort.
    • Milyen utasítással írathatunk ki formázott kivitelt.
    • Formázott kivitel esetén, valós számot milyen formátumkarakterrel tudunk kiíratni?

Véletlen szám

Függvények

Egy számítógépen nem könnyű valódi véletlen számot generálni, mivel minden folyamat jól meghatározott algoritmusok mentén fut. A C nyelvben az stdlib.h fejállományban (programozói könyvtár), van definiálva két függvény, ami véletlen szám generálást, segíti.

A következő táblázatban ezeket látjuk:

int rand(void) Véletlen szám generálás
void srand(unsigned int) A véletlen szám generálás előkészítése

A két függvény az ANSI szabvány része.

A rand() függvény 0 és egy nagy egész szám között generál véletlen számot. A nagy egész szám a stdlib.h fejállományban van definiálva, neve RAND_MAX, ami egy állandó. Ez tartalmazza a legnagyobb generálható egész számot. 64 bites Debian GNU/Linux 10 verzióban ez: 2147483367.

Általában nem ilyen számra van szükség van szükség, helyette valamilyen adott tartományban van véletlen számra szükségünk.

Ezért a kapott véletlen számon, elvégzünk egy egész osztást, és annak maradékát vesszük figyelembe. Ha például 0 és 3 közötti számra van szükségünk (ebbe bele értve a 0 és a 3-t is, akkor, 4-gyel osztunk. A kapott maradék 0, 1, 2 vagy 3 lesz. Ez lesz a véletlen számunk.

Ha 1, 2, 3 és 4-s számok közül szeretnénk véletlenszerűen számokat kapni, akkor az előző eredményhez adjunk hozzá 1-t. A véletlen szám generálása, ekkor így néz ki:

int vel = rand() % 4 + 1;

Az srand() függvényre azért van szükségünk, mert nélküle mindig ugyanazt a véletlen számot kapjuk. Paraméterként egy time() függvény szokás futtatni NULL paraméterrel:

srand(time(NULL));
int vel1 = rand() % 4 + 1;
int vel2 = rand() % 4 + 1;

A time() függény a time.h fejállományban található.

Maximum kiíratása

Írjunk egy programot, ami kiírja a legnagyobb generálható egész számot.

maxrand.c
#include <stdio.h>
#include <stdlib.h>
 
int main() {
    printf("%d\n", RAND_MAX);
}

Fordítás és futtatás:

cc -o maxrand maxrand.c
./maxrand

Példa 1

veletlen.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
 
int main() {
    printf("Véletlen\n");
    srand(time(NULL));
    printf("0 és 4 között: %d\n", rand() % 5);
    printf("20 és 29 között: %d\n", rand() % 10 +20);
    return 0;
}

Újabb példa

veletlen.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
	srand(time(NULL));
 
	int a = rand() % 2;	
	int b = rand() % 2;	
 
	printf("Elso   : %d\n", a);
	printf("Masodik: %d\n", b);
 
}

Valódi véletlen szám

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.

valid.c
#include <stdio.h>
#include <stdlib.h>
 
int veletlen(int n) {
	unsigned int vel;
	FILE *f;
 
	f = fopen("/dev/urandom", "r");
	fread(&vel, sizeof(vel), 1, f);
	fclose(f);
 
	return vel % n;
}
 
int main() {
	printf("%d\n", veletlen(6));
}

Gyakorlat 010

  • Válaszoljon a következő kérdésekre:
    • Milyen fejállományban van a rand() függvény?
    • Milyen fejállományban található a time() függvény?
    • Milyen fejállományban található a RAND_MAX állandó?
    • Milyen fejállományban található a srand() függvény?
  • Írjon programot, amely kockadobást szimulál.
    • Mentés: kocka.c
  • Írjon programot, amely pókerhez 5 kockadobást szimulál.
    • Mentés: poker.c

Konverzió

Típusátalakítások

Egy int típusú változó értéke gond nélkül áttehető egy short típusú változóba, amíg az elfér a a short típusban.

int a = 35;
short b = a;

A short típusban a legnagyobb tárolható szám: 32767. Mi történik, akkor, ha 32768 számot tesszük „a” változóba, ami int típusú?

int a = 32768;
short b = a;

Az eredeti szám ilyenkor nem marad meg, értékvesztés történik.

Ha valós típusú változó értékét tesszük egész típusú változóba, akkor a törtrészek elvesznek, de a típusátalakítás működik:

double a = 35.123;
int b = a;

Karaktersorozatok és számok közötti konverziók

A függvények az stdlib.h programozói könyvtárban találhatók.

double atof(const char*)
int atoi(const char*)
long atol(const char*)
 
int ltoa(int, char*, int)  

String egész számmá

A következő példában egy s változót hozunk létre, amelyben 2 karakter szeretnénk tárolni. A C nyelvben nincs karaktersorozat (string) számára típus. Ezért egy karakter tömböt fogunk használni. A char s[3], egy tömb amiben karaktereket tárolhatunk, összesen 3 darabot.

Mi csak két karakter szeretnénk tárolni „15”, de a karaktersorozatot lezáró \0 érték számára is helyet kell foglalni, ezért 3 byte számára foglalunk helyet.

char s[3];
strcpy(s, "15");
int a = atoi(s);

Az atoi() függvény a stdlib.h programozói könyvtárból érhető el. Az strcpy() függvény a string.h programozói könyvtárból érhető el.

A helyfoglalást, néha a malloc() függvénnyel szokták megoldani, amelynek a használata kicsit összetettebb, de az eredmény egyenértékű a fenti megoldással:

char *s = (char*) malloc(5 * sizeof(char));
strcpy(s, "15");
int a = atoi(s);

A (char*) típusátalakítás jelent. A malloc() a típusfoglalást után void típust ad vissza, de nekünk char* típusra van szükség.

Ha azt írom char s; akkor egyetlen karakter akarok tárlni az s változóban. Ha azt írom char *s; akkor egy karaktersorozatra (string) mutató értéket szeretnék tárolni s-ben.

String hosszú egész számmá

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.

String valós számmá

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.

Egész stringgé

char *s = (char*) malloc(10 * sizeof(char));
int a = 15;
sprintf(s, "%d", a);

Egész stringgé

char *s = (char*) malloc(10 * sizeof(char));
float a = 15.7;
sprintf(s, "%f", a);

Karakter stringgé

char *s = (char*) malloc(10 * sizeof(char));
char ch = 'a';
sprintf(s, "%c", ch);

String karakterré

char *s = (char*) malloc(10 * sizeof(char));
strcpy(s, "a");
char ch = s[0];

Gyakorlat 011

  • Válaszoljon a következő kérdésekre:
    • Milyen függvénnyel lehet átkonvertálni egy karaktersorozatot (string), egész számmá?

Bevitel

Szám bekérése

Bekéréshez használhatjuk a scanf() függvényt, ami a stdio.h fejállományban van leírva. A scanf() a printf() függvényhez hasonlóan használható.

Formátumkarakterek
Karakter mikor használható
%d egész szám bekérése
%f valós szám bekérése
%lf valós szám bekérése double típusú változóba
%c karakter bekérése
%s egy szó bekérése

A scanf() függvénynek minimum két paramétere van. Az első a formátumstring, utána pedig annyi változó, amennyi a formátumstringben megadtunk, vesszővel tagolva. A formtáumstringbe egy változó leírását a „%” karakterrel kezdjük, és egy vagy két formátumkarakterrel fejezzük be. Egy egész szám bekérése például:

scanf("%d", &a);
prog.c
#include <stdio.h>
 
int main() {
	int a;
	printf("Szam: ");
	scanf("%d", &a);
	printf("Ezt írtad: %d\n", a);
}

Ebben a példában egyetlen egész számot kérünk be az „a” változóba. Vegyük észre az „a” változó előtt a „&” karaktert. Erre azért van szükség, mert a második paraméter nem a változó neve, hanem annak címe kell legyen.

Ebből következik, hogy egy változó címét úgy kapjuk meg, hogy „&valtozonev” formát használom.

Több változó is bekérhető egyszerre. A következő példában egyszerre két változót kérünk be.

prog.c
#include <stdio.h>
 
int main() {
	int szam1, szam2;
	printf("Két szám: ");
	scanf("%d %d", &szam1, &szam2);
	printf("Ezeket írtad: %d %d\n", szam1, szam2);
}

A program végrehajtásakor, ilyenkor az első szám után 1 vagy több szóközt, tabulátor vagy sortörést (whitespace karakterek) írok. az első bekért szám után egy Enter is írható, így a második számot a következő sorba írom be.

Valós szám bekérése

Ha egy float típusú változóba kérünk be számot, a formátumkarakter „f” karakter:

prog.c
#include <stdio.h>
 
int main() {
    float szam1;
    printf("Valós szám: ");
    scanf("%f", &szam1);
    printf("Ezt írtad: %f\n", szam1);
}

Dupla pontos szám bekérésénél a formátumkarakterből kettő kell: „lf”.

prog.c
#include <stdio.h>
 
int main() {
    double szam1;
    printf("Valós szám: ");
    scanf("%lf", &szam1);
    printf("Ezt írtad: %f\n", szam1);
 
}

Karakter bekérése

Egy karakter bekérése esetén a formátumkarakter egy „c” betű.

prog.c
#include <stdio.h>
 
int main() {
    char ch;
    printf("Karakter: ");
    scanf("%c", &ch);
    printf("Ezt írtad: %c\n", ch);
}

Szó bekérése

A scanf() függvény alkalmas karaktersorozat beolvasására. A scanf() azonban a whitespace karaktereket szeparátornak tekinti, ezért csak szavakat képes beolvasni.

Az alábbi program képes beolvasni egy nevet, amiben nincs szóköz vagy tabulátor.

prog.c
#include <stdio.h>
#include <stdlib.h>
int main() {
	char *nev;
	nev = (char*) malloc(100 * sizeof(char));
	printf("Név: ");
	scanf("%s", nev);
	printf("Ezt írtad: %s\n", nev);
}

Vegyük észre, hogy a scanf() függvény, második paraméterében, a nev változó neve elé nem tettünk „&” karaktert, pedig azt írtuk, hogy oda cím kell. Azért nem szükséges, mert a nev változót eleve mutató típusnak deklaráltam, ez a „*” karakter jelzi a kódban:

char *nev;

Ez pedig azt jelenti, hogy eleve a változó címével dolgozunk.

A char nev[30] egy karaktertömb, amiben maximum 30 karakter tárolható. Így is használható, így nem szükséges külön malloc() vagy hasonló függvénnyel helyet foglalni a nev változónak:

char nev[30]

Egy konkrét példa a használatra:

prog.c
#include <stdio.h>
 
int main() {
    char nev[30];
    printf("Keresztnév: ");
    scanf("%s", nev);
    printf("Ezt írtad: %s\n", nev);
}

A bekérésnél itt sem kell a „&” karakter.

Mondat bekérése

Az fgets() függvény az állományok olvasására használható és az stdio.h fejállományban található. Az stdio.h fejlécállományban deklarálva van egy stdin nevű fájl is, amely billentyűzetet szimbolizálja. Így egy valódi állomány helyett a billentyűzetet is olvashatjuk az fgets() függvénnyel.

prog.c
#include <stdio.h>
 
int 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ést törölni, ami az Enter hatására kerül az adatszerkezetbe, 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és-e. Ha igen, akkor lecseréljük karaktersorozat végét jelölő null karakterre.

prog.c
#include <stdio.h>
#include <stdlib.h>
int main() {
	char *nev;
	nev = (char*) malloc(100 * sizeof(char));
	printf("Név: ");
	fgets(nev, 100, stdin);
	printf("Ezt írtad: %s\n", nev);
}

A Karaktersorozat fejezetben még lesz szó, azok bekéréséről.

Gyakorlat 012

  • Válaszoljon a következő kérdésekre:
    • Milyen fejállományban található a scanf() függvény?
    • Mire használhatjuk a scanf() függvényt?
    • Melyik függvénnyel lehet mondatokat bekérni? scanf() vagy fgets()

Szelekció

if

Bizonyos utasítások végrehajtását feltételhez köthetjük.

#include <stdio.h>
 
int main() {
	int a = 5;
	if(a > 0)
		printf("Pozitív szám\n");
}

if-else

Program01.c
#include <stdio.h>
 
int main() {
	int a = 5;
	if(a > 0)
		printf("Pozitív szám\n");
	else
		printf("Nulla vagy negatív\n");
 
}

switch

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.

Program01.c
#include <stdio.h>
 
int 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.

Gyakorlat 013

  • Írjon programot, amely -2 és 2 között generál egy számot.
    • A lehetséges számok -2, -1, 0, 1, 2
    • Ha szám nagyobb mint 0, írja a képernyőre, hogy a következőt: „Pozitív”
  • Az előző programot, egészítse ki:
    • 0 vagy negatív szám esetén írja a képernyőre: „Nulla vagy negatív”
  • Az előző programot, írja át:
    • 0-nál nagyobb szám esetén kiírja: „Pozitív”
    • 0 esetén „Nulla”
    • 0-nál kisebb szám esetén: „Negatív”

Iteráció

for

A for() utasítás, egy növekményes ciklusok számára megvalósított eszköz. Három paramétere van. A paramétereket „;” karakterrel tagoljuk. Az első paraméter a kezdőérték. A második paraméter a feltétel, amely tartalmazza, meddig ismétlődjön a ciklus. Az utolsó paraméter a növekmény. Itt növeljük a i változó tartalmát.

for(kezdőérték ; feltétel ; növekmény ) {
    //ciklus törzse
}

A ciklus törzsébe írjuk azokat az utasításokat, amelyek szeretnénk minden egyes iterációban végrehajtani. A ciklus törzsét „{” karakterrel kezdjük, és „}” karakterrel zárjuk. Ha csak egyetlen utasításunk van, nem kötelező a kapcsos zárójelek használata. A kód átláthatósága érdekében, azonban ajánlott használni mindig.

#include <stdio.h>
 
int main() {
	int i;
	for(i=0; i<10;i++)
		printf("%d\n", i);
}

while

A while() ciklusnak egyetlen paramétere van, a feltétel. Ha feltétel igaz, a ciklus törzsében található utasítások végrehajtódnak. Ha hamis, a ciklus véget ér.

#include <stdio.h>
 
int main() {
	int i=0;
	while(i<10) {
		printf("%d\n", i);
		i++;
	}
}

do-while

A do-while, egy hátul tesztelő ciklus. A ciklus törzsét először végrehajtja, és csak ez után vizsgálja meg, hogy a feltétel igaz vagy hamis. Ha feltétel igaz, a ciklus törzsét újra végrehajtja. Ha hamis, a ciklus véget ér.

#include <stdio.h>
 
int main(){
	int i=0;	
	do {
		printf("%d\n", i);
		i++;
	}while(i<10);
}

Gyakorlat 014

  • Írjon programot, amely véletlen számokat generál.
    • Mentés: nulig.c
    • A program írja ki a generált véletlen számot.
    • Ha a szám nem 0, akkor generáljon újabb számot.

Összetett típusok

enum

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.

enum.c
#include <stdio.h>
 
int 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.

Gyakorlat 015

  • Írjon programot, amelyben az év hónapjait egy enum szerkezetben tárolja.
    • Mentés: havi.c
    • Írassa ki a hónapokat az 5-dik hónapot.

Karaktersorozat

Tárolás

A karaktersorozat (angolul string) típus nem létezik a C nyelvben. Ha karaktersorozatot 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'

A példa kedvéért legyen a „Hello” szöveg eltárolva a fe1011 memóriacímtől kezdve. Az alábbi táblázat mutatja, mit tartalmaz a memória.

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 karaktersorozat végére tenni, mert azt a fordító automatikusan megteszi. Egy ilyen mutató létrehozása a következő módon valósítható meg:

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ényből egyet szoktunk használni:

  • malloc()
  • calloc()

Mindkettő 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ípust 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>
 
int 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 rendszeren. Erre a sizeof() operátort használhatjuk. Az általa visszaadott értéket szorozzuk be 30-al:

#include <stdlib.h>
int main() {
    char *s;
    s = (char*) malloc(30 * sizeof(char));
}

Ha szeretnénk megváltoztatni a lefoglalt hely méretét az következő utasítással tehetjük meg:

  • realloc()

A lefoglalt hely felszabadítása:

  • free()

Példa:

prog.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
int main() {
    char *nev;
    nev = (char*) malloc(10* sizeof(char));
    strcpy(nev, "Lajos");
    printf("%s\n", nev);
 
    nev = (char*) realloc(nev, 30 * sizeof(char));
    strcpy(nev, "Ferdinándházi Lajos");
    printf("%s\n", nev);
 
    free(nev);
}

A változó használata

Most már használhatjuk a „s” változót. A C nyelvben egy karaktersorozatot nem adhatunk meg egy egyenlőség jel jobboldalán. Ha egy változóba 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>
int main() {
    char *gyumolcs;
    gyumolcs = (char*) malloc(30 * sizeof(char));
    strcpy(gyumolcs, "szilva");
}

Ha tömböt használunk, akkor nem szükséges külön helyfoglaló utasítást használni, például a malloc(), mivel a eleve helyfoglalás történik. Legyen a példa kedvéért egy „nev” nevű tömb, amiben 30 karakternek foglalok helyet:

char nev[30];

Itt lefoglaltam a helyet 30 karakter számára, nem kell külön malloc() függvény.

A használatra példa:

#include <string.h>
int main() {
    char gyumolcs[30];    
    strcpy(gyumolcs, "szilva");
}

Kiíratás

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:

gyumolcs.c
#include <stdio.h>
#include <stdlib>
#include <string.h>
int main() {
    char *gyumolcs;
    gyumolcs = (char*) malloc(30 * sizeof(char));
    strcpy(gyumolcs, "szilva");
    printf("%s\n", gyumolcs);
}

Bekérés

A karaktersorozatok bekéréséről már volt szó a Bekérés című fejezetben. Itt most átismételjük a tanultakat.

bekerstr.c
#include <stdio.h>
#include <stdlib>
 
int main() {
    char *gyumolcs;
    gyumolcs = (char*) malloc(30 * sizeof(char));
    scanf("%s" gyumolcs);
    printf("%s\n", gyumolcs);
}

Ha egy változóba egy értéket szeretnénk beolvasni a scanf() függvénnyel, akkor sosem magát a változó nevét kell megadni, hanem annak címét. 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.

Mondatok beolvasását az fgets() függvénnyel valósítjuk meg. Rendelkezésre áll a gets() függvény is, de használata biztonsági kockázatot jelent.

Példa az fgets() használatára.

mondat.c
#include <stdio.h>
#include <stdlib.h>
 
int main() {
	char *mondat;
	mondat = (char*) malloc(30 * sizeof(char));
	printf("Mondat: ");
	fgets(mondat, 30, stdin);
	printf("Ezt írtad: %s\n", mondat);
}

Az fgets() valójában fájlok olvasására lett kitalálva, de van egy speciális fájl, aminek a neve stdin. Ha ezt a fájlt olvassuk, akkor billentyűzet leütéseit kapjuk meg.

A nem ajánlott gets() függvényre példa:

noget.c
#include <stdio.h>
#include <stdlib.h>
 
int main() {
	char *mondat;
	mondat = (char*) malloc(30 * sizeof(char));
	printf("Mondat: ");
	gets(mondat);
	printf("Ezt írtad: %s\n", mondat);
}

Karaktersorozat darabolása

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:

kardarab.c
#include <stdio.h>
#include <stdlib.h>
int 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:

kardarab2.c
#include <stdio.h>
#include <stdlib.h>
int 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:

kardarab3.c
#include <stdio.h>
#include <stdlib.h>
int 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:

kardarab4.c
#include <stdio.h>
#include <stdlib.h>
int 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:

Üres vagy nem üres

prog.c
#include <stdio.h>
 
int main () {
    char szoveg[30];
    if(szoveg[0]) {
        printf("Üres\n");
    }
}
prog.c
#include <stdio.h>
#include <string.h>
 
int main () {
    char szoveg[30];
    if(szoveg && szoveg[0]) {
        printf("Üres\n");
    }
}
prog.c
#include <stdio.h>
#include <string.h>
 
int main () {
    char szoveg[30];
    strcpy(szoveg, "");
    if(szoveg[0] == '\0') {
        printf("Üres\n");
    }
}

Összefűzés

prog.c
#include <stdio.h>
#include <string.h>
 
int main () {
    char szoveg1[30];
    char szoveg2[30];
 
    strcpy(szoveg1, "alma");
    strcpy(szoveg2, "körte");
    strcat(szoveg1, szoveg2); 
    printf("Szöveg: %s\n", szoveg1);
 
}

Összehasonlítás

prog.c
#include <stdio.h>
#include <string.h>
 
int main () {
    char szoveg[30];
    strcpy(szoveg, "alma");
 
    if(!strcmp(szoveg, "alma")) {
        printf("Egyezik\n");
    }else {
        printf("Nem egyezik\n");
    }  
}

Gyakorlat 016

  • Írjon programot, amelyben bekéri egy dolgozó nevét, települését és fizetését.
    • Mentés: dole.c

Tömb

Vektor

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]);

Tömb kezdőértékkel

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.

tomb1.c
#include <stdio.h>
int main() {	
	int tomb[] = {3, 5, 6, 7, 8, 9 };
	printf("%d\n", tomb[0]);
}

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};

tomb.c
#include <stdio.h>
#include <math.h>
#define MAX 2
typedef int Ttomb[MAX];
 
void kiir(int *tomb) {
	tomb[0]=8;	
}
 
int main() {	
	Ttomb tomb;
 
	tomb[0] = 5;
	tomb[5] = 3;
 
	kiir(tomb);
	printf("%d\n", tomb[0]);
}

Tömb bejárása

tomb.c
#include <stdio.h>
int main() {	
	int tomb[] = {3, 8, 5, 6, 2, 9 };
    int i;
    for(i=0; i<6; i++) {
    	printf("%d\n", tomb[i]);
    }
}

Tömb mérete

int tomb[] = {3, 8, 5, 6, 2, 9 };
int n = sizeof tomb / sizeof tomb[0];

Vagy:

int tomb[] = {3, 8, 5, 6, 2, 9 };
size_t meret = sizeof(tomb) / sizeof(tomb[0]);

Vagy:

int tomb[] = {3, 8, 5, 6, 2, 9 };
int n = sizeof tomb / sizeof *tomb;

Komplett példa:

tomb.c
#include <stdio.h>
int main() {	
	int tomb[] = {3, 8, 5, 6, 2, 9 };
    int n = sizeof tomb / sizeof *tomb;
    int i;
    for(i=0; i<n; i++) {
    	printf("%d\n", tomb[i]);
    }
}

Mátrix

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

Mátrix létrehozása C nyelven

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;

Mátrix kezdőértéke

int tomb2[2][4] = {
	{3, 13, 8, 9},
	{2, 15, 17, 7}
};

Feltöltés

GCC-ben:

int tomb[10] = {[0 ... 9] = 5};

Egyébként meg egy for ciklussal.

Tömb paraméterként

Tömb átadása paraméterként:

rendez.c
#include <stdio.h>
 
void rendez(int *tomb) {
	if(tomb[0] > tomb[1]) {
		int tmp = tomb[0];
		tomb[0] = tomb[1];
		tomb[1] = tmp;
	}
}
 
int 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.

Gyakorlat 017

  • Írjon programot, amelyben valós számokat tárol.
    • Mentés: valostomb.c

Saját típus

A saját típusok alapjai

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;
//...

Saját típus a paraméterlistában

Még hasznosabb, amikor a saját típust paraméterlistában használjuk.

main.c
#include <stdio.h>
 
typedef TEgeszTomb[100];
 
 
int sum(TEgeszTomb ptomb) {
	int osszeg = 0, i;
	for(i=0; i<5; i++)
		osszeg = osszeg + ptomb[i];
	return osszeg;
}
 
int main() {
 
	TEgeszTomb tomb = {3, 5, 8, 2, 7};
 
	printf("%d\n", sum(tomb));
 
}

Fájlkezelés

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");

Írás

main.c
#include <stdio.h>
int main() {
	FILE *f;	
	f = fopen("adat.txt","w");	
	fputs("alma",f);
	fputs("szilva",f);
	fputs("barack",f);
	fclose(f);
}

Hozzáfűzés

main.c
#include <stdio.h>
int main() {
	FILE *f;	
	f = fopen("adat.txt","a");	
	fputs("Valami",f);	
	fclose(f);
}

Olvasás fájlból

adat.txt
alma
szilva
barack
olvas.h
#include <stdio.h>
#include <stdlib.h>
 
int 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:

olvas.c
#include <stdio.h>
#include <stdlib.h>
 
int 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.

adat.txt
alma 250
szilva 300
barack 450
olvas.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int 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);
}
olvas.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int 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);
}

Olvasás karakterenként

olvaskar.c
#include <stdio.h>
#include <stdlib.h>
int 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.

olvaseof.c
#include <stdio.h>
#include <stdlib.h>
int main() {
 
	FILE *fp = fopen("adat.txt", "r");
	char *str = (char*) malloc(30 * sizeof(char));
 
	char ch;
	while((ch = fgetc(fp)) != EOF)		
		printf("%c", ch);
 
}

Hibakezelés

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.

hibakez.c
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int 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);
 
}

Függvények

É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;
}
 
int main() {
    int szam = 0;
    novel(&szam);
}

stdlib.h

Memóriafoglalás

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)

Egyéb

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

time.h

localtime()

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.

time()

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.

strftime

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

Példa

time_t datum = time(NULL);
strftime(sz, sizeof(sz), "%c", &datum);
printf("%s\n", sz);

Fogalmak

Függvény

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.

Kimeneti eszköz

Mivel számítógépről beszélünk, ezért ez monitor, nyomtató vagy más ehhez hasonló eszköz.

Standard Output

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.

Standard Input

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.

Paraméter

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.

Utasítás

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.

Előfordító

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:

  • fájlok beépítése
  • makrók
  • feltételes fordítás

Példák az előfordítói utasításokra:

#include <stdio.h>
#define A 3
oktatas/programozas/c/c_nyelv.txt · Utolsó módosítás: 2023/03/14 21:58 szerkesztette: admin