Programozás alapjai gyakorlat 2014-2015/1

10. gyakorlat



    Pointer - alap

  • Új dinamikus változó létesítése

  • p=malloc(sizeof(E));

    A malloc(S) függvény lefoglal egy S méretű memóriaterületet a program számára. A sizeof(E) megadja, hogy egy E típusú változó mekkora helyet igényel a memóriában. A malloc(sizeof(E)) hatására tehát létrejön egy új E típusú érték tárolására (is) alkalmas változó, és ez a változó lesz a p értéke.

  • Pointer dereferencia

  • *p

    A * művelet segítségével érhetjük el a p értékét vagyis a dinamikus változót. A *p változóhivatkozás a p értékére, vagyis a dinamikus változóra hivatkozik, tehát a *p értéke a dinamikus változó értéke lesz.

  • Dinamikus változó törlése

  • free(p);

    A művelet hatására a p-hez tartozó memóriaterület felszabadul ezáltal a dinamikus változó megszűnik. A művelet végrehajtása után a p pointerhez nem tartozik érvényes változó, ezért a *p változóhivatkozás végrehajtása jobb esetben azonnali futási hibát eredményez. (Rosszabb esetben pedig olyan lappangó hibát, aminek az eredménye a program egy teljesen más pontján jelenik meg.)

  • A címképző művelet

  • p = &i;

    A művelet meghatározza egy változóhoz tartozó memória mező címét. Ha egy p pointer típusú változó értéke az i változóhoz tartozó memória címe, akkor azt mondjuk, hogy a p i-re mutat. Ez a referencia szerinti értékátadás.


P: Nézzük meg, mi a különbség p, q, illetve *p és *q értéke között.

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

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

int main()
{
	int *p, *q;
	p = malloc(sizeof(int));
	q = malloc(sizeof(int));
	*p = 3;
	*q = 3;
	printf("p es q %s\n", p == q ? "megegyezik" : "nem egyezik meg");
	printf("*p == %d, *q == %d\n", *p, *q);
	*p = 4;
	printf("*p == %d, *q == %d\n", *p, *q);
	free(p);
	p = q;
	printf("p es q %s\n", p == q ? "megegyezik" : "nem egyezik meg");
	printf("*p == %d, *q == %d\n", *p, *q);
	*p = 4;
	printf("*p == %d, *q == %d\n", *p, *q);
	free(p);
	return 0;
}

P: Futtassuk le a következő programot, és értelmezzük! Melyik érték melyik
   értékkel egyenlő, és miért ?

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

#include <stdio.h>

int main()
{
	int a = 10;
	int *pa;
	pa = &a;
	printf("%d %#x\n", a, (int)pa);
	printf("%#x %#x\n", (int)&a, (int)&pa);
	printf("%d\n", *pa);
	return 0;
}

F: Írj egy csere() függvényt, ami megcseréli két int típusú változó értékét.

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

#include <stdio.h>

void csere(int *x, int *y)
{
	int tmp;
	tmp = *x;
	*x  = *y;
	*y  = tmp;
}

int main()
{
	int x = 3, y = 4;
	printf("A fuggveny elott: x = %d, y = %d\n", x, y);
	csere(&x,&y);
	printf("A fuggveny utan: x = %d, y = %d\n", x, y);
	return 0;
}

F: Deklarálj egy 20 elemű int tömböt, majd töltsd fel értékekkel az inputról.
   Deklarálj egy pointert, és a beolvasást azon keresztül valósítsd meg.

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

#include <stdio.h>
#define N 20

int main()
{
	int t[N], *p, i;
	for(i=0; i<N; i++) {
		p=&(t[i]);
		scanf("%d", p);
	}
	for(i=0; i<N; i++) {
		printf("%d\n", t[i]);
	}
	return 0;
}

F: Először olvasd be a tömb méretét, és foglalj neki dinamikusan helyet!

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

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

int main()
{
	int *t, *p, i, N;
	scanf("%d", &N);
	t=(int*)malloc(N*sizeof(int));
	for(i=0; i<N; i++) {
		p=&(t[i]);
		scanf("%d", p);
	}
	for(i=0; i<N; i++) {
		printf("%d\n", t[i]);
	}
	free(t);
	return 0;
}

F: Olvass be 5 darab maximum 99 karakter hosszú szót úgy, hogy mindegyiknek
   pontosan annyi helyet foglalsz, amennyi kell! A sztringeket írasd ki, majd
   szabadítsd fel a lefoglalt területet!

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

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

