Informatika2-2013/Eloadas

A MathWikiből
(Változatok közti eltérés)
186. sor: 186. sor:
 
== A nyelv részei ==
 
== A nyelv részei ==
 
=== Adatok (Változók, konstansok) ===
 
=== Adatok (Változók, konstansok) ===
 +
* Ha egy adat értéke változhat a futtatás során, akkor '''változó''', ha nem, akkor '''konstans'''.
 +
* Egy változót deklarálunk, ha  ...
 +
<C>
 +
int sum;
 +
</C>
 +
* Az előző példában az int a változó típusára utal, melyről a továbbiakban lesz szó.
 +
=== Típusok===
 
=== Operátorok ===
 
=== Operátorok ===
 
=== Vezérló struktúrák===
 
=== Vezérló struktúrák===
 
=== Kulcsszavak ===
 
=== Kulcsszavak ===
 
=== ... ===
 
=== ... ===

A lap 2013. február 19., 19:32-kori változata

Tartalomjegyzék

Bevezetés

Gyakran egy matematikai probléma megoldását, vagy a megoldás egy lépését számítógép segítségével határozhatjuk meg. A probléma megfogalmazása után először a megoldáshoz vezető algoritmust kell megadnunk. Ezután következik az algoritmus implementálása, beprogramozása a számítógépbe.

Algoritmusok

Az algoritmusnak nincs egységesen elfogadott definíciója, így az alábbi megfogalmazás sem definícióként értendő.

Algoritmus: (Cormen, T., Leiserson, C. E., Rivest, R. L., Stein, C. Introduction to Algorithms) Bármely jól meghatározott számítási eljárás, amelynek bemenete egy bizonyos érték vagy értékhalmaz, és amely létrehoz valamilyen értéket vagy értékhalmazt kimenetként.

Egy algoritmust sokféleképp jellemezhetünk. Knuth könyvében az alábbi, 5 fontos tulajdonságot emeli ki: (Knuth, D. E. - The Art of Computer Programming)

  • Végesség: Az algoritmus véges sok lépés után befejeződik.
  • Meghatározottság: Az algoritmus minden lépése pontosan definiált.
  • Bemenet (Input): Az algoritmus igényelhet olyan értékhalmazt (adatokat), amiket elindítása előtt meg kell adnunk.
  • Kimenet (Output): Az algoritmushoz tartozhat olyan kimeneti értékhalmaz, mely meghatározott kapcsolatban van a bemenettel.
  • Elvégezhetőség: Elvárjuk, hogy az algoritmust végre lehessen hajtani

Számítási módszernek nevezünk egy olyan eljárást, mely a végességet leszámítva teljesíti a fenti feltételeket.

Egy példa algoritmusra - Euklideszi algoritmus:

  • A feladat: Adott két pozitív egész szám, m és n, keresendő legnagyobb közös osztójuk.
  • Az Euklideszi algoritmus lépései:
    • E0 Ha m < n , akkor cseréljük meg a két számot:  m \leftrightarrow n
    • E1 Osszuk el m-et n-nel, legyen a maradék r.
    • E2 Ha r = 0, akkor az algoritmus véget ért, az eredmény n.
    • E3 Legyen  m \leftarrow n és  n \leftarrow r és menjünk vissza E1 lépésre.

Programozás, programozási nyelvek

Célunk algoritmusok beprogramozása a számítógépbe. Ehhez először a számítógép fogalmát kell megértenünk. A mai számítógépek Neumann János 1946-ban kidolgozott elvei szerint működnek. Felépítésük az ún. Neumann és Harvard architektúrák felépítését követi. Leegyszerűsített felépítésük a következő:

  • Processzor (CPU): beolvassa a memóriából az utasításokat és az adatokat, az utasítások alapján műveleteket végez, az eredményt visszaírja a memóriába; valamint vezérli a perifériákat - adatokat olvas belőlük, és ír ki
  • Memória: általános tároló, mely utasításokat és adatokat tartalmaz
  • Perifériák: háttértárolók, beviteli eszközök, monitor, ...

Egy algoritmust programozási nyelv használatával adjuk meg a számítógépnek.

  • Egy programozási nyelvvel képesek vagyunk vezérelni, utasítások sorozatát előírni a számítógép processzorának.
  • Az egyes nyelvek között compiler-ek fordítják át az utasításokat.
  • Gépi kódnak nevezzük azon nyelvet, mellyel a számítógép processzora közvetlenül vezérelhető.

Egy algoritmus programozásakor a választott programozási nyelvvel képesnek kell lennünk:

  • az algoritmus álltal használt adatok tárolására/kezelésére.
  • az algoritmus utasítás sorozatának megadására.

