Informatika2-2012/Eloadas07

A MathWikiből
A lap korábbi változatát látod, amilyen Ador (vitalap | szerkesztései) 2012. március 20., 16:24-kor történt szerkesztése után volt.

Tartalomjegyzék

Egyéb hasznosságok C programozáshoz

Konstansok és makrók

A konstansok olyan "változók" amiknek az értékét nem lehet megváltoztani. Például hasznos lehet ilyen, ha a programban a pi értékét sok helyen szeretnénk használni (ehhez elég lenne egy globális változó..), és még véletlenül sem szeretnénk felülírni az értékét a program futása során, hogy minden számítás ugyanazzal a pontossággal számolódjon. Többféle megoldást is használhatunk erre.

Előfordítóval

Konstansokat az eredeti C-ben csak az előfordító segítségével hozhattunk létre. Minden olyan kódsor ami '#' jellej kezdődik, a preprocesszornak vagyis az előfeldolgozónak szóló utasítás. (Kicsit lejjebb lesznek még példák.)

A "#define" preprocesszor-utasítással definiálhatunk konstansokat. Csak abban a file-ban érvényes egy konstans ahol deklaráltuk.

/* Konstansdeklaráció: eredeti C-s megoldás, előfordítónak szóló utasítással */
#define PI 3.14159
#define NULLA 0
#define SZOVEG "Ha ezt a sort el tudja olvasni, nincs szüksége szemüvegre \n"

Itt nem kell pontosvessző a sorok végére. Az előfordító a program lefordítása előtt a konstansok minden előfordulási helyére behelyettesíti a megfelelő értéket (betűről betűre, kiértékelés nélkül, lecseréli pl. a "PI"-t "3.14"-re).

A konstansneveket mindig nagybetűvel kell írni!

C-ben egy konstansot megsemmisíteni is lehet az "#undef konstansnév" paranccsal, illetve ellenőrizhetjük az "#ifdef" ill. "#ifndef" preprocesszor-parancsokkal, hogy van-e (ill. nincs-e) már definiálva adott névvel konstans (ne felejtsük el lezárni az if-et):

#ifndef PI
#define PI 3.14
#endif
Makro-függvények

Akár kicsi makrofüggvények írásásra is használhatjuk ezt a behelyeetesítő mechanizmust. De vigyáznunk kell arra hogy az előfordító, mint nyelven kívüli eszköz mindent gondolkodás nélkül helyettesít, ráadásul az eredményt egy sorba írva azt sem teszi lehetővé, hogy a makrohelyettesítést lépésenként nyomkövessük.

Egy elrettentő példa:

// a háromoperandusú feltételes operátort használjuk, mert rövid
#define abs(val) (val < 0) ? -val : val      
 
int y, x = 3;
y = abs( x++ );     // Várt eredmény: x = 4, y = 3;

Az abszolút érték makro fenti alkalmazása esetén az előfordító a hívás helyén (ahol y-nak adunk értéket) behelyettesíti az abs() makrot, a belsejébe pedig paraméterként behelyettesíti a "val" helyére az "x++"-t. Első ránézésre azt várnánk, hogy az y = abs(x++) végrehajtása után, mivel előtte x értéke 3 volt, x értéke 4 lesz, míg y értéke 3. Ez így is lenne, ha az abs-ot függvényként realizálnánk. Ezzel szemben a előfordító ebből a sorból a következőt készíti:

y = (x++ < 0) ? - x++ : x++;

azaz az x-et kétszer növeli, minek következtében az utasítás végrehajtása után x értéke 5, míg y-é 4 lesz. A előfordítóval definiált makrok tehát veszélyesek lehetnek.


A const típusmódosítóval

Az ANSI (szabványos) C-ben (és majd C++-ban) a const típusmódosító szó segítségével bármely memóriaobjektumot definiálhatunk konstansként, vagyis "csak olvasható változó"-ként. Ez azt jelenti, hogy a fordító figyelmeztet, ha a változó nevét értékadás bal oldalán szerepeltetjük, vagy ebből nem konstansra mutató pointert inicializálunk.

/* ANSI C megoldás PI definiálására */
const float PI = 3.14;

Mutatók esetén lehetőség van annak megkülönböztetésére, hogy a mutató által megcímzett objektumot, vagy magát a mutatót szeretnénk csak olvashatóvá tenni:

const char * p;  // p által címzett karakter nem módosítható, (const char) * p
char * const q;  // q mutatót nem lehet megváltoztatni, (char *) const q

Fájl olvasás és írás

Egy komoly adatfeldolgozást vagy bármilyen számításokat végző program praktikus ha fájlokból olvassa be az adatokat, és az eredményeket is fájlba írja.

Sajnos a file-kezelés C-ben nem túl egyszerű és nem "programozóbarát", de néhány működő példából kiindulva nem is olyan nehéz megoldani.

A fájlkezeléshez szükséges függvényeket az "stdio.h" függvénykönvtár include-olásával tudjuk használni (ez kellett már korábban ahhoz is hogy a felhasználótól bekérjünk adatokat (vagyis a standard inputról olvassunk), vagy hogy a képrenyőre (standard output) írjunk).

Két sorban így néz ki egy fájl megnyitása (az "r" itt azt jeneti hogy olvasásra nyitjuk meg a szöveges fájlt):

FILE *fp;
fp = fopen("alma.txt", "r");

Az "r" helyett lehetne "b" (bináris file olvasása), "w" (írás), "a" (a fájl végére írás (append)).

A "FILE * fp" egy olyan mutató ami "FILE" típusnevű struktúrára mutat. Ez a struktúra olyan információkat tartalmaz amik segítségével megvalósulhat a kapcsolat a programunk és az operációs rendszer (ami kezeli a fájlrendszert) között. A belsejét nem kell ismerni, elég azt tudni hogy hogy használjuk.

Fájl olvasás fscanf()-fel

A scanf() függvényt arra használtuk hogy a felhasználótól (a standard bemenetről) olvassunk vele egy-egy szót. Ehhez hasonló a fscanf(), ami fájlból (input) tud olvasni, amit olvasott azt az output változó(k)ba írja, itt egy karaktertömbbe(stringbe):

fscanf(input, "%s", output);

Azonban vigyázni kell vele, mert így csak az első szóközig (egy szót) olvas be! általános szöveges fájl beolvasására kevéssé alkalmas, inkább akkor hasznos, ha ismert szerkezetű fájlt kell beolvasni. Például egy 3 dimenziós pont-koordinátákat tartalmazó fájlt beolvashatunk így, ha tudjuk hogy minden sorban egy pont adatai vannak, a sorokon belül pedig pontosan egy szóközzel vannak elválasztva a számok:

float x, y, z;
FILE *fp;
fp = fopen("koordinatak.txt", "r");
fscanf(fp, "%f %f %f", &x, &y, &z);

A fenti példában az fscanf() csak az első sort olvassa be a fájlból, még egy ciklus kell köré hogy az egész fájlt feldolgozhassuk.

Példa: fileread1.c

Fájl olvasás fread()-del

Az fread() függvénnyel általánosabb file-beolvasót is írhatunk. Az fread() adott méretű blokkokat olvas be egy fájlból, elég nagy méretű blokk esetén akár az egészet. Visszatérési értéke hogy hány egységet (ált. karaktert) sikerült beolvasnia.

  size_t fread(void *ptr,     // ide olvassa be a fájl tartalmát
               size_t size,   // ekkora méretű elemekbe (tipikusan sizeof(char) lesz itt)
               size_t nmemb,  // ennyi darab (ennyi karakter) beolvasása maximum
               FILE *stream); // ebből a fájlból

A beolvasott adatoknak a heap-en kell dinamikusan memóriát foglalnunk, ehhez pedig meg kell tudnuk a fájl méretét:

 // fp egy megnyitott file mutatója
 fseek(fp, 0L, SEEK_END);      // a file végére ugrunk az "olvasófejjel"
 long lFileLen = ftell(fp);    // a file hosszát elkérjük az ftell()-lel
 rewind(fp);                   // visszatekerünk a file elejére az "olvasófejjel"

A memóriába beolvasás után úgy dolgozzuk fel a fájl tartalmát (egy nagy karaktertömböt), ahogy akarjuk.

(Ha nagyon nagy fájllal dolgozunk ami nem fér a memóriába, akkor kisebb blokkokat olvassunk be egy ciklusban, és blokonként dolgozzuk fel a tartalmat.)

Példa: fileread2.c

