[[oktatas:programozás:C|< C]]
====== C programozási nyelv ======
* **Szerző:** Sallai András
* Copyright (c) Sallai András, 2011, 2013, 2014, 2015, 2020, 2023
* Licenc: [[https://creativecommons.org/licenses/by-sa/4.0/|CC Attribution-Share Alike 4.0 International]]
* Web: https://szit.hu
===== 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 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.
{{:oktatas:programozás:c:c_nyelvek_halmaza.png|}}
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
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
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 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.
{{:oktatas:programozas:c:c_fuggveny_reszei.png?400|}}
===== 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:
#include
int main() {
printf("alma");
}
#include
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;
{{:oktatas:programozas:c:tipusokmodositok.png?400|}}
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
#include
int main() {
printf("%d\n", INT_MAX);
}
===== Valós típus =====
Legnagyobb tárolható double típus:
#include
#include
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.
{{:oktatas:programozas:c:allandok.png?400|}}
==== 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. 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
=== 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 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
=== 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;
#include
int main() {
const int a = 5;
printf("%d\n", a);
}
#include
#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.
{{:oktatas:programozas:c:nevesitett_literalis_allando.png?300|}}
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.
{{:oktatas:programozas:c:szoveges_allando.png?400|}}
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)
#include
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.
{{:oktatas:programozas:c:egsz_szam_formazasa.png?200|}}
Az f konverziós karaktert valós számok kiíratására használjuk.
{{:oktatas:programozas:c:valos_szam_formazasa.png?200|}}
A c konverziós karaktert, karakterek kiíratására használjuk.
{{:oktatas:programozas:c:karakter_formazasa.png?200|}}
Az s konverziós karaktert, karaktersorozatok, vagyis szövegek kiíratására használjuk.
{{:oktatas:programozas:c:szoveg_formazasa.png?200|}}
Kiíratás 2 tizedesjegy pontossággal:
#include
int main() {
double a = 31.123456;
printf("%.2f\n", a);
}
Kiíratás 20 szélesen:
#include
int main() {
double a = 31.123456;
printf("%20.2f\n", a);
}
Balra igazítás:
#include
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
#include
#include
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.
#include
#include
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);
#include
#include
int main() {
printf("%f\n", sqrt(9.5));
}
#include
#include
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:
#include
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.
#include
#include
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:
#include
#include
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.
#include
#include
int main() {
printf("%d\n", RAND_MAX);
}
Fordítás és futtatás:
cc -o maxrand maxrand.c
./maxrand
==== Példa 1 ====
#include
#include
#include
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 ====
#include
#include
#include
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.
#include
#include
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);
#include
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.
#include
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:
#include
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".
#include
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ű.
#include
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.
#include
#include
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:
#include
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.
#include
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.
#include
#include
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
int main() {
int a = 5;
if(a > 0)
printf("Pozitív szám\n");
}
==== if-else ====
#include
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.
#include
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
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
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
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.
#include
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
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
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:
#include
#include
#include
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
#include
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
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:
#include
#include
#include
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.
#include
#include
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.
#include
#include
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:
#include
#include
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
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
#include
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:
#include
#include
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:
#include
#include
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:
#include
#include
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:
* [[oktatas:programozás:c:c_példaprogram#sztring_darabolás]]
==== Üres vagy nem üres ====
#include
int main () {
char szoveg[30];
if(szoveg[0]) {
printf("Üres\n");
}
}
#include
#include
int main () {
char szoveg[30];
if(szoveg && szoveg[0]) {
printf("Üres\n");
}
}
#include
#include
int main () {
char szoveg[30];
strcpy(szoveg, "");
if(szoveg[0] == '\0') {
printf("Üres\n");
}
}
==== Összefűzés ====
#include
#include
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 ====
#include
#include
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.
#include
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};
#include
#include
#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 ====
#include
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:
#include
int main() {
int tomb[] = {3, 8, 5, 6, 2, 9 };
int n = sizeof tomb / sizeof *tomb;
int i;
for(i=0; 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;
{{:oktatas:programozas:c:c_ket_dimenzios_tomb.png?400|}}
=== 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:
#include
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.
#include
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 ====
#include
int main() {
FILE *f;
f = fopen("adat.txt","w");
fputs("alma",f);
fputs("szilva",f);
fputs("barack",f);
fclose(f);
}
==== Hozzáfűzés ====
#include
int main() {
FILE *f;
f = fopen("adat.txt","a");
fputs("Valami",f);
fclose(f);
}
==== Olvasás fájlból ====
alma
szilva
barack
#include
#include
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:
#include
#include
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.
alma 250
szilva 300
barack 450
#include
#include
#include
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);
}
#include
#include
#include
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 ====
#include
#include
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.
#include
#include
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.
#include
#include
#include
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
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_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
#define A 3