Programozás alapjai gyakorlat 2015-2016/1

10. gyakorlat



    Deklaráció - haladó



F: Készíts egy pointert, ami egy konstans értékre mutat.
F: Készíts egy konstans pointert, ami egy nem konstans értékre mutat.
F: Készíts egy pointert, ami egy tömbre mutat.

==============================================================================

#include <stdio.h>

int main()
{
    const int *p = NULL;	// pointer, ami egy konstans értékre mutat
    int * const c = NULL;	// konstans pointer, ami egy nem konstans értékre mutat
    int (*t)[20];		// pointer, ami egy tömbre mutat
    
    p = malloc(sizeof(int));
    *p = 2007; /* HIBÁS - mivel konstans értékét nem tudjuk megváltoztatni */
    free(p);
    c = malloc(sizeof(int)); /* HIBÁS */
    *c = 2007;
    free(c);
    return 0;   
}


    Tárolási osztályok



Tárolási osztályokat bemutató program

==============================================================================

#include <stdio.h>

#define MAX_NUM 5

/* A static tárolási osztály azt jelenti, hogy az adott változó megmarad a
 * blokkból kilépés után is, és a következő belépéskor elérhető lesz a
 * legutóbbi tartalom.
 * 
 * Használhatjuk arra, hogy megszámoljuk, hányszor hívtuk az adott függvényt.
 */
int* counter()
{
    static int count = 0;
    count++;
    printf("Count : %d\n",count);
    return &count;
}

int* kell()
{
	return counter();
}

int main()
{
    /* Az auto tárolási osztály az alapértelmezett, ki sem szükséges tenni.
     */
    auto int valami = 10;
    
    /* A register tárolási osztály arra szolgál, hogy jelezzük a fordítónak,
     * hogy olyan gépi kódot PRÓBÁLJON meg csinálni, amely során ez a változó
     * folyamatosan a CPU egy regiszterében van. -> Gyorsabb elérésű, mint a
     * memória, de sokkal kevesebb ilyen változó létezhet. Gyakran változó
     * változót érdemes.
     *
     * A fordító figyelmen kívül hagyhatja!
     */
    register int i;

    /* A volatile módosító azt mondja a fordítónak, hogy:
     * "Vigyázat, ez a változó értéke úgy is módosulhat, hogy a kódban nincsen
     * annak módosítására szolgáló utasítás!"
     * Pl. a változó egy porthoz csatlakozik, ahová az adott eszköz írhat!
     */
    volatile unsigned short int device = (unsigned short int)0;

    /* A const módosító azt mondja a fordítónak, hogy az érték nem
     * megváltoztatható. Ez viszont csak annyit jelent, hogy az adott
     * változóhivatkozás nem szerepelhet értékadás bal oldalán.
     */
    const long int nemvaltozo = 2007;

    int *p;
    p=&i; /* EZ HIBÁS */
    p=counter();
    printf("*%p = %d\n", p, *p);
    p=kell();
    printf("*%p = %d\n", p, *p);
    *p = 100;
    counter();

    return 0;
}


    Függvény pointer


Több feladat közül futási időben döntöm el, hogy melyiket hajtom végre. A függvényre mutató pointer a függvény kódjának a címére mutat, azon keresztül meghívhatom a függvényt. A pointernek tudnia kell a függvény típusát (paraméterek és visszatérési érték típusa). Pl.:

double fgv(double, double);     /* függvény deklarációja */
double (*fptr)(double, double); /* ilyen típusú függvényre mutató pointer deklarációja */
fptr = fgv;                     /* a függvény nevét adom kezdőértékül, a fordító persze ebből címet állít elő */
fptr(x, y);                     /* meghívom a függvényt a pointeren keresztül */


F: Egy tömbben soroljunk fel függvényeket, és hívjuk meg valahányadikat.

==============================================================================

#include <stdio.h>
#include <math.h>

/* sinus négyzet - beépített függvénnyel */
double sin2(double x) {
	double sinx = sin(x);
	return sinx*sinx;
}

/* kettes alapú logaritmus - beépített függvénnyel */
double log2(double x) {
	return log(x) / log(2);
}

/* cosinus négyzet - beépített függvénnyel */
double cos2(double x) {
	double cosx = cos(x);
	return cosx*cosx;
}

typedef double (*fgvtip)(double);  /* függvényre mutató pointer típusa */
                                   /* melynek double a visszatérési értéke és double típust vár a paraméterlistában */

fgvtip tabla[] = {   /* ilyen pointerek tömbje */
	sin2, 	     /* a függvény neve értékül adható függvényre mutató pointernek */
	log2,
	cos2
};
 
