Programozási Nyelvek (C++) Gyakorlat

Report
Programozási Nyelvek (C++) Gyakorlat
Gyak 01.
Török Márk
[email protected]
D-2.620
1
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.out
Hello Világ!
• $ gcc main.c -o main
$ ls
main.c, main (ez futtatható)
• $ ./main
Hello 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.out
Hello Világ!
• $ g++ main.cpp -o main
$ ls
main.cpp, main (ez futtatható)
• $ ./main
Hello 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
•
•
•
•
%i or %d
int
%c
char
%f
float
%lf
double
%s
string
int a = 9; int b = 10; int
printf("%d|%d|%d\n", a, b,
printf("%3d|%3d|%2d\n", a,
printf("%03d|%03d|%02d\n",
c = 100;
c); // 9|10|100
b, c); //
9| 10|100
a, b, c); // 009|010|100
16
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 main
main.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 main
main.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);
#include <stdio.h>
// hello.c
#include "hello.h”
// 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.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, 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_GUARD
void 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.h
namespace 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:
namespace X
{
int a;
}
namespace X
{
int a;
int b;
}
namespace X
{
int b;
}
56
Névtér
namespace NS
{
int a = 10;
}
namespace NS
{
void f() { std::cout << a; }
}
57
Névtér
…
NS::f();
// 10
NS::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ér
namespace
{
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>
#include <sstream>
std::stringbuf
#include <string>
// std::cout
// std::stringstream,
// 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

similar documents