Példák programozási nyelvekre: Assembly, C, C++, Java, Python, ...

A C programozási nyelv

Bevezetés

A C programozási nyelv egy általános célú nyelv, melyet eredetileg Dennis Ritchie fejlesztett az AT&T Bell laboratóriumában 1969-1973 között. A nyelv néhány tulajdonsága:

  • Gépi kódra fordul
  • Hordozható
  • Hatékony programok írására alkalmas
  • Egyszerű nyelv, tömör szintaktika
  • Előfeldolgozó
  • Szabványos könyvtári függvények
  • Nagyon sok nyelv "alapja"

A nyelv lényeges részei

A nyelv lényeges részei, melyekről részletesebben lesz szó:

  • Adatok (változók, konstansok)
  • Adattípusok
  • Operátorok
  • Vezérlő struktúrák
  • Kulcsszavak
  • Paraméterek, argumentumok
  • Láthatóság
  • Függvények, függvény könyvtárak
  • Pointerek
  • Struktúrák

Kódolás, fordítás a gyakorlatban

Forráskód

A nyelv részletes elemzése előtt érdemes tanulmányozni a "Hello World!" program C-ben írt forráskódját.

#include <stdio.h>
 
int main(void){
   printf("Hello world!\n");
   return 0;
}
  • A program első sora egy #include utasítás, hatására az előfeldolgozó erre a helyre bemásolja a megnevezett állomány tartalmát. Ez most az stdio.h állomány.
  • Az első sorban a "<>" jelek arra utalnak, hogy az stdio.h állomány a fordító részére megadott ún. „include path” által definiált helyen van.
  • A következő nem üres sor a main függvény definíciója. A main függvény egy speciális függvény a C programokban, amely a program indításakor legelőször hívódik meg.
  • Az int megadja, hogy a „main” függvény egy egész szám típusú adatot ad vissza.
  • A void azt jelenti, hogy a függvény nem vár paramétereket vagy adatokat az őt meghívó rutintól.
  • A kapcsos zárójel a függvény törzsének kezdetét jelzi.
  • A következő sor futtatja a printf függvényt. Az stdio.h tartalmazza a printf függvény meghívásának leírását (prototípusát, deklarációját).
  • Ebben az esetben, a printf függvényt mindössze egyetlen paraméterrel hívjuk meg, mégpedig egy fix szöveggel: „Helló, világ!\n”, ahol \n új sort jelent.
  • A printf függvény ugyan ad vissza értéket (a kiírt karakterek számát), de ezt most nem használjuk ki.
  • A return utasítás jelenti a kilépést az aktuális függvényből (ami ebben az esetben a main), és megadja a hívónak visszaadandó értéket-Ez az esetünkben 0, ami a program hibátlan lefutását jelzi.
  • Végül a záró kapcsos zárójellel jelezzük a „main” függvénytörzs végét.

Fordítás

  • Egy egyszerű szövegszerkesztőben is megírható C forráskódot compiler-el fordíthatunk le gépi kódra. Az így kapott fájl már futtatható lesz.
  • Itt most a linux-on, gcc-vel való fordítást mutatjuk be.
  • A [ http://gcc.gnu.org/ GCC ] a GNU Compiler Collection rövidítése. Kezdetben GNU C Compilert, tehát GNU C fordítót jelentett. Mára a GCC kiegészült egyéb nyelvek fordítóival is. Elsősorban linux és BSD rendszereken használják, de Windows-on is elérhető.
  • A fordítás legegyszerűbb esetben, ha a forrásunk a hello.c file:
gcc hello.c
  • Ekkor a futtatható file-unk az "a.out" lesz.
  • Előfordulhat, hogy a megírt forráskódunk a nyelv szerint hibás részt tartalmaz. Ekkor a kód nem fordul le, a fordító pedig hibaüzeneteket küld a felhasználónak, például:
hello.c: In function ‘main’:
hello.c:3:1: error: expected ‘,’ or ‘;’ before ‘}’ token
hello.c:3:1: error: expected declaration or statement at end of input

Kapcsolók

  • A fordítás folyamatát különböző kapcsolókkal befolyásolhatjuk
  • Sokféle akpcsoló létezik: gcc command options
  • Néhány fontos kapcsoló:
    • -o a létrejövő futtatható file nevét állíthatjuk be. Az alábbi példában a hello file lesz a futtatható file