int main()
{
	char buff[100];
	char *ptr_tomb[5];
	int i;
	for(i=0; i<5; i++) {
		scanf("%s", buff);
		ptr_tomb[i] = (char*)malloc(strlen(buff)+1);
		strcpy(ptr_tomb[i], buff);
	}
	for(i=0; i<5; i++) {
		puts(ptr_tomb[i]);
	}
	for(i=0; i<5; i++) {
		free(ptr_tomb[i]);
	}
	return 0;	
}

char Honap[][20]

char *honap[]




    Pointer - haladó


F: Olvasd be egy tömb méretét, foglalj neki dinamikusan helyet, majd olvasd be
   az elemeit!

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

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

int main()
{
	int *t, *p, i, N;
	scanf("%d", &N);
	t=(int*)malloc(N*sizeof(int));
	for(i=0, p=t; i<N; i++, p++) {
		scanf("%d", p);
	}
	for(i=0, p=t; i<N; i++) {
		printf("%d\n", *(p++));
	}
	free(t);
	return 0;
}	 

F: Adott egy kétdimenziós tömb. Pointer segítségével járjuk be az összes
   elemét.

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

#include <stdio.h>

#define SIZE 3

int main()
{
    int tomb[SIZE][SIZE] = {{0, 1, 2 },
                            {3, 4, 5 }, 
                            {6, 7, 8 } };
    int i,j;
    int *pa = NULL;

    pa = tomb;  /* pa = &tomb[0][0] */
    for(i = 0; i< SIZE*SIZE; i++)
        printf("%2d ", pa[i]);
    printf("\n");
    for(i = 0; i< SIZE*SIZE; i++)
        printf("%2d ", *(pa+i));
    printf("\n");
    for(i = 0; i< SIZE*SIZE; i++, pa++)
        printf("%2d ", *pa);
    printf("\n");

    /* vigyázat! mivel pa-t növeltük a for ciklusban, ezért a ciklus után már
     * nem a tömb legelső elemére fog mutatni!
     */
    return 0;
}

F: Dinamikus kétdimenziós tömb létrehozása

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

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

int main()
{
    int *p, **t;
    int N=3, M=4;
    int i, j, v=0;
    /* V1: egydimenziós tömb */
    p=malloc(N*M*sizeof(int));
    for(i=0; i<N; i++) {
        for(j=0; j<M; j++) {
            p[i*M+j]=v++;
        }
    }
    free(p);
    /* V2: sorokat külön-külön */
    t=malloc(N*sizeof(int*));
    for(i=0; i<N; i++) {
    	t[i]=malloc(M*sizeof(int));
    }
    for(i=0; i<N; i++) {
        for(j=0; j<M; j++) {
            t[i][j]=v++;
        }
    }
    for(i=0; i<N; i++) {
    	free(t[i]);
    }
    free(t);
    
    
    return 0;
}

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

