Programozási Nyelvek (C++) Gyakorlat Gyak 01.
Török Márk
[email protected] D-2.620
1
Tartalom • • • • • • •
Hello Világ! Fordítás, futtatás, debuggolás Interpretált vs bájtkód Névtér printf vs cout Fejállományok Streamek 2
Tartalom – Hello Világ • C vs C++ – Különbségek, okai – Egyszerűsítés (?)
• Mi az a belépési pont? • Mikor van vége? • stdio, stdlib
3
Hello Világ (C) • Példa 1 : #include <stdio.h> /* * Több soros komment */ int main() { printf("Hello World\n"); return 0; // egy soros komment }
4
Hello Világ (C) • Példa 2 : #include <stdio.h> #include <stdlib.h> /* * Több soros komment */ int main() { printf("Hello World\n"); system("pause"); return 0; // egy soros komment } 5
Hello Világ (C) – Fordítás • $ gcc main.c • $ ls main.c, a.out (ez futtatható)
• ./a.out Hello Világ! • $ gcc main.c -o main $ ls main.c, main (ez futtatható)
• ./main Hello Világ!
6
Hello Világ (C++) #include
/* * Komment maradt a régi */ int main() { std::cout <<"Hello World\n" << std::endl; }
7
Hello Világ (C++) – Fordítás • $ g++ main.cpp • $ ls main.cpp, a.out (ez futtatható)
• ./a.out Hello Világ! • $ g++ main.cpp -o main $ ls main.cpp, main (ez futtatható)
• ./main Hello Világ!
8
Hello Világ – C vs C++ • C : procedurális, struktúrális, C++: objektumorientált (később) • stdio.h iostream • printf std::cout • A változások okai az objektumorientált programozási paradigmák követése, új könyvtárak, állományok implementálása • Belépési pont: – int main() vagy – int main(int argc, char* argv[]) vagy – int main(int argc, char** argv) 9
Hello Világ – C vs C++ • ISO C++ standard 3.6.1 – void main(/*…*/) { … } – main(/*…*/) { … }
• Kilépési pont, visszatérési érték típusa mindig int. • Visszatérés legyen 0! Minden más érték a hibáké! • C++ esetében: – Nem kötelező megadni visszatérési értéket – main() esetében ez mindig 0 lesz! – Más esetén egy memóriaszemét. 10
Hello Világ – stdio.h • C Standard Input and Output Library ― C-ben stdio.h ― C++ cstdio ― Az input-output műveletekért felelős ― macrok, típusok, függvények, konstansok ― Billentyűzet, nyomtató, egyéb perifériaeszközök ― stream: lehetőséget biztosít ezek írására, olvasására. ― stdin, stdout, stderr (később)
11
Hello Világ – stdlib.h • C Standard General Utilities Library • Memóriahasználathoz szükséges metódusok – calloc, malloc, realloc, free
• Véletlenszám generálás • Környezettel kommunikáló metódusok: – exit, system, …
• Konverzió • Keresés, rendezés • ... 12
Fordítás, futtatás, debuggolás • Ahogy már láttuk: – $ g++ main.cpp -o main – $ ./main
• Parancssori paraméterrekel – $ ./main param1 param2 – argv[0] maga a futtatható állomány; argv[1], ... a tényleges paraméterek
• Hibákat irassuk ki: – $ g++ -Wall main.cpp -o main
• Error, warning • Hiba kiírása: – fájlnév:sor száma:hibaüzenet 13
Fordítás, futtatás, debuggolás • printf – format specifiers
• • • •
%i or %d
int
%c
char
%f
float
%lf
double
%s
string
int a = 9; int b = 10; int c = 100; printf("%d|%d|%d\n", a, b, c); // 9|10|100 printf("%3d|%3d|%2d\n", a, b, c); // 9| 10|100 printf("%3d|%3d|%2d\n", a, b, c); // 009|010|100 14
Fordítás, futtatás, debuggolás • Nézzünk egy példát: #include bool f() { }
int main() { std::cout << f(); }
• $ g++ main.cpp -Wall -o main main.cpp: In function ‘bool f()’: main.cpp:4: warning: control reaches end of non-void function 15
Fordítás, futtatás, debuggolás • Nézzünk egy példát: #include int main() { printf("%f", 99); }
• $ g++ main.cpp -Wall -o main main.cpp: In function ‘int main()’: main.cpp:8: warning: format ‘%f’ expects type ‘double’, but argument 2 has type ‘int’
16
Fordítás, futtatás, debuggolás • Több állomány fordítása: – void sayhello (const char* name); // hello.h – #include <stdio.h> // hello.c #include "hello.h” void sayhello(const char* name) { printf ("hello, %s", name); } – #include "hello.h” // main.c
int main() { sayhello ("world"); return 0; } 17
Fordítás, futtatás, debuggolás • $ gcc -Wall main.c hello.c -o hmain • Header: – Két félét különböztetünk meg. – #include : system header fájlok között nézi meg. (linuxon: /usr/include/stdio.h) – #include ”fájl.h”: a lokális mappában keres, majd a system headerek között.
18
Fordítás, futtatás, debuggolás • Fordítás lépései: – Forrásból object: először nem is futtatható fájl keletkezik, hanem egy object. Ennek kiterjesztése .o. – A második lépés a linkelés: a linker az objectfájlok összefésülését teszi lehetővé. Ebből lesz a futtatható állomány. – $ gcc -Wall -c main.c – Eredmény egy main.o, mely a főprogramunk gépi kódját tartalmazza. – $ gcc main.o -o main
19
Fordítás, futtatás, debuggolás • Külső könyvtárakból: – Archive állományok, kiterjesztése: .a // statikus könyvtárak – Matematikai függvények Math.h-ban, ez viszont a libm.a könyvtárban. – $ gcc -Wall calc.c /usr/lib/libm.a -o calc – Kapcsoló: $ gcc -Wall calc.c -lm -o calc – Ez annyit jelent, hogy lm = libm.a
20
Fordítás, futtatás, debuggolás • Library-k: – Static library és shared library – Static library kiterjesztése: .a • A linkelést követően az használt függvény gépi kódja a library-ból bemásolódik a futtatható állomány-ba.
– Shared library kiterjesztése: .so • Dinamikus kötés (dynamic linking): a shared library táblázatot tartalmaz hivatkozással az egyes függvényekre. • Fordításkor a linker egy ilyen hivatkozást rak be a futtatható állományba, a teljes gépi kód helyett. A futtatáskor a gépi kód bemásolódik a memóriába a megadott hivatkozás alapján.
21
Fordítás, futtatás, debuggolás • -Wall kapcsoló: – -Wreturn-type: figyelmeztet, hogy az adott függvény definíció szerint kér visszatérési értéket (azaz nem void), de ezt az implementációjában nem tettük meg. – -Wformat: hibás formatstring a printf, scanf függvényekben. Eltér a format a paraméter típusától. – -Wunused: figyelmeztet, ha használatlan változók vannak a kódban. – -Wimplicite: ha előzőleg nem adtuk meg a függvény specifikációját.
22
Fordítás, futtatás, debuggolás • Preprocessor: – A fordító által meghívott, a tényleges fordítás előtt lefutó program. – Mit csinál? Kezeli az alábbi direktívákta: • #include : forrásmegjelölés • #define: macro definiálása • #if: feltételes forrásbetöltés, macrodefiniálás
23
Fordítás, futtatás, debuggolás • Macro-k: – C-ben fontosabb, C++-ban kevésbé fontos szerepet töltenek be. (Grrrrhhh!) – Ha tehetjük, akkor kerüljük őket. (Nagyon erőssen ajánlott!) – Mivel a fordítóprogram futása előtt a macro-k meghívásra kerülnek, és belenyúlnak a kódba, nem ajánlatos használni őket. Csökken a hatékonysága azon eszközöknek, melyekkel a programunk hatékonyságát, biztonságát tudjuk mérni. Pl.: hibakeresők, kereszthivatkozás-vizsgálók.
24
Fordítás, futtatás, debuggolás • Macro-k: – #define CSERELD_EZT erre – csere = CSERELD_EZT – A macro lefutását követően az eredmény ez lesz: csere = erre – #define SQUARE(a) a*a – Ebből: int b = 0; int i = SQUARE(b + 2); – Igen ám! De ebből: b + 2*b + 2 => 3b + 2 lesz! 25
Fordítás, futtatás, debuggolás • Macro-k: – Feltétel: … #ifdef AAA printf(”ez az ág lefutott!”); #endif … – Fordítás: $ gcc -Wall -DAAA main.c – A -D kapcsoló prefixe az adott AAA nevű macro-nak. Így tudunk a macro-nak értéket is adni. – Nyilván ha ezt a kapcsolót kihagyjuk, az adott ág lefutása is elmarad.
Fordítás, futtatás, debuggolás • Debuggoláshoz: – Fordítsunk a -g kapcsolóval. – Hogy miért? Amikor a programunk abnormális futást produkál (elszáll menetközben), az operációs rendszer elmenti a program memóriabeli állapotát egy core nevű fájlba. – Nézzük meg, hogy mi van a core fájlba.
Fordítás, futtatás, debuggolás • Töltsük be a core fájlt a GNU Debuggerbe az alábbi módon: – $ gdb futtatható-állomány core-állomány – Csak együtt tudjuk őket használni, külön nem tudjuk betölteni a core-állományt. – $ gdb a.out core Core was generated by ‘./a.out’. Program terminated with signal 11, Segmentation fault. Reading symbols from /lib/libc.so.6...done. Loaded symbols for /lib/libc.so.6 Reading symbols from /lib/ld-linux.so.2...done. Loaded symbols for /lib/ld-linux.so.2 #0 0x080483ed in a (p=0x0) at null.c:13 13 int y = *p; (gdb)
Fordítás, futtatás, debuggolás • Debuggolás: – Stack backtrace kilistázása: • (gdb) backtrace • Kilistázza a hívási listát.
Interpretált vs bájtkód • C++-forráskód lefordításával hagyományos natív kódot kapunk. A natív kód a processzornak megfelelő módon lefordított gépi kód. • Ha natív C++-alkalmazásunkat több különböző környezetben (32bites, 64bites platformon) is szeretnénk futtatni, akkor külön le kell fordítanunk.
Interpretált vs bájtkód • Hogy megy ez máshol? – C# kód fordításával kezelt kódot kapunk. Ez egy közbenső nyelv (intermediate language, IL). A magas szintű (C#) és a legalacsonyabb szintű nyelv (assem., gépi kód) között helyezkedik el. – A közös nyelvű futási idejű környezet (Common Language Runtime, CLR) futási időben, menet közben fordítja le a kódot a Just-in-time (JIT) fordító alkalmazásával.
Interpretált vs bájtkód • Mit csinál a JIT fordító? – Nem túl hatékony a kódot futásidőben fordítani? – Ez nem interpreting! – A JIT-fordító nem fordít le egy adott metódust vagy függvényt annak minden egyes meghívásakor, ezt csak az első esetben teszi meg, és ekkor a platformnak megfelelő gépi kódot állít elő. – Használatával csökken az alkalmazás munkahalmaza, a közbenső kód munkaigénye kisebb lesz.
Interpretált vs bájtkód • Hogy megy ez Java-ban? – A forrásfájlokban (.java) definiált minden egyes típus fordítása eredményeként külön létrejövő típusnév.class fájl tartalmazza a Java virtuális gép által végrehajtható bináris bájtkódot. – A hordozható bájtkódot interpretálás helyett, közvetlenül futtatás előtt platformfüggő gépi kódra fordítja át, mely kód sokkal gyorsabban fut majd, mint a bájtkód interpretálása. – Előnye, a biztonság és a hordozhatóság, hátránya, hogy néhol lassú.
Interpretált vs bájtkód • Jittelés Java-ban: – A gyorsabb futás érdekében dinamikus fordítási technika használata: JIT (Just-in-time compiler). – Minden osztály bájtkódjának betöltése után az egész osztály bájtkódját lefordítja gépi kódra. (A további használatban a kód már nem interpretált.) – Előny: többszörösen is gyorsabb lehet a Java kód végrehajtása, viszont egy osztály bájtkódjának (vagy egy metódus végrehajtása) betöltési ideje növekszik.
Fejállományok • Azaz a header-ök! • Lehetővé teszik a kód elkülönítését. • Tartalma: forward declaration (forward reference) – Osztályok: class Clazz; – Alprogramok: void get(Clazz &clazz); – Váltózókat: hivatkozhatsz egy változóra, mielőtt deklarálnád. class Clazz { public: int get() { return value; } private: int value; }
– Egyéb azonosítók. 35
Fejállományok • Programok kisebb részekre bontása – Osztályokra – Alprogramokra
• Nézzük meg, hogy épül fel: • get.h #ifndef GET_H_GUARD #define GET_H_GUARD void get(int a); #endif
• Használata:
#include ”get.h” int f() { return get(10); }
Névtér • Mindig valamilyen logikai csoportosítást fejeznek ki. (Egyes deklarációk valamilyen jellezmő alapján összetartoznak.) • Külön hatókört alkotnak • Másik névtérből származó osztályt a névtér nevének minősítésével tudjuk elérni. • Ha egy nevet sokat használunk saját névterén kívül, egyszerűsíthetünk. Használjuk a using direktívát. • using namespace std;
Stream • Kimenet: – #include – using namespace std; – Az iostream minden beépített típusra meghatároz kimenetet. – Alapértelmezésben a cout-ra kerülő kimeneti értékek karaktersorozatra alakítódnak át. – cout << ”hello vilag!” << 10 << endl;
Stream • Bemenet: – istream: beépített típusok karaktersorozatként történő ábrázolásával dolgozik. – A >> jobb oldalán álló típus határozza meg, hogy bemenet fogadható el, és mi a beolvasó művelet célpontja. int i; cin >> i;