780 likes | 966 Views
Programozási Nyelvek (C++) Gyakorlat Gyak 01. Török Márk tmark @ caesar.elte.hu D-2.620. Jelmagyarázat. Elméleti vizsgán lesz! Interjúkérdés lehet Haladó C++ -n kifejtve!. Tartalom. Hello Világ! Fordítás, futtatás, debuggolás Interpretált vs bájtkód, fordítás Névtér printf vs cout
E N D
Programozási Nyelvek (C++) GyakorlatGyak 01. Török Márk tmark@caesar.elte.hu D-2.620
Jelmagyarázat • Elméleti vizsgán lesz! • Interjúkérdés lehet • Haladó C++-n kifejtve!
Tartalom • Hello Világ! • Fordítás, futtatás, debuggolás • Interpretált vs bájtkód, fordítás • Névtér • printfvscout • Fejállományok • Streamek
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
Hello Világ (C) • Példa 1 : #include <stdio.h> /* * Több soros komment */ int main() { printf("Hello World\n"); return0; // egy soros komment }
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"); return0; // egy soros komment }
Hello Világ (C) – Fordítás • $ gccmain.c • $ ls main.c, a.out (ez futtatható) • $ ./a.outHello Világ! • $ gccmain.c-o main$ ls main.c, main (ez futtatható) • $ ./mainHello Világ! • $
Hello Világ (C++) #include <iostream> /* * Komment maradt a régi */ int main() { std::cout <<"Hello World\n"<< std::endl; }
Hello Világ (C++) • Kiiratáshoz: • std::ostream::operator<< • Hol van az ostreaminclude? • include-oltuk az iostream-et. • Az iostream-en belüli include-ok: • istream, ostream, …
Hello Világ (C++) – Fordítás • $ g++ main.cpp • $ ls main.cpp, a.out(ez futtatható) • $ ./a.outHello Világ! • $ g++ main.cpp-o main$ ls main.cpp, main(ez futtatható) • $ ./mainHello Világ! • $
Hello Világ – C vs C++ • C : procedurális, strukturálisC++: objektumorientált paradigmával bővítve (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(intargc, char* argv[]) vagy • int main(intargc, char** argv)
Hello Világ – C vs C++ • ISO C++ standard 3.6.1 (mind a kettő helyes) • void main(/*…*/) { … } • Error: main must return int • main(/*…*/) { … } • Fordul, szép, jó és fut! • 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.
Hello Világ – stdio.h • CStandardInput 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)
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 • ...
Fordítás, futtatás, debuggolás • Ahogy már láttuk: • $ g++ main.cpp-o main • $ ./main • Parancssori paraméterekkel • $ ./main param1 param2 • argv[0] maga a futtatható állomány; argv[1], ... a tényleges paraméterek • Hibákat irassuk ki: • $ g++ -Wallmain.cpp-o main • Error, warning • Hiba kiírása: • fájlnév:sor száma:hibaüzenet
Fordítás, futtatás, debuggolás • printf – formatspecifiers • 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("%03d|%03d|%02d\n", a, b, c);// 009|010|100
Fordítás, futtatás, debuggolás • Nézzünk egy példát:#include <iostream>boolf() {}intmain() {std::cout << f();}
Fordítás, futtatás, debuggolás • $ g++ main.cpp-Wall-o mainmain.cpp: In function ‘bool f()’:main.cpp:4: warning: control reaches end of non-void function
Fordítás, futtatás, debuggolás • Nézzünk egy példát:#include <cstdio>intmain() {printf("%f", 99);} • $ g++ main.cpp -Wall -o mainmain.cpp: In function ‘int main()’:main.cpp:8: warning: format ‘%f’ expects type ‘double’, but argument 2 has type ‘int’
Fordítás, futtatás, debuggolás • Több állomány fordítása: voidsayhello (constchar* name);// hello.h #include <stdio.h>// hello.c#include "hello.h”voidsayhello(constchar* name) {printf ("hello, %s", name);} #include "hello.h” // main.cintmain() {sayhello ("world");return0;}
Fordítás, futtatás, debuggolás • $ gcc-Wallmain.chello.c-ohmain • Header: • Két féle include-olást különböztetünk meg. • #include <fájl.h>: systemheader 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 systemheaderek között.
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 végzi függőségek szerint. Ebből lesz a futtatható állomány. • Csak fordítás -c kapcsolóval:$ gcc-Wall-cmain.c • Eredmény egy main.o, mely a főprogramunk gépi kódját tartalmazza. • $ gccmain.o-o main
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, implementációja viszont a libm.a könyvtárban (fordított állomány!). • $ gcc-Wallcalc.c /usr/lib/libm.a-ocalc • Kapcsoló: elkerülhető a hosszú elérésiút:$ gcc-Wallcalc.c-lm-ocalc • Ez annyit jelent, hogy lm = libm.a • Elmondható, hogy lNAME = libNAME
Fordítás, futtatás, debuggolás • Library-k: • Staticlibrary és sharedlibrary (dynamic) • Staticlibrary kiterjesztése: .a • A linkelést követően a használt függvény gépi kódja a library-ból bemásolódik a futtatható állományba. • Sharedlibrary kiterjesztése: .so • Dinamikus kötés (dynamic linking): a sharedlibrary 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.
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.
Fordítás, futtatás, debuggolás • Preprocesszor: • 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ákra: • #include : forrásmegjelölés • #define: macro definiálása • #if: feltételes forrásbetöltés, macrodefiniálás
Fordítás, futtatás, debuggolás • Macro-k: • C-ben fontosabb, C++-ban kevésbé fontos szerepet töltenek be. • Ha tehetjük, akkor kerüljük őket. (Nagyon erősen 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.
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!
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 coreCore 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.6Reading 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:1313 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, fordítás • 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, fordítás • Hogy megy ez máshol? • C# kód fordításával (interpretálásával) kezelt kódot kapunk. Ez egy közbenső nyelv (intermediatelanguage, 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 (CommonLanguageRuntime, 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, fordítás • Mit csinál a JIT fordító? • Nem túl hatékony a kódot futásidőben fordítani? • 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, fordítás • Hogy megy ez Java-ban? • A forrásfájlokban (.java) definiált minden egyes típus interpretálásának eredményeként külön létrejövő típusnév.class fájl tartalmazza a 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, fordítás • 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: forwarddeclaration (forwardreference) • Osztályok: classClazz; • Alprogramok: voidget(Clazz&clazz); • Váltózókat: hivatkozhatsz egy változóra, mielőtt definiálnád. classClazz{public: intget() { returnvalue; }private: intvalue;} • Egyéb azonosítók.
Fejállományok • Programok kisebb részekre bontása • Osztályokra • Alprogramokra • Definiálása: • #define … • Használata: • #include <fajl.h> • #include ”fajl.h”
Fejállományok • A többszörös include-ok: • Több fejállományt include-oltunk, melyek között van olyan, mely include-ol egy másikat. • Így akár egy fejállomány kétszer vagy többször is include-olásra kerülhet. • A fordítás tovább tart, mert lesznek állományok, melyek többször kerülnek feldolgozásra. • Hatalmasra nő a szemantikus gráfunk. • Ezek megelőzésére: • includeguardok
Fejállományok • Includeguard: • #ifndef, azaz ha még nincs definiálva • #define, akkor definiáljuk • … jön a kód • #endif lezárom az if-et • Más: • #pragma once
Fejállományok • Egyéb kulcsszavak: • #ifdef • #if • #elif • #else
Fejállományok • Nézzük meg, hogy épül fel: // get.h #include<vector>#ifndefGET_H_GUARD #defineGET_H_GUARD voidget(int a); vector<int> getAll(int a);#endif
Fejállományok // a masikallomany #include<vector> #include”get.h”int f() { vector<int> getAll(5);returnget(10);}
Névtér • Programok tagolásának egy eszköze • Mindig valamilyen logikai csoportosítást fejeznek ki. (Egyes deklarációk valamilyen jellemzői 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. • namespace kulcsszó. • Konvenció szerint : lowelCamelCase • Legismertebb, legtöbbet használt (a félév során!): std
Névtér • std: • C++ Standard Library • Osztályok és függvények gyüjteménye • Containers (array, list, map,…) • General (algorithm, memory, iterator,…) • Streams • …
Névtér • Más nyelveknél: • Java: package • Itt láthatósági módosító is van! • C++-ben nincs • Ada: csomag • Félúton az osztály és a namespace fogalma között. • Működhet úgy is, mint egy C++-snamespace. • C#: namespace • Még fejlettebb láthatóságikör
Névtér • Példa névtér definiálására: // a.h namespaceNS { classA { // functions… } int f(char a) { // … } }
Névtér • Fordítás:$ g++ a.h-c • -c kapcsoló: • Csak fordít, de nem keres main belépési pontot. • -c kapcsoló nélkül fordítási hiba!
Névtér • Használata: • :: operator • NS:: az NS névtérben lévő neveket (fgv, osztály, …) akarjuk használni