1 / 51. C alapok. nem kiadasi verzio. sok helyen nem pontos informaciok talalhatoak!

Hasonló dokumentumok
Programozás alapjai C nyelv 4. gyakorlat. Mit tudunk már? Feltételes operátor (?:) Típus fogalma char, int, float, double

Mit tudunk már? Programozás alapjai C nyelv 4. gyakorlat. Legnagyobb elem keresése. Feltételes operátor (?:) Legnagyobb elem keresése (3)

Járműfedélzeti rendszerek II. 1. előadás Dr. Bécsi Tamás

1.1. A forrásprogramok felépítése Nevek és kulcsszavak Alapvető típusok. C programozás 3

3 A C programozási nyelv szintaktikai egységei

A C programozási nyelv I. Bevezetés

Java II. I A Java programozási nyelv alapelemei

Vezérlési szerkezetek

A C programozási nyelv I. Bevezetés

C programozási nyelv Pointerek, tömbök, pointer aritmetika

Programozás alapjai. (GKxB_INTM023) Dr. Hatwágner F. Miklós augusztus 29. Széchenyi István Egyetem, Gy r

Programozás alapjai gyakorlat. 4. gyakorlat Konstansok, tömbök, stringek

Programozás BMEKOKAA146. Dr. Bécsi Tamás 2. előadás

Készítette: Nagy Tibor István

8. gyakorlat Pointerek, dinamikus memóriakezelés

A C# programozási nyelv alapjai

Számítástechnika I. BMEKOKAA152 BMEKOKAA119 Infokommunikáció I. BMEKOKAA606. Dr. Bécsi Tamás 2. előadás

Programozás II. 2. Dr. Iványi Péter

A C programozási nyelv III. Pointerek és tömbök.

Java II. I A Java programozási nyelv alapelemei

A C programozási nyelv III. Pointerek és tömbök.

Bevezetés a C++ programozási nyelvbe

C memóriakezelés. Mutató típusú változót egy típus és a változó neve elé írt csillag karakterrel hozhatjuk létre.

Programozás alapjai gyakorlat. 2. gyakorlat C alapok

Tömbök kezelése. Példa: Vonalkód ellenőrzőjegyének kiszámítása

Programozás 3. Dr. Iványi Péter

Programozás I gyakorlat. 10. Stringek, mutatók

Vezérlési szerkezetek. Szelekció Ciklusok

Programozás I. 3. gyakorlat. Szegedi Tudományegyetem Természettudományi és Informatikai Kar

Occam 1. Készítette: Szabó Éva

Programozás I. gyakorlat

Függvények. Programozás alapjai C nyelv 7. gyakorlat. LNKO függvény. Függvények(2) LNKO függvény (2) LNKO függvény (3)

Programozás alapjai C nyelv 7. gyakorlat. Függvények. Függvények(2)

A C programozási nyelv V. Struktúra Dinamikus memóriakezelés

Programozás 1. Dr. Iványi Péter

Járműfedélzeti rendszerek II. 3. előadás Dr. Bécsi Tamás

Webprogramozás szakkör

Operációs rendszerek. 11. gyakorlat. AWK - szintaxis, vezérlési szerkezetek UNIVERSITAS SCIENTIARUM SZEGEDIENSIS UNIVERSITY OF SZEGED

Készítette: Nagy Tibor István

5. gyakorlat. Konstansok Tömbök Stringek