(Megjegyzés: van még néhány módja a file olvasásnak (karakterenként is olvashatjuk a fájlt például), de általában a fenti két módszer egyikével elég jól megoldható bármilyen fájl-olvasási feladat.)


Fájl írás

Nézzünk egy teljes példát fájl írására is. A következő kis példaprogram 0-tól kezdve számokat ír ki egy fájlba, a fájl nevét és a számok számát a felhasználótól kéri be:

#include <stdio.h>
 
int main() {
    int i, N;
    FILE *fp;        // fájl mutató
    char fname[80];  // a fájl neve lesz itt
    printf("Mennyi számot generáljak?\n");
    scanf("%d", &N);
    printf("A fájl neve ahova írjam:\n");
    scanf("%s", fname);
    fp = fopen(fname, "w");   // itt nyitjuk meg a fájlt, írásra
    if (fp == NULL) {         // hibakezelés
        printf("Nem sikerült megnyitni a fájlt: %s", fname);
        return 1;
    }
    for (i = 0; i < N; i++) {
        fprintf(fp, "%d\n", i);   // az "fprintf()" fájlba ír
    }
    fclose(fp);   // be is zárjuk a fájlt !
}


A programunk paraméterezése

main() paraméterezése

#include <stdio.h>
#include <stdlib.h>
 
int main(int argc, char **argv) {
    while(argc--)
    printf("%s\n", *argv++);
    exit(EXIT_SUCCESS);   //  "return 0" helyett... az stdlib-ben van az exit() fv
}

Example 10.1

http://www.cprogramming.com/tutorial/c/lesson14.html

  1. include <stdio.h>
int main ( int argc, char *argv[] ) {
    /* argc should be 2 for correct execution */
    if (argc != 2) {
        /* We print argv[0] assuming it is the program name */
        printf( "usage: %s filename", argv[0] );
    }
    else {
        // We assume argv[1] is a filename to open
        FILE *file = fopen( argv[1], "r" );
 
        /* fopen returns 0, the NULL pointer, on failure */
        if (file == 0) {
            printf( "Could not open file\n" );
        }
        else {
            int x;
            /* read one character at a time from file, stopping at EOF, which
               indicates the end of the file.  Note that the idiom of "assign
               to a variable, check the value" used below works because
               the assignment statement evaluates to the value assigned. */
            while ((x = fgetc(file)) != EOF ) {
                printf( "%c", x );
            }
            fclose( file );
        }
    }
}

Több fájlból álló program

röviden a linkelésről.

http://www.acm.uiuc.edu/webmonkeys/book/c_guide/1.7.html: 1.7.3 #include

The #include directive allows external header files to be processed by the compiler.

Syntax:

   #include <header-file>
   or
   #include "source-file"

When enclosing the file with < and >, then the implementation searches the known header directories for the file (which is implementation-defined) and processes it. When enclosed with double quotation marks, then the entire contents of the source-file is replaced at this point. The searching manner for the file is implementation-specific.

Examples:

   #include <stdio.h>
   #include "my_header.h"

Gyakorlaton include-oljunk egy másik fáljt, ha lesz rá idő.


C függvénykönyvtárak

Matematikai függvények

math.h http://www.acm.uiuc.edu/webmonkeys/book/c_guide/2.7.html

Standard definíciók

stddef.h http://www.acm.uiuc.edu/webmonkeys/book/c_guide/2.11.html

Standard I/O

stdio.h http://www.acm.uiuc.edu/webmonkeys/book/c_guide/2.12.html

Standard dolgok

stdlib.h http://www.acm.uiuc.edu/webmonkeys/book/c_guide/2.13.html

Macros:

   NULL
   RAND_MAX

Variables:

   typedef size_t
   typedef wchar_t

Functions: abs(); labs(); atof(); atoi(); atol(); malloc(); calloc(); free(); qsort(); rand(); srand();


Stringből számmá konvertáló függvények

atoi(), atof(),

Random számok generálása

rand(); srand();


Stringek

string.h http://www.acm.uiuc.edu/webmonkeys/book/c_guide/2.14.html


Ellenőrző kérdések

Amit a zh-ra tudni kell:

  • makrók definiálása
  • stringek kezelése (előző? előadás végén is volt)
  • main paraméterezése hogy néz ki
  • file olvasás, írás

Források és további olvasnivalók:

Személyes eszközök