gcc hello.c -o hello
    • -W a legfontosabb figyelmeztető üzeneteket (warning) bekapcsolja
    • -Wall még néhány fontos figyelmeztető üzenetet bekapcsol
    • -s felesleges részeket (pl. nyomkövetési információk és szimbólumok) eltávolítja kimenetből
    • -g hibakereső információk hozzáadása a kimenethez
    • -gdb gdb (lsd. később) futtatásakor használt információk hozzáadása a kódhoz
    • -O1 (vagy -O), -O2, -O3' különböző szintű optimalizálások bekapcsolása. Magasabb szám magasabb szintű optimalizálást jelent. Ez hosszabb fordítási idővel, nagyobb memóriahasználattal, azonban csökkentett futtatási idővel és kisebb kódmérettel jár.
    • -Os kódméretre való optimalizálás
    • további optimalizálási kapcsolók


Linking and Compiling

  • Eddig fordításnak (compiling) neveztük azt a folyamatot, amikor a forráskódból a fordító (gcc) futtatható (gépi) kódot hozott létre. A fordítás folyamata azonban nem mindig ilyen "egyszerű". Ez történik, ha a programunk egyes részei különböző forrás file-okban találhatóak:
    • helyesen inkább "build"-nek kéne nevezni azt a folyamatot, mely során a forráskódból futtatható file-t állítunk elő.
    • compile folyamat során minden egyes forrásfájlból ún. object (.o) file-ok keletkeznek
    • linking alatt pedig azt a folyamatot értjük, amikor az object file-okból egyetlen, végső futtatható file jön létre. A fordítónak az object file-okból kell összeraknia a végső futtatható file-t.
  • A forráskódtól a futtatható file-ig tehát a compile és linking folyamatok vezetnek.
  • Ezt azért érdemes tudni, mert ha egy program nem fordul, akkor ennek oka
    • lehet a forráskódban lévő hiba miatt,
    • de az is elképzelhető, hogy a fordító a "linking" során nem talál egy forrásfájlt, amire egy másik hivatkozik.
  • A hibakeresésben tehát segíthet, ha megértjük hogy melyik részből adódik a hiba.
  • Ne feledkezzünk meg a C preprocesszorról, ami a fordítás és linkelés előtt fut le.


Hibakeresés (Debugging)

  • Előfordulhat, hogy a megírt programunk hibás. Ennek oka lehet
    • szintaktikai hiba: ekkor a nyelvi hibát vétünk, a kód nem fordul le, a fordító álltal küldött hibaüzenetekből tájékozódhatunk a hiba forrásáról.
    • szemantikai hiba: ekkor a kód ugyan lefordul, de a futás nem úgy történik, ahogy azt elvárnánk (például végtelen cikulsba fordul a programunk, vagy összeomlik). Ennek forrásáról először a fordító álltal adott "warning" figyelmeztetések adhatnak információt. Ha ez sem segít, akkor hibakereső programok használatára van szükségünk.
  • A gdb (GNU Project debugger) egy ilyen hibakereső program. Néhány hasznos tudnivaló a gdb-ről (fejlesztés alatt, mivel itt sok fogalom tisztázatlan még, ez itt csak egy terv):
  • Használatához először fordítsuk le az adott kódot:
 gcc hello.c -gdb -Wall -o hello
  • Ezután hívjuk meg a gdb-t az adott kóddal:
  gdb hello
  • Később lesz szó argumentumokról. ezeket a következőképp adhatjuk meg:
  set args argumentum1 argumentum2 ...
  • Ha ezzel is megvagyunk, akkor futtathatjuk a kódot
run
  • ha a kódunk összeomlott, akkor visszakérhetjük, hogy pontosan milyen hívás okozta ezt:
backtrace

...

Fejlesztői környezetek

  • Fejlesztői környezetek célja egy program fejlesztési folyamatának, egy programozó munkájának a megkönnyítése.
  • Egy fejlesztői környezet nagyon sokoldalú lehet. Az alábbiak csak példák arra, hogy milyen funkciókat tartalmazhat:
    • forráskód szerkesztés,
    • "syntax highlighting" (adott nyelv forráskódjának színezése),
    • automatikus kiegészítés,
    • forráskód automatikus formázása,
    • hibakeresés segítése, egyszerűsítése,
    • több forrásfájlból álló, bonyolult kód hatékony kezelése ...
  • Példák fejlesztői környezetekre:
    • Code::Blocks
    • CodeLite
    • Eclipse
    • NetBeans
    • Visual Studio ...

A nyelv részei

Adatok (Változók, konstansok)

  • Ha egy adat értéke változhat a futtatás során, akkor változó, ha nem, akkor konstans.
  • Egy változót deklarálunk, ha ...
int sum;
  • Az előző példában az int a változó típusára utal, melyről a továbbiakban lesz szó.

Típusok

Operátorok

Vezérló struktúrák

Kulcsszavak

...

Személyes eszközök