C változók. Feladat: Deklaralj egy valos, egy karakter es ket egesz tipusu valtozot! int main() {

Járműfedélzeti rendszerek II. 4. előadás Dr. Bécsi Tamás

1. Egyszerű (primitív) típusok. 2. Referencia típusok

Programozás alapjai 3.Gy: C elágazások, ciklusok P R O

Struktúrák (struct) A struktúra szerkezetét meghatározó deklaráció általános formája:

Programozás I. gyakorlat

Szoftvertervezés és -fejlesztés I.

Programozás alapjai C nyelv 8. gyakorlat. Mutatók és címek (ism.) Indirekció (ism)

Kifejezések. Kozsik Tamás. December 11, 2016

Pénzügyi algoritmusok

Változók. Mennyiség, érték (v. objektum) szimbolikus jelölése, jelentése Tulajdonságai (attribútumai):

C programozás. 1 óra Bevezetés

Java programozási nyelv

Programozás I gyakorlat

Megoldott programozási feladatok standard C-ben

Kifejezések. Kozsik Tamás. December 11, 2016

Mechatronika és mikroszámítógépek 2017/2018 I. félév. Bevezetés a C nyelvbe

C programozás. 6 óra Függvények, függvényszerű makrók, globális és

10. gyakorlat Struktúrák, uniók, típusdefiníciók

Programozás C- és Matlab nyelven C programozás kurzus BMEKOKAM603 Mutatók. Dr. Bécsi Tamás 7. Előadás

Mintavételes szabályozás mikrovezérlő segítségével

Mutatók és címek (ism.) Programozás alapjai C nyelv 8. gyakorlat. Indirekció (ism) Néhány dolog érthetőbb (ism.) Változók a memóriában

Függvények. Programozás I. Hatwágner F. Miklós november 16. Széchenyi István Egyetem, Gy r

1. Alapok. #!/bin/bash

Programozás alapjai 2.Gy: A C nyelv alapjai P R O

1. Template (sablon) 1.1. Függvénysablon Függvénysablon példányosítás Osztálysablon

AWK programozás, minták, vezérlési szerkezetek

Programozás C és C++ -ban

A C programozási nyelv II. Utasítások. A függvény.

Programozás I gyakorlat

Programozási nyelvek (ADA)

Programozás alapjai. (GKxB_INTM023) Dr. Hatwágner F. Miklós október 11. Széchenyi István Egyetem, Gy r

AWK programozás, minták, vezérlési szerkezetek

Felvételi tematika INFORMATIKA

A szemantikus elemzés helye. A szemantikus elemzés feladatai. A szemantikus elemzés feladatai. Deklarációk és láthatósági szabályok

1. Alapok. Programozás II

Programozás alapjai 9.Gy: Struktúra 2.

Számítástechnika II. BMEKOKAA Előadás. Dr. Bécsi Tamás

Karakterkészlet. A kis- és nagybetűk nem különböznek, a sztringliterálok belsejét leszámítva!

file./script.sh > Bourne-Again shell script text executable << tartalmat néz >>

Informatika terméktervezőknek

1. Feladat: beolvas két számot úgy, hogy a-ba kerüljön a nagyobb

Programozás I gyakorlat

5. Gyakorlat. struct diak {

Kifejezések. A programozás alapjai előadás. Operátorok. Kifejezések. Operátorok precedenciája. Operátorok precedenciája

A C# PROGRAMOZÁSI NYELV

Információs Technológia

Bevezetés a programozásba I.

Programozás. (GKxB_INTM021) Dr. Hatwágner F. Miklós február 18. Széchenyi István Egyetem, Gy r

1. Jelölje meg az összes igaz állítást a következők közül!

BASH script programozás II. Vezérlési szerkezetek

INFORMATIKA javítókulcs 2016

OOP #14 (referencia-elv)

Struktúra nélküli adatszerkezetek

Szövegek C++ -ban, a string osztály

A programozás alapjai előadás. A C nyelv típusai. Egész típusok. C típusok. Előjeles egészek kettes komplemens kódú ábrázolása

S z á m í t ó g é p e s a l a p i s m e r e t e k

11. gyakorlat Sturktúrák használata. 1. Definiáljon dátum típust. Olvasson be két dátumot, és határozza meg melyik a régebbi.

Programozás. (GKxB_INTM021) Dr. Hatwágner F. Miklós április 4. Széchenyi István Egyetem, Gy r

Bevezetés a programozásba Előadás: A const

Átírás:

1 / 51 C alapok nem kiadasi verzio sok helyen nem pontos informaciok talalhatoak!

2 / 51 IDE (Integrált fejlesztő környezet): Microsoft Visual Studio 2005 Új Project létrehozása: File -> New -> Project Itt a Project Types közül válasszuk ki a Vicual C++-t, azon belül a Win32-t, majd jobb oldalt a Win32 Console Application-t. Adjunk nevet a projectnek, majd OK. A feljött ablakban nyomjunk Next-t. Az Application type legyen Console Application. Az Additional options-nál pedig legyen bepipálva az Empty project box. A Solution Explorer-ben a Source Files- könyvtárra kattintsunk jobb gombbal, majd Add -> New Item. A felugró ablakban válasszuk ki a Visual C++ / C++ file (.cpp) t. Adjunk meg egy filenevet, majd OK. A project nevére (NE a felette található Solution névre) kattintsunk jobb gombbal, majd Properties. Itt a Configuration Options -> C/C++ ->Advanced nél a Compile As-t állitsuk át Compile as C Code (/TC) re. Erre azért van szükség, mert a Visual Studio alapvetően C++-t fordít, így viszont C kódot. A két nyelv között alapvető különbségek vannak Írjuk meg a helyes programot. Futtatás: CTRL+F5 GCC: gcc -ansi -Wall -pedantic -o <kimeneti_filename> forras.c -ansi: ANSI szabványos C kód ellelnőrzés -Wall: több hibára figyelmeztetés (Warning all) -pedantic: egyéb megszorítások kikényszerítése Futtatás:./kimeneti_filename

3 / 51 Egy C állomány felépítése: [library_hivatkozások] [prototípusok] [main] [definíciók] (A prototípusokról és a definíciókról később, a függvényeknél lesz szó.) A legegyszerűbb C program: Ez nem csinál semmit. Minden C program fügvényekből és változókból áll. A függvények utasításokat tartalmaznak, amelyek azt írják le, hogy a program mit csinál. Ez a program egy main nevű függvényből áll. A függvényeknek tetszőleges nevet adhatunk, azonban a main speciális: ez jelenti a főprogramot. A főprogram végrehajtása a main elején kezdődik, az utasításokat a {-ek között lehet elhelyezni. printf("hello World!"); A program kiírja, hogy Hello World! A #-el kezdődő utasítások az előfordítónak szólnak. #include: azt írja le, hogy a fordítás során az stdio könyvtárra vonatkozó információkat töltse be. stdio: standard input/output rövidítése, és az alapvető be/kimenetet kezelő utasítások, függvények stb. találhatóak meg benne. Ilyen pl. a printf(), ami a képernyőre ír ki tetszőleges karaktersorozatot. A ()-ek közötti "Hello World!" t aktuális paraméternek hívjuk, és azt mondjuk, hogy a printf() függvényt az adott aktuális paraméterrel hívtuk meg. (Az aktuális paraméter szinonímája az argumentum). Bővebb magyarázat:függvények. A printf() nem hajt végre soremelést. Ezt akár így is írhattuk volna: printf("hello "); printf("world!"); A soremelésre a \n ún. escape jelsorozatot (escape szekvencia) használhatjuk. Erre azért van szükség, mert az ASCII kódtáblában vannak karakterek, amelyekhez nem tartozik karakterkép. Ezek az ún. vezérlőkarakterek (a 32-es kód alatt). Az escape szekvenciák segítségével ezeket a vezérlőkaraktereket adhatjuk meg. Bővebb lista: a függelékben. printf("hello World!\n");

4 / 51 Literálok A konstans (állandó) szinonímája. A literál egy öndefiniáló eszköz. Két komponense van: típus és érték, ezeket a felírási módja határozza meg. Egész literál: előjellel vagy számjeggyel kezdődik, és több számjegyből állhat. Pl: -122 egy egész literál. Módosító betűk: a szám után álló betű(k): L vagy l: hosszú egész U vagy u: unsigned, azaz előjel nélküli Pl.: 111u: előjel nélküli egész, 55Lu, előjel nélküli hosszú egész Az egész számok felírása történhet oktális és hexadecimális formában is. A 0-val kezdődő számok oktális, a 0x-el kezdődő számok hexadecimális alakúak. Pl.: 031: oktálisan 31, decimálisan 25 (ezért keverik a programozók a helloweent és a karácsonyt: oct 31 = dec 25 :) ) 0x1F: hexadecimálisan 1F, decimálisan 31 Ezek után is lehet azokat a jelzőket írni, amelyek az egész számoknál lehetségesek. Pl.: 0XFALU egy szabályos hexadecimális alakú egész szám Lebegőpontos literál (valós szám): van benne pont, e betű, vagy mindkettő. Pl.: 12.2, 12e- 1, 12.2E+3, 1e3 Értéke: az e betű előtt álló részt szorozzuk 10 e betű után álló hatványával. pl.: 12e-1 = 12*10-1 Módosító betűk: F vagy f: float, egyszeres pontosságú lebegőpontos szám L vagy l: long double, háromszoros pontosságú lebegőpontos szám Módosító nélkül: double, kétszeres pontosságú lebegőpontos szám Pl: 11.2f egy float típusú szám Karakter literál: 'a' azaz aposztrófok között egy karakter. String literál (karakterlánc): "szoveg", azaz dupla aposztófok közé zárt karakter vagy karaktersorozat. Lehet üres string is, ekkor csupán: ""-t kell írni. String literálok esetén, ha nem fér ki egy sorban, a \ karakter segítségével új sorban folytathatjuk. Pl.: "valami \ amerika" megegyezik a "valami amerika" string literállal. A stringek és a karakterek között lényeges a különbség: 'a' "a" :1 byte :2 byte, 'a' és '\0'

5 / 51 '' :HIBÁS : nincs üres karakter! "" :1 byte, a lezáró \0 (A \0 karakter: lásd a stringeknél) Változók, típusok, deklarációk: A változónak 4 komponense van: név, attribútumok, cím, érték. A típust és az attribútumot szokták egymás szinonimájaként is emlegetni, azonban helytelenül. Az attribútum a változó futás közbeni viselkedését határozza meg. Az attribútum rögzíti a változó számára lefoglalandó memória mennyiségét, a benne található információ belső ábrázolási módját, hatáskörét, láthatóságát, élettartamát stb. A típus csak az első kettőt határozza meg. A legfontosabb attribútum a típus. Egész típusok kulcsszavai: int, short, long, char Megengedett a short int és a long int is. int: egész, short: rövid egész, long: hosszú egész típus. Lehetőség van előjel nélküli egészek használatára is, az unsigned kulcsszóval. Pl.: unsigned int. Az előjeles egész kulcsszava: signed. Az int mérete általában a gépi alapszó méretével egyezik meg (általában 4 byte, esetleg 2 byte), a short általában 2 byte-os, a long általában 4 byte-os (32 bites rendszeren). A char pontosan 1 byte-os. Az egész típusok méretére nincs konkrét megkötés (a char típus kivételével), de a következő egyenlőtlenség feltétlenül teljesül: short int long. Lebegőpontos típusok kulcsszavai: float, double, long double. float: lebegőpontos szám, double: duplapontos lebegőpontos szám, long double: kiterjesztett pontosságú lebegőpontos szám. A float általában 4 byte-os, de hasonlóan az egész típusokhoz, a méretek implementációfüggőek: float double long double Karakter: char. A kezelése speciális. A C 1 byte-os egész típusként kezeli, azaz műveletek is végezhetőek vele (+,-,* stb.). Mivel egész típusú, ezért lehet előjeles vagy előjel nélküli. A karakter belső ábrázolása: értéke mindig a karakter ASCII kódja. Speciális a void típus: nincs tartománya, művelete, reprezentációja. A típusnélküliséget jelöli. Logikai típus nincs a C nyelvben, az int típus helyettesíti. A 0 érték jelenti a hamis értéket, minden 0-tól különböző érték az igaz értéket jelöli. C-ben minden változót a használata előtt deklarálni kell. Ez általában a függvények elején, az első végrehajtható utasítás előtt történik. Deklarálni a következő képpen lehet: tipus valtozo [= kezdeti_ertek] [, valtozo [= kezdeti_ertek]]...; ekkor egy tipus típusú, valtozo nevű változót hoztunk létre. Egy deklarációs utasításban több változót is deklarálhatunk, ekkor a típusaik azonosak lesznek. Kezdeti értéket is adhatunk a változónak, kifejezéssel. Pl.: double valtozo, meg_egy_valtozo; int valtozo = 3 + 2; long int valtozo2 = 5L; long int b = valtozo + 3 + valtozo2;

6 / 51 Deklarálni blokk elején lehet, mindíg az első végrehajtható utasítás előtt. Blokk az, ami {-ek között van. Tehát először minden változót deklarálni kell, és csak azután használhatjuk fel a programunkban. Fontos megjegyezni, hogy a változók, ha mi nem adunk explicite kezdőértéket, akkor deklarációkor nem kapnak kezdeti értéket, az érték valamilyen véletlen bitkombináció lesz. Nem okoz fordítási hibát, azonban a program futása során felléphetnek egyéb problémák. Nevesített konstans 3 komponense van: név, típus, érték. Szintén deklarációval hozzuk létre. Értéke a deklarációnál eldől, és nem változtatható meg futás közben. Szerepe, hogy bizonyos gyakran előforduló értékeket névvel lássunk el, és csak egy helyen kelljen értéket változtatni, ha ez szükséges. Pl.: egy személy neve általában állandó, ez többször is megjelenhet, azonban ha valamikor változtatni kell, akkor csak egy helyen kell, nem szükséges a forrás teljes szövegében manuálisan keresni. Létrehozása 3-féle módon történhet: Konstans létrehozása előfordítónak szóló utasítással: #define nev ertek ahol a nev a változó neve, ertek az ertéke. Pl.: #define ANYU 46 Ez tulajdonképpen makrózást végez. Ahol a forrás szövegében szerepel a nev, ott az előfordító a nev helyére az ertek-et illeszti be. Konstans létrehozása deklarációs utasításban: const tipus nev = kifejezes [,nev = kifejezes]...; ahol a kifejezés típusa és értéke fordítási időben eldönthető. Ez valójában nem nevesített konstanst hoz létre, hanem a fordító fogja ellenőrizni, hogy a változó kap-e valahol új értéket (szerepel értékadás bal oldalán), és fordítási hibával leáll, ha valahol talál olyan értékadást, amelynek bal oldalán konstans szerepel. Pl.: const double valami = 3.0; Konstans létrehozása felsorolásos típussal (enumerátor, enum konstans, felsorolásos állandó) enum [enum_név] {név [=konstans_kifejezés] [, név [=konstans_kifejezés]]... [változólista]; A felsorolásos állandó egész (int) értékek listájából áll. Az enum_név a felsorolásos állandó neve. A listában szereplő nevek implicit módon 0-tól kezdve, egyel növekedve kapnak értéket. Azonban megadható kifejezéssel a névhez tartozó érték, és ekkor az azt követő név eggyel nagyobb értéket kap. Több névnek ugyanaz lehet az értéke, azonban két különböző nevű enumerációban nem lehet azonos név.

7 / 51 Pl.: enum napok { hetfo = 1, kedd, szerda, csutortok, pentek, szombat, vasarnap ; enum napok isten_megpihent = vasarnap; Ciklusok while-ciklus Feladat: írassuk ki az első 10 számot, és négyzetét: int i; i = 0; while( i <= 10 ){ printf("i=%d", i); printf(" i^2=%d\n", i*i); i++; Működése: i-nek adtunk egy értéket: 0-t. A while ciklus fejében levő feltétel kiértékelődik. Ha a feltétel igaz, végrehajtódik a ciklusmag, egyébként pedig a ciklus után folytatódik a program. A ciklusmagban található printf() kiírja az i változó aktuális értékét, mellé pedig a négyzetét (tehát nem csak változót tudunk kiíratni, hanem kifejezést is). Az i++ az i értékét növeli eggyel (inkrementálja), ugyanaz, mintha a következőt írtuk volna: i = i + 1; Kétféle inkrementálás van: a prefix (++i) és a postfix (i++). Különbség a prefix és a postfix használat között: int i,j; i = 0; j = ++i; printf("%d", j); i = 0; j = i++; printf("%d", j); Az első esetben az i értéke növekszik 1-el, és ez lesz j értéke. A második esetben i értékét kapja j, majd utána növekszik az i változó értéke. Tehát ha elől van a ++, akkor előbb cselekszik, utána gondolkodik, ha hátul, akkor előbb gondolkodik, aztán cselekszik. A printf() segítségével egyszerre több dolgot is ki lehet íratni:

8 / 51 int i; i=0; while( i <= 10 ){ printf("i=%d i i^2=%d\n", i, i*i); i++; A printf() formátumozó stringje: az első paramétere mindig egy string, ez a formátumozó string. Lehet több paramétere is. A formátumozó stringben tudjuk megadni, hogy mit írunk ki és milyen típusú változót íratunk ki. Itt a %d azt jelenti, hogy egy egész típusú változót írunk. Konverziós karakterek: %c - karaktert írat ki %d - egész számot ír ki %ld - hosszú egész számot ír ki %f float vagy double típust írat ki %L - long double típust írat ki %s - stringet írat ki %p - mutatót írat ki (lsd a mutatóknál) Másik példa: int vmi, geza; float i; char j; vmi = 12; geza = 23; i = 0; j = 'a'; printf("%d geza=%d %f %c", vmi, geza, i, j); Ekkor összerendeli a konverziós karaktereket és a változókat. Fontos, hogy mindig pontosan adjuk meg a formátumkaraktereket a megfelelő típusú változókhoz. Ha több változó van, mint formátumkarakter, nincs semmi gond, csupán a felesleges változókat nem írja ki. Ha több a formátumkarakterek száma, mint a változóké, valamilyen szemetet ír ki, esetleg lefagy a program, még rosszabb esetben elpárolog a sör. Pl.: cseréljük ki a printf-t a következőkre: printf("%d %d %f", vmi, geza, i, j); printf("%d %d %f %c", vmi, geza, i); A négyzetes kiírató program megírható a következőképpen is:

9 / 51 int i for( i = 0 ; i <= 10 ; i++ ) printf("%d %d\n", i, i*i); a ciklusmag nincs körülvéve { -el. Ez azért van, mert ha csak egy utasítást írunk a ciklus után, akkor nem szükséges kitenni. Ki lehet egyébként. Fontos: a for ciklus nem előírt lépésszámú ciklus! Csupán a while ciklus általánosítása. A két ciklus tulajdonképpen átírható egymásba: for(a;b;c){ D Equivalens ezzel: A; while(b){ D C; ahol D több utasítás is lehet. Tipikus hiba for ciklus esetén: for( i = 0 ; i <= 10 ; i++ ); printf("%d %d\n", i, i*i); nem szabad a for ciklus feje után ;-t tenni (hacsak nem ez a cél) ebben az esetben csak egyszer íródik ki a két szám, ugyanis a for ciklus magja így az üres utasítás. Ugyanez vonatkozik a while ciklusra is, csak ott valószínűleg végtelen ciklus kapunk. Feladat: kérjünk be egy számot, és döntsül el, hogy páros vagy páratlan: int i; printf("kerem a szamot: "); scanf("%d", &i); if( i%2 == 0 ) printf("paros\n"); else printf("paratlan"); a scanf() formátumstringje hasonlóképpen működik, mint a printf()-é. Azonban a változók felsorolásakor a változók neve elé & operátort (ún. címe operátor) kell tenni! Ne felejtsük el, ez tipikus kezdő hiba.

10 / 51 Még egy különbség: double típus esetén nem %f t használunk, hanem %lf szükséges: i%2: a % az egészosztás maradékát adja meg (moduloképzés). Pl.: 3%2 azt jelenti, hogy a 3- at elosztva 2-vel 1 lesz a maradék. Jelen esetben azt jelenti, hogy az i osztható-e maradék nélkül 2-vel (azaz páros-e). if-else: A kétirányú elágazás megvalósítása. Ha igaz az if utáni feltétel, akkor az if ág-ban levő utasítások hajtódnak végre, egyébként az else ágbeliek. Az else ág opcionális, nem szükséges kitenni. Ha nincs else ág, és nem teljesül a feltétel, akkor ez egy üres utasítás. Itt is érvényes, hogy egy utasítás esetén nem kell {, 2 vagy több esetén viszont szükséges. pl.: if( i%2 == 0 ){ printf("ez a szam "); printf("paros\n"); else printf("paratlan"); ugyanez vonatkozik az else-re is. Feladat: kérjünk be egy zh pontszámot, és írjuk ki, mennyire sikerült teljesíteni: 40 pont alatt sikertelen, 40-50 pont között javithat, 50 és felette megvan az aláírás int i; printf("kerem a pontszamot: \n"); scanf("%d", &i); if( i < 40 ) printf("sikertelen"); if( i >= 40 && i <50 ) printf("javithat az alairasert\n"); else printf("sikeres alairas\n"); Csellengő else probléma: A program egy számról döntse el, hogy páros-e vagy páratlan. Ha páros, Határozza meg, hogy nagyobb-e egy másik számnál. int n = 5, i = 6; long eredmeny = 1; if( n%2 == 0 ) if( n > i ) printf("paros, n nagyobb\n"); else

11 / 51 printf("paratlan\n"); printf("valami"); Ekkor a program csak a "valami"-t írja ki. A C az else ágat a hozzá legközelebbi else néküli if-hez társítja, és nem nézi, hogyan van tagolva a forrás szövege. Helyesen: int n = 5, i = 6; long eredmeny = 1; if( n%2 == 0 ){ if( n > i ) printf("paros, n nagyobb\n"); else printf("paratlan\n"); printf("valami"); Egyébként pedig így néz ki az if (ahogy a C is értelmezi): if( n%2 == 0 ) if( n > i ) printf("paros, n nagyobb\n"); else printf("paros, n kisebb\n"); else-if Az else-if szerkezet a többszörös elágazások egy általános lehetősége. A gép sorban kiértékeli a feltételeket, és ha ezek közül talál egy igazat, akkor az ott található utasításokat végrehajtja. Az utolsó else ág opcionális, azt jelöli, hogy ha egyik feltétel sem teljesült, akkor milyen tevékenységet hajtsunk végre. Feladat: Írjuk át a zh pontos feladatot else-if-re int i; printf("kerem a pontszamot: \n"); scanf("%d", &i); if( i < 40 ) printf("sikertelen"); else if( i >= 40 && i <50 ) printf("javithat az alairasert\n"); else

12 / 51 printf("sikeres alairas\n"); Írjunk faktoriális megoldó programot. N faktoriálisa: 1 * 2 * 3 * * N-1 * N int n, i = 1; long eredmeny = 1; printf("kerek egy szamot: "); scanf("%d", &n); for(; i <=n; i++) eredmeny *= i; printf("%d faktorialisa: %d\n", n, eredmeny); A for és a while ciklus tetszőleges komponense elhagyható. Itt a for ciklus első komponense hiányzik, értéket deklarációkor kapott. Változót felhasználhatunk úgy is, hogy nem adunk értéket neki. Ekkor a tartalma véletlenszerű, a memóriában levő valamilyen bitsorozat értéke. Nem okoz fordítási hibát, de futási hibát eredményezhet. Határozzuk meg 0-100-ig a páros számok harmadik hatványát: int i; for( i = 0; i <= 100; i = i + 2) printf("%d %d\n", i, i * i * i); Tehát a for ciklusban (és a while ciklusban is) nemcsak egyesével lehet a ciklusváltozót léptetni, hanem tetszőleges lépésközzel. A ciklusokon belül pedig a ciklusváltozónak lehetséges új értéket is adni, pl.: int i; for( i = 0; i <= 100; i = i + 2){ i = i + 1; printf("%d %d\n", i, i * i * i); Határozzuk meg, hogy egy szám prímszám-e: Nézzük végig, hogy van-e osztója. Ha van az 1-en és önmagán kívül, akkor nem prím.

13 / 51 int n = 5, i = 6; int prim = 1; scanf("%d", &n); for( i=2;i<n;i++ ){/*n-et ne erjuk el, mert akkor nem veszi primnek*/ if(n%i == 0){ prim = 0; break; if( prim ) printf("prim"); else printf("nem prim"); printf("\n\n"); /* ez tomorebb kodot eredmenyez valamint egyes esetekben gyorsabban hajtodik vegre, mint az if */ ( prim )? printf("prim\n") : printf("nem prim\n"); Amire figyelni kell: i nem mehet nullától: osztási hiba. 1 ne legyen, mert akkor nem veszi prímnek, valamint ne érje el a szám értékét sem, hasonló okok miatt. Új eszközök: Megjegyzés: /* megjegyzés */. Ezt a fordító nem veszi figyelembe, nem lesz eleme a kódnak. Ez a programozónak, illetve a forrás szövegét olvasónak szól. Informálhat a felhasznált algoritmusról, a program működéséről, az alkalmazott megoldásról. Bárhol állhat, ahol szóköz állhat és több soron keresztűl folytatódhat. Egy jó program sok megjegyzést tartalmaz. break utasítás: a tartalmazó ciklust leállítja. continue utasítás: folytatja a ciklust, az utána következő utasításokat nem hajtja végre. Azaz a következő iterációba lép. Pl.: for(i = 2 ; i < n ; i++){/*n-et nem erjuk el,mert akkor nem veszi primnek*/ if( n % i == 0 ){ prim = 0; else break; continue; printf("itt vagyok\n"); Egyszer sem fogja kiírni, hogy itt vagyok.

14 / 51 A continue Különbsége az egyes ciklusok esetén: i = 2; while( i < n )/* n-et nem erjuk el, mert akkor nem veszi primnek */{ if( n % i == 0 ){ prim = 0; else{ break; printf("vegtelen ciklus\n"); continue; printf("itt vagyok\n"); ++i; A kettőnél nagyobb számok esetén ez egy végtelen ciklust eredményez, ugyanis az i-t nem fogja növelni, és így mindig kisebb lesz a megadott számnál. Azaz a ciklus feltétele mindig igaz lesz. Fogalmak: végtelen ciklus: az ismétlődés soha nem áll le. Végtelen ciklusból szabályosan kilépni a break utasítással lehet. Üres ciklus: a ciklusmag nem kerül végrehajtásra, a ciklus feltétele hamis. A? : operátor hasonló az if-hez, if( feltetel ) utasitas1 else utasitas2 átírható a ( feltetel )? utasitas1 : utasitas2 ; alakba. Pontosvesszőt nem lehagyni! Program: kérjünk be egy számot, és írjuk ki fordítva. int szam; int eredmeny; scanf("%d", &szam); while(szam){ eredmeny = szam % 10; printf("%d", eredmeny);

15 / 51 szam /= 10; puts(""); Működése: a legutolsó számjegyhez úgy férünk hozzá, hogy vesszük a tízzel vett modulóját (azaz az utolsó számjegyet). Kiírjuk, majd a számot újra elosztjuk tízzel. Mivel \n nincs a printf()-ben, ezért egymás mellé írja ki a számjegyeket. Eszközök: /= valtozo /= kifejezes equivalens a valtozo = valtozo / kifejezes alakkal. Hasonlóan van +,-,*,% stb is, lsd a precedenciatáblázatot. A while(szam) ugyanaz, mint a while(szam!= 0). Magyarázat: a C logikai érték kezelése. A szám nulla lesz az utolsó osztás esetén. Kisebb lesz, mint 10 (0 és 0.9 közé esik). Lsd később. Ezzel az a gond, hogy a nullára nem működik, ugyanis hamis lesz a feltétel. Megoldás lehet, hogy előtte megvizsgáljuk, és ha nulla, kiírjuk. Másik megoldás: do-while ciklus: do{ eredmeny = szam % 10; printf("%d", eredmeny); szam /= 10; while(szam); Belép a ciklusba, végrehajtja az utasításokat, majd megvizsgálja a ciklus feltételét, Ha igaz, újra végrehajtja a ciklusmagot. Nem lehet üres ciklus, mivel egyszer mindenképp lefut a ciklusmag. Program: kérjünk be egy számot, és írjuk ki szövegesen. Lehet sok if-else if -et használni, ami nem túl szép. switch-case: switch(egesz_kifejezes){ case konstans_kifejezés : utasítás(ok) case konstans_kifejezés : utasítás(ok)... default : utasítás(ok) A kifejezés típusának egészre konvertálhatónak kell lennie. A case ágak értékei nem

16 / 51 lehetnek azonosak. A default ág bárhol állhat. Az utasításokat nem kötelező megadni (azaz lehet üres case-ág is). Ugyanígy a default ág is opcionális. Működése: Kiértékelődik a kifejezés, majd értéke a felírás sorrendjében hasonlításra kerül a case ágak értékeivel. Ha van egyezés, akkor végrehajtódik az adott ágban megadott tevékenység, majd a program a következő ágakban megadott tevékenységeket is végrehajtja, ha még van ág. Ha nincs egyezés, de van default ág, akkor az ott megadott tevékenység hajtódik végre, és végrehajtja az összes többi ág utasításait is. Ha nem talált megfelelő ágat, akkor ez egy üres utasítás. Ahhoz, hogy csak egy adott ágat hajtsunk végre, külön utasítást kell elhelyezni. int szam = 5, osztas = 6; int eredmeny; scanf("%d", &szam); switch(szam){ case 4 : case 1 :printf("egy"); case 2 :printf("ketto"); case 3 : printf("harom"); default : printf("sok"); Ez nem működő megoldás. Helyesen: break-el le kell zárni az ágakat. switch(szam){ case 4 : case 1 : printf("egy"); break; case 2 : printf("ketto"); break; case 3 : printf("harom"); break; default : printf("sok"); Mint látható, ha több utasítást akarunk elhelyezni egy case ágban, itt nem szükséges a {-ek használata. Kapcsolat a karakterek és számok között: A karakter belső ábrázolása: értéke a karakter ASCII kódja. A %c konverzió fogja "értelmezni", és az adott ASCII kódhoz tartozó karaktert fogja kiírni. Feladat: készítsünk ASCII kódtáblát.

17 / 51 int i; for(i = 0; i <= 255; i++) printf("ascii kod: %d \t karakter: %c\n", i, i); A csipogás nem hiba miatt van, az egyik kód a speakert csipogtatja. A 10 esetén nem véletlenül van új sor, az az enter kódja (soremelés, hexában: 0x0A). Feladat: bekérünk két számot, egy műveleti jelet, és írjuk ki a művelet eredményét. float a,b; char c; scanf("%f %c %f", &a, &c, &b); switch(c){ case '+' : printf("%f", a + b); break; case '-' : printf("%f", a - b); break; case '*' : printf("%f", a * b); break; case '/' : printf("%f", a / b); break; default: printf("nem definialt muvelet\n"); Hatáskör Vegyük a következő programot: int i; i = 3; if( i == 3){ int i; i = 2; printf("%d", i);

18 / 51 A program végén a kiíratás eredményeképpen 3-t kapunk, és nem 2-t. A két deklarált i változónak nem sok köze van egymáshoz. Mint már korábban volt róla szó, deklarálni minden blokk elején lehet. A változók akkor jönnek létre, ha arra a blokkra kerül a vezérlés, amelyben deklarálták, és megszűnnek, ha a blokkot elhagyja a vezérlés. Az if-en belüli változó teljesen független a main-beli változótól, és az if blokkja után már nem is létezik, csupán a nevükben van egyezés. Ezt nevezzük a változó hatáskörének. Egy változó hatáskörének meghatározása pedig a hatáskörkezelés. A hatáskör fogalma névhez és deklarációhoz kapcsolódik. Mindkét i változó lokális a saját blokkjára nézve. Lokális változó hatásköre az a blokk, amelyben definiálták, élettartama pedig az az idő, amíg a vezérlés a saját blokkjában van (ekkor rendelkezik címkomponenssel, ekkor foglal helyet a memóriában). Változó hatásköre az a programrész, ahol érvényesen hivatkozni lehet rá. Lokális változó hatásköre az a blokk, és annak minden beágyazott blokkja, amelyben definiálták. Ha ugyanolyan névvel deklarálok egy másik nevet két, egymásba ágyazott blokkban, akkor a két név másik változót nevez meg. Ez a lyuk a hatáskörben esete: a külső változó számára lyuk keletkezik. Ha nem deklarálom újra, akkor elérhető, és ekkor a belső blokk számára nem lokális, hanem globális változó. Globális változó: ha egy blokkban nincs deklarálva, de érvényesen hivatkozható. Kifejezések, kifejezések kiértékelése, operátorok Kifejezések Operátorokból és operandusokból, esetleg kerek zárójelekből épülnek fel. Két komponensük van: típus és érték. Mindkettő a kiértékelés során a legutolsó operátor által kerül meghatározásra. A legegyszerűbb kifejezés egyetlen operandusból áll. Kiértékelés : az a folyamat, amelynek során az operandusok és operátorok által reprezentált értékeket és az operandusok egy sorrendjét figyelembe véve a konkrét érték előáll. A ()-ek között álló kifejezést mindig hamarabb kell kiértékelni, mint a többit. pl: 2*(3+4) esetén előbb 3+4, majd ezt szorozzuk 2-vel. Az infix kifejezések kiértékelése nem egyértelmű. Pl.: 2*3+4 helyesen: (2*3)+4 és nem 2*(3+4). A nyelvek az operátorokhoz megadnak egy ún. precedenciatáblázatot. Felülről lefelé haladva egyre kevésbé fontosak, erősek az adott operátorok (azaz precedencia szerint csökkenő sorban vannak), valamint megadják a kötési irányt, amely azt írja le, hogy az azonos erősségű operátorok közül melyiket kell hamarabb kiértékelni (merről-merre haladunk). Az azonos precedenciájú operátorok egy sorban találhatóak. A C precedencia táblázata: Operátor Asszociativitás () []. -> * & + -! ~ ++ -- SIZEOF (típus) * / % + - >> << < > <= >=

19 / 51 ==!= & ^ &&?: = += -= *= /= %= >>= <<= &= ^= =, A ++ és -- esetén a post nagyobb precedenciájú, mint a pre. Operátorok és jelentésük () : függvényoperátor. Lsd a függvényeknél. [] : tömb operátor. : statikus minősítés. Lsd.: struktúrák ->: dinamikus minősítés. Lsd.: mutatók * : indirekciós (mutató, pointer) operátor. Lsd pointerek. A mutató típusú operandusa által hivatkozott értéket adja. & : címe operátor. Lsd pointerek. Az operandusának a címkomponensét adja + - : előjelek! : logikai negáló operátor. Aritmetikai és mutató típusokon működik. ~ : egyes komplemens képzés. Egész típusokon alkalmazható. + * - / : aritmetikai műveletek << >> : léptető operátorok. A baloldali operandust lépteti a jobboldali által meghatározott számú bittel jobbra illetve balra.. Ha balra léptet, akkor 0-kat hoz be jobbról. Ha jobbra léptet, akkor az előjelbitet lépteti végig bal oldalon. Egész típusú operandusokon működik. ++ -- : inkrementáló és dekrementáló operátor. A dekrementáló operátor hasonló az inkrementálóhoz, de az 1-el való csökkentést valósítja meg. Aritmetikai típusokon működik. sizeof() : operandusának (amely tetszőleges objektum lehet) a belső ábrázolásához szükséges byte-ok számát adja meg. Pl.: int valtozo; printf("%d", sizeof(valtozo)); megadja a valtozo memóriabeli méretét byte-okban. A valtozo helyere írhattunk volna int-et is. Érdekessége, hogy konstans kifejezést is meglehet adni, amelyet a fordító értékel ki. Azonban ekkor az értéket nem határozza meg, csak a típust, és az ehhez szükséges memória méretét.

20 / 51 (típus) : kasztolás, casting, explicit típuskényszerítés. Prefix operátor, operandusát az adott típusra konvertálja. < > <= >= : a szokásos matematikai értelemben vett hasonlító operátorok ==!= : hasonlító operátorok, operandusainak értéke megegyezik-e, vagy nem egyezik-e meg. & ^ : egész típusú operandusok esetén használható. Bitenkénti AND, XOR, OR művelet && : egész típusú operandusok esetén használható. Rövidzár AND, OR művelet, int 0 és 1 értékek esetén működik.?: : feltételes kifejezés. 3 operandusú operátor, már volt róla szó, azonban általánosabb, mint az ott leírtak. Ha igaz a? előtt található kifejezés, akkor a teljes kifejezés értéke a? és : között található kifejezés értéke, egyébként pedig a : utáni kifejezés értéke. Pl.: két változó közül válasszuk ki a nagyobb értéket: int a,b; int i; a = 5; b = 9; i = (a > b)? a : b; Itt ha az a a nagyobb értékű, akkor az a értékét, ha nem, akkor a b értékét kapja meg az i változó. Határozzuk meg egy változóról, hogy igaz-e: int i = 1; printf("i %s", (i)? "igaz" : "hamis"); =, +=, -=, *=, /=, %=, >>=, <<=, &=, ^=, = értékadó vagy más néven hozzárendelés operátorok, már volt róluk szó. Fontos azonban megjegyezni, hogy a nyelvben nincs értékadó utasítás! Helyette értékadó kifejezés van. A hozzárendelés a következő alakú: objektum = kifejezés Az értékadó operátor bal oldalán olyan objektumnak kell állnia, amely értéket képes felvenni. Ezt (módosítható) balértéknek hívjuk. Jobb oldalán meghatározható értékű kifejezésnek kell állni (jobbérték). A hozzárendelés hatására a kifejezés értéke felülírja az objektum értékét (esetleg konverzió történhet). Ekkor az értékadó kifejezés értéke az objektum új értéke, típusa az objektum típusa. Az objektum bármi lehet, kivéve tömböt, függvényt, nem lehet const, ha struktúra vagy union, akkor nem lehet egyetlen tagja sem const minősítésű., : vessző operátor. A balról-jobbra kiértékelést kényszeríti ki. Előbb a baloldali, majd a

21 / 51 jobboldali operandusát értékeli ki. Fontos fogalom a konstans kifejezés: értéke és típusa fordítási időben eldönthető, a fordító értékeli ki. Operandusai általában literálok és nevesített konstansok. Logikai tagadás:! operátorral történik. int vmi; vmi = 12; vmi =!vmi; printf("%d", vmi); vmi =!vmi; printf("%d", vmi); Tagadás esetén bámilyen értékből 0 lesz, 0-ból viszont mindig 1. Logikai AND, OR műveletek: Feladat: írjuk ki, hogy egy év szökőév-e: Szökőév akkor, ha osztható 4-el, de nem osztható százzal, kivéve, ha osztható 400-al. int i; printf("kerem az evszamot: "); scanf("%d", &i); if( i%4 == 0 && i%100!= 0 i%400 == 0 ) printf("szokoev"); else printf("nem szokoev"); A rövidzár operátor a következőt jelenti: logikai értékek esetén nem szükséges minden operandust kiértékelni. Pl.: AND operátor esetén, ha az egyik operandus hamis, akkor a művelet eredménye a másik operandustól függetlenül hamis lesz. Hasonlóan az OR művelet, ha az egyik operandus igaz, akkor a másik operandustól függetlenül igaz lesz a művelet eredménye. Léptető (shift) operátorok: Határozzuk meg, hogy hány bites az int típus az adott számítógépen: unsigned int i = 1; unsigned short bitek = 0;

22 / 51 do{ ++bitek; i = i<<1; while( i ); printf("%u\n", bitek); Az előjel nélküli i változónak a kezdőértéke 1 (kettes számrendszerben: 00000001). Ezt az 1 db 1-es értékű bitet toljuk végig a változón belül, egyesével, közben számoljuk az eltolások számát. Bitenkénti műveletek Vegyük egy int típusú változó alsó 5 bitjét, majd kapcsoljuk be a 10. bitjét: int i = 0x0425; int alsobitek; alsobitek = i & 0x001F; i = i 0x0200; Explicit típuskényszerítés (kasztolás, casting) pl.: int a = 8; double b; b = (double) a; Ezzel az a értékét double típusra kényszerítettük. Ezt nevezik explicit típuskényszerítésnek. Implicit típuskényszerítés: ha a kényszerítés automatikusan jön létre. Pl.: b = a; esetén a értéke automatikusan konvertálódik double típusra. Kifejezés típusát az operandusai és operátorai együtt határozzák meg. Pl. int op int esetén int lesz a kifejezés típusa, int op double esetén double (ahol op egy tetszőleges aritmetikai operátor). Aritmetikai típuskonverziók 1. Ha az egyik operandus long double, akkor a másik long double-á alakul 2. Ha az egyik operandus double, akkor a másik is double-á alakul 3. Ha az egyik operandus float, akkor a másik is float-á alakul 4. A char és a short típusok int-é konvertálódnak, ha megőrzi értékét, egyébként unsigned int lesz 5. Ha az egyik operandus unsigned long int, a másik is unsigned long int-é alakul

23 / 51 6. Ha az egyik operandus long int, a másik unsigned int, és a long int ábrázolható az unsigned int értékével, a közös típus long int lesz, egyébként unsigned long int 7. Ha az egyik operandus long int, a másik long int típusúvá alakul 8. Ha az egyik operandus unsigned int típusú, a másik unsigned int-é alakul 9. Egyébként mindkét operandus int típusú Általánosságban elmondható, hogy ha van egy szélesebb operandus a kifejezésben, akkor a kifejezés típusa a szélesebb operandus típusa lesz. pl.: int a, b; double c; a = 1; b = 2; c = a / b; ha c értékét kiíratjuk, akkor 0-t kapunk. Ugyanis bár az a / b művelet eredménye 0.5, a C nem kerekít, hanem levágja a tizedespont utáni részt, és az eredmény 0 lesz. A helyes megoldás az, hogy az egyik operandust lebegőpontos számmá alakítjuk: double c; c = (double) a / b; Ugyanezek literálokra is érvényesek. Azonban literálok esetén fontos a felírási mód. pl.: double c; c = 1 / b; ez szintén 0-t ad eredményül. helyesen: c = 1. / b; Kerekítsünk egy számot. Egy szám kerekített értéke: ha < x.5, akkor x, ha >=x.5, akkor x+1 pl. 0.5 felfele kerekítve 1. Ezt úgy érjük el, hogy hozzáadunk 0.5-öt, majd csonkítjuk. Ha a szám tizedesrésze >= 0.5- nél, akkor a szám egész része így 1-el növekszik, amit ha csonkítunk, megkapjuk a helyes eredményt. ha a tizedesrész kisebb, mint 0.5, és hozzáadunk 0.5-öt, és csonkítjuk, akkor szintén megkapjuk a várt eredményt. Tehát: double x = 3.2, y; y = (int)(x + 0.5); És megkapjuk a 3-at. Ha pl.: x = 3.75, akkor 4-et kapunk.

24 / 51, operátor: a balról-jobbra kiértékelési irányt kényszeríti ki. Két operandusa van, egy bal és egy jobb oldali kifejezés. Először a bal oldali, majd a jobb oldali kifejezés hajtódik végre. A kifejezés értéke az utoljára végrehajtott kifejezés értéke. pl.: int x=5, y=3, z=2, w=6; x=5, y+=3,z=0,w++; ekkor x=5 y=6 z=2 w=7 lesz az egyes operandusok értéke, a kifejezés értéke 6. Fontos: a deklarációs utasításban a, nem operátor, hanem a változók felsorolását jelző karakter! Kérdés: mi lesz x=(5, y+=3,z=0,w++); után x értéke, és miért? int a=5,b=7,c=5; (a==c && a++!=b-- --b>c); a kifejezés értéke 1 lesz, típusa int. Struktúrák: A rekord adatszerkezet megjelenése a nyelvben. Egy vagy több, azonos vagy különböző típusú változók összessége. Tulajdonképpen változókat gyűjtünk össze, és egyben tudjuk kezelni. Pl.: egy személy azonosítható a nevével, születési idejével és helyével, valamint az anyja nevével. Deklarációja: struct [struktúraazonosító] { típus azonosító; [típus azonosító;]... [változó]...; Itt a struktúraazonosítót struktúracímkének, a struktúrában felsorolt neveket a struktúra tagjainak nevezzük. A struktúraazonosító és a változó(k) megadása opcionális. Ha nincs változólista, akkor nem foglal helyet, és ha címkézett volt a struktúra, akkor a címke a későbbi definíciókban a struktúra konkrét előfordulása helyett használható. pl.: struct descartes { int x; int y; ; a Descartes-féle koordináta rendszer koordinátáit reprezentálja. Ezután a struct descartes együtt egy változó típus. Változó deklarációja descartes típussal: struct descartes a,b,c; A fordító éppen annyi helyet foglal le a struktúra számára, hogy benne a struktúra minden

25 / 51 tagja elférjen. A struktúratagok a deklaráció sorrendjében, egyre növekvő memóriacímeken foglalnak helyet. Lehetőség van kezdeti érték adására is: struct descartes a = { 15, 20 ; Fontos, hogy a típus definiálásakor ; választja el a tagokat (ugyanis az deklaráció), míg kezdeti érték adásakor szimplán egy,! Egy struktúrának ugyanúgy adhatunk egy másik struktúrát értékként, azonban azonos típusúaknak kell lenniük: struct descartes a,b = { 15, 20 ; a = b; ekkor a tagjainak értéke ugyanaz lesz, mint b-nek. Struktúra adattagjainak elérése a. operátorral történik (statikus minősítés operátor). Pl.: adjunk az a struktúra egyik tagjának értéket: a.x = 2; Struktúrák nem hasonlíthatóak össze (==)! Az összehasonlítást tagonként kell elvégezni. Unionok Hasonlít a struktúrához, azonban a tagok között 0 a címeltolás. Ugyanazon a memóriaterületen több, különböző típusú adatot tárolhatunk. Pl.: egy változóhoz hozzáférhetünk int, valamint float típusként is. Deklarációja: union azonosito { tipus valtozo; [, tipus valtozo]... [valtozo] [, valtozo]...; union unio { int i; float f; char c[4]; ; union unio u; u.i = 1869180533; printf("egesz:\t%d\n", u.i); printf("valos:\t%f\n", u.f); printf("karakter:\t%c \tszam: %d\n", u.c[0], u.c[0]); printf("karakter:\t%c \tszam: %d\n", u.c[1], u.c[1]); printf("karakter:\t%c \tszam: %d\n", u.c[2], u.c[2]); printf("karakter:\t%c \tszam: %d\n", u.c[3], u.c[3]);

26 / 51 A használt számítógépen az int 4 byte-os, ezért a 4 byte-os float-ot fel lehetett használni. A négyelemű karaktertömb pedig pontosan byte-onként éri el a másik két 4 byte-os változót. Nem szükséges minden változó méretének megegyeznie, ekkor a fordító a legnagyobb méretű változónak foglal helyet, így minden tagja el fog benne férni. Azonban a változók ekkor is azonos kezdőcímen helyezkednek el. Pl.: a következő union az ábrán látható módon értelmezhető: union unio { float f; short s; char c; ; ahol LSB a legalacsonyabb helyiértékű bit, MSB a legmagasabb helyiértékű bit a használt számítógépen. Figyelem!!! Ez egy erősen implementációfüggő és hardware függő eszköz!!! Tömbök: Olyan összetett adattípus, amely azonos típusú elemekből áll és a memóriában folytonosan helyezkedik el. Deklaráció: tipus azonosito[elemszam1]{[elemszam2]... Az azonosito lesz a tömb neve, [] ek között az egyes dimenziókhoz tartozó elemszámot adjuk meg, ennek konstans kifejezésnek kell lennie. Az indexelés 0-tól elemszám-1-ig tart. Legalább 1 dimenziót meg kell adni. Egy tömbelemre hivatkozás: név[indexkifejezés1]{[indexkifejezés2]... Az index kifejezés tetszőleges egész típusú kifejezés. Az index érvényességét a fordítóprogram nem ellenőrzi, azaz a hibás indexelés csak futás közben derül ki. Feladat: kérjünk be 10 számot, és írjuk ki. int i,t[10]; for( i = 0; i < 10; i++ ) scanf("%d", &t[i]); for( i = 0; i < 10; i++ )

27 / 51 printf("%d\n", t[i]); Bővítsük ki a kódot úgy, hogy írja ki a tömbben levő elemek összegét, és átlagát. int i,t[10], osszeg; osszeg = 0; for( i = 0; i < 10; i++ ) scanf("%d", &t[i]); for( i = 0; i < 10; i++ ){ osszeg += t[i]; printf("osszeg= %d, atlag= %f\n", osszeg, osszeg/10.); Tömb deklarációjakor is lehet kezdőértéket adni: int t[10] = { 10, 20, 30 ; ekkor a tömb első 3 eleme rendre 10, 20 és 30, a maradék 7 elem mind 0 értéket kap. Az int t[] = { 10, 20, 30 ; tömbdeklarációkor egy 3 elemű tömböt deklarálunk, azaz a kezdeti érték adásakor a kapcsos zárójelek közötti elemek darabszáma határozza meg a tömb méretét, ha azt nem adtuk meg. Struktúratömbök struct descartes tomb[22]; Ez egy 22 elemű, descartes struktúrákat tároló tömböt hoz létre. Egy tömbelem egy struktúratagjának elérése: tomb[0].x Többdimenziós tömbök Elhelyezkedése a memóriában általában sorfolytonos (lsd. adatszerkezetek), azaz a jobb oldali index fut gyorsabban. Pl egy matrix[2][3] tömb elemeire a hivatkozás (tulajdonképpen 2x3-as matrix): matrix[0][0], matrix[0][1], matrix[0][2], matrix[1][0],matrix[1][1], matrix[1][2] sorrendben történik. Ez valójában nem többdimenziós tömb. A C azt mondja, hogy tömbök tömbjeiből épül fel, azaz ennek az 1 dimenziós tömbnek az elemei 1 dimenziós tömbök.

28 / 51 Kezdőértékadás: int t[2][3]={{1,2,3,{4,5,6; azaz felsoroljuk a tömböket, és azok elemeit. Lehetséges az is, hogy nem adunk meg elemszámot: int t[][3]={{1,2,3,{1,2; Az utolsó indexnek azonban mindig ismertnek kell lennie, csak az első index lehet határozatlan! Magasabb dimenzióra hasonlóan működnek az itt leírtak. A többdimenziós tömb elemeit egyszerű felsorolással is megadhatjuk: int t[2][3]={ 1, 2, 3, 4, 5, 6 ; Ennek az az oka, hogy a tömb a memóriában folytonosan (méghozzá sorfolytonosan) helyezkedik el. Megjegyzés: a más nyelvek által használt tomb[i,j] hivatkozás itt is szabályos, azonban nem az i,j-edik elemet érjük el, hanem a sorfolytonosan tárolt tömb j-edik elemét (lsd C kifejezések). Többdimenziós tömböket általában matrixok reprezentációjára használunk. Feladat: határozzuk meg, hogy egy kvadtratikus mátrix szimmetrikus-e? Kvadratikus a mátrix, ha a sor és oszlopszáma megegyezik (azaz négyzetes alakú). Szimmetrikus, ha a főátlóra szimmetrikus, azaz a főátló alatt és felett ugyanazok az elemek vannak. Kérjük be a dimenziót, majd olvassuk be a mátrixot, az elemeit sorfolytonosan megadva: int i,j,n; int t[50][50]; int symm = 1; printf("a matrix merete: "); scanf("%d", &n); printf("a matrix elemei:\n"); for( i = 0; i < n; i++ ) for( j = 0; j < n; j++ ) scanf("%d", &t[i][j]); /* szimmetria ellenorzes */ for( i = 0 ; i < n ; i++ ){ for( j = 0 ; j < n ; j++ ){ if( j==i ) continue; if( t[i][j]!= t[j][i] ){ /* a foatlot felesleges ellenorizni */

29 / 51 symm = 0; break; if(!symm ) break; /* ha mar tudjuk, hogy nem szimmetrikus, ne folytatodjon a program */ ( symm )? printf("szimmetrikus\n") : printf("nem szimmetrikus\n"); Feladat: Adott egy n X n -es egészeket tartalmazó mátrix. (n, n < 200) Írjuk a kimenetre növekvő sorrendben azoknak az oszlopoknak az indexét (a sorszámozás 0-ról indul), amelyekben több a számok összege, mint a mátrix transzponáltjának ugyanilyen indexű oszlopában. A bemenet első sora tartalmazza n-et, a többi sor pedig a mátrix elemeit n sorba és n oszlopba rendezve. int i,j,n,t[200][200]; int sum1,sum2; scanf("%d", &n); for( i = 0 ; i < n ; i++ ) for( j = 0 ; j < n ; j++ ) scanf("%d", &t[i][j]); for( i = 0 ; i < n ; i++ ){ sum1 = sum2 = 0; for( j = 0 ; j < n ; j++ ) sum1+=t[i][j],sum2+=t[j][i]; if( sum1 < sum2 ) printf("%d\n", i); /* a transzponaltra ilyen egyszeruen hivatkozhatunk*/ Stringek (karaktertömbök, karakterláncok) A C nyelvben nincs külön típus stringekre, azokat karakterek tömbjeként értelmezi. A stringeket karaktertömbökben kell elhelyezni. A karakterlánc végét külön speciális, zérus értékű byte, a nullkarakter jelzi ('\0'). Pl.: a "string" karakterlánc a következőképpen kell letárolni egy karakter tömbben: A karakterlánc első karaktere a tömb 0. eleme, a t az 1., a legutolsó karakter a t[5]-ben van, és az ezt követő t[6]-ban található a lezáró karakter. Tehát a string tárolására használt tömb mérete mindig 1-el nagyobb, mint a stringet alkotó karakterek száma, a lezáró karakter miatt. Fontos megjegyezni, hogy a lezáró karakterlánc tömbindexe pontosan megegyezik a

30 / 51 karakterlánc hosszával. A '\0' karakter nem egyezik meg a 0-val, mint karakterrel ('0'), de megegyezik a 0-val, mint egész számmal. char str[] = "string"; printf("%s\n", str); Karakterenkénti kiíratás: char str[] = "string"; int i; for( i = 0; str[i]!= '\0'; i++ ) printf("%c\n", str[i]); Ekkor, amíg el nem éri a lezáró karaktert, kiírja az aktuális indexű karaktert. String bekérése: char str[100]; printf("kerem a nevedet: "); scanf("%s", str); printf("a neved: %s\n", str); Fontos megjegyezni, hogy stringek bekérése esetén nem kell a & címe operátort a változó elé tenni! Mivel a tömb csak 100 elemet, azaz 100 karaktert képes tárolni, ezért bemenetként maximum 99 karaktert tudunk megadni (bár ritka az az ember, akinek 100 karakter hosszú a neve). Ha 99 karakternél többet adunk meg, akkor a tömböt túlindexeljük, és futási hibával leáll a program. Pointerek (mutatók) A mutató adattípus létrejöttének okai: - előre nem ismert méretű tömbök létrehozása (eddig csak fix méretű tömbökről volt szó) - egyetlen adatelemre való többszörös hivatkozás - dinamikus függvényhivatkozás A mutatók memóriacímeket tárolnak. Minden típushoz tartozik egy mutatótípus, amely az adott típusú változó címeit képes eltárolni.

31 / 51 Egy változónak 4 komponense van: név, attribútumok, cím, érték. A mutató egy olyan speciális változó, amelynek típusa: egy adott típusú változóra mutató mutató, értéke: egy adott típusú változó címe. Deklarációja: típus *azonosító[, *azonosító]...; a * deklarációkor nem a típushoz, hanem az azonosítóhoz tartozik, azaz csupán egy jelöléstípus. Értelmezés: int *bela; bela egy int * típusú változó, azaz olyan mutató típusú változó, ami egy int típusú változóra mutat. Érdemes a deklarációt jobbról balra olvasni, így könnyebb megérteni: bela egy (*) mutató, ami egy (int) egészre mutat. int *bela; *bela azaz a hivatkozott változó egy int típusú változó. Mutató típusú változó inicializálása: int sor, j; int *bela; bela = &sor; A hivatkozott változónak ismertnek kell lennie! Azonban nem szükséges, hogy a hivatkozás előtt legyen értékadás. & operátor : egy adott változó címét adja meg. * operátor : egy adott memóriacímen található (meghatározott típusú) aktuális érték előállítása, az indirekció operátora. Pl.: j = *bela; Mutató mutathat egy másik mutatóra is. Nézzünk meg egy programot: int i; int *j; int **k;

32 / 51 i = 5; printf("i = %d\n", i); j = &i; *j = 42; printf("i = %d\n", i); printf("*j = %d\n", *j); k = &j; **k = 52; printf("i = %d\n", i); Három változót deklaráltunk: egy egész, egy egészre mutató mutatót, valamint egy egészre mutató mutató mutatójára mutató mutatót. Az ilyen mondatok elkerülése miatt a mutató, mint eszköz helyett a pointert fogom használni. Első körben az i értéke 5 lett. Ez egy nagyon jó ötlet. Majd a j pointer értékéül i címét adtuk a & operátor segítségével. Ekkor azt mondjuk, hogy a j pointer az i változóra mutat, röviden i-re mutat. A mutatott változóhoz a * operátorral férhetünk hozzá, így az eredeti i változó értéke fog módosulni. A k változó már kétszeres indirekciót valósít meg, amelynek értéke a j pointer címe, egyszeres indirekcióval j-hez, kétszeres indirekcióval pedig i értékéhez férhetünk hozzá (lsd az ábrákat). Ha felvennénk még egy 3 csillagos pointert, akkor a jobb alsó ábra szerint lehetne értelmezni. Nem a konkrét memóriacím a fontos, amelyre mutatnak. A pointereket változókra pozícionálhatjuk, ezáltal műveleteket végezhetünk velük. A mutatott változót ugyanúgy felhasználhatjuk, mintha a sima változót használnánk. Pl.: ++*pi az i változót fogja egyel növelni. Azonban: *pi++ helytelen, ugyanis ez a mutatót növelné, és azután használja a * operátort. Helyesen: (*pi)++. Oka: a precedencia sorrend. Speciális mutató érték a NULL érték:: olyan mutatók értéke, amelyek nem mutatnak változóra. Általában az stdio.h header-ben definiálják a következő módon: #define NULL ((void *)0) ritkábban: #define NULL ((char *)0) itt a void * az általános mutatót jelöli (az ANSI szabvány előtt ezt a szerepet a char * töltötte be). Minden tárbeli cím tulajdonképpen egy egész szám, amely egyértelműen azonosítja az adott tárhelyet. Azonban a pointerek és az egész számok nem felcserélhetőek, kivéve a 0 értéket. A 0 konstansként összehasonlítható pointerekkel, valamint azokhoz hozzárendelhető. Pointer aritmetika A pointer értéke egy előjel nélküli egész, vele matematikai műveletek végezhetőek el.

33 / 51 Két pointer értéke összehasonlítható ( ==,!=) int i,j; int *pi,*pj; i=6; j=6; pi = &i; pj = &j; if (pi == pj) printf("egyenloek\n"); else printf("nem egyenloek\n"); Értelmezése: a két pointer ugyanoda mutat-e? Pointer összehasonlítható a NULL értékkel és a 0 konstanssal is. Összeadás Pointerhez adható egy tetszőleges egész érték, amelynek jelentése: érték darabszámú, adott típusú objektummal való előre mutatás a memóriában (lsd. a tömbök és pointerek kapcsolatánál) Pointerből kivonható egy tetszőleges egész érték, amelynek jelentése: visszafelé mutatás a memóriában. Távolság Két pointer kivonható egymásból, ha azonos tömbben, azonos típusú objektumot címeznek, az eredmény pedig a két objektum közti címkülönbség (távolság). Tömbök és pointerek közötti kapcsolat A tömb és a pointer majdnem ugyanaz. Minden tömb azonosítója, mint kifejezés, megegyezik a tömb első elemére mutató pointerrel. Egy tömbelemre hivatkozás pedig megegyezik a neki megfelelő pointer aritmetikai kifejezéssel. Deklaráljunk egy 5 elemű, int típusú t tömböt és egy p, int-re mutató pointert, és tekintsük a következő ábrát:

34 / 51 A t egy pointer, értéke a tömb nulladik elemének a címe. A nulladik elemhez a következő módokon férhetünk hozzá: t[0] tömbhivatkozással *t pointerként *(t+0) pointeraritmetika segítségével Pointerhez tetszőleges egész szám adható. Itt az objektumok int méretűek, és ha a pointerhez hozzáadunk 1-et, akkor pontosan 1 int mérettel fog a pointer arrébb mutatni. Tehát a *(t+1) megegyezik a t[1] tömbhivatkozással, a *(t+2) a t[2]-vel stb. A t értékül adható egy másik pointernek. A p = &t[0]; utasítás megegyezik a p = t ; utasítással (ezért is nem kellett stringek bekérésénél a & operátor, ugyanis a tömb értéke önmagában egy cím) A tömb elérés és a pointer aritmetika keverhető: *(t+1) megyegyezik a p[1]-el. Egy fontos megjegyzés: a p++ megengedett, a t++ viszont nem, ugyanis a t egy lefoglalt memóriaterület első elemére mutató konstans pointer. A p viszont kaphat új értéket, mert a pointer egy változó, a tömb neve viszont nem az. String bejárása pointeraritmetika segítségével: char t[]="ez egy string"; char *p; p = t; for( ; *p; ++p ) printf("%c\n", *p); A fordító nem ellenőrzi, hogy egy tömbre hivatkozáskor érvényes indexet adtunk-e meg. Ha egy tömb nem valós indexű elemére hivatkozunk, akkor ez futási hibát eredményezhet. Azonban egy tömb utolsó utáni elemére szabályosan hivatkozhatunk (ez néhány algoritmus implementálásához szükséges) Struktúrákra és unionokra mutató pointerek Deklaráljunk egy struktúrát, valamint egy rá mutató pointert: struct list{ int data; int index; ;

35 / 51 struct list l; struct list *pl; a struktúra egy elemére a l.data módon hivatkozhatunk. A következő, struktúra tagjaira való hivatkozások egyenértékűek. (*pl).data; pl->data; Az első esetben a struktúrára mutató pointer segítségével férünk a struktúra adattagjához. A második esetben szintén pointer segítségével érjük el az adattagot, azonban a dinamikus minősítés operátor segítségével. A -> operátort használhatjuk struktúrára vagy unionra mutató pointer mellett, a kétféle elérés equivalens, azonban így egyszerűbb és érthetőbb a kód. A pointer használatát indokolja még a dinamikus adatszerkezetek használata (dinamikus tömb), adatszerkezetek szétszórt reprezentációja, függvények esetén a cím szerinti paraméterátadás. Függvények Az alprogramok a proceduláris absztrakció megjelenése a programozási nyelvekben. Akkor alkalmazható, ha a program különöző pontjain ugyanaz a programrész megismétlődik. Az ismétlődő programrészt csak egyszer kell megírni, és a program azon pontján, ahol ez a programrész szerepelt volna, csak hivatkozni kell rá. Formális paraméterekkel látjuk el, és általánosabban írjuk meg, mint ahogy az adott helyen szerepelt volna. A C nyelvben nincs eljárás, csak függvény. A függvény feladata: egy érték meghatározása, amelynek típusa meg kell, hogy egyezzen a függvény visszatérési típusával. A visszatérési értéket a függvény neve hordozza. Függvény létrehozása: Meg kell adni a függvény prototípusát (a specifikációt, amely általában a main() előtt található) a következő módon: tipus fuggveny_neve([fpl]); ahol a tipus a visszatérési érték típusa, a fuggveny_neve egy azonosító, fpl pedig a formális paraméterek listája. Speciális típus a void típus: ez jelöli, hogy nem függvényről, hanem eljárás-szerű alprogramról van szó. Tehát C-ben nincs definíció szerint vett eljárás, csak void típusú függvény. A definíciós részben kell megadni, hogy a függvény mit csináljon (implementáció). Hozzunk létre egy függvényt, ami visszatér a paramétereinek az összegével: Legyen a következő prototípusunk: int osszead(int a, int b); a hozzá tartozó definíció: int osszead(int a, int b){ return (a + b);