int main()
{
    int i;
    const int maxsize = 100;

    /* Kétdimenziós tömbök */
    float tomb_statikus[100][100];
    float tomb_valtozo_meretu_de_a_gcc_nem_szereti[maxsize][maxsize];
	
    /* 
     * Mivel a program futása során tudjuk meg a maxsize -t (tegyük fel, hgoy így van)
     * dinamikusan kell (így a leghelyesebb, így ragaszkodunk a szabványhoz, és így lesz hordozható a kódunk )
     * helyet foglalni a kétdimenziós tömbünknek.
     *
     * A kétdimenziós tömb olyan egydimenziós tömb, amelynek minden eleme egy egydimenziós tömb.
     * 
     * egydimenziós tömböt tudunk dinamikus létrehozni : 
     *	float *tomb = (float*)calloc(meret, sizeof(float));
     * -> tehát az első egydimenziós tömb elemeit (egydimenziós tömböket) létre tudjuk hozni.
     *  azaz a deklarációnk nézhetne így is ki : float *tomb[meret] (hiszen ez egy pointerekból álló tömb.)
     *  Ezzel nem kerültük ki a meret kiírását ->
     *  csináljunk egy pointert erre a tömbre : float **tomb. 
     *
     *  És KÉSZ!
     * 
     * */

    /* Egy olyan pointer, amely egy pointerre mutat, amely egy float tipusú változó címét tudja tárolni. */
    float **tomb_dinamikus;
	
    /* Foglaljuk le a helyet a külső tömbnek, azaz hozzunk létre egy olyan egydimenziós tömböt,
     * amelynek az elemei olyan pointerek, melyek float változók címeit tudják tárolni*/
    
    tomb_dinamikus = (float**)calloc(maxsize, sizeof(float*));
    
    /* Magyarázat : (float**) : mivel calloc egy void* -al tér vissza, helyes, ha típuskonverziót hajtunk végre.
     *							mi a típusa tomb_dinamikus -nak? float**.
     *				sizeof(float*) :	minden egyes indexén a tomb_dinamikus tömbnek egy 
     *									float-ra mutató pointert akarunk tárolni.
     *									Ezért az alaptípusának a mérete float*, tehát ennek a méretét kell megadnunk.
     * */
	
    /* Foglaljunk helyet a most létrehozott tömb elemeinek (hiszen azok is pointerek) */
    for(i = 0; i< maxsize; i++)
        tomb_dinamikus[i] = (float*)calloc(maxsize, sizeof(float));

    /* Magyarázat : (float*) :  mivel calloc egy void* -al tér vissza, helyes, ha típuskonverziót hajtunk végre.
     *							mi a típusa tomb_dinamikus[i] -nek? float* .
     *				sizeof(float) :	minden tomb_dinamikus[i] egy float-ra mutató pointer.
     *								Ezért az alaptípusának a mérete float, tehát ennek a méretét kell megadnunk.
     * */
	
    tomb_dinamikus[2][5] = 12.45;

    printf("%f\n",tomb_dinamikus[2][5]);

    /* Ha már nem használjuk a tömbünket, fel kell szabadítani az általunk lefoglalt memóriaterületet. 
     * 
     * Először a pointereket tartalmazó tömbben lévő pointereket kell felszabadítanunk, majd utána a tömbnek lefoglalt memóriaterületet.
     * */
	
    for(i = 0; i< maxsize; i++)
        free(tomb_dinamikus[i]);
    free(tomb_dinamikus);	
	
    return 0;	
}


    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;
    int * const c=NULL;
    int (*t)[20];
    
    p=malloc(sizeof(int));
    *p=2007; /* HIBÁS */
    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;
}



Plusz pontos házi

Megvalósítandó program: Piramis és rombusz kirajzolása képernyőre karakterek segítségével.

Kiírás: Írjunk egy olyan programot, mely bekér egy egyész számot, ez legyen a piramis magassága, majd pl. 'a' betűkből rajzoljon ki egy piramist. Két lehetséges futási eredmény:

Add meg milyen magas legyen a piramis: 6
     a
    aaa
   aaaaa
  aaaaaaa
 aaaaaaaaa
aaaaaaaaaaa
Add meg milyen magas legyen a piramis: 10
         a
        aaa
       aaaaa
      aaaaaaa
     aaaaaaaaa
    aaaaaaaaaaa
   aaaaaaaaaaaaa
  aaaaaaaaaaaaaaa
 aaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaa

Fejlesszük tovább a programot, hogy rombuszt rajzoljon ki. Természetesen ez nem más, mint a már meglévő piramisunk tükörképe. Vegyük figyelembe, hogy egy sorral kevesebbet rajzoltunk ki alul, hogy szép középvonalat kapjunk. Egy lehetséges futási eredmény:

Add meg milyen magas legyen a piramis: 10
         a
        aaa
       aaaaa
      aaaaaaa
     aaaaaaaaa
    aaaaaaaaaaa
   aaaaaaaaaaaaa
  aaaaaaaaaaaaaaa
 aaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaa
 aaaaaaaaaaaaaaaaa
  aaaaaaaaaaaaaaa
   aaaaaaaaaaaaa
    aaaaaaaaaaa
     aaaaaaaaa
      aaaaaaa
       aaaaa
        aaa
         a

Elég csak a rombusz forma megvalósítását elküldeni, abból látni fogom, hogy megy a felső és alsó rész generálása is.

Megoldások beküldésének határideje: 2014.11.09., vasárnap éjfél.

Egy lehetséges megoldás letöltése .




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ó.

Házi feladat 01-hazi.txt , 02-hazi.txt .




    Jövő héten 7. (UTOLSÓ) miniZH (2014.11.10.)

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ó:

  A gyakorlat 8:10-kor kezdődik és a ZH-t 8:15-kor kezdhetitek írni. Előreláthatóan 75 percetek lesz a feladat megoldására és beadására (tehát 9:30-ig). A feladatot 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 (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.