C! Binäärioperaatiot Tiedostot ja I/O 16.3.2017
Tiedotteita Määräaikoja pidennetty Myös 7. kierros uudestaan auki perjantaihin asti Seuraava ja viimeinen luento 30.3. Makrot, funktio-osoittimet Kokeen ja loppukurssin järjestelyistä Hyviä tyyppejä 2
Pistemäärä / kierros 600 Pistemäärä / kierros 500 400 300 200 100 0 1 2 3 4 5 6 7 > 0 >= 50 >= 100 3
Läpäistyjä kierroksia 600 Läpäissyt kierrokset 500 400 300 200 100 0 1 2 3 4 5 6 7 Opiskelijoita 4
.c tiedostot ja.h - tiedostot C-ohjelman lähdekoodi kirjoitetaan.c päätteiseen tiedostoon Ohjelman käyttämät määrittelyt kirjoitetaan.h päätteiseen otsaketiedostoon Funktioiden esittelyt (mutta ei toteutuksia) Tietotyypit, tietorakenteet, jne Ohjelmakoodia tai funktioiden toteutuksia ei kirjoiteta.h tiedostoon.c - lähdetiedostoista viitataan.h - otsaketiedostoihin 5
Binääriluvut Laitteistolle luonnollinen tapa käsitellä tietoa Tietokone on (iso) sarja kytkimiä jotka ovat joko päällä tai pois Kaikki isommat lukuarvot rakentuvat biteistä Esim tietokoneen muistissa 10-järjestelmän 6 == 110 bitteinä Binäärilukuja ei voi suoraan esittää standardi C-kielellä (jotkut kääntäjät toteuttavat oman binäärivakio-esitysmuotonsa) Esitetään usein heksadesimaalivakioiden avulla Yksi heksadesimaalimerkki vastaa neljää binäärimerkkiä (bittiä) 6
Heksadesimaaliluvut 16-kantainen lukujärjestelmä Voidaan helposti muuntaa binääriluvuiksi (koska 16 == 2 4 ) Yksi heksadesimaalimerkki vastaa neljää bittiä Yksi tavu (= 8 bittiä) mahtuu kahteen heksamerkkiin C-kielessä heksavakiot esitetään 0x - alkuliitteellä 16 merkkiä: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F 0xF == 15, 0x10 == 16, 0x1F == 31, 0x20 == 32, Printf ja scanf tulostusformaatti: %x tai %X Vaikka esitystapa vaihtuu, luku pysyy samana 74 == 0x4A == 10001010 == J 7
Lukujärjestelmien vertailua 8
Bittitason operaattorit Lukujen bittisisällön käsittelyyn Vastaavat loogisia operaattoreita (AND, OR, NOT) Loogiset operaattorit (&&,,!) palauttavat kokonaisluvun 0 tai 1 Binäärioperaattorit (&,, ~, ^) muokkaavat lukua yksittäisten bittien tasolla Kaksi lukua käydään läpi bitti kerrallaan, tuloksena syntyy uusi luku kokonaisluvun arvoksi voi tulla muutakin kuin 0 tai 1 Vertailuehdoissa kannattaa olla tarkkana Esim. && vs. & Toimivat vain kokonaislukutyypeillä 9
Bittitason JA A & B: Jos sekä A- ja B-bitti ovat 1, tulos on 1. Muuten tulos on 0 10
Bittitason TAI A B: Jos joko A - tai B - bitti on 1, tulos on 1. Jos molemmat ovat 0, tulos on 0 11
Bittitason poissulkeva TAI A ^ B: Jos vain jompikumpi bitti on 1, tulos on 1. Jos molemmat bitit ovat samoja, tulos on 0 12
Bittitason EI ~A: Käännä bittien tila vastakkaisiksi: 0:sta tulee 1; 1:stä tulee 0 13
Esimerkki bittitason operaatioista int main(void) { unsigned char a = 0x69; // 01101001 unsigned char b = 0xca; // 11001010 printf("a & b = %x\n", a & b); unsigned char c = a b; } printf("a b = %x\n", c); b ^= a; // b = b ^ a printf("a ^ b = %x\n", b); printf("~a = %x\n", ~a); printf("~a & 0xff = %x\n", ~a & 0xff); Tulostuu: a & b = 48 a b = eb a ^ b = a3 ~a = ffffff96 ~a & 0xff = 96 14
Bittien siirtäminen >> operaattori siirtää bittejä oikealle Vastaa kahden potenssilla jakamista << - operaattori siirtää bittejä vasemmalle Vastaa kahden potenssilla kertomista Käytetään kokonaislukutyypin kanssa Arvoalueen ulkopuolelta siirtyvien bittien paikalle tulee 0 15
Bittien siirtäminen -- esimerkkejä 105 210 105 52 105 164 105 144 16
http://presemo.aalto.fi/cbits A) 0110 heksadesimaalimuodossa B) 0x37 binäärimuodossa C) 0xB3 binäärimuodossa D) 0101 & 1100 E) 0101 1100 F) 6 & 1 G) 0100 1100 << 2 H) 0x4C >> 4 source: eugbug.hubpages.com 17
2 tavun paketti 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 A B 18
I/O virrat FILE *f = fopen("testfile", "w"); if (!f) { fprintf(stderr, "Opening file failed\n"); exit(exit_failure); } writestring(f, mystring); fclose(f); gcc /home/psarolah/file.txt /etc/httpd.conf 19
I/O - virta C-kielessä (tiedoston, terminaalin, ) luku ja kirjoitus tapahtuu I/O virtojen kautta Ohjelman liittymä ulkomaailmaan Ennen käyttöä virta pitää avata Muutama oletusvirta on valmiiksi avattuna Useita virtoja voi olla auki kerrallaan Toistaiseksi ollaan käytetty paria I/O operaatiota printf: muotoillun tekstin kirjoittaminen virtaan scanf: tietyn muotoisen syötteen lukeminen virrasta Nämä ovat kohdistuneet oletusvirtoihin, mutta muitakin on 20
Puskurointi Syöttö- ja tulostusvirrat ovat puskuroituja Puskuri on tietorakenne, joka taltioi tietoa tilapäisesti Voi näkyä viiveenä syötteen/tulostuksen toimituksessa 21
Puskurointi Syöttö- ja tulostusvirrat ovat puskuroituja Puskuri on tietorakenne, joka taltioi tietoa tilapäisesti Voi näkyä viiveenä syötteen/tulostuksen toimituksessa Erityyppisiä puskurointipolitiikkoja Käyttäjän syöte ja tuloste oletuksena rivipuskuroituja Data toimitetaan kun rivi vaihtuu I/O virran puskurin toimintaa voidaan ohjata Ei puskuria tai täysi puskurointi setvbuf funktio Lisäksi käyttöjärjestelmässä on muita puskureita Kaikkeen puskurointiin ei voi helposti vaikuttaa 22
I/O-virran käsittely: FILE* - tietotyyppi I/O virtaa käsitellään FILE* - tietotyypin kautta Vain siihen tarkoitettuja funktoita käyttäen I/O - toiminnallisuus määritelty stdio.h otsakkeessa I/O virta avataan fopen - kutsulla: FILE *fopen(const char *path, const char *mode) Path: esimerkiksi tiedoston nimi Mode: käsittelymoodi (luetaanko r, kirjoitetaanko w, jne.) I/O virta täytyy sulkea käytön jälkeen fclose(file *fp) Vapautetaan käytetyt resurssit Virtaa ei voi tämän jälkeen enää käyttää 23
I/O-virran avaaminen ja sulkeminen #include <stdlib.h> #include <stdio.h> int main(void) { char *mystring = "One line written to file\n"; } // open 'testfile' for writing (remove previous content) FILE *f = fopen("testfile", "w"); if (!f) { fprintf(stderr, "Opening file failed\n"); exit(exit_failure); } writestring(f, mystring); fclose(f); 24
Merkin luku ja kirjoitus int fputc(int c, FILE *virta) kirjoittaa merkin Virta pitää ensin avata Palauttaa kirjoitetun (8-bittisen) merkin tai EOF jos virhe EOF: end-of-file käytetään myös muissa poikkeustilanteissa int fgetc(file *stream) lukee yhden merkin Palauttaa luetun merkin, tai EOF jos virhe Luettava merkki on 8-bittinen, huolimatta paluuarvon tyypistä Periaatteessa nämä riittävät Isompia tietomääriä käsitellessä helpompaa (ja tehokkaampaa) käyttää jalostuneempia funktioita 25
Esimerkki: merkin kirjoitus int writestring(file *fp, const char *str) { while (*str) { // write characters until the end of string if (fputc(*str, fp) == EOF) return -1; // error in writing } } return 0; str++; Huom: stdio.h:ssa myös valmis funktio merkkijonon kirjoitukseen (fputs) 26
Standardi I/O - virrat Järjestelmässä on oletusarvoisesti auki kolme I/O-virtaa stdin käyttäjältä tuleva syöte Luetaan esim. scanf funktiolla stdout käyttäjälle menevä tuloste Esim. printf kutsun tuottama teksti stderr virheulostulo puskuroimaton: tuloste näkyy heti Oletuksena stdin ja stdout ovat rivipuskuroituja Oletusarvoisesti syötteet ja tulosteet ohjautuvat terminaali-ikkunaan (mutta voidaan ohjata muualle) 27
Eri tyyppisiä I/O - virtoja Putki Esim. terminaalisyöte ja tuloste (esim. stdin ja stdout) Rivipuskuroitu Ei ennalta määriteltyä loppua Syötettä ja tulostetta luetaan/kirjoitetaan koko ajan eteenpäin Tiedosto Täysin puskuroitu Tiedostolla on määritelty loppu Käytetään vakiota EOF: end-of-file Tiedoston käsittelyssä voidaan hypätä mielivaltaiseen kohtaan Ja esim. lukea sama kohta useaan kertaan 28
Tekstidata ja Binääridata Monet I/O virrat ovat tekstimuotoisia Lukemisen jälkeen voidaan käsitellä ja tulostaa kuten merkkijonoja Koostuu yleensä riveistä Funktiot fputs ja fgets kirjoittavat ja lukevat tekstisisältöä Olettavat sisällön merkkijonoiksi I/O-virta voi olla myös binäärimuotoinen Esim. Muistissa olevien tietorakenteiden sisältö sellaisenaan Virran arvoja ei voi tulkita tulostettavina merkkeinä Esim. Arvoilla 0 tai 10 (ASCII \n ) ei ole mitään erityismerkitystä Funktiot fwrite ja fread kirjoittavat ja lukevat binäärisisältöä 29
Fgets/fputs - esimerkki int main(void) { FILE *f; char buffer[100]; } f = fopen("test.c", "r"); // open file for reading if (!f) { fprintf(stderr, "Opening file failed\n"); return EXIT_FAILURE; } while (fgets(buffer, sizeof(buffer), f)!= NULL) { if (fputs(buffer, stdout) == EOF) { fprintf(stderr, "Error writing to stdout\n"); fclose(f); return EXIT_FAILURE; } } fclose(f); 30
Tiedoston loppu ja virhetilanteet Mikäli lukeminen tai kirjoittaminen loppuu yllättävästi, ei välttämättä heti tiedetä mitä on tapahtunut feof kutsulla voidaan kysyä ollaanko tiedoston lopussa Palauttaa 0 mikäli tiedosto ei ole vielä lopussa, jotain muuta jos tiedosto on lopussa Eli voidaan käyttää suoraan if ehtolausekkeessa ferror kutsulla voidaan kysyä onko tapahtunut I/O virhe Palauttaa 0 mikäli virhettä ei ole tapahtunut, jos tapahtui virhe, palauttaa jotain muuta 31
Muotoiltu syöte ja tulostus fprintf: yleisempi muoto printf tulostuksesta Voidaan kirjoittaa mihin tahansa virtaan muotoilumääreitä käyttäen Esim. fprintf(stream, %d, luku); printf( %d, luku); on sama kuin fprintf(stdout, %d, luku); fscanf: yleisempi muoto scanf syötteen lukemisesta Lähteenä voi olla mikä tahansa virta Esim. fscanf(stream, %d, &luku); scanf( %d, luku); on sama kuin fscanf(stdin, %d, luku); 32
Merkkijonoon tulostus sprintf(char *merkkijono, muotoilu,.) tulostaa annetun muotoilun merkkijonoksi snprintf(char *merkkinono, size_t size, muotoilu, ) Kuten edellä, mutta rajaa merkkijonon enintään size pituiseksi 33