Gyakorlat, 7. hét: karakterek, számrendszerek
Czirkos Zoltán · 2023.10.13.
Karakterek kezelése, a karakterkódolás fogalma. Számrendszerek, számrendszerek közötti átalakítások.
Felkészülés a gyakorlatra:
- A számrendszerekről szóló előadás megértése.
- A karakterekről szóló előadásrészlet átolvasása.
A feladat segít felmérni, hogy állsz a tanulással. A szövege csak a többi megoldással együtt jelenik meg. Óra után pedig rögzítsd a pontszámod az admin portálon – a dolgozat nálad marad.
Feladat
Írj függvényt, amely paraméterként vesz át egy egészekből álló tömböt! A függvény adja vissza címükkel átvett változókban a tömb legkisebb és legnagyobb elemének indexét! Ha több egyforma érték a legkisebb/legnagyobb, akkor ezek közül bármelyik indexét visszaadhatja.
Mutass példát a függvény hívására! Definiálj egy ötelemű tömböt, hívd meg a függvényt, majd a kapott indexeket felhasználva írd ki a legkisebb és legnagyobb elem értékét!
A feladat megoldására 7 perc áll rendelkezésre.
Mintamegoldás és pontozási útmutató
void minmax(int *tomb, int n, int *pmin, int *pmax) {
int min = 0, max = 0;
for (int i = 1; i < n; i++) {
if (tomb[i] < tomb[min]) min = i;
if (tomb[i] > tomb[max]) max = i;
}
*pmin = min;
*pmax = max;
}
int tomb[5] = { 89, 76, 34, 19, 47 };
int min, max;
minmax(tomb, 5, &min, &max);
printf("Legkisebb: %d, legnagyobb: %d", tomb[min], tomb[max]);
Mindegyik tétel 1 pontos:
- a) Példa: tömb létrehozása (legyen méret megadva vagy inicializálva)
- b) eredmény változók, min és max; int típusúak, nem int*!
- c) tömb (& nélkül!), tömb mérete és min/max (& kell) átadása
- d) eredmény kiírásánál a kapott indexek helyes felhasználása
- e) Függvény paraméterei: int* és int a tömbhöz, int* és int* az eredményekhez
- f) lokális min, max változók, amik nem pointerek (!), VAGY mindenhol *pmin, *pmax használata
- g) szélsőérték keresése: változók kezdetben nullára állítása; ez a függvény feladata, nem a hívóé!
- h) szélsőérték keresése: ciklus vizsgálja a többi elemet
- i) min, max eredmények végleges beírása a kért helyre (ha mindenhol *pmin, *pmax volt, akkor automatikusan teljesül)
- j) Összkép: helyes indentálás, nincs *(tomb+i) alakú kifejezés, szerepre utaló változónevek
Ha hely (index) helyett a tömbbeli értéket adja a függvény a hívójának, akkor a g-h-i pontok nem járnak.
Írjunk programot, amelyik beolvas egy szöveget, és csupa kisbetűsítve (vagy csupa nagybetűsítve) írja ki azt! Az összes többi karakter maradjon változatlan a szövegben! Oldjuk meg a feladatot úgy is, hogy saját magunk kezeljük a karakterkódot, és úgy is, hogy az erre való beépített függvényt használjuk!
Megoldás
A betűk is csak számok a gép számára. Így aztán két karakter (karakterkód!) kivonása is értelmes művelet. Az általában használt ASCII
kódtáblában egymás mellett vannak ábécé sorrendben a nagybetűk, és egy másik tartományban egymás mellett a kisbetűk. Emiatt a
'C'-'A'
kifejezés 2-t ad, mivel a C és az A betű között 2 a távolság. Ez az a gondolat, ami a megoldáshoz vezet: ha
egy nagybetű kódjából levonjuk az 'A'
kódját, megkapjuk, hogy hányadik betű az ábécében; utána hozzáadva az 'a'
kódját, megkapjuk az annyiadik kisbetűt, és fordítva. Másképpp gondolkodva: az 'a'-'A'
kifejezés a két
tartomány távolságát adja meg, ezt kell hozzáadni vagy kivonni egy kódhoz, hogy a nagybetűt kisbetűvé alakítsuk, és fordítva.
Fontos, hogy ehhez nem kell tudnunk a karakterkódok konkrét számértékeit! A programba karakterkódokat írni barbár dolog.
Kisbetűből nagybetű:
#include <stdio.h>
int main(void) {
char cbe, cki;
while (scanf("%c", &cbe) == 1) {
if (cbe >= 'a' && cbe <= 'z') {
cki = cbe - 'a' + 'A';
} else {
cki = cbe;
}
printf("%c", cki);
}
return 0;
}
A program fájl vége jelig olvas, azaz F6Enter-rel lehet kilépni belőle (Windows), vagy Ctrl+D-vel
(Linux). Ugyanez a ctype.h
toupper()
függvényét használva:
#include <stdio.h>
#include <ctype.h>
int main(void) {
char cbe;
while (scanf("%c", &cbe) == 1) {
printf("%c", toupper(cbe));
}
return 0;
}
hex | bin | dec |
---|---|---|
0 | 0000 | 0 |
1 | 0001 | 1 |
2 | 0010 | 2 |
… | … | … |
E | 1110 | 14 |
F | 1111 | 15 |
Váltsuk át papíron a következő 2-es számrendszerbeli (bináris) számokat 10-esbe (decimális): 1011, 1101, 10000, 10101! Váltsuk át papíron a következő decimális számokat binárisba: 13, 27, 35.
Váltsuk át a következő számokat hexadecimálisból binárisba: 0x1F, 0xFCE2, 0xABBA, 0xC0FFEE! Váltsuk át ezeket binárisból hexadecimálisba: 11111111, 1011100, 101, 10101! Ezeket a feladatokat úgy végezzük el, hogy közben nem használjuk a tízes számrendszert!
Tipp
24=16, ezért minden hexadecimális (16-os számrendszerbeli) számjegynek pontosan 4 darab bináris számjegy felel meg, ahogyan az jobb oldalt is látható. Emiatt pl. a bináris 101011 hexadecimális értéke 0x2B, mivel alulról 4-es csoportokban 10'1011, és 10=0x2, illetve 1011=0xB.
Végezzük el az alábbi műveleteket kettes számrendszerben, tízesbe átváltás nélkül!
1001 + 101 ─────
1111 +1000 ─────
1111 + 1 ─────
Olvassunk be egy felhasználó által megadott számot, egy felhasználó által megadott számrendszerben!
Először elég, ha tízes számrendszerig működik a program, csak utána írjuk át úgy, hogy működjön nagyobb alap esetén is!
Hányas számrendszerben fogsz írni? 16 Ird be a szamot! fce2 A beolvasott szám 10-es számrendszerben: 64738
Megoldás
Megoldási terv, ötletek
- Beolvasunk egy számjegyet (karaktert), és kivonjuk belőle a
'0'
karakterkódját. Így megkapjuk az értékét. - Hogy lesz egy számjegyből sok? Pl. 123 esetén, ha a 12-t már beolvastuk, és jön még egy 3-as, akkor a 12 igazából a 120-at jelentette (megszorozzuk 10-zel), és ahhoz adjuk a 3-at. Ha 123 esetén 4-es jön, akkor igazából 1230 volt, és ahhoz adjuk a 4-et.
- Vagyis mindig az eddigi, szorozva 10-zel (a számrendszer alapjával), plusz az új számjegy.
A megoldáshoz nem használhatjuk közvetlenül a scanf()
-et, mert az csak a tízes (%d
),
a tizenhatos (%x
) és a nyolcas (%o
) számrendszert ismeri. Egyesével kell beolvasnunk
a számjegyeket, és a helyiértékek kiszámításával meghatároznunk a számot.
Az egyszerű számításhoz felhasználhatjuk a Horner-elrendezést. Vegyük példának ehhez 10-es számrendszerben
a 234-et! Ezt az egyes számjegyekből 2×102+3×101+4×100
formában
határozhatjuk meg. Ami pedig ugyanaz, mint ((2×10)+3)×10+4
, azaz (((0×10+2)×10)+3)×10+4
,
amiből már látszik, hogyan kell dolgoznunk: mindig a meglévő részeredményt megszoroznunk tízzel, és hozzáadni az
új számjegy értékét. Ha eddig a 23-at láttuk, és megkapjuk a 4-est, akkor a szóban forgó lépés 23×10+4 = 230+4
lesz. Ezt kell folytatni egészen addig, amíg határoló karaktert nem kapunk, persze a tízzel szorzás helyett az
adott számrendszer alapját tekintve.
A számjegy beolvasásánál figyelembe kell venni, hogy bár számjegyről beszélünk, tízes számrendszer fölött
ez lehet betű is. Ezért a beolvasott karaktert meg kell vizsgálni, számjegyről van-e szó (0...9) vagy betűről (a...z).
Ha számjegyről, akkor a '0'
-s számjegy karakterkódját kell kivonni belőle, hogy számértéket kapjunk, amúgy pedig az
'a'
betű karakterkódját, és hozzáadni 10-et, mert A=10
, B=11
stb. Itt kapóra
jönnek a ctype.h
függvényei: isdigit(c)
= számjegy-e?, isalpha(c)
= betű-e?,
toupper(c)
= a karakter nagybetűként (ha szépen szeretnénk csinálni).
#include <stdio.h>
#include <ctype.h>
int main(void) {
/* alap */
int alap;
printf("Hányas számrendszerben fogsz írni?\n");
scanf("%d", &alap);
/* szám beolvasása */
printf("Ird be a szamot!\n");
int szam = 0;
char c;
/* az alap beírása után nyomott enter eldobása.
* különben a ciklus scanf %c-je azt is visszaadná! */
scanf(" ");
while (scanf("%c", &c) == 1 && !isspace(c)) {
/* karakter (lehet betű és számjegy is) számértékké alakítása */
int ertek;
if (isdigit(c))
ertek = c - '0';
if (isalpha(c))
ertek = toupper(c) - 'A' + 10;
/* Horner-elrendezés */
szam = szam*alap + ertek;
}
printf("A beolvasott szám 10-es számrendszerben: %d\n", szam);
return 0;
}
Írjunk programot, amelyik adott számot ír ki adott számrendszerben!
Oldjuk meg a feladatot úgy is, hogy tömböt használunk, és úgy is, hogy nem!
Megoldás
Ötletek a megoldáshoz
- Ha a számot elosztjuk a számrendszer alapjával, és a maradékot tekintjük, az a legutolsó számjegy. Például 1234 % 10 = 4, a szám 4-esre végződik.
- Ha a hányadost, az a többi számjegyet adja meg: 1234 / 10 = 123.
- Ezt folytatva megkapjuk az összes számjegyet, csak meg kell a sorrendet fordítani majd.
Első verzió
A számjegyeket meghatározni könnyű: ha a számot modulózzuk a számrendszer alapjával
(tehát elosztjuk vele, és a maradékot vesszük, a %
operátorral), akkor megkapjuk
a legalsó számjegyet. Ha pedig elosztjuk vele, akkor az egész szám csúszik az egyesek felé
– hogy után a következő számjegyet kapjuk meg a modulózás által. Ezt kell folytatni egészen
addig, amíg el nem érünk a nulláig, mert a sok osztás után a szám előbb-utóbb nulla lesz:
#include <stdio.h>
int main(void) {
int alap, szam;
printf("Melyik számrendszerben?\n");
scanf("%d", &alap);
printf("Melyik szám (tízesben)?\n");
scanf("%d", &szam);
while (szam > 0) {
printf("%c", (szam%alap) + '0'); // rossz sorrend :(
szam /= alap;
}
return 0;
}
A gond ezzel „csak” az, hogy így előbb az egyeseket kapjuk meg, és csak utána az egyre nagyobb helyiértékeket... Miközben először a legnagyobb helyiérték kellene, és csak legutoljára az egyesekhez tartozó számjegy:
Melyik számrendszerben? 10 Melyik szám (tízesben)? 123 A szám az adott számrendszerben: 321
Második verzió
A legegyszerűbb a sorrend megfordítására, ha egy tömbben eltároljuk a kapott karaktereket, aztán a végén kiírjuk őket fordított sorrendben. Karaktereket kell eltárolni, tehát karakterek tömbjére van szükségünk:
a tömbös megoldás
#include <stdio.h>
int main(void) {
int alap, szam;
printf("Melyik számrendszerben?\n");
scanf("%d", &alap);
printf("Melyik szám (tízesben)?\n");
scanf("%d", &szam);
/* számjegyek előállítása, eltárolás */
char szamjegyek[32];
int db = 0;
while (szam > 0) {
szamjegyek[db++] = szam%alap + '0';
szam /= alap;
}
/* kiírás fordított sorrendben */
for (int i = db-1; i >= 0; --i)
printf("%c", szamjegyek[i]);
return 0;
}
Tömb nélkül kicsit másképp kell gondolkodunk. A legrövidebb megoldás azon alapul, hogy megkeressük a számrendszer legkisebb hatványát, amelyik már nagyobb az átalakítandó számnál, és attól indulva jövünk visszafelé. Pl. ha a kiírandó szám 1234 (tízes számrendszerben), akkor 1000-rel, 100-zal, 10-zel és 1-gyel osztva az eredeti számot, kapunk olyan számokat, amelyben a legalsó helyiértéken van a kiírandó számjegy (1, 12, 123, 1234).
#include <stdio.h>
int main(void) {
int alap, szam;
printf("Melyik számrendszerben?\n");
scanf("%d", &alap);
printf("Melyik szám (tízesben)?\n");
scanf("%d", &szam);
/* következő nagyobb hatvány megkeresése */
int kitevo = 1;
while (kitevo <= szam)
kitevo *= alap;
/* eggyel visszalépünk, és 1-ig jönnek az osztók */
kitevo /= alap;
while (kitevo > 0) {
printf("%d", (szam/kitevo) % alap);
kitevo /= alap;
}
return 0;
}
A fenti megoldás hiányossága még, hogy 0-ra nem ír ki semmit, mivel az alsó ciklus egyszer sem fut le.
A tömb nélküli megoldás előnye, hogy bármekkora számra tud működni. A tömbösnél meg kellett
becsülnünk a tömb méretét (a mintamegoldás abból indult ki, hogy a kettes számrendszerhez
kell a legtöbb számjegy, és hogy az int
általában 32 bites).
Módosítsuk úgy a programot, hogy 10-nél nagyobb alapú számrendszerben is (pl. 16-osban) működjön! A 10-et, és annál nagyobb számjegyeket ilyenkor betűkkel szokás jelölni. Pl. 16-osban a 0…15 számjegyek: 012…89ABCDEF.
Megoldás
Ehhez csak a kiírást kell módosítani, figyelembe véve, hogy 10-nél nagyobb-e a kapott számjegy. A vonatkozó programrész, kiegészítve az esetlegesen 0 értékű szám kiírásával is:
if (szam == 0)
printf("0");
else {
/* eggyel visszalépünk, és 1-ig jönnek az osztók */
kitevo /= alap;
while (kitevo > 0) {
int ertek = (szam/kitevo) % alap;
printf("%c", ertek >= 10 ? ertek-10+'A' : ertek+'0');
kitevo /= alap;
}
}
Igazságtáblák
Készítsünk programokat, amelyek 2 bitre vagy több bitre készítik el a logikai függvények igazságtábláit!
Bitsorozat kivágása
Oldjuk meg a feladatgyűjtemény bitsorozat kivágása című feladatát!