Upload
lucien
View
69
Download
4
Embed Size (px)
DESCRIPTION
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 - PowerPoint PPT Presentation
Citation preview
Jelmagyarázat
• Elméleti vizsgán lesz!• Interjúkérdés lehet• Haladó C++-n kifejtve!
2
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• Fejállományok• Streamek
3
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
4
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
}
5
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
} 6
Hello Világ (C) – Fordítás• $ gcc main.c• $ ls
main.c, a.out (ez futtatható)• $ ./a.outHello Világ!
• $ gcc main.c -o main$ lsmain.c, main (ez futtatható)
• $ ./mainHello Világ!
• $
7
Hello Világ (C++)#include <iostream>/* * Komment maradt a régi */int main() {
std::cout << "Hello World\n" << std::endl;}
8
Hello Világ (C++)
• Kiiratáshoz:• std::ostream::operator<<• Hol van az ostream include?• include-oltuk az iostream-et.• Az iostream-en belüli include-ok:
– istream, ostream, …
9
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$ lsmain.cpp, main (ez futtatható)
• $ ./mainHello Világ!
• $
10
Hello Világ – C vs C++• C : procedurális, strukturális
C++: 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(int argc, char* argv[]) vagy– int main(int argc, char** argv)
11
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.
12
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)
13
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• ...
14
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++ -Wall main.cpp -o main
• Error, warning• Hiba kiírása:
– fájlnév:sor száma:hibaüzenet
15
Fordítás, futtatás, debuggolás
• printf – format specifiers
• 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
16
%i or %d int%c char%f float%lf double%s string
Fordítás, futtatás, debuggolás•Nézzünk egy példát:#include <iostream>
bool f() {}
int main() { std::cout << f();}
17
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
18
Fordítás, futtatás, debuggolás
• Nézzünk egy példát:
#include <cstdio>
int main() { 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’
19
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;} 20
Fordítás, futtatás, debuggolás• $ gcc -Wall main.c hello.c -o hmain• Header:
– Két féle include-olást különböztetünk meg.– #include <fájl.h>: 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.
21
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 -c main.c
– Eredmény egy main.o, mely a főprogramunk gépi kódját tartalmazza.
– $ gcc main.o -o main
22
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 -Wall calc.c /usr/lib/libm.a -o calc– Kapcsoló: elkerülhető a hosszú elérésiút:
$ gcc -Wall calc.c -lm -o calc– Ez annyit jelent, hogy lm = libm.a– Elmondható, hogy lNAME = libNAME
23
Fordítás, futtatás, debuggolás• Library-k:
– Static library és shared library (dynamic)– Static library 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.
– 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.
24
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.
25
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
26
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.
27
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!
28
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.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 (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, 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: forward declaration (forward reference)
– Osztályok: class Clazz;– Alprogramok: void get(Clazz &clazz);– Váltózókat: hivatkozhatsz egy változóra, mielőtt
definiálnád. class Clazz {
public: int get() { return value; }private: int value;
}– Egyéb azonosítók. 38
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:– include guardok
40
Fejállományok
• Include guard:– #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
41
Fejállományok
• Egyéb kulcsszavak:– #ifdef– #if– #elif– #else
42
Fejállományok
• Nézzük meg, hogy épül fel:
// get.h#include <vector>#ifndef GET_H_GUARD #define GET_H_GUARDvoid get(int a); vector<int> getAll(int a);#endif
43
Fejállományok
// a masik allomany#include <vector>#include ”get.h”int f(){ vector<int> getAll(5); return get(10);}
44
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• …
46
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++-s namespace.
– C#: namespace• Még fejlettebb láthatóságikör
47
Névtér
• Példa névtér definiálására:// a.hnamespace NS{
class A {
// functions…}int f(char a){
// …}
}48
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!
49
Névtér
• Használata:– :: operator– NS:: az NS névtérben lévő neveket (fgv, osztály, …)
akarjuk használni
50
Névtér
• Használat:
#include ”a.h”…NS::A a;a.f();NS::f();…
51
Névtér
• Egymásba ágyazhatóság
namespace Outer{
//… namespace Inner {
//… }}
52
Névtér
• Lehetőség nevesítés feloldására• 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;
53
Névtér
• using más nyelvekben:– Ada: with (kicsit más)– Java: import– C#: using– Ruby: require (kicsit más)
54
Névtér
• Aliasing:namespace NameSpaceLongName{
//…}
namespace nsln = NameSpaceLongName;
55
Névtér
• Névtér kiterjesztése:
56
namespace X{
int a;}
namespace X{
int b;}
namespace X{
int a;int b;
}
Névtérnamespace NS{
int a = 10;}namespace NS{
void f() { std::cout << a; }}
57
Névtér…NS::f(); // 10NS::a = 20;NS::f(); // 20…
58
Névtér
• Veszélyek kiterjesztés esetén (namespace extending)
– Átláthatatlan kód kialakulása– Névterek feldolgozása sorrendben történik!– Ha hamarabb használok valamit, mint deklaráltam:
• Hiba!
59
Névtér
• Nem fordul, nem látja az a változót!namespace NS{
void f() { std::cout << a; }}
namespace NS{
int a = 10;}
60
Névtér
• using használata nem csak névtérre:namespace NS{
int x = 5;int y = 10;
}
// using NS::x;int main(){
using NS::x;std::cout << x << std::endl;std::cout << NS::y << std::endl;
} 61
Névtér
• overloading:namespace NS{ int f(int i) { return i; }}
namespace NS{ int f(char c) { return c; }}
62
Névtér
• Deklaráció és definíció szétválasztása– Sérült design, körültekintően, csak indokolt esetben!
namespace A {
namespace B {
void f(); } void B::f() { /* defined outside of B
*/ } } 63
Névtér
• Unnamed namespace– Név nélküli névtér– Globális statikus változók helyett!
64
Névtérnamespace {
int i = 10;}namespace NS{
int i = 15;namespace {
int i = 20;void f() { return i; } // return 20;
}}std::cout << i << std::endl; // 10;std::cout << NS::f(); // 20
65
Névtér
• Fordul?namespace foo {
int f() { return 1; } } namespace {
using namespace foo; } int a() { return f(); }
66
Névtér
• Fordul, fut.• A using feloldja a nevesítést, így a
nevesítetlen névtérben elérhetővé válik nevesítés nélkül is az f függvény.
• Vigyázz, hibás design, nagy kód esetén hibához, hibás logikához vezethet.
67
Stream
• istream, ostream, kimeneti és bemeneti műveletekért felelős
• ifstream, ofstream : (fstream), fájlműveletek– Írás, olvasás– Flagek: in, out, app, …
• istringstream, ostringstream: stringek írása, olvasása, feldolgozása
• Stream manipulátor• iterátorok
68
Stream
• Kimenet:– #include <iostream>– 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;
Stream
• Stringstream– Stringek kezeléséhez– Elérése: #include <sstream>– iostream-ből származtatva
• Azaz stream műveletek!– Valójában:
typedef basic_stringstream<char> stringstream;– Flagek, statek, műveletek
71
Stream
• Stringek:– Karakterláncok reprezentálására– #include <string>– Az std namespace-ben : std::string– typedef basic_string<char> string;
• string vs stringstream?– A string reprezentál, a stream használja a
reprezentációt.
72
Stream
• Műveletek:– Konkatenáció: +– Írás, olvasás a streamről/re: >>, <<
73
Stream// stringstream::str#include <iostream> // std::cout#include <sstream> // std::stringstream, std::stringbuf#include <string> // std::string
int main () { std::stringstream ss; ss.str ("Example string"); std::string s = ss.str(); std::cout << s << '\n'; return 0;}
74
Stream
• Többszörös öröklődésnél példa:– istream, ostream osztályokból: iostream– Diamond!
75
HF
• Mi a legrövidebb helyes c++ program?– Fordul, fut hiba nélkül
• Írj egy egyszerű hellovilag programot, ahol minden lehetséges whitespace helyén újsor karakter van.
76
Megoldás
• 4.8.1-es fordítóval:main() {}
• 4.8.1-es fordítóval:#include <iostream>
int main() {
std::cout << "hello vilag"; return 0;
} 77