Pénzváltás

Pénzváltás

Tanulófeladat

Bevezetés

A korábbi fogalmakban már szó esett róla, hogy a global direktíva láthatóvá teszi a többi fájl számára az egyik forrásfájlban definiált függvényt vagy változót. Hasonlóképpen az extern direktíva tudatja az assemblerrel, hogy az aktuális forrásfájlban használt függvény vagy változó egy másikban van definiálva.

Ez a két direktíva jelenti a fő módját annak, ahogy az assembly kód más forrásfájlokkal együttműködik a modularizáció érdekében.

E direktívák szempontjából közömbös, hogy a külső kódot assemblyben vagy egy magas szintű nyelvben definiálták-e, illetve használják-e. Így például egy magas szintű nyelvben definiált függvény is használható x86-64 kódban, ha extern-ként deklarálták.

Ugyanígy a hívási konvenció mindegyik ilyen függvényre érvényes, legyenek assemblyben vagy magas szintű nyelvekben definiálva, sőt még az operációs rendszerrel (OS) is közös.

Mivel azonban assemblyben az adat csupán bájtok sorozata, fontos tisztában lenni azzal, hogyan kezeli a memóriát a kapcsolódó magas szintű nyelv.

C típusok

A C nyelv gyakori választás olyan magas szintű nyelvként, amely assembly kóddal kapcsolódik össze. Megkönnyíti az operációs rendszerrel való együttműködést, mivel magas szintű burkolókat kínál számos fontos feladathoz, például a képernyőre íráshoz, a billentyűzetről való olvasáshoz vagy dinamikus memória lefoglalásához.

Ezen a kurzuson a tesztek C-ben íródnak, ezért fontos átfogó képet adni a nyelvről.

Note

Az alább felsorolt típusok bármelyike minősíthető a const kulcsszóval. Ettől csak olvashatóvá válnak.

Primitív típusok

Sok primitív típus létezik C-ben, és a méretük bájtban kifejezve változó lehet. Az alábbi táblázat ezek egy részét foglalja össze, a tipikus méretükkel egy x86-64 rendszeren:

típus bájtok száma egész / lebegőpontos
_Bool 1 egész
char 1 egész
short 2 egész
int 4 egész
long 4 vagy 8 egész
long long 8 egész
float 4 lebegőpontos
double 8 lebegőpontos

Ezen egész típusok mindegyike lehet előjeles (ez az alapértelmezett) vagy előjel nélküli. Ha előjel nélküliek, ezt külön meg kell adni, például: unsigned int.

Az egész típusokhoz léteznek aliasok, amelyeket a méretük pontos szabályozása érdekében definiáltak:

alias bájtok száma
int8_t 1
int16_t 2
int32_t 4
int64_t 8

Ezek az aliasok kiegészíthetők egy u előtaggal az előjel nélküli típus jelzésére, például: uint64_t.

További fontos aliasok:

alias típus
bool _Bool
size_t általában egy előjel nélküli, 8 bájtos egész szám

A _Bool, illetve annak bool aliasa abban egyedülálló, hogy bár egy egész bájtot foglal el, csupán két érték egyikét veheti fel: true (1) és false (0).

E primitív típusok értékei az egész és lebegőpontos értékek szokásos szabályai szerint adódnak át a függvényeknek, illetve térnek vissza azokból.

Enumok

Az enum olyan típus, amely számos, kifejezetten megadott elem közül vehet fel egyet:

enum example {
    example_1,
    example_2,
    example_3,
    example_4
};

C-ben az enumok implicit módon egész típusra konvertálódnak, amely általában egy int, azaz egy 4 bájtos egész szám.

Alapértelmezés szerint az enum első lehetséges eleme 0-ra konvertálódik, és minden rákövetkező elem a következő egész számra. Tehát a fent definiált enum example-ben az example_1 értéke 0, az example_2 értéke 1 lenne, és így tovább.

Lehetőség van arra, hogy megadjuk egy enum bármely elemének értékét, és ilyenkor alapértelmezés szerint a következő elem a következő egész számot kapja értékként:

enum example_2 {
    example_1 = 4,
    example_2 = 8,
    example_3
}; // example_3 is equal to 9

Memóriacímek

C-ben egy memóriacímre egy mutató hivatkozik, amely egy adott típusra mutat, és jelölése a * operátor. Így például az int64_t * egy olyan memóriahelyre utal, ahol egy vagy több int64_t található.

A címeket a szokásos módon 8 bájtos egészként kezeljük.

Tömbök

A tömböket a függvények az első elemükre mutató mutatóként kapják meg, és így is adják vissza.

int64_t example_arr[] = {1, 2, 3}; // this is an array of 3 signed 8-byte integers
fn(example_arr); // this is a function that passes a pointer (int64_t *) to the beginning of the array as an argument

Egy tömb minden eleme a tömb elemtípusának méretével rendelkezik. A fent definiált example_arr például 3 elemből áll, egyenként 8 bájttal, ami összesen 24 bájt.

Mivel a tömbök nem tárolják a hosszukat, általában külön értékre van szükség az elemek számának megadásához, kivéve, ha egy őrszemérték jelöli a végét.

Stringek

A C-beli stringek char-okból álló tömbök, a char pedig egy 1 bájtos típus. A legtöbb string ASCII-karakterekből áll, és NUL-terminált, azaz akkor ér véget, amikor egy 0 értékű bájtot találunk. Ez azt jelenti, hogy a string hosszát általában nem kell külön argumentumként átadni.

