Programozás (NGB_SZ019_1)
Programozás villamosmérnököknek (NGB_SZ002_1)
Takács Gábor
Széchenyi István Egyetem Matematika és Számítástudomány Tanszék
1 / 92
A számítógép felépítése I
A számítógép legfontosabb logikai alkotóelemei a processzor, a memória és a perifériák.
I
A processzor utasításokat és adatokat olvas be a memóriából, végrehajtja az utasításokat az adatokon, és esetenként az eredményt visszaírja a memóriába.
I
A perifériák (pl. háttértár, billentyűzet, képernyő) kibővítik a számítógép képességeit, és lehetővé teszik a külvilággal való kommunikációt.
2 / 92
A memória
I
A számítógép memóriája címezhető memóriaelemekből (rekeszekből) tevődik össze. A memóriaelemek mérete a legtöbb mai számítógépben 1 bájt (= 8 bit).
I
A memóriacímeket és a memóriatartalmat általában hexadecimális formában szokás megadni (pl. mondhatunk olyat, hogy a 4b00 címen található érték 1f).
3 / 92
Milyen nyelven ért a számítógép? 1
...
2 3 4
8b 04 25 90 08 60 00 # másolás a 600890 memóriacímről # az eax regiszterbe
5 6 7
8b 1c 25 94 08 60 00 # másolás a 600894 memóriacímről # az ebx regiszterbe
8 9 10
01 c3
# eax és ebx értékének összeadása, # és az eredmény beírása ebx-be
11 12 13 14
89 1c 25 98 08 60 00 # ebx tartalmának másolása # a 600898 memóriacímre ...
4 / 92
Programozási alapfogalmak
I
Algoritmus: Valamely probléma megoldására alkalmas véges hosszú lépéssorozat.
I
Adatszerkezet: Adatelemek tárolására és hatékony használatára szolgáló séma (példa: tömb).
I
Programozási nyelv: Szigorú szabályokra épülő nyelv, melynek segítségével az ember képes a számítógép felé kommunikálni az utasításait.
I
Programozás: Algoritmusok és adatszerkezetek megtervezése illetve megvalósításuk valamilyen programozási nyelven (kódolás).
5 / 92
A teafőzés algoritmusa
I
Bemenet: forraló, csésze, teafilter, cukor
I
Kimenet: egy csésze tea Lépések:
I
1. Önts vizet a forralóba! 2. Kapcsold be a forralót! 3. Forr már a víz? I
I
Ha nem, akkor várj 1 percet, és folytasd a 3. lépéstől! Ha igen, akkor folytasd a 4. lépéstől!
4. Töltsd meg a csészét forró vízzel! 5. Tedd bele a teafiltert! 6. Kérsz hozzá cukrot? I
Ha igen, akkor tegyél bele!
6 / 92
vs. „keyboard monkey”
„igazi programozó”
7 / 92
A C nyelv története A kezdeti fejlesztések az AT&T Bell Laboratóriumában történtek. A nyelv története szorosan kapcsolódik a UNIX operációs rendszer történetéhez. I
A C nyelv közvetlen elődje a BCPL (1966) és a B (1969).
I
1969: Ritchie és Thompson elkezdenek dolgozni a nyelven.
I
1973: A UNIX rendszermagját újraírják C-ben.
I
1978: Megjelenik Kernighan és Ritchie „C bibliája”.
8 / 92
Dennis Ritchie (jobbra) és Ken Thompson, a C nyelv megalkotói.
9 / 92
A C nyelv jellemzői
I
Általános célú programozási nyelv.
I
Kevés nyelvi elem és tömör utasításformák jellemzik.
I
Megtalálhatók benne a strukturált programozáshoz szükséges vezérlési szerkezetek.
I
Alacsony szintű, ezért nagy hatékonyságú programok készíthetők benne és hardverprogramozásra is alkalmas.
I
Nem korlátozza a programozó szabadságát.
„C is quirky, flawed, and an enormous success.”
10 / 92
Prominens alkalmazások I
C: - operációs rendszer kernelek (pl. Windows, OS X, Linux, Android, iOS) - rengeteg eszközmeghajtó és beágyazott szoftver - CPython, PostgreSQL - ...
I
C/C++: -
Chrome, Firefox, Internet Explorer MS Office, Photoshop, Acrobat, VLC HotSpot JVM, Oracle Database, MySQL ...
11 / 92
C fordítók I
GCC: GNU Compiler Collection. Tartalmaz C, C++, Objective-C, Fortran, Java, Ada valamint Go fordítót.
I
Clang: Az LLVM projekt C, C++, Objective-C és Objective C++ fordítója. A GCC-nél kevésbé elterjedt, de felhasználóbarátabbak a hibaüzenetei.
I
Visual C++: A Microsoft integrált fejlesztői környezete, amely C és C++ fordítót is tartalmaz.
I
Intel C++: Az Intel C és C++ fordítója, amely Intel processzorokra optimalizált kódot készít.
I
... A kurzus során GCC-t vagy Clanget javasolt használni.
12 / 92
Szabványos C Kezdetben annyiféle C nyelv létezett, ahány C fordító. Idővel megkezdődött a nyelv szabványosítása. I
K&R C (Kernighan és Ritchie 1978-as könyve alapján)
I
ANSI C (ANSI X3.159-1989 szabvány)
I
C90 (ISO/IEC 9899:1990 szabvány)
I
C99 (ISO/IEC 9899:1999 szabvány)
I
C11 (ISO/IEC 9899:2011 szabvány) A kurzus során ANSI C-t fogunk használni, néhány C99 kiterjesztéssel.1
1
Pl. egysoros komment, nem blokk eleji változódeklaráció. 13 / 92
Fejlesztőkörnyezetek I
Nehézsúlyú: -
I
Visual Studio NetBeans Eclipse Code::Blocks ...
Könnyűsúlyú: -
Emacs Vim SciTE Notepad++ ...
A kurzus során Code::Blocks-ot fogunk használni.
14 / 92
Telepítés I
Linux alatt a C fordító és a Code::Blocks csomagkezelővel telepíthető. Ha szeretnénk, akkor telepítés után be lehet állítani, hogy a Code::Blocks alapértelmezett C fordítója a Clang legyen.
I
Windows alatt: - GCC-t használó Windows-os Code::Blocks-ot a www.codeblocks.org/downloads/binaries weboldalról szerezhetünk be. - A Clang fordítót használó Code::Blocks hivatalos forrásokból történő telepítése meglehetősen bonyolult, ezért készítettem egy egyedi csomagot a kurzus hallgatói számára, amely a www.sze.hu/~gtakacs/oktatas/cprog/CodeBlocks.zip címen érhető el. Letöltés után a CodeBlocks.zip tartalmát a C meghajtó gyökerébe kell kicsomagolni.
15 / 92
Hello, World! 1 2 3 4 5
#include <stdio.h> int main() { printf("Hello, World!\n"); return 0; } hello.c
I
#include <stdio.h>: Lehetővé teszi a printf kiíró függvény használatát.
I
int main() {: A C nyelvű programok végrehajtása a main függvény elejétől indul. Az int azt jelzi, hogy a függvény egész számot ad vissza.
I
printf("Hello, World!\n");: Kiírja a standard kimenetre a „Hello, World!” szöveget valamint egy újsor karaktert.
I
return 0;: Kilép a programból, 0-ás kilépési kóddal. 16 / 92
Előfeldolgozás ⇒ Fordítás ⇒ Linkelés 1. Előfeldolgozás: Forráskódból forráskódot készít. 1
gcc -E hello.c > hello_preprocessed.c
2. Fordítás: Forráskódból tárgykódot készít. 1
gcc -c hello_preprocessed.c -o hello.o
3. Linkelés: Tárgykódokból futtatható állományt készít. 1
I 1
gcc hello.o -o hello
Az összes fázis futtatása: gcc hello.c -o hello
17 / 92
18 / 92
Azonosítók I
Az azonosítók (állandók, változók, függvények, adattípusok nevei) alfanumerikus karaktereket valamint aláhúzást tartalmazhatnak, és nem kezdődhetnek számjeggyel. - Alfanumerikus karakternek az angol ABC kis és nagybetűi valamint a számjegyek számítanak. - A nagy- és kisbetűk különbözőnek számítanak! - Néhány érvényes azonosító: line1, _getFirst, MAX_SIZE. - Néhány érvénytelen: $foo, 7UP, get-first.
I
A nyelv kulcsszavai nem használhatók azonosítóként. - Az ANSI C kulcsszavak listája: auto, break, case, char, const, continue, default, do, double, else, enum, extern, float, for, goto, if, int, long, register, return, short, signed, sizeof, static, struct, switch, typedef, union, unsigned, void, volatile, while.
19 / 92
Egyszerű adattípusok
A C nyelv alapvető adattípusai: char Egyetlen bájt. int Egész szám (mérete architektúrafüggű). float Egyszeres pontosságú lebegőpontos szám. double Kétszeres pontosságú lebegőpontos szám. Típusminősítők: I
signed, unsigned (egész típusokhoz)
I
short, long (egész típusokhoz)
20 / 92
Egyszerű adattípusok
Az adattípusok méretének feltérképezése: 1 2 3 4 5 6 7 8 9 10
#include <stdio.h> int main() { printf("char: %d\n", sizeof(char)); printf("short: %d\n", sizeof(short)); printf("int: %d\n", sizeof(int)); printf("long: %d\n", sizeof(long)); printf("float: %d\n", sizeof(float)); printf("double: %d\n", sizeof(double)); return 0; }
21 / 92
Literálok I
Egész literálok: - decimális megadás: 42, 123, ... - oktális megadás: 052, 0173, ... - hexadecimális megadás: 0x2a, 0x7b, ... (0X előtag ill. A-F karakterek is megengedettek) - unsigned (u vagy U) utótag: 42u, 123U, ... - long (l vagy L) utótag: 42l, 123L, ...
I
Karakteres literálok: - ASCII karakterek: ’A’, ’b’, ’1’, ... - escape szekvenciák: ’\t’, ’\r’, ’\n’, ’\0’, ’\0x7b’, ...
I
Lebegőpontos literálok: - double állandók: 1.2, 1E-6, 5.1e3, ... - float állandók: 1.2f, 1E-6f, 5.1e3F, ... - long double állandók: 1.2l, 1E-6l, 5.1e3L, ...
22 / 92
Deklarációk
I
A felhasználása előtt minden változót deklarálni kell. - A deklaráció formátuma: 1
TÍPUS NÉV1 = ÉRTÉK1, NÉV2 = ÉRTÉK2, ... - A kezdeti értékadás opcionális. Példa:
1 2 3
char c; int i, j, counter = 0; double epsilon = 1e-6;
23 / 92
Tömbök I
1 2 3 4
I 1 2
Tömb: Azonos típusú elemek sorozata, ahol az elemeket a sorszámukon (index) keresztül érhetjük el. A C nyelvben a tömbök indexe nullától indul. int a[3]; a[0] = 10; a[1] = 20; a[2] = 30;
A tömböknek kezdeti értéket is adhatunk: int a[] = {10, 20, 30}; double b[10] = {1.1, 2.2};
24 / 92
Sztringek I 1 2 3 4 5 6 7
I 1
Sztring (karakterlánc): ’\0’ karakterrel végződő karaktertömb. char s[] // s[0]: // s[1]: // s[2]: // s[3]: // s[4]: // s[5]:
= "hello"; 'h' 'e' 'l' 'l' 'o' '\0'
Kiírás a standard kimenetre: printf("%s\n", s);
25 / 92
Operátorok I
Aritmetikai: + - * / %
I
Összehasonlító: > >= < <= == !=
I
Logikai: !
I I
Inkrementáló és dekrementáló: ++ -Bitenkénti logikai: & | « » ^ ~
I
Értékadó: = += -= *= /= %= «= »= &= ?= |=
&& ||
Kifejezés: Minden, ami operátorokból és operandusokból felépíthető (példa: 2 * 3 + 5). Egy kifejezés értékét a kifejezés kiértékelésével állíthatjuk elő.
26 / 92
Operátorok precedenciája Operátor () [] -> ! ? ++ – + - * & (type) sizeof * / % + « » < <= > >= == != & ? | && || ?: = += -= *= /= %= &= ?= |= «= »=
Kiértékelés iránya balról jobbra jobbról balra balról jobbra balról jobbra balról jobbra balról jobbra balról jobbra balról jobbra balról jobbra balról jobbra balról jobbra balról jobbra jobbról balra jobbról balra
I
Az operátorok csökkenő precedencia szerint vannak felsorolva, azaz a táblázatban feljebb találhatók erősebben kötnek.
I
Az egyoperandusú +, - és * operátorok nagyobb precedenciájúak, mint a kétoperandusú operátorok. 27 / 92
Példák operátorokra 1 2 3 4 5
#include <stdio.h> int main() { printf("%f\n", 5.0 / 2.0); printf("%d\n", 5 / 2); printf("%d\n", 5 % 2);
// => 2.5 // => 2 // => 1
6 7 8
printf("%d\n", 42 == 42); printf("%d\n", 10 && 20 || 0);
// => 1 // => 1
int i = 10; printf("%d\n", printf("%d\n", printf("%d\n", printf("%d\n", return 0;
// // // //
9 10 11 12 13 14 15 16
}
++i); i--); 0x07 & 0x1c); 0x07 | 0x1c);
=> => => =>
11 11 6 511
28 / 92
Mohó és lusta kiértékelés
I
Egy művelet elvégzéséhez általában szükséges az operandusok előzetes kiértékelése (példa: 2 * 3 + 4 * 5).
I
A logikai && és || művelet esetén a második operandus nem értékelődik ki, amennyiben nem befolyásolhatja az eredményt!
29 / 92
Típuskonverzió
I 1 2
I 1 2
Automatikus: double a = 1 / 2; double b = 1.0 / 2;
// => 0.0 // => 0.5
Kényszerített: double c = (double) 1 / 2; // => 0.5 double d = (int) 0.5 / 2.0; // => 0.0
30 / 92
Példa: Celsius–Fahrenheit átalakítás 1 2 3 4 5 6 7 8
#include <stdio.h> int main() { int cels, fahr; scanf("%d", &cels); fahr = 9 * cels / 5 + 32; // A törtrész elveszik! printf("%d\n", fahr); return 0; }
⇒ 1 2 3 4 5 6 7 8
#include <stdio.h> int main() { float cels, fahr; scanf("%f", &cels); fahr = 9 * cels / 5 + 32; // A törtrész megmarad. printf("%f\n", fahr); return 0; } 31 / 92
Utasítások és blokkok I I
Az utasítások a program alapvető építőelemei. Egy utasítás lehet... I 1 I 1
egyszerű, printf("Hello, World!\n"); összetett (blokk). {
2 3 4 5
}
printf("Hello, "); printf("World!"); printf("\n");
I
A pontosvessző része az egyszerű utasításnak.
I
Önmagában álló pontosvessző: üres utasítás.
32 / 92
Elágazás: if utasítás 1 2 3 4
if (KIFEJEZÉS) UTASÍTÁS1 else UTASÍTÁS2
Megjegyzés: Az else ág elhagyható. 33 / 92
Példa if utasításra
1 2 3 4 5 6 7 8 9 10 11 12
#include <stdio.h> int main() { int y; scanf("%d", &y); if (y % 4 == 0 && y % 100 != 0 || y % 400 == 0) { printf("Szokoev!"); } else { printf("Nem szokoev."); } return 0; }
34 / 92
Többirányú elágazás if utasításokkal I 1 2 3 4 5 6 7
I 1 2 3 4 5 6
A többirányú elágazás megvalósítható több kétirányúval: if (KIFEJEZÉS1) UTASÍTÁS1 else if (KIFEJEZÉS2) UTASÍTÁS2 else UTASÍTÁS3
Ugyanez más írásmóddal: if (KIFEJEZÉS1) UTASÍTÁS1 else if (KIFEJEZÉS2) UTASÍTÁS2 else UTASÍTÁS3 35 / 92
Többirányú elágazás if utasításokkal I 1 2 3 4 5 6 7 8 9
I 1 2 3
Példa többirányú elágazásra: #include <stdio.h> int main() { int n; scanf("%d", &n); if (n < 10) { printf("10-nel kisebb.\n"); } else if (n > 10) { printf("10-nel nagyobb.\n"); } else { printf("Eppen 10!\n"); } return 0; }
Az else ág a közvetlenül megelőző if utasításhoz tartozik! if (n < 10) if (n > 5) printf("5 es 10 kozotti.\n"); else printf("Legalabb 10.\n"); // HIBÁS!!!
36 / 92
Többirányú elágazás switch utasítással
1 2 3 4 5 6 7 8 9
switch (KIFEJEZÉS) { case KONSTANS1: UTASÍTÁSOK1 case KONSTANS2: UTASÍTÁSOK2 ... default: UTASÍTÁSOK }
37 / 92
Többirányú elágazás switch utasítással
38 / 92
Példa switch utasításra 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
#include <stdio.h> int main() { char answer; printf("Szeretnel-e jelest programozasbol? "); scanf("%c", &answer); switch (answer) { case 'i': case 'I': printf("Akkor gyakorolj!\n"); break; case 'n': case 'N': printf("Helytelen a hozzaallasod.\n"); break; default: printf("Ervenytelen valasz.\n"); } return 0; }
39 / 92
Feltételes kifejezések
1 2 3 4
if (a > b) z = a; else z = b;
A fenti programrészlet hatására z az a és b értékek közül a nagyobbikat veszi fel. Ilyen és hasonló szerkezetek egyszerűbben is megvalósíthatók K1 ? K2 : K3 alakú feltételes kifejezéssel: 1
z = a > b ? a : b;
Először a K1 kifejezés értékelődik ki. Ha értéke nem nulla, akkor K2, egyébként K3 értékelődik ki, és lesz a feltételes kifejezés értéke.
40 / 92
Elöltesztelő ciklus: while utasítás
1 2
while (KIFEJEZÉS) UTASÍTÁS
41 / 92
Példa: Celsius–Fahrenheit táblázat
1 2 3 4 5 6 7 8 9 10 11
#include <stdio.h> int main() { printf("Celsius Fahrenheit\n"); float cels = -40; while (cels <= 40) { float fahr = 9 * cels / 5 + 32; printf("%6.1f %6.1f\n", cels, fahr); cels += 5; } return 0; }
42 / 92
Elöltesztelő ciklus: for utasítás 1 2
for (KIFEJEZÉS1; KIFEJEZÉS2; KIFEJEZÉS3) UTASÍTÁS
43 / 92
Példa: Celsius–Fahrenheit táblázat for ciklussal
1 2 3 4 5 6 7 8 9
#include <stdio.h> int main() { printf("Celsius Fahrenheit\n"); for (float cels = -40; cels <= 40; cels += 5) { float fahr = 9 * cels / 5 + 32; printf("%6.1f %6.1f\n", cels, fahr); } return 0; }
44 / 92
Hátultesztelő ciklus: do-while utasítás
1 2 3
do
UTASÍTÁS while (KIFEJEZÉS);
45 / 92
Példa: Celsius–Fahrenheit táblázat do utasítással
1 2 3 4 5 6 7 8 9 10 11
#include <stdio.h> int main() { printf("Celsius Fahrenheit\n"); float cels = -40; do { float fahr = 9 * cels / 5 + 32; printf("%6.1f %6.1f\n", cels, fahr); cels += 5; } while (cels <= 40); return 0; }
46 / 92
A break és continue utasítások I 1 2 3 4 5 6
I
1 2 3 4
break: Kilép az aktuális for, while, do vagy switch utasításból. int i = 1; while (1) { if (i > 10) { break; } printf("%d\n", i * i); i++; }
continue: A következő iterációra lép az aktuális for, while vagy do utasításban. for (int i = 1; i <= 10; i++) { if (i % 2 == 1) { continue; } printf("%d\n", i * i); }
47 / 92
Standard adatfolyamok Az operációs rendszer minden program számára létrehoz 3 standard adatfolyamot a külvilággal való kommunikációra: I
Standard bemenet (stdin): Bemeneti adatfolyam, alapértelmezés szerint a billentyűzettel van összekötve.
I
Standard kimenet (stdout): Kimeneti adatfolyam, alapértelmezés szerint a képernyőre íródik.
I
Standard hibakimenet (stderr): Alternatív kimeneti adatfolyam, alapértelmezés szerint a képernyőre íródik.
48 / 92
Standard adatfolyamok: Átirányítás
A standard adatfolyamok összekapcsolhatók fájlokkal... 1 2 3
1 2
program < in.txt program > out.txt program 2> err.txt
// A bemenet az in.txt-ből érkezik. // A kimenet az out.txt-be íródik. // A hibakimenet az err.txt-be íródik.
program >> out.txt program 2>> err.txt
// A kimenet az out.txt végére íródik. // Hibakimenet az err.txt végére íródik.
...illetve egyéb programokkal: 1 2 3
program1 | program2 > out.txt // Az 1. program kimenete lesz a 2. program bemenete. // A 2. program kimenete az out.txt-be íródik.
49 / 92
Standard adatfolyamok: Pufferelés
I
Pufferelés nélküli adatfolyamoknál a beolvasott vagy kiírt bájtok azonnal továbbítódnak.
I
Soronként pufferelt adatfolyamoknál a továbbítás sorvége karakter beérkezése esetén történik meg.
I
Teljesen pufferelt adatfolyamoknál a továbbítás adott mennyiségű bájt beérkezése esetén történik meg.
50 / 92
Standard adatfolyamok kezelése C-ben 1 2 3 4
#include <stdio.h> int main() { char c = getchar(); // 1 karakter beolvasása putchar(c); // 1 karakter kiírása
5
char s[100]; gets(s); // NE HASZNÁLJUK, VESZÉLYES! fgets(s, 100, stdin); // 1 sor beolvasása helyesen
6 7 8 9
printf("hello"); // írás stdout-ra fprintf(stdout, "hello"); // ugyanez másképp fflush(stdout); // puffer ürítése
10 11 12 13 14 15 16
}
fprintf(stderr, "ERROR!\n"); // írás stderr-re return 0;
51 / 92
Printf formátumleírók2 I
Felépítés: %[jelzők][szélesség][.pontosság][hossz][típus]
I
Gyakran használt típusok: típus d, i u x, X f, F e, E g, G c s p %
leírás előjeles decimális egész előjel nélküli decimális egész előjel nélküli hexadecimális egész decimális lebegőpontos tudományos írásmód az előző kettő közül a rövidebb karakter sztring memóriacím ’%’ karakter
I
Szélesség, pontosság: egész számok.
I
Hossz: h („short”) vagy l („long”).
2
Részletek: http://en.cppreference.com/w/c/io/fprintf
példa 124 7235 7fa 392.65 3.9265e+2 392.65 ’a’ "sample" b8000000
52 / 92
Kilépési kód A programok kilépéskor egy egész számot adnak vissza a szülőfolyamat számára. A hibamentes lefutást 0-val szokás jelezni. 1 2 3 4 5
#include <stdio.h> int main() { // <= a main visszatérési értéke int! printf("hello\n"); return 0; // <= itt adjuk meg kilépési kódot }
A kilépési kódot más programok felhasználhatják: 1 2
./hello && echo "rendben lefutott" ./hello || echo "hiba történt!"
53 / 92
Függvények I
Függvény: névvel ellátott alprogram, amely a program más részeiből meghívható.
I
Függvények használatával a számítási feladatok kisebb egységekre oszthatók. A gyakran használt függvények kódját könyvtárakba rendezhetjük (példa: standard C könyvtár).
I
A matematikában egy függvénynek nincsenek mellékhatásai. Egy C nyelvű függvénynek lehetnek!
I
C-ben a függvények egyetlen közös névtérben vannak. Függvénydefiníciók nem ágyazhatók egymásba.
54 / 92
Függvények: Mintapélda 1 2
#include <stdio.h> #include <math.h>
3 4 5 6 7 8
/* Derékszögű háromszög átfogójának kiszámítása. */ float hypotenuse(float a, float b) { float c = sqrt(a * a + b * b); return c; }
9 10 11 12
int main() { // 1. teszteset printf("%f\n", hypotenuse(3, 4));
13
// 2. teszteset float a = 5, b = 6; printf("%f\n", hypotenuse(a, b));
14 15 16 17 18 19
}
return 0;
55 / 92
Paraméterátadás, lokális változók
I
Függvényhíváskor érték szerint paraméterátadás történik, azaz a paraméterben megadott kifejezés kiértékelődik, és a kiértékelődés eredményét kapja csak meg a függvény.
I
Egy függvény paraméterei és lokális változói nem érhetők el a többi függvény számára, és a függvényből való visszatéréskor megsemmisülnek (kivéve a static kulcsszóval deklarált változókat).
56 / 92
Deklaráció és definíció square.c 1 2 3
double square(double x) { return x * x; } gravity.c
1 2 3 4
/* Két test között fellépő gravitációs erő kiszámítása. */ double gravity(double m1, double m2, double r) { return 6.67428e-11 * m1 * m2 / square(r); } I
A C fordító egymástól függetlenül fordítja le a forrásfájlokat.
I
A gravity függvény fordításakor hogyan deríthető ki a square függvény bemenetének és kimenetének típusa? 57 / 92
Deklaráció és definíció square.c 1 2 3
double square(double x) { // <= függvénydefiníció return x * x; } gravity.c
1
double square(double x); // <= függvénydeklaráció
2 3 4 5 6
/* Két test között fellépő gravitációs erő kiszámítása. */ double gravity(double m1, double m2, double r) { return 6.67428e-11 * m1 * m2 / square(r); } I
Nagyobb projektek esetén a függvénydeklarációkat header (.h) fájlokban szokás elhelyezni. 58 / 92
Deklaráció és definíció square.h 1 2 3 4
#ifndef _SQUARE_H #define _SQUARE_H double square(double x); #endif
square.c
3
double square(double x) { return x * x; }
1
#include "square.h"
1 2
gravity.c 2 3 4 5 6
/* Két test között fellépő gravitációs erő kiszámítása. */ double gravity(double m1, double m2, double r) { return 6.67428e-11 * m1 * m2 / square(r); } 59 / 92
Rekurzív függvények 1
#include <stdio.h>
2 3 4 5 6 7 8 9
/* Az n. Fibonacci-szám kiszámítása. */ int fibonacci(int n) { if (n <= 2) { return 1; } else { return fibonacci(n - 1) + fibonacci(n - 2); } }
10 11 12 13 14 15 16
int main() { printf("%d\n", fibonacci(1)); printf("%d\n", fibonacci(3)); printf("%d\n", fibonacci(5)); return 0; }
60 / 92
Makróhelyettesítés I 1
Legegyszerűbb formája: #define NEV helyettesito szoveg
Az előfeldolgozó megadott név minden előfordulási helyére beírja a helyettesítő szöveget. I
1
Ha a helyettesító szöveg egyetlen literál, akkor a makró szimbolikus állandót definiál. Példa: #include <stdio.h>
2 3
#define MAXLEN 100
4 5 6 7 8 9
int main() { char s[MAXLEN]; fgets(s, MAXLEN, stdin); // 1 sor beolvasása return 0; } 61 / 92
Paraméteres makrók
1
#include <stdio.h>
2 3 4
#define square1(x) x * x /* HIBÁS! */ #define square2(x) ((x) * (x))
5 6 7 8 9 10 11 12 13
int main() { int y = 3; printf("%d\n", printf("%d\n", printf("%d\n", printf("%d\n", return 0; }
square1(y + 1)); // => square2(y + 1)); // => square2(y++)); // => ? y); // => 5 (y kétszer
7 16 (fordítófüggő) növelődött!)
62 / 92
C forrásfájlok szokásos felépítése
1 2
függvénydeklarációk ...
// gyakran #include-dal // illesztődnek be
3 4 5
adattípus deklarációk // gyakran #include-dal ... // illesztődnek be
6 7 8
globális változók deklarációja ...
9 10 11
függvénydefiníciók ...
// ezek tartalmazzák // az utasításokat
63 / 92
C forrásfájlok szokásos felépítése pelda.c 1 2
// függvénydeklarációk float square(float z);
3 4 5 6 7 8
// adattípusok deklarációja struct Point { float x; float y; };
9 10 11 12
// globális változók deklarációja int a = 10; char b[] = "hello";
13 14 15 16 17
// függvénydefiníciók float square(float z) { return z * z; }
64 / 92
Mutatók
I
Mutató: Memóriacím tárolására alkalmas speciális változó. A mutatóhoz a megcímzett memóriaterületen található adat típusa is hozzátartozik (kivétel: void *).
I
A mutatók használata a címképzés (&) és az indirekció (*) operátoron keresztül történik.
1 2
int c; // int típusú változó int *pc; // int típusú adatot megcímző mutató
3 4 5 6
c = 5; pc = &c; // pc mostantól c-re mutat *pc = 2; // c értéke 2 lesz
65 / 92
Mutatók
66 / 92
Mutatók és függvényargumentumok 1
#include <stdio.h>
2 3 4 5 6 7 8 9 10 11 12 13 14
/* Két int típusú változó értékének megcserélése HIBÁSAN. */ void swap1(int a, int b) { int tmp = a; a = b; b = tmp; } /* Két int típusú változó értékének megcserélése helyesen. */ void swap2(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; }
15 16 17 18 19 20 21 22 23
int main() { int x = 1, y = 2; swap1(x, y); // <= Nincs csere az érték printf("x=%d, y=%d\n", x, y); // szerinti paraméterátadás miatt! swap2(&x, &y); // <= Megtörténik a csere. printf("x=%d, y=%d\n", x, y); return 0; } 67 / 92
Mutatók és tömbök I
Egy tömb neve a tömb nulladik elemének memóriacímét jelenti.
I
Minden művelet, ami tömbindexeléssel elvégezhető, megvalósítható mutatók segítségével is.
I
A mutatót használó változat bizonyos esetekben gyorsabb lehet, de általában nehezebben olvasható.
1 2
char s1[] = "hello"; char *s2 = s1;
3 4 5
putchar(s1[2]); putchar(s2[2]);
// => 'l' // => 'l'
6 7 8
putchar(*(s1 + 2)); // => 'l' putchar(*(s2 + 2)); // => 'l'
9 10
s2 = 0; // s2-nek adhatunk értéket, de s1-nek nem!
68 / 92
Címaritmetika
1 2
int a[] = {10, 11, 12, 13, 14, 15, 16, 17}; int *p = a;
3 4 5 6 7 8 9 10
printf("%d\n", *p); // => 10 printf("%d\n", *(p + 3)); // => 13 p++; // p mutasson a következő elemre! printf("%d\n", *p); // => 11 p += 3; // p mutasson az aktuális elem utáni 3. elemre! printf("%d\n", *p); // => 14 printf("%d\n", p[1]); // => 15
69 / 92
Mutatók és sztringek 1 2 3 4 5 6
1 2 3 4 5 6 7
/* Az s sztring hosszának lemérése. */ int mystrlen(char *s) { int n = 0; while (s[n] != '\0') { n++; } return n; } /* A t sztring tartalmának másolása s-be. */ void mystrcpy(char *s, char *t) { while ((*s = *t) != '\0') { s++; t++; } }
70 / 92
Mutatókat megcímző mutatók 1 2 3
char c0[] = "red"; char c1[] = "green"; char c2[] = "blue";
4 5 6 7
char *colors1[] = { c0, c1, c2 }; char **colors2 = colors1; // karaktert megcímző mutatót // megcímző mutató
8 9 10 11
printf("%s\n", colors2[0]); printf("%s\n", colors2[1]); printf("%s\n", colors2[2]);
12 13 14
colors2 = 0; // colors2-nek adhatunk értéket, // de colors1-nek nem!
71 / 92
Parancssori argumentumok 1 2 3 4 5 6
#include <stdio.h> int main(int argc, char **argv) { if (argc != 2) { printf("HASZNALAT: %s e|d|k|n\n", argv[0]); return 1; }
7 8 9 10 11 12 13 14
}
char c = argv[1][0]; if (c == 'e') { printf("eszak\n"); } else if (c == 'd') { printf("del\n"); } else if (c == 'k') { printf("kelet\n"); } else if (c == 'n') { printf("nyugat\n"); } return 0;
72 / 92
Többdimenziós tömbök 1 2 3 4 5 6 7 8
#include <stdio.h> int main() { char world[4][5] = { {'#', '#', '#', '#', {'#', '.', '#', '.', {'#', '.', '#', '.', {'#', '@', '.', '.', };
'#'}, '#'}, '#'}, '#'},
9
int heroi = 3, heroj = 1; putchar(world[heroi][heroj - 1]); // => # putchar(world[heroi - 1][heroj]); // => .
10 11 12 13 14 15 16 17 18
}
char* actrow = world[heroi]; for (int j = 0; j < 5; j++) { putchar(actrow[j]); } // => #@..# return 0;
73 / 92
Struktúrák I
Struktúra: Változók együttese, amelyet a kényelmes kezelhetőség céljából önálló névvel látunk el.
I
A struktúrák lehetővé teszik, hogy az egymással kapcsolatban lévő változók csoportját egyetlen egységként kezeljük.
I
A változóknak (másnéven tagoknak) nem kell azonos típusúaknak lenniük.
1 2 3 4 5
struct Student { char name[100]; char id[6]; int birthyear; };
74 / 92
Struktúrák használata 1 2
#include <stdio.h> #include <math.h>
3 4 5 6 7
struct Vector { // 2 dimenziós vektor tárolására float x; // alkalmas adatszerkezet float y; };
8 9 10 11 12 13 14 15 16 17 18
int main() { struct Vector v; printf("x: "); scanf("%f", &v.x); // x koordináta beolvasása printf("y: "); scanf("%f", &v.y); // y koordináta beolvasása float length = sqrt(v.x * v.x + v.y * v.y); printf("a vektor hossza: %f\n", length); return 0; } 75 / 92
Struktúrákat megcímző mutatók 1 2
#include <stdio.h> #include <math.h>
3 4 5 6 7
struct Vector { // 2 dimenziós vektor tárolására float x; // alkalmas adatszerkezet float y; };
8 9 10 11
float calclength(struct Vector *v) { return sqrt(v->x * v->x + v->y * v->y); }
12 13 14 15 16 17 18 19 20 21
int main() { struct Vector v; printf("x: "); scanf("%f", &v.x); // x koordináta beolvasása printf("y: "); scanf("%f", &v.y); // y koordináta beolvasása printf("a vektor hossza: %f\n", calclength(&v)); return 0; }
76 / 92
A typedef utasítás
I
A C typedef utasításával új adattípus neveket hozhatunk létre.
I
Szintaxis: typedef adattípus név;
I
Példák:
1 2 3
typedef struct Vector Vector; // Mostantól a struct Vector adattípusra // Vector néven is hivatkozhatunk.
4 5 6 7
typedef float scalar; // Mostantól a float adattípusra // scalar néven is hivatkozhatunk.
77 / 92
A standard C könyvtár elemei Karakterosztályozó függvények. Lebegőpontos típusokkal kapcsolatos állandók. Egész típusokkal kapcsolatos állandók. <stdio.h> Bemenettel és kimenettel kapcsolatos függvények. <stdlib.h> Típuskonverziós, véletlenszám-generáló, rendező, kereső, memória-allokáló és egyéb függvények. <math.h> Alapvető matematikai függvények. <string.h> Sztringkezelő függvények. Dátum- és időkezelő függvények. ...
78 / 92
I
Függvényeket tartalmaz annak tesztelésére, hogy egy karakter alfanumerikus-e (isalnum), betű-e (isalpha), vezérlőkarakter-e (iscntrl), decimális számjegy-e (isdecimal), megjeleníthető-e (isgraph), kisbetű-e (islower), nyomtatható-e (isprint), központozás-e (ispunct), whitespace-e (isspace), nagybetű-e (isupper), hexadecimális számjegy-e (isxdigit).
I
Emellett nagybetűről kisbetűre és kisbetűről nagybetűre alakító függvényt is tartalmaz (tolower, toupper).
1 2
printf("%d\n", isalpha('1')); // => 0 printf("%c\n", toupper('b')); // => B
79 / 92
I
A lebegőpontos számok tárolásával kapcsolatos szimbolikus állandókat tartalmaz, mint például: - FLT_MAX, DBL_MAX, LDBL_MAX: A legnagyobb float / double / long double érték. - FLT_MIN, DBL_MIN, LDBL_MIN: A legkisebb 0-nál nagyobb float / double / long double érték. - FLT_EPSILON, DBL_EPSILON, LDBL_EPSILON: A legkisebb 1-nél nagyobb float / double / long double érték mínusz 1. - FLT_DIG, DBL_DIG, LDBL_DIG: Legfeljebb ennyi decimális számjegyű egész számot lehet float / double / long double típusban pontosan tárolni. - ...
1 2
printf("%d\n", 1 + FLT_EPSILON > 1); // => 1 printf("%d\n", 1 + FLT_MIN > 1); // => 0
80 / 92
I
Az egész számok tárolásával kapcsolatos szimbolikus állandókat tartalmaz, mint például: - SHRT_MIN, INT_MIN, LONG_MIN: A legkisebb short / int / long érték. - SHRT_MAX, INT_MAX, LONG_MAX: A legnagyobb short / int / long érték. - USHRT_MAX, UINT_MAX, ULONG_MAX: A legnagyobb unsigned short / int / long érték. - ...
1 2 3
printf("%d\n", SHRT_MAX); printf("%d\n", INT_MAX); printf("%ld\n", LONG_MAX);
81 / 92
<math.h>
I
Alapvető matematikai függvényeket tartalmaz, mint például: -
1 2
Hatványfüggvény: pow. Exponenciális és logaritmusfüggvény: exp, log. Trigonometrikus függvények: sin, cos, tan, .... Inverz trigonometrikus függvények: asin, acos, atan, ... Abszolút érték: fabs. Kerekítés: round, floor, ceil. ...
printf("%f\n", pow(2, 0.5)); // => 1.414214 printf("%f\n", 4 * atan(1)); // => 3.141593
82 / 92
<stdio.h> I
Fájlmegnyitás (fopen), fájlbezárás (fclose).
I
Olvasás standard bemenetről (getchar, scanf), fájlból (fgetc, fgets, fscanf, fread), sztringből (sscanf).
I
Írás standard kimenetre (putchar, printf), fájlba (fputc, fputs, fprintf, fwrite), sztringbe (sprintf).
I
Pozícionálás (fseek, ftell), hibakezelés (feof, ferror).
I
Törlés (remove), átnevezés (rename), átmeneti fájlt megnyitás (tmpfile).
I
...
83 / 92
<stdio.h>: Fájlkezelés I
Egy fájl életciklusa: 1. megnyitás 2. olvasás, írás, pozícionálás, ... 3. bezárás
I 1 2 3 4 5 6 7
Példa: FILE *f = fopen("numbers.txt", "r"); // megnyitás if (f == 0) { exit(1); } int a[10]; for (int i = 0; i < 10; i++) { fscanf(f, "%d", &a[i]); // olvasás } fclose(f); // bezárás
84 / 92
<stdio.h>: Fájlkezelés
I I
Hiba esetén az fopen függvény NULL (0) értéket ad vissza. Fájl hozzáférési módok: - Olvasás ("r"), írás ("w"), hozzáfűzés ("a"). - Olvasás és írás ("r+", "w+", "a+"). - Bináris mód ("rb", "wb", "ab").
I
Néhány gyakori használati eset: -
Olvasás soronként fgets függvénnyel. Szövegfájl olvasása / írása fscanf / fprintf függvénnyel. Bináris fájlban lévő adatok olvasása fread / fwrite függvénnyel. Ugrás a fájl elejére fseek függvénnyel.
85 / 92
<string.h> I
1 2 3
I
1 2
Sztringműveletek: hossz lemérése (strlen), másolás (strcpy, strncpy), konkatenálás (strcat), összehasonlítás (strcmp, strncmp), mintakeresés (strchr, strstr), tokenizálás (strtok). char s1[] = "piros"; char s2[] = "piros"; if (!strcmp(s1, s2)) { puts("mindketto piros"); }
Memóriaműveletek: konstans értékre állítás (memset), másolás (memcpy), mozgatás (memmove), összehasonlítás (memcmp). int a[100]; memset(a, 0, 100 * sizeof(int)); // feltöltés nullával
86 / 92
<string.h>: További példák 1 2 3 4 5 6
1 2 3 4 5 6 7
char s1[100] = "alma"; char s2[100] = "korte"; strcat(s1, s2); puts(s1); // => almakorte strcpy(s1, s2); puts(s1); // => korte char data[] = "alma,korte,szilva,barack"; char *tok = strtok(data, ","); puts(tok); while (tok = strtok(NULL, ",")) { puts(tok); } puts(data); // => alma
87 / 92
I
time: Az 1970-01-01 00:00:00 óta eltelt időt adja vissza másodpercben mérve.
I
clock: A program által felhasznált processzoridő közelítését adja vissza, „tick” számban mérve.
I
CLOCKS_PER_SEC: Ennyi „tick” tesz ki egy másodpercet.
1 2 3 4 5 6 7 8 9
// 1/1 + 1/2 + 1/3 ... 1/10^9 kiszámítása int t0 = clock(); double s = 0; for (int k = 1; k <= 1000000000; k++) { s += 1.0 / k; } printf("az osszeg: %f\n", s); printf("szamitasido: %f mp\n", (double) (clock() - t0) / CLOCKS_PER_SEC);
88 / 92
<stdlib.h>: Véletlenszám generálás 1 2
srand(time(0)); // álvéletlenszám generátor // kezdőállapotának beállítása
3 4 5 6 7 8
// kockadobások szimulálása for (int i = 0; i < 100; i++) { int x = rand() % 6 + 1; printf("%d\n", x); }
9 10 11 12 13 14
// [0, 1] intervallumba eső álvéletlen számok sorsolása for (int i = 0; i < 10; i++) { double y = (double) rand() / RAND_MAX; printf("%f\n", y); }
89 / 92
<stdlib.h>: Rendezés és keresés 1 2 3 4 5 6
/* Két egész számot összehasonlító függvény. */ int intcmp(const void *x, const void *y) { int x2 = *((int *) x); int y2 = *((int *) y); return x2 < y2 ? -1 : (x2 > y2 ? 1 : 0); }
7 8 9 10 11
int main() { // tömb inicializálása int n = 6; int a[] = {10, 5, 2, 13, 7, 3};
12
// tömb rendezése qsort(a, n, sizeof(int), intcmp); for (int i = 0; i < n; i++) { printf("%d\n", a[i]); }
13 14 15 16 17 18 19 20 21 22
}
// keresés a rendezett tömbben int z = 10; int *j = bsearch(&z, a, n, sizeof(int), intcmp); printf("%d\n", j - a); // => 4 (azaz a 10 a rendezett tömb 4. eleme) return 0;
90 / 92
<stdlib.h>: Rendszerszolgáltatások, konverzió
1 2
int code = system("ls -la"); // parancs végrehajtása printf("%d\n", code); // a kilépési kód kiírása
3 4 5 6
char *path = getenv("PATH"); // környezeti változó // értékének lekérdezése puts(path);
7 8
1 2
exit(0);
// azonnali kilépés 0 kóddal
int x = atoi("42"); // sztring => int konverzió double y = atof("1.23"); // sztring => double konverzió
91 / 92
<stdlib.h>: Dinamikus memóriakezelés I
I
Gyakori eset, hogy a program számára szükséges memória mérete csak a futtatás során derül ki. Az ilyen feladatok dinamikus memóriakezelés segítségével oldhatók meg hatékonyan. Stack memória: - Mérete előre rögzített. - „Lefelé” (azaz a kisebb címek felé) haladva töltődik fel. - Ebben tárolódnak a függvények paraméterei és lokális változói.
I
Heap memória: - Mérete dinamikusan változhat. - A számítógép (szinte) teljes memóriájához hozzáférhetünk a segítségével.
1 2 3
int *a = malloc(1000000000); // memóriafoglalás if (a == 0) { exit(2); } // (primitív) hibakezelés free(a); // lefoglalt memória felszabadítása
92 / 92