2. praktikum - Makefile, sisend, teisendused, massiivid, pointerid (itv0020)

Allikas: Lambda
Süsteemprogrammeerimine keeles C

Praktikumid

Laborid

Praktikum eeldab, et oskate dijkstra serverisse sisse logida, seal faile redigeerida ja et oskate anda kompileerimiskäsku.

Makefile

Make on utiliit keeruliste kompileerimiste lihtsustamiseks nii. Võrdle näiteks kaht kompileerimiskäsku (esimene kasutab neist make'i):

make prog
gcc -o prog -Wall --pedantic main.c networking.c prevent_meltdown.c maintenance.c

Esimene neist on lihtne kirjutada ja kasutada, teine neist mitte niiväga. Siit järeldus: kui kompileerimiskäsk on pikem ja sisaldab parameetreid, tuleb alati alati Make utiliiti kasutada, et programmi testitaks pärast iga muudatust ja mitte siis, kui "jälle viitsin kompileerimiseks vajaliku rea sisse toksida".

Make töötab nii, et parajasti aktiivsesse kataloogi kirjutatakse fail, mille nimi on 'Makefile' või 'makefile' (Unixis on need failinimed erinevad -- jälgige suur- ja väiketähti!). See fail kirjeldab mis failid millest sõltuvad ja mis käske nende loomiseks teha on vaja.

Make'i lisainfo:

Make'i ülesanded

  • Tänase programmi nimed on 'praks2a', 'praks2b'. Kirjutage makefile, mis kompileerib -Wall hoiatustega kõik programmid, kui anda käsk 'make all' ja nii, et oleks võimalik eraldi kompileerida ka individuaalseid programme (st 'make praks2a').
  • Boonus: Mõelge kuidas kasutaksite utiliiti 'cat' veebilehe halduseks, kui tahate teha staatilist lehte sedasi, et kõikide lehtede sisud ja menüüd oleksid kirjeldatud eraldi failides ja 'make pages' tekitaks neist "päris" .html failid. Kas on võimalik teha ka make upload, mis laeks failid veebi?

Sisend ja väljund

Kirjuta programm, mis loeb standardsisendit kuni see lõppeb (CTRL+D klaviatuurilt saadab faililõpukonstandi EOF). Loetud tähtedest kirjutab ta standardväljundisse ainult numbrid: 0, 1, 2, 3 ... 8, 9.

Kõrvalmärkus: Failide suunamine sisendisse ja väljundisse
Standardsisend ja väljund on tegelikult salaja samuti failid (peaaegu kõik seadmed on Unixis failid): seega on programmi käivitades võimalik öelda, et ühenda need failiga. Alljärgnevad näited näitavad kuidas utiliidile 'cat' saab faile saata.
cat < sisendfail                   - näitab faili sisu
cat > väljundfail                  - kirjutab klaviatuurilt faili
cat < sisendfail > väljundfail     - kopeerib sisendfaili väljundfailiks
ls > väljundfail                   - saadab kataloogide nimekirja faili
cat asdasda 2> error.log           -tekitab veateate ja saadab selle faili 'error.log' 
Nii saate ka oma programmi tulemusi failidesse suunata ja neile failidest andmeid ette sööta.

Muutujate teisendamine

Mis on muutujate int x, y väärtuseks? Lahendage paberil/peas ja siis kontrollige programmi abil.

x = (2 < 3) - 5 / 2 * 3;
y =  ( (1 / 2) && (1.0 / 2) ) + 'A';
Kõrvalmärkus
Selliste tehete ja nende järjekorra peale on eksamil üks ülesanne, aga nende meeldejätmiseks on semestri lõpu poole üks nipp ja praegu siin seda eriti tarvis polegi.

Pointerid ja massiivid

Võta korra aeg maha ja mõtle millestki lihtsast nagu raamatukogu ja selles paiknevate raamatute kohaviidad. Raamatukogu on nagu mälu ja kohaviidad on justnagu mäluaadressid. Tehe * tähendab, et antud muutujas olevalt aadressilt tuleb lugeda selle sisu (raamatukoguhoidja läheb toob kohaviida abil sulle raamatu) ja tehe & ütleb, et palun anna selle konkreetse muutuja mäluaadress mulle (st vaatad raamatu pealt tema kohaviida).
Kui raamatukogus on kohaviit mingi kole 2/45-T moodi asi, on arvutis asi lihtsam: mäluaadress on lihtsalt 4-baidine täisarv (mille väljatrükiga kogu siinne harjutus tegelebki).
  • Kirjuta programm, mis loob täisarvulise muutuja ja trükib selle ekraanile.
  • Täienda programmi nii, et see trükiks ekraanile ka selle muutuja aadressi, kus too paikneb. (nt "Muutuja x väärtusega 10 aadress on 0xfdff020d" )
  • Täienda programmi nii, et deklareerid täisarvude massiivi, kus on sees 5 esimest Fibonacci arvu.
  • Täienda programmi nii, et trükid välja iga arvu (1) indeksi, (2) mäluaadressi, (3) väärtuse.
  • Võrdle massiivi liikmete indekseid ja mäluaadresse: kas leiad seose?
  • Mõtle mõni suvaline arv kuueteistkümnendsüsteemis (näiteks 0xdeadbeef või 0x00dfbg40) ja proovi sellel aadressil paiknevat mälu lugeda. Mis juhtub?
  • Trüki välja kogu mälu sisu massiivi viimasest elemendist tagasi -- kas leiad üles selle täisarvulise muutuja, mille esimeses punktis tekitasid?

Vihjeid

  • Mäluaadressi on kõige mõnusam trükkida printf'ist %p asendusmärgi abil (sobib ka %#x).
  • Mäluaadressi ise välja mõtlemisel peate muutuja teisendama mäluaadressi tüübiks -- selleks saab kasutada vägisi teisendamist kujul (int*)0xdeadbeef näiteks .
Kõrvalmärkus: Mäluaadresside lugemisest ja kaitsmisest
Selleks, et katkised ja kuritahtlikud programmid operatsioonisüsteemi kahjustada ei saaks, on sinu programmile kasutada antud vaid osa aadressiruumist (aadressiruum on siis (32-bitise arhitektuuri puhul) aadressid 0x00000000 kuni 0xffffffff . Kui sinu programm sellest alast väljaspool kirjutada/lugeda tahab, visatakse ta mälu kaitsmise ettekäändel mälust välja.
Kui mäletad DOSi või Windows 9x aegu, siis programmid just seepärast katki läksidki, et nad said üksteise mälu lugeda ja kirjutada. Tegelikult on see võimalik ka täna: programmid saavad operatsioonisüsteemi abil mälu jagada ja teatud privileegide olemasolul seda ka kirjutada või hallata. Sellel põhimõttel töötavad näiteks silumisprogrammid (debuggerid), mida järgmises praktikumis vaatame (või selles, kui tõesti koledasti aega üle peaks jääma).