int main() {
    int f, x;
    char* valasztek = "{  1: sin2x  }\n{  2: log2x  }\n{  3: cos2x  }\n";	/* karakterekre mutató pointer, lényegileg egy string */
    printf("Melyik fuggveny legyen?:\n%s?:\t", valasztek);
    scanf("%d", &f);
    printf("Argumentum erteke?:\t");
    scanf("%d", &x);
    printf("A fuggveny erteke az adott pontban:\t%lf\n", tabla[f-1](x) );
		/* a tömbben lévő pointeren keresztül hívom a függvényt */
		/* 1-től 3-ig várt bemenetet a program. 
		   0-tól való indexelés miatt levon 1-et [f-1] 
		   és paraméterként átadódik az úgyszint beolvasott érték (x) */
    return 0;
}


F: Egy tömbbe olvassunk be, majd írassuk ki az elemeket úgy, hogy a tömb
   bejárását egy függvény végezze, ami megkapja a műveletet.
   
==============================================================================

#include <stdio.h>

#define N 10

typedef void (*muvelettipus)(int*); /* függvényre mutató pointer típusa */
                                    /* melynek nincs visszatérési értéke (void) és int-re mutató pointert vár a paraméterlistában */

/* egy tömböt vár illetve a bejar függvény alatt található művelet függvények egyikét */
void bejar(int* tomb, muvelettipus muvelet) {
	int i;
	for(i=0; i<N; i++) {
		(*muvelet)(tomb+i);   // ide kerül behelyettesítésre a paraméterként kapott művelet-függvény, mely saját bemeneti paraméterét is megkapja.
		                      // azért kerül 'tomb+i' a paraméterlistába, mert a 'tomb' alapjáraton a paraméterként kapott tömb kezdőpozíciójára mutat, 
		                      // így a for ciklusban végig haladva, mindig i-t hazzáadva mindig a tömb rákövetkező elemét kapjuk 
	}
}

void beolvas(int* v) {
	scanf("%d", v);
}

void kiir(int* v) {
	printf("%d\n", *v);
}

void duplaz(int* v) {
	(*v) *= 2;
}

int main()
{
	int t[N];
	bejar(t, beolvas);
	bejar(t, kiir);
	bejar(t, duplaz);
	bejar(t, kiir);
	return 0;
}

Egy egyszerűbb példa:


#include <stdio.h>
#include <time.h>

/* Csinálunk 4 fuggvenyt, ahol mindegyiknek megegyező lesz a tipusa es a bemeneti parametereknek a tipusa illetve szama is */
/* Egyszeru pelda reven legyen ez a 4 fuggveny a 4 alapmuvelet */
int osszead (int a, int b){
    return a+b;
}

int kivon (int a, int b){
    return a-b;
}

int szoroz (int a, int b){
    return a*b;
}

int oszt (int a, int b){
    if (b > 0)	// mivel 0-val nem osztunk!
        return a/b;
    else
        return 0;
}

/* hozzuk letre az ilyen fuggvenyekre mutato pointer tipust */
typedef int (*muveletekPointer)(int, int);

/* hozzunk letre egy tombot, mely az elozoleg definialt tipusu legyen,
    es a 4 eleme a 4 fuggvenyunk neve legyen */
muveletekPointer muveletekTomb[] = {osszead, kivon, szoroz, oszt};

int main(){
    srand(time(NULL));
    int a, b, eredmeny, // 3 segedvaltozo a 2 bemeneti paramterhez es a visszaadott ertekhez
    random = rand()%4;  // random szam sorsolasa 0 es 3 kozott [0,3]
    printf("Add meg az elso parametert: ");
    scanf("%d",&a);
    printf("Add meg a masodik parametert: ");
    scanf("%d",&b);
	
    /* a radnom elem alapjan kivalasztott eleme a tombnek az adott sorszamu fuggveny,
        mely megkapja az elozoleg beolvasott ket erteket bemeneti parameternek.
        Mint latszik a fuggvenyek hasonlosagabol adodoan az osszes fuggvenyre raillik ez a hivasmod */
    eredmeny = muveletekTomb[random](a, b);

    /* csak hogy legyen egy kis regen latott vezerlesi szerkezet,
        az alapjan, hogy melyik muveletet sorsoltuk,
        az alapjan valtozik az eredmeny kiiratasa */
    switch(random){
        case 0: printf("A ket szam osszege: "); break;
        case 1: printf("A ket szam kulonbsege: "); break;
        case 2: printf("A ket szam szorzata: "); break;
        case 3: printf("A ket szam hanyadosa: "); break;
    }
    printf("%d\n\n", eredmeny);

    return 0;
}



    Parancssori argumentumok


A main függvénynek három paramétere lehet. Az első egy int, a parancssorban kapott argumentumok száma + 1 (a program neve maga is egy argumentum). A második egy olyan pointer-tömb, mely azokra a memóriaterületekre mutatnak, ahol sztringként vannak letárolva a parancssori argumentumok. A harmadik hasonló a másodikhoz, csak ez a környezeti változók címeit tartalmazza.

