Programtervezés, programozás I. 2.5 tömbök,stringek Rendszer és Szoftvertechnológia Tanszék
2.5. Információtárolás tömbökben Definíció : A tömb összetett adatszerkezet, véges számú, azonos típusú elem összessége, melyek a memóriában folytonosan helyezkednek el. Fajtái : Vektor : egydimenziós tömb - egy elem megadásához egy index kell Mátrix : kétdimenziós tömb - egy elem megadásához két index kell Többdimenziós tömb
2.5.1. Egydimenziós tömbök Deklaráció: típus tömbnév[ méret ] ; méret: a fordító által kiszámítható Pl. int vektor[10] , v [20] ; Egy vektorelemre hivatkozás : tömbnév [ index ] az elemek indexelése 0-tól méret-1 -ig történik Pl. vektor[1], v[1] , v[5] , vektor[i], v[j]
2.5.1. Egydimenziós tömbök int v [10]; A tömbindexek általában 1-től indulnak, DE a C-ben az indexek mindig 0-tól indulnak !!!!! A vektor első elemére hivatkozás C-ben : v [ 0 ] !!! A vektor utolsó elemének indexe C-ben: elemszám-1 Pl. v [0], v[1], v[2], ... , v[9] Nincs ellenőrzés az indexhatár túllépésére!!!!
Vektor Tárolás a memóriában : egy adott címtől kezdve folytonosan ... v[9] A v vektor kezdőcíme
2.5.1. Egydimenziós tömbök Inicializálás ( kezdőértékadás ): A deklarációban { } között felsoroljuk vesszővel elválasztva az elemek értékét is. Pl. int v1[5] = { 1, 2, 3, 4, 5 } ; Inicializáláskor a deklarációban elhagyható az elemszám. Pl. int v2[ ] = { 1, 2, 3} ; Ekkor a fordítóprogram a kezdőértékek száma alapján foglalja le a szükséges tárterületet.
2.5.1. Egydimenziós tömbök Konstans vektor képzése: const módosító alkamazásával , az inicializált vektort konstansként deklarálhatjuk. Pl. const int vekt[ ] = { 1, 2, 3, 4 } ; Ajánlás: Vektor deklarációjakor az elemszámot közvetett módon, konstansként adjuk meg. Pl. const n = 20; int v [ n ] ; Így az elemszám módosításakor csak egy sorban kell a programot is módosítanunk.
Vektorok - példa Írjunk programot egy 10 elemű egész vektor beolvasására. majd írjuk ki a beolvasott vektor elemeit! void main(void ) { const n=10; int i, v[n]; clrscr( ); printf("\n A v vektor elemeinek beolvasása "); for(i=0; i<n; i++) printf("\n %d. elem =", i ); scanf("%d", &v[i] ); } // for
Vektorok - példa clrscr( ); //A vektor elemeinek kiíratása: printf("\n A beolvasott értékek "); for(i=0; i<n; i++) printf("\n %d. elem = %d" , i , v[i] ); getch(); } // main
Vektorok – 2.5.1.1. feladat Készítsen programot, amely bekéri a csoport zh-eredményének pontszámait, kiszámítja az átlagot, majd kiírja - sorszámozva - az egyes pontszámokat és az átlagtól való eltérésüket. Megoldás: Elemezzük a feladatot Tudnunk kell, hogy hány főből áll a csoport. Az aktuális csoport-létszámot a program futásakor is megadhatjuk, de - mivel a pontszámokat tárolnunk kell egy egydimenziós tömbben - a csoport lehetséges (maximális) létszámát már a forráskód megírásakor ismerni kell, legyen ez a példánkban 20 fő.
*** #include <stdio.h> #include <conio.h> #define MAXL 20 void main (void) { int letszam, cv, pontszam [MAXL]; float atlag = 0; do //ne legyen túlcímzés !!! printf ("\n Kérem az aktuális csoportlétszámot:"); scanf ("%d", &letszam); fflush (stdin); } //do while (letszam < 1 || letszam > MAXL);
** printf ("\nKérem a pontszámokat !\n"); for (cv=0; cv<letszam; cv++) { scanf ("%d", &pontszam [cv]); atlag += pontszam [cv]; } //for atlag = atlag / letszam; printf ("\nAz átlag: %10.2f", atlag); printf ("\nSorszám Pontszám Eltérés"); printf ("\n%7d %9d %9.2f",cv+1, pontszam [cv],\ pontszam[cv]-atlag); getch (); } // program vége
Ellenőrző kérdések Egydimenziós tömb deklarálásának általános alakja. Írjon fel egy konkrét mintapéldát is. Lehet-e olyan tömböt deklarálni, amelynek elemei void típusúak ? Ha igen, írjon fel egy konkrét példát is. Hány értéket kell megadni az öt egész értéket tartalmazó, egydimenziós tömb inicializálásakor? Írjon fel konkrét példát a lehetséges esetekre és írja le, mi történik az inicializálás során.
Egydimenziós tömbök: feladat Egy éven keresztül naponta megmértük és rögzítettük a lehullott csapadék mennyiségét (mm-ben). Vigye gépre az adatokat. Volt-e olyan időszak, amikor 10 napon keresztül egyetlen napon sem volt mérhető csapadék ?
Megoldás: … void main (void) { int eso [366] ; // a lehullott csapadék mennyisége mm-ben int csapadek; // napi csapadék mm-ben int i; // ciklusváltozó int aszaly; // az eső nélküli napok száma // 1. for (i=0; i<366; i++) //eso tömbelemek nullázása eso[i] = 0; do //eso tömb nem nulla elemeinek bevitele printf (”\nNap sorszám és csapadék:”); scanf (”%d”,&i); scanf (”%d”,&csapadek); if (i>0 && i<=366) // i értéke 1 és 366 közé essen ! eso [i-1] = csapadék; } while (csapadék != 0);
** // 3. for (i=0; i<356; i=i+1) { aszaly = 0; while (eso[i]==0 && aszaly <10) aszaly = aszaly + 1; i = i + 1; } if (aszaly == 10) printf (”\nAz első aszályos időszak kezdete: %3d”, i-10+1); break; printf (”\n Program vége.”); getch ();
2.3. Többdimenziós tömbök Példa: egy autó helyzete és sebessége: közút száma km szelvény sebesség időpont Ebben a fejezetben csak a kétdimenziós tömbökkel foglalkozunk
Mátrixok Kétdimenziós tömbök. 1. sor 3 x 4 -es mátrix 1. oszlop sorok oszlopok száma Általánosan : a mátrix mérete n x m
Mátrixok Egy mátrixot kétféle módon tárolhatunk a memóriában : Sorfolytonos tárolás : Az elemek tárolása soronként történik, azaz egy adott címtől kezdődően (mátrix kezdőcíme) először az 1. sor elemei, majd utánuk a 2. sor elemei , stb. kerülnek tárolásra. Így tárolja a mátrixokat a PL/I , a Pascal , a C .
Pl. ha a mátrixunk a következő : 11 12 13 14 21 22 23 24 31 32 33 34 Tárolása sorfolytonosan : 11 12 13 14 21 22 23 24 31 32 33 34 1.sor 2.sor 3.sor
Oszlopfolytonos tárolás : Az elemek tárolása oszloponként történik, azaz egy adott címtől kezdődően először az 1. oszlop elemei, majd a 2. oszlop elemei, stb. kerülnek tárolásra. Így tárolja a mátrixokat a FORTRAN. Az előbbi mátrix oszlopfolytonosan tárolva : 11 21 31 12 22 32 13 33 14 24 34 1.oszlop 2.oszlop 3.oszlop 23 4.oszlop
Mátrixok Kétféle deklarálást engednek meg a programnyelvek : Egy n x m -es mátrix n*m darab elem összessége. A deklaráláskor a következő formában adjuk meg a mátrix méretét : Pl. Egy 3 x 4-es mátrix esetében: típus nev [ 3, 4 ] Egy elemre hivatkozás : nev [ i , j ]
Mátrixok Másik megadási mód: Egy n x m -es mátrixot úgy deklarálunk, mint egy n elemű vektort, melynek minden eleme egy m elemű vektor, azaz vektorok vektoraként. Pl. A 3 x 4 -es mátrix esetén : típus nev [ 3 ] [ 4 ] Egy elemre hivatkozás : nev [ i ] [ j ]
Mátrix A C-ben vektorok vektoraként kell megadni a mátrixot: nev [sor][oszlop] Pascalban mind a két féle deklarálást lehet használni: nev [sor, oszlop] nev [sor][oszlop]
Mátrixok Deklaráció C-ben: típus azonosító[sorok száma][oszlopok száma]; Pl. Egy 3 x 4 -es , integer típusú elemekből álló mátrix deklarációja : int mx [ 3 ] [ 4 ] ;
Mátrixok C-ben Mind a sor- , mind az oszlopindex 0 - tól indul !! Az utolsó sorindex értéke : sorok száma -1, Az utolsó oszlopindex értéke: oszlopok száma -1 Egy elemre való hivatkozás : mx [ 0][0] , mx [ 0][1], ..., mx [2][3] , vagy mx[i][j]
Mátrixok C-ben, kezdőértékadás int mx[3][4] = {11,12,13,14,21,22,23,24,31,32,33,34}; ugyanez táblázatos formában: int mx[3][4] = {11,12,13,14, 21,22,23,24, 31,32,33,34}; a sorokat külön is zárójelezhetjük: {{11,12,13,14}, {21,22,23,24}, {31,32,33,34}};
Mátrixok C-ben, kezdőértékadás Az indexméretek közül csak a sorok számát hagyhatjuk el int mx[][4] = {{11,12,13,14}, {21,22,23,24}}; Ilyenkor a fordító határozza meg az értékek alapján a sorok számát.
Mátrixok, feladat Feladat: Rajzoljon fel egy 3 sorból és 4 oszlopból álló táblázatot. Hogyan hivatkozhatunk a tömb egyes elemeire ?
Mátrixok, feladat int a [3][4]; a[2][3] a[2][2] a[2][1] a[2][0] Rajzoljon fel egy 3 sorból és 4 oszlopból álló táblázatot. int a [3][4]; a[2][3] a[2][2] a[2][1] a[2][0] a[1][3] a[1][2] a[1][1] a[1][0] a[0][3] a[0][2] a[0][1] a[0][0]
Mátrixok, feladat void main (void) { const sor=3,oszlop=4; //mi jelenik meg a képernyőn ??? int i, j, k, s[sor][oszlop]; for (i=0, k=1; i<sor; i++) for (j=0; j<oszlop; j++,k++) s[i][j] = k; printf ("\n%d %d %d", s[0][0], s[1][1], s[2][2]); getch (); }
Mátrixok, feladat for (i=0, k=1; i<sor; i++) for (j=0; j<oszlop; j++,k++) s[i][j] = k; i=0 j=0, k=1 s[0][0]=1 j=1, k=2 s[0][1]=2 j=2, k=3 s[0][2]=3 j=3, k=4 s[0][3]=4 j=4, k=5 - i=1 j=0 s[1][0]=5 j=1, k=6 s[1][1]=6 …..
2.3.1. feladat Egy háromfős társaság minden tagja 2-2 lottószelvényt vásárol minden héten. 5-ös lottón, állandó számokkal játszanak. Készítsen programot, amely tárolja a szelvényeken megjátszott tippeket, bekéri a nyerőszámokat és megmondja, hogy volt-e ötös a szelvények között. Megoldás: Elemezzük a feladatot Összesen 6 szelvényünk (tipp-sorunk) van, egy-egy tipphez öt darab egész szám tartozik. Mivel a társaság állandó számokkal játszik, a tippeket előre megadhatjuk a programban. Bekérni csak a nyerőszámokat kell.
2.5.3.1. feladat Egy háromfős társaság minden tagja 2-2 lottószelvényt vásárol minden héten. 5-ös lottón, állandó számokkal játszanak. ... A 6 szelvényen szereplő tippsor tárolása egy kétdimenziós tömbben történik, melynek egy-egy sora tartalmaz egy tippsort: 1. tipp: 11, 34, 45, 66, 89 2. tipp: 3, 13, 43, 52, 78 3. tipp: 25, 31, 72, 86, 90 4. tipp: 8, 15, 26, 48, 81 5. tipp: 19, 29, 39, 49, 69 6. tipp: 21, 32, 43, 54, 65 A C++-ban az alábbi tömböt fogjuk deklarálni : int tippek [6][5];
void main (void) { int tippek [6][5] = //Az állandó tippek { 11, 34, 45, 66, 89, 3, 13, 43, 52, 78, 25, 31, 72, 86, 90, 8, 15, 26, 48, 81, 19, 29, 39, 49, 69, 21, 32, 43, 54, 65 }; int nyeroszamok [5], i, j, egyezik; printf ("\nKérem, adja meg az eheti nyerőszámokat:"); for (i=0; i<5; i++) scanf ("%d", &nyeroszamok [i]); } //for i
//a tippek és a nyerőszámok összevetése for (i=0; i<6; i++) //a tipp-sorok { egyezik = 0; for (j=0; j<5; j++) //a tippek egyezik += (tippek [i][j] == nyeroszamok [j]); } //for j if (egyezik == 5) break; // kiugrás a for i... ciklusból } //for i …………
Ellenőrző kérdések A többdimenziós tömbök deklarációjának általános alakja a C nyelvben. Milyen módon történhet egy kétdimenziós tömb inicializálása ? Írjon fel konkrét példát egy 2 sorból és soronként 3 elemből álló tömbre. Kétdimenziós tömböknél mit jelent a sorfolytonos tárolás ?
2.5.2. Stringek, string függvények String, sztring, karakterlánc , karakterfűzér. Eddig használtuk: stringkonstans v. stringliterál: idézőjelek közé zárt, nulla, vagy több karakterből álló karaktersorozat. ( A " idézőjelek nem részei a karakterláncnak, csak annak határolására szolgálnak.) Pl. "Ez itt egy string” ”Helló !” "" - az üres string jelölése Stringben szerepelhetnek az escape szekvenciák is. Pl. a printf függvény, formátumvezérlő részben Emlékeztető: A nemgrafikus karakterek ábrázolására az escape szekvenciákat használhatjuk.
Escape szekvenciák escape ASCII jelentése szekvencia kód ‘\a’ 7 hangjelzés ‘\b’ 8 backspace ‘\n’ 10 soremelés ‘\\’ 92 \ (backslash) ‘\’’ 39 ‘ (single quote) ‘\”’ 34 “ (double quote) ‘\0’ 0 EOS(end of string)
Stringek a C-ben A C nyelv nem rendelkezik önálló string típussal, a karakterláncokat olyan char típusú egydimenziós tömbben tárolja, melynek utolsó, még a karakterlánchoz tartozó eleme után egy '\0' (ASCII 0) karakter áll. A string tehát olyan egydimenziós karaktertömb (char[]), amelyben a karaktersorozat végét nulla értékű byte ('\0') jelzi. Karaktervektor: Olyan egy dimenziós tömb, melynek elemei karakterek. Stringkonstansoknál a fordító automatikusan elhelyezi a string végét jelző ‘\0’ karaktert a string végén. A stringkonstans tárolásához szükséges memóriaterület 1 byte-tal nagyobb, mint az idézőjelek közé írt karakterek száma !!!
Változó deklarálása string tárolásához a C-ben Karaktervektort kell definiálni . Mivel vektor, így ugyanazok a szabályok érvényesek a stringekre, mint amiket a vektoroknál láttunk, egy elem hossza 1 byte (a vektor elemei karakterek) Deklaráció: char azonosító [string karakterszáma+1]; Pl. char nev [31], eha_kod [12]; Indexeléssel elérhetjük a string egyes karaktereit közvetlenül. Pl. nev[0], nev[1], ... , nev[29], nev[30]
String változó inicializálása Inicializálhatjuk a karaktervektort, de nem { } között, hanem "-k között kell megadni az elemeket folytonosan, elválasztó vessző nélkül. Pl. char s1 [ ] = "abcdef"; Ábrázolása a memóriában: ‘a’ ‘b’ ‘c’ ‘d’ ‘e’ ‘f’ ‘\0’ s1[0] s1[6]
String változó inicializálása char s1 [ ] = "abcdef"; Inicializálhatjuk a karaktervektort így is :?? char s1 [ ] = {‘a’,’b’,’c’,’d’,’e’,’f’,’\0’};
String beolvasása A string formátumvezérlő karaktere: %s Pl. char str [20]; scanf("%s", str); printf ("\nAz s string : %s", str); A scanf használata stringek esetén nem célszerű, mert space-t tartalmazó stringet nem tudunk beolvasni vele.
String műveletek ? Mivel a stringet a C-ben vektorban tároljuk, a következő értékadások nem megengedettek : stringváltozó = stringkonstans; stringváltozó1 = stringváltozó2; Pl. char s[10], s1[10]; s = "szöveg"; //Hibás !! s1 = s; //Hibás !!
String műveletek ? s1 a l m f á k \0 s a l m f á k \0 Megoldások : Karakterenként adhatunk értéket a megfelelő ciklusok megírásával Pl. for( i = 0; i<=7; i++ ) s [ i ] = s1 [ i ] ; s1 a l m f á k \0 s a l m f á k \0
String műveletek ? s1 a l m s a l m f á k \0 \0 Részstring átvitelekor a string végét jelző ‘ \0’ elhelyezéséről nekünk kell gondoskodni !!! Pl. for( i = 0; i<4; i++ ) s [ i ] = s1 [ i ] ; s [ 4 ] = '\0'; s1 a l m f á k \0 s a l m \0
Stringfüggvények Használatukhoz általában include-olni kell a string.h file-t. Néhány stringfüggvény az stdio.h-ban van. Alkalmazásukkal könnyebb a stringek kezelése, nem nekünk kell ciklusokat írogatni. A string függvények argumentumaként a karakterláncot tartalmazó speciális tömb nevét kell átadni, a string tényleges hosszát a ‘\0’ lezáró karakter alapján állapítja meg a függvény !
gets - string beolvasása puts – string kiírása gets ( stringváltozó ) ; stdio.h Egy stringet olvas be ENTER-ig a paraméterként megadott változóba, az ENTER-t a string végét jelző '\0' karakterrel helyettesítve. Visszatérési érték: siker esetén az s string, hiba esetén ’\0’ puts ( stringváltozó ) ; Egy stringet ír ki a standard outputra, a string végéhez az új sor karaktert illesztve. Visszatérési érték: siker esetén egy nemnegatív érték, hiba esetén EOF.
gets példa #include <stdio.h> void main(void) { char string[80]; printf("Írja be a nevét:"); gets(string); printf("A név: %s\n", string); } Van-e védelem túlcímzés ellen ?
puts példa #include <stdio.h> void main(void) { char string1[]= "Hahó, "; char string2[]= "esik a hó !"; puts(string1); puts(string2); } Kimeneti képernyő: Hahó, esik a hó !
strcpy - string másolása egy másikba strcpy ( mibe , mit ); mibe, mit stringek string.h A string végét jelző '\0' -ig másolja át az egyik stringből a másikba a karaktereket. A másolás akkor fejeződik be, ha a '\0' -t is átmásolta. Figyelem ! Túlírhatunk a fogadó string számára lefoglalt memóriaterületen - hibaüzenet nelkül ! #include <stdio.h> #include <string.h> void main(void) { char hova_string[10]; char mit_string[] = "csillámló"; strcpy(hova_string, mit_string); printf("%s\n", hova_string); }
strcpy - string másolása egy másikba Ez a másik megoldás az értékadási problémánkra. s = "szöveg"; helyett strcpy( s , "szöveg" ); a helyes s = s1; helyett strcpy( s , s1 ); a helyes
strcat - konkatenálás strcat ( mihez , mit ); string.h } Egy string végéhez hozzámásol egy másik stringet. Az eredmény hossza a két string együttes hossza. Itt is túlléphetjük a string méretét! #include <string.h> #include <stdio.h> void main(void) { char cel[25]; char szokoz[] = " ", c[] = "C++"; char turbo[] = "Turbo"; strcpy(cel, turbo); strcat(cel, szokoz); strcat(cel, c); printf("%s\n", cel); //Turbo C++ }
strcmp, strcmpi - két string összehasonlítása egész = strcmp ( s1, s2 ) ; string.h egész= strcmpi (s1, s2 ); mindkét függvény összehasonlítást végez s1 és s2 stringek között. strcmpi nem tesz különbséget a kis és nagybetűk között. Az összehasonlítás a stringek első karaktereivel kezdődik és addig tart, amíg az első eltérő karaktereket megtalálja, vagy pedig a string végéig. visszatérési érték: < 0 ha s1 < s2 == 0 ha s1 == s2 > 0 ha s1 > s2
strcmp - példa #include <string.h> #include <stdio.h> void main(void) { char a3[]="aaa", b3[]="bbb"; int miez; miez = strcmp(b3, a3); if (miez > 0) printf("b3 nagyobb, mint a3\n"); else printf("b3 kisebb, mint a3\n"); }
strlen - string hossza egész = strlen ( string ) ; string.h strlen függvény megadja az argumentumban megadott string hosszát, vagyis a stringben található karakterek számát, nem számítva a ’\0’ karaktert. Ez egyben a visszatérési érték is. #include <stdio.h> #include <string.h> void main(void) { char string[] = "Informatika"; printf("%d\n", strlen(string)); }
2.5.2.1. feladat Készítsen programot, amely bekér egy nevet (vezetéknév, keresztnév), majd "angolosan" kiírja azt (keresztnév, vezetéknév) ! #include <stdio.h> #include <conio.h> #include <string.h> void main (void) { //A program bekér egy magyar nevet (vezetéknév, keresztnév), // majd felcserélve kiírja (keresztnév, vezetéknév) char nev [31], snev [31]; //max. 30 karakteres név int i, j, poz; // poz a szóköz indexe char *pozblank; //karakterre mutató pointer
** printf ("\nKérem, adja meg a teljes nevét: "); gets (nev); pozblank = strchr(nev, ' '); if (pozblank) poz = pozblank - nev; //A szóköz pozíciója a névben else { printf ("\nA megadott név egytagú."); return; }
** strcpy (snev, nev); //A nevet az snev-be is átmásolja snev [poz] = ’\0’; //A vezetéknév után '\0' karakter ! printf ("\nA vezetéknév: "); puts (snev); i=poz+1; //A keresztnevet a név elejére másoljuk j=0; // karakterenként while (nev [i] != ’\0’) { nev [j] = nev [i]; i++; j++; } nev [j] = ' '; //szóköz, majd a lezáró ’\0’ karakter nev [j+1] = ’\0’; strcat (nev, snev); //hozzáfűzzük a vezetéknevet printf ("\nA név angolosan: %s", nev);
A képernyő tartalma egy futtatás után: Kérem, adja meg a teljes nevét: Szabó Ida A vezetéknév: Szabó A név angolosan: Ida Szabó