Utasítások

A barátod, Chandler, egzotikus országokba készül utazni a világ minden tájára. Sajnos Chandler matektudása nem jó. Eléggé aggódik, hogy az utazása során átverik a pénzváltók, és szeretné, ha készítenél neki egy pénznemkalkulátort.

Itt vannak a specifikációi az alkalmazáshoz.

Note

A feladat függvényei egy C forrásfájlban vannak deklarálva a megadott szignatúrákkal. Az x86-64-assembly kódodnak követnie kell ezeket az előírásokat.

1. Tartsd nyilván a különböző pénznemeket

Létrehoztál egy enum currency_t típust, hogy nyilván tartsd a különböző pénznemeket, amelyeket Chandler használ az utazásai során:

enum currency_t {
    GBP,
    EUR,
    JPY,
    AUD,
    BRL,
    CNY,
    CAD,
    INR
};

Néha ki kell írnod ezeket a pénznemeket a képernyőre, hogy ellenőrizhesd, helyesek-e.

Hozd létre a stringify_currency függvényt:

void stringify_currency(char *buffer, enum currency_t currency);

Ennek a függvénynek nincs visszatérési értéke, és egy C-stílusú stringet kell tárolnia a buffer által jelzett helyre. A stringnek vizuálisan kell ábrázolnia, hogy milyen típusú pénznemet adtunk át a függvénynek:

char buffer[4];
stringify_currency(buffer, CAD);
// => "CAD"

2. Ellenőrizd az átváltási árfolyamot

Hozd létre az exchange_rate függvényt:

double exchange_rate(enum currency_t domestic_currency, enum currency_t foreign_currency, const double *value_in_US_dollars);

A domestic_currency argumentum Chandler helyi pénznemét jelöli, amelyet foreign_currency-re váltunk.

value_in_US_dollars egy mutató egy double-tömbre, amely minden pénznem egy egységének értékét tartalmazza amerikai dollárban. A pénznemek indexek ebben a tömbben. Tehát ha az EUR érvényes elem az enum currency_t típusban, akkor a value_in_US_dollars[EUR] megadja 1 EUR értékét amerikai dollárban.

Ennek a függvénynek egy egység külföldi pénznem értékét kell visszaadnia a helyi pénznemben.

const double value_in_US_dollars[8] = {
    1.33329, 1.16443, 0.00654374, 0.650888,
    0.185577, 0.140420, 0.714142, 0.0113863
};

exchange_rate(JPY, GBP, value_in_US_dollars);
// => 203.750454633

3. Számítsd ki a bankjegyek értékét

Hozd létre a get_value_of_bills függvényt:

uint64_t get_value_of_bills(unsigned long long denomination, unsigned short number_of_bills);

A denomination argumentum egyetlen bankjegy értéke, míg a number_of_bills a bankjegyek teljes száma.

Ez a pénzváltó csak bizonyos címletű készpénzzel foglalkozik. A kapott összegnek oszthatónak kell lennie egy „bankjegy” vagy egység értékével, ami törtet vagy maradékot hagyhat maga után. A függvényed csak a bankjegyek összértékét adja vissza (a törtrészeket kihagyva), amit a pénzváltó visszaadna. Sajnos a pénzváltó megtarthatja a maradékot/visszajárót ráadásként.

get_value_of_bills(5, 128);
// => 640

4. Számítsd ki a bankjegyek számát

Hozd létre a get_number_of_bills függvényt:

unsigned int get_number_of_bills(float amount, unsigned long long denomination);

Ennek a függvénynek a bankjegyek számát kell visszaadnia, amennyit a megadott összegből kaphatsz. Más szóval: hány egész bankjegy fér bele a kezdőösszegbe? Ne feledd, csak egész bankjegyeket kaphatsz, nem bankjegyek törtrészeit, ezért ennek megfelelően ossz. Lényegében lefelé kerekítesz a legközelebbi egész bankjegyre/címletre.

get_number_of_bills(127.5, 5);
// => 25

5. Számítsd ki az átváltás utáni értéket

Hozd létre az exchangeable_value függvényt:

uint32_t exchangeable_value(float budget, double exchange_rate, uint8_t spread, unsigned long long denomination);

A spread paraméter az átváltási díjként levont százalék, egész számként megadva. Tizedes törtté kell alakítani úgy, hogy elosztjuk 100-zal. Ne feledd, hogy a spread hozzáadódik az átváltási árfolyamhoz, így a 10 értékű spread 10%-kal magasabb átváltási árfolyamot eredményez.

Ennek a függvénynek az új pénznem maximális értékét kell visszaadnia, miután kiszámította a spread-del korrigált átváltási árfolyamot. Ne feledd, hogy a pénznem címlete egész szám, és nem osztható tovább.

exchangeable_value(127.25, 0,873350884, 10, 20);
// => 120
exchangeable_value(127.25, 0,873350884, 10, 5);
// => 130
Szerkesztés GitHubon A hivatkozás új ablakban vagy lapon nyílik meg
x86-64 Assembly Exercism

Készen állsz elkezdeni a(z) Pénzváltás feladatot?

Iratkozz fel az Exercism-re, hogy megtanuld és elsajátítsd a(z) x86-64 Assembly nyelvet 22 fogalom130 feladat segítségével, valódi emberi mentorálással, mindez ingyen.