Írjuk ki a parancssorban lévő argumentumokat:


#include <stdio.h>

main(int argc, char **argv)
// vagy így is lehet az argv-t deklarálni:
// main(int argc, char *argv[])
{
    int i;
    printf("argc = %d\n\n",argc);
    for (i=0; i<argc; ++i) {
        printf("argv[%d]: %s\n", i, argv[i]);
    }
}

Mentsük el a fenti programot arg.c néven és fordítsuk le!

$ gcc -o arg arg.c
$ ./arg alma korte szilva barack palinka
argc = 6

argv[0]: ./arg
argv[1]: alma
argv[2]: korte
argv[3]: szilva
argv[4]: barack
argv[5]: palinka


F: Írj egy programot, ami összeadja a parancssori paramétereket.

==============================================================================

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[])
{
    int i;
    int arg = 0;
    printf("Osszesen %d programargumentumot kaptam!\n",argc);
	
    for(i = 0; i< argc; i++)
        printf("%d : %s\n",i, argv[i]);
	
    if(argc > 1)
    {
        for(i = 1; i< argc; i++)
            arg += atoi(argv[i]);
    }

    printf("Az argumentumok osszege : %d\n", arg);
	
    return 0;
}


F: Írj egy programot, amely n-szer egymás után fűzi ugyanazt az s sztringet,
   ahol n és s is parancssori paraméter.

==============================================================================

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char* argv[])
{

    int i;
    char *er;

    if(argc < 3)
    {
        printf("Használat: %s <valami szám> <valami sztring>\n", argv[0]);
        return 1;
    }
    
    er = (char*) calloc ( atoi(argv[1]) + 1 , strlen(argv[2]) );

    strcpy(er,argv[2]);
    for(i=0;i<atoi(argv[1])-1;i++){
        strcat(er,argv[2]);
    }
    
    printf("%s\n",er);

    free(er);
    return 0;
}



Órai pluszpontos

Készíts egy programot, amely bekér egy magasságot illetve egy karaktert, majd kirajzol egy ilyen magas piramist, az így megadott karakterből.

Példa:

Add meg, hogy milyen magas legyen a piramis: 5
Add meg milyen karakterbol epuljon fel a piramis: A

    A
   AAA
  AAAAA
 AAAAAAA
AAAAAAAAA

Fejlesszük tovább úgy, hogy egy rombuszt rajzoljunk ki.

Példa:

Add meg, hogy milyen magas legyen a piramis: 5
Add meg milyen karakterbol epuljon fel a piramis: A

    A
   AAA
  AAAAA
 AAAAAAA
AAAAAAAAA
 AAAAAAA
  AAAAA
   AAA
    A

Egy lehetséges megoldás elérhető itt .




Házi feladat

A házi feladatot megoldani nem kötelező és bemutatni sem kell, viszont a következő gyakorlaton visszakérhető (kikérdezés, táblához hívás, stb. formájában)! Ha a hallgató megoldása ötletes, szép kivitelezésű, plusz pont adható. Amennyiben viszont nem tudja megoldani gyakorlaton a házi feladatban szereplő példákat vagy nem tud válaszolni az azzal kapcsolatban feltett kérdésekre, mínusz pont adható. Plusz és mínusz pontból is egyaránt maximum 10 pontot gyűjthet össze egy-egy hallgató.

A házi feladat és további gyakorló példák elérhetőek a PUB-ban ( /n/pub/ProgramozasAlapjai/Gyakorlat/ )

Bárkinek bármi kérdése adódik a feladatokkal kapcsolatban írjon nyugodtan! ( Gyakoroljatok sokat! )




    Jövő héten 7. miniZH (2015.11.10./12.)

Téma:

  3. - 10. gyakorlat anyaga.

  Gyakorlásra:

    A honlapomon a 3. - 10. gyakorlathoz tartozó anyag, magyarázatokkal, példákkal.

    A gyakorlatok végén lévő házi feladat és gyakorló feladatok megoldása.

    A honlapom mellet további feladatok találhatóak a PUB-ban. (/n/pub/ProgramozasAlapjai/Gyakorlat/ - erős átfedés van az "itt" és "ott" található feladatok között).

Egyéb infó:

Előreláthatóan 45 percetek lesz a feladatok megoldására és beadására (tehát 8:55-ig/12:45-ig). A feladatokat a BÍRÓ rendszeren keresztül fogjátok megkapni és beadni is, és az értékelést is a bíró fogja csinálni ott helyben. Tehát egyből látni fogjátok a pontszámokat amiket a bíró adott. Aki késik, az is csak a fenti időintervallum alatt írhatja a ZH-t, mivel a bíró rendszer nyit, majd automatikusan zár is. Hiányozni csak igazolással lehet, de a ZH akkor sem pótolható!


     Vissza a lap tetejére.