24
Opphavsrett: Forfatterne og Stiftelsen TISIP Avdeling for informatikk og e-læring, Høgskolen i Sør-Trøndelag 1. Introduksjon. Pekere og referanser. Mildrid Ljosland og Else Lervik 27.01.2009 Lærestoffet er utviklet for faget LV195D Objektorientert programmering i C++ 1. Introduksjon. Pekere og referanser. Resymé: Leksjonen introduserer studenter med ulik bakgrunn til faget. Deretter starter vi ganske tungt med pekere og referanser. Dette er både vanskelig og grunnleggende, og det anbefales at du bruker god tid på dette emnet. Til slutt tar vi med litt om bruk av tekstfiler og spesielt det tilfellet der vi har << og getline() om hverandre. Innhold 1.1. INTRODUKSJON TIL KURSET ..................................................................................................................... 2 1.2. NY C++ STANDARD ................................................................................................................................. 2 1.2.1. Nytt klassebibliotek ............................................................................................................................ 3 1.2.2. Ny datatype: bool ............................................................................................................................... 3 1.2.3. Nytt reservert ord: namespace ........................................................................................................... 3 1.2.4. Nye header-filer.................................................................................................................................. 3 1.2.5. Nytt om for-setningen ......................................................................................................................... 4 1.2.6. Nytt om main() .................................................................................................................................... 4 1.2.7. Et ”moderne” C++-program ............................................................................................................ 4 1.3. HVA GJØR VI DENNE UKA? ....................................................................................................................... 5 1.3.1. Til studenter med bakgrunn i C (og ikke C++) ................................................................................. 5 1.3.2. Til studenter med bakgrunn i C++ .................................................................................................... 6 1.3.3. Til studenter med bakgrunn i Java .................................................................................................... 6 1.4. Å LAGE OG BRUKE EN PEKER ................................................................................................................... 7 1.4.1. Oppgaver (løsninger bakerst i leksjonen) ......................................................................................... 8 1.5. TABELL SOM PEKER ................................................................................................................................. 9 1.6. CONST-PEKERE ......................................................................................................................................... 9 1.7. ARITMETIKK PÅ, OG SAMMENLIKNING AV PEKERE................................................................................ 10 1.7.1. Å addere et heltall til en peker ......................................................................................................... 10 1.7.2. Å subtrahere to pekere ..................................................................................................................... 12 1.7.3. Sammenlikning ................................................................................................................................. 12 1.8. PEKERE MÅ BEHANDLES MED FORSIKTIGHET! ....................................................................................... 13 1.8.1. Oppgaver, forts................................................................................................................................. 14 1.9. REFERANSER .......................................................................................................................................... 14 1.9.1. Oppgaver, forts................................................................................................................................. 15 1.10. ARGUMENTOVERFØRING ....................................................................................................................... 15 1.10.1. Verdioverføring ........................................................................................................................... 16 1.10.2. Referanseoverføring .................................................................................................................... 17 1.11. LØSNINGER TIL OPPGAVENE FORAN ...................................................................................................... 18 1.12. ENKEL FILBEHANDLING ......................................................................................................................... 20 1.13. Å BRUKE << OG GETLINE() OM HVERANDRE ......................................................................................... 22 Referanser til lærebøkene. Hovedtemaet pekere og referanser er dekker på følgende måte:

1. Introduksjon. Pekere og referanser....1. Introduksjon. Pekere og referanser. side 4 av 24 Opphavsrett: Forfatterne og Stiftelsen TISIP 1.2.5. Nytt om for-setningen Se eksempel side

  • Upload
    others

  • View
    2

  • Download
    0

Embed Size (px)

Citation preview

Opphavsrett: Forfatterne og Stiftelsen TISIP

Avdeling for informatikk og e-læring, Høgskolen i Sør-Trøndelag

1. Introduksjon. Pekere og referanser. Mildrid Ljosland og Else Lervik

27.01.2009 Lærestoffet er utviklet for faget LV195D Objektorientert programmering i C++

1. Introduksjon. Pekere og referanser. Resymé: Leksjonen introduserer studenter med ulik bakgrunn til faget. Deretter starter vi ganske tungt med pekere og referanser. Dette er både vanskelig og grunnleggende, og det anbefales at du bruker god tid på dette emnet. Til slutt tar vi med litt om bruk av tekstfiler og spesielt det tilfellet der vi har << og getline() om hverandre.

Innhold

1.1. INTRODUKSJON TIL KURSET ..................................................................................................................... 2 1.2. NY C++ STANDARD ................................................................................................................................. 2

1.2.1. Nytt klassebibliotek ............................................................................................................................ 3 1.2.2. Ny datatype: bool ............................................................................................................................... 3 1.2.3. Nytt reservert ord: namespace ........................................................................................................... 3 1.2.4. Nye header-filer .................................................................................................................................. 3 1.2.5. Nytt om for-setningen ......................................................................................................................... 4 1.2.6. Nytt om main() .................................................................................................................................... 4 1.2.7. Et ”moderne” C++-program ............................................................................................................ 4

1.3. HVA GJØR VI DENNE UKA? ....................................................................................................................... 5 1.3.1. Til studenter med bakgrunn i C (og ikke C++) ................................................................................. 5 1.3.2. Til studenter med bakgrunn i C++ .................................................................................................... 6 1.3.3. Til studenter med bakgrunn i Java .................................................................................................... 6

1.4. Å LAGE OG BRUKE EN PEKER ................................................................................................................... 7 1.4.1. Oppgaver (løsninger bakerst i leksjonen) ......................................................................................... 8

1.5. TABELL SOM PEKER ................................................................................................................................. 9 1.6. CONST-PEKERE ......................................................................................................................................... 9 1.7. ARITMETIKK PÅ, OG SAMMENLIKNING AV PEKERE ................................................................................ 10

1.7.1. Å addere et heltall til en peker ......................................................................................................... 10 1.7.2. Å subtrahere to pekere ..................................................................................................................... 12 1.7.3. Sammenlikning ................................................................................................................................. 12

1.8. PEKERE MÅ BEHANDLES MED FORSIKTIGHET! ....................................................................................... 13 1.8.1. Oppgaver, forts................................................................................................................................. 14

1.9. REFERANSER .......................................................................................................................................... 14 1.9.1. Oppgaver, forts................................................................................................................................. 15

1.10. ARGUMENTOVERFØRING ....................................................................................................................... 15 1.10.1. Verdioverføring ........................................................................................................................... 16 1.10.2. Referanseoverføring .................................................................................................................... 17

1.11. LØSNINGER TIL OPPGAVENE FORAN ...................................................................................................... 18 1.12. ENKEL FILBEHANDLING ......................................................................................................................... 20 1.13. Å BRUKE << OG GETLINE() OM HVERANDRE ......................................................................................... 22

Referanser til lærebøkene. Hovedtemaet pekere og referanser er dekker på følgende måte:

1. Introduksjon. Pekere og referanser. side 2 av 24

Opphavsrett: Forfatterne og Stiftelsen TISIP

H. M. Deitel & P. J. Deitel: C++ How to Program, 6.Ed: Kap. 8.1-8.9. Else Lervik og Mildrid Ljosland: Programmering i C++, Stiftelsen TISIP og Gyldendal, 2003: Kap. 16.1-16.4 inneholder omtrent det samme som leksjonen. (Kapittel 1.4-1.8 i leksjonen er en omarbeidet utgave av kapittel 15.1-15.4 i boka: Else Lervik og Mildrid Ljosland: Grunnleggende programmering i C++. Gyldendal 1993)

1.1. Introduksjon til kurset Velkommen til kurs i Objektorientert programmering basert på C++!

I løpet av semesteret skal vi arbeide oss gjennom størstedelen av den standardiserte delen av C++. Samtidig skal vi bli kjent med grunnleggende objektorientert tankegang, hvordan dette uttrykkes i modelleringspråket UML og hvordan vi programmerer det i C++.

Dette er ikke en snau målsetting, og det krever både modning og arbeidsinnsats. Det tar tid å lære seg å tenke objektorientert (som et svangerskap, minst ni måneder, sier mange!), og det tar masse tid å lære seg å programmere i C++.

Dere har selvfølgelig et problem når dere sitter hjemme i deres egen stue og skal finne kompileringsfeil og andre feil i programmet. Det har lett for å gå i stå, og man har ingen å spørre der og da. Det vi tilbyr er diskusjonsforum på Internett (”news”). Jeg håper at dere vil bruke dette forumet jevnlig. Dere er dessuten velkommen til å sende e-post til meg og eventuell veileder (se opplysninger om veileder på forsiden av faget). Sitt iallfall ikke å slit med den samme feilen i ”timesvis”, legg akkurat det problemet til side (ved for eksempel å kommentere det bort) og prøv heller å løse en annen del av oppgaven, dersom mulig.

Det beste er selvfølgelig om dere klarer å finne så mange feil som mulig selv. Kompileringsfeil er en side av saken. De kan av og til være forferdelig vriene å forstå. Her er det vanskelig å gi generelle råd, annet enn å prøve (enda en gang!) å forstå hva feilmeldingen sier. Men ettersom det ofte krever svært gode kunnskaper i C++ (som dere da per definisjon ikke har), er det ikke alltid enkelt.

Kjørefeil finner dere enklest ved å bruke debugger. Dere kan selvfølgelig legge inn skrivesetninger mange steder i programmet, men det erstatter sjelden en god debugger. Det er vanskelig å gi noen oppskrift på bruk av debugger, da det varierer fra verktøy til verktøy. Vedlegg I og J i Deitel&Deitel viser bruk av debuggerne i henholdsvis Visual Studio og GNU C++.

Leksjonene inneholder flere mindre oppgaver med løsning som dere bør gjøre for å lære stoffet. Det vil ofte være helt nødvendig å ha gjort disse oppgavene for å klare innleveringsoppgavene, som naturlig nok er litt større. Dere får 12 innleveringsoppgaver, og minst åtte av dem må være godkjent for å få gå opp til eksamen. Selvfølgelig bør alle 12 øvingsoppgavene gjøres, nye temaer behandles jo hver eneste uke. Som vanlig er jeg fleksibel når det gjelder innleveringsfristene, men dette er ikke et kurs der alle øvingsoppgavene kan gjøres i løpet av en uke!

1.2. Ny C++ standard Vel, ny og ny…standarden kom i 1998, og de fleste kompilatorene støtter den etter hvert. Mange av dere har kanskje lært grunnleggende C++ ved å bruke eldre bøker, og da er det et par ting dere bør være oppmerksomme på.

1. Introduksjon. Pekere og referanser. side 3 av 24

Opphavsrett: Forfatterne og Stiftelsen TISIP

1.2.1. Nytt klassebibliotek Den mest betydelige forskjellen er at C++ nå kommer med et standard klassebibliotek, blant annet er datatypen string et meget velkomment tilskudd til språket. Den gjør strenghåndteringen betydelig enklere. Å bruke string krever en viss forståelse for objektorientert tankegang. Vi kommer derfor tilbake til denne datatypen når vi begynner med objektorientering neste uke.

Ellers er Standard Template Library (STL) en del av dette biblioteket. Deler av STL behandles i leksjon 10 og 11.

1.2.2. Ny datatype: bool C++ har nå fått en egen primitiv datatype for logiske verdier. Datatypen heter bool og aktuelle verdier er true og false. Denne datatypen tar vi i bruk så snart den trengs. Tidligere har man ofte laget sin egen logiske type ved å bruke for eksempel enum.

Se side 139 i Deitel & Deitel, side 49 i Lervik og Ljosland.

1.2.3. Nytt reservert ord: namespace Det nye reserverte ordet namespace (navnerom) gjør håndtering av navnekonflikter ved utvikling av større systemer mye ryddigere. Nå kommer imidlertid ikke vi til å lage så mange navnerom, men vi trenger å bruke allerede definerte navnerom nå helt fra begynnelsen av. Og aller mest vil vi bruke navnerommet som heter std, som omfatter alle standardbibliotekene. Det oppnår vi ved å sette følgende direktiv i begynnelsen av programmene våre:

using namespace std;

Generelt bør man imidlertid kvalifisere navnene der de brukes, eventuelt spesifisere eksakt hvilke navn i et navnrom man bruker. Eksempel på den første metoden finner du f.eks. i programmet side 45 i Deitel & Deitel:

std::cout << ”Welcome to C++!\n”;

mens den andre metoden er brukt på side 85:

using std::cout; using std::endl;

I eksemplene og løsningsforslagene i dette kurset nøyer vi oss med setningen

using namespace std;

i begynnelsen av programmet.

Detaljer angående namespace, se kapittel 25.3 i Deitel & Deitel (s. 1241-1245), kap. 12.2 i Lervik og Ljosland.

1.2.4. Nye header-filer Foran namespace-direktivet plasserer vi de include-kommandoene vi trenger. Du er kanskje vant med at header-filene heter for eksempel string.h og stdlib.h. I henhold til den nye standarden er ”.h” fjernet. Filnavnene som hører til standard C-biblioteket har fått en c foran seg, eksempel: cstring, cctype, sctdlib, osv.

1. Introduksjon. Pekere og referanser. side 4 av 24

Opphavsrett: Forfatterne og Stiftelsen TISIP

1.2.5. Nytt om for-setningen Se eksempel side 196 i Deitel & Deitel, side 416 i Lervikog Ljosland. Dersom du, slik som her, definerer løkketelleren inne i for-setningen, er det for-setningen som beskriver rekkevidden av denne variabelen. Den er ikke tilgjengelig utenfor løkken. Men se portabilitetstips 5.1, side 197 i Deitel & Deitel, og Merk-boksen side 417 i Lervik og Ljosland.

1.2.6. Nytt om main() Første linje i hovedprogrammet skal ikke lenger være void main(void), men int main(). Dette fører til at main() også bør ha retur-verdi, vanligvis 0.

1.2.7. Et ”moderne” C++-program // // ModerneCppProgram.cpp // #include <iostream> // C++-bibliotek, filendelsen h er tatt bort #include <cstring> // C-bibliotek, filendelsen er borte, det er... #include <cctype> // ... kommet en c først i navnet using namespace std; // bruker standard navnerom int main() { // ikke void her! char tekst[20]; // her vil vi etter hvert bruke string cout << "Skriv et ord: "; cin >> tekst; for (int i = 0; i < strlen(tekst); i++) { // strlen fins i <cstring> tekst[i] = toupper(tekst[i]); // toupper() og tolower() fins i <ctype> } cout << "Bare store bokstaver: " << tekst << endl; for (int j = 0; j < strlen(tekst); j++) { tekst[j] = tolower(tekst[j]); } cout << "Bare små bokstaver: " << tekst << endl; return 0; // nødvendig pga at main() er av typen int }

Men, så fungerer ikke kompilatoren helt som den skal likevel. Dersom jeg prøver å bruke løkketelleren i i begge løkkene får jeg feilmelding. Så akkurat det har ikke Visual C++ 6.0 fått med seg…

Dette unngås ved å bruke ulike navn på løkketellerne. Kanskje din kompilator er bedre?

1. Introduksjon. Pekere og referanser. side 5 av 24

Opphavsrett: Forfatterne og Stiftelsen TISIP

1.3. Hva gjør vi denne uka? Ettersom bakgrunnen deres er forskjellig vil dere nok oppleve denne første leksjonen noe ulikt. Temaet er pekere og referanser1. De store tankene om ”objekter og sånt” venter vi derfor med til neste uke. Men les gjerne kapittel 1.21 (side 25-29) i Deitel & Deitel allerede nå!

1.3.1. Til studenter med bakgrunn i C (og ikke C++) Pekere er velkjent for de av dere som er ”rene” C-programmerere, og som tar dette kurset for å lære objektorientering. Referanser er imidlertid nytt, og det er sentralt i forbindelse med funksjoner i C++.

Dere trenger å lære dere litt om C++ input/output. Det er egentlig veldig enkelt, eller iallfall er det enkelt det lille vi trenger vi nå. Studer eksemplet foran (kompiler og kjør det!) Du trenger ikke å angi formateringsstrenger slik du må med printf() og scanf(). En standard formatering blir brukt. En kort innføring finner du på http://www.cprogramming.com/tutorial/iomanip.html. Du kan også lese side 113-115 i Lervik og Ljosland.

Bruk ikke C++-biblioteker for innlesing/utskrift sammen med C-bibliotekene for det samme.

I C brukes ofte DEFINE for å lage konstanter. I C++ bruker vi const som er en modifikator som brukes i forbindelse med en variabeldeklarasjon, eksempel:

const maksAntall = 20;

Variabelen maksAntall kan ikke endre verdi.

Du er vant med å definere variabler i begynnelsen av en funksjon. I C++ definerer vi en variabel i nærheten av (men foran) det stedet der den brukes første gang. Dette er ikke helt gjennomført i Deitel & Deitel-boka, men det er en god ting ettersom vi da ofte kan initiere variabelen i samme setning som vi definerer den. Eksempel: #include <iostream> using namespace std; int main() { cout << "Skriv tallene som skal summeres. " << endl; cout << "Avslutt med 0 eller negativt tall" << endl; int tall; int sum = 0; cin >> tall; while (tall > 0) { sum = sum + tall; cin >> tall; } cout << "Summen er " << sum; return 0; }

1 til Java-programmere: En referanse i Java er det samme som en peker i C++. I Java har vi ikke noe som ligner på det som kalles for referanser i C++.

1. Introduksjon. Pekere og referanser. side 6 av 24

Opphavsrett: Forfatterne og Stiftelsen TISIP

Funksjoner i C++ kan du lese om i Lervik og Ljosland kapittel 4. Deitel & Deitel gjennomgår ikke funksjoner uten i tilknytning til klasser. Men – kort sagt, det du gjør i C kan du også gjøre i C++. Imidlertid bruker man sjelden adresseoverføring for å lage ut-argumenter. C++ har i stedet noe som heter referanseoverføring. Vi kommer tilbake til ulike typer argumentoverføring senere i denne leksjonen.

1.3.2. Til studenter med bakgrunn i C++ Dette kurset bygger på det som ble gjennomgått i faget ”Programmering i C++”. Fagbeskrivelsen sier følgende om emnene i det faget: ” Datatyper, betingelser og løkker, uttrykk, funksjoner, funksjonsbibliotek, tabeller, tekststrenger, strukturer, datafiler, sortering, søking. Program som består av flere filer. Bruk av "header"-filer. Kompilering og lenking i integrerte programmeringsomgivelser og bruk av "debugger". Algoritmer, skrittvis forfining, testing og feilsøking.”

Kort sagt betyr dette at du har erfaring med strukturert programmering i C++. Du har ingen kjennskap til klasser og nesten ingen kjennskap til pekere. Denne uka bruker vi på temaet ”pekere”. Dette er egentlig helt grunnleggende i C++ (og i C), og det vil være et emne vi kommer tilbake til flere ganger utover i kurset. Pekere oppleves av mange som svært vanskelig. Du må virkelig tenke, og blyant, viskelær og papir er gode hjelpemidler når du skal ha dette på plass. I tillegg må du lage små programmer som prøver ut at du tenker riktig.

1.3.3. Til studenter med bakgrunn i Java Du har aldri programmert verken C eller C++. Derimot har du god greie på objektorientering. Dette kurset er egentlig ikke laget for deg, men dersom du insisterer på å følge det og er motivert nok, så får du det nok til. Du vil nok spesielt oppleve problemer i begynnelsen. Du trenger rett og slett å gå gjennom de første kapitlene i bøkene (Deitel & Deitel, kap. 1-5, 6.1-6.17, 7.1-7.10 og 8.13, Lervik og Ljosland, kap. 1-9) for å gjøre deg kjent med de grunnleggende forskjellene. Men fortvil ikke, mye er likt, iallfall når du kommer til klasser. Det som nok vil ta mest tid for deg, er forskjeller i grunnfilosofien bak de to språkene:

C++ passer ikke så godt på deg som det Java gjør. Det vil si at du får færre feilmeldinger. Ting som Java-kompilator eller kjøresystem fanger opp, blir ikke fanget opp av C++. I stedet får du logiske feil som det kan være svært vanskelig å finne ut av.

Merk deg spesielt følgende:

- Lær at du ikke trenger å putte main() inn i en klasse! Se på de små programeksemplene foran i denne leksjonen.

- Lær å bruke cin og cout til enkel innlesing og utskrift i konsollvinduet.

- Husk at alle variabler må gis verdi i programmet. Dersom du ikke gjør det får de en tilfeldig verdi. Og da blir selvfølgelig resultatene feil!

- Algoritmeabstraksjon, Lervik og Ljosland, f.eks. kap. 6.7, slå også opp i indeksen bakerst i boka . Her ser du prinsippet for ”gammeldags”, strukturert programmering: Algoritmer på flere nivåer og skrittvis forfining. Uten å knytte algoritmene til objekter. Deitel & Deitel er blitt så moderne nå, at dette knapt er tatt med. Men tankegangen går i korthet ut på at du deler kompliserte algoritmer opp i mindre håndterbare deler – og det er jo nettopp det du er vant med fra objektorientert programmering – der en funksjon er liten, og den gjør kun én ting.

1. Introduksjon. Pekere og referanser. side 7 av 24

Opphavsrett: Forfatterne og Stiftelsen TISIP

- En funksjon i C++ er det samme som en metode i Java. Du oppdager at funksjoner kan sende data ut gjennom parameterlisten. Og du lærer hva en referanse i C++ er. Det er ikke det samme som en referanse i Java.

- Funksjoner i strukturert programmering er ikke knyttet til objekter. Slik ligner de på klassemetoder i Java. Alle data som du er vant til at er samlet i objektet, må i stedet sendes fram og tilbake via parameterlisten (og eventuelt returverdien). Eksempler på bruk av slike funksjoner finner du i kap 4 i Lervik & Ljosland.

- Tabeller i C++ er ikke objekter. De kjenner ikke til sin egen lengde. Du kan gjerne bruke ugyldig indeks, kjøresystemet regner ut den adressen du ber om og henter ut eller lagrer data der, uavhengig av om datacellen tilhører tabellen eller ikke. Dette kan selvfølgelig få uante konsekvenser. Tabellstørrelsen må være kjent under kompilering. Du lager ikke tabeller ved å bruke new.

- Du må kunne litt om tekststrenger slik de var i C++ før klassen string kom. Deitel&Deitel, kap. 8.13 dekker temaet grundig, Lervik og Ljosland nevner det såvidt i kap. 6.4. Dessverre er det mye du ikke kan gjøre med klassen string, og mange biblioteksfunksjoner krever at du har strengene på denne gammeldagse måten.

- I Java kompilerer du til class-filer, og kjører en tolker som oversetter til maskinkode der og da. Et C++-program kompileres direkte til maskinkode. De kompilerte filene lenkes sammen til et kjørbart program. Dette er en viktig forskjell, les kap. 1.15 i Deitel & Deitel, kap.1.2 i Lervik og Ljosland.

Til slutt: C++ er ikke plattformuavhengig. Eksempelvis kan tallområdet for heltall være forskjellige på ulike plattformer. Dette er en av grunnene til at Deitel&Deitel har 36 ”Portability Tips”, se side XXVII, f.eks. Tip 1.3 side 17 …

Du trenger å programmere litt før du begynner på leksjonen. Prøv f.eks å lage varianter av eksemplene i boka.

1.4. Å lage og bruke en peker En peker er en variabel som kan inneholde adressen til en annen variabel. * i variabeldefinisjonen markerer at vi snakker om en peker.

Eksempel:

int tallet; int *pekeren; pekeren = &tallet; *pekeren = 6;

Merk Når &-tegnet står på høyre side av = i en variabeldefinisjon eller i en tilordningssetning, betyr det at vi skal finne adressen til det som kommer bak, og la denne adressen være verdien til det som står på venstre side (eksempel: int *pekeren = &tallet;).

1. Introduksjon. Pekere og referanser. side 8 av 24

Opphavsrett: Forfatterne og Stiftelsen TISIP

Variablene tallet og pekeren er vist på figur 1. Figur 1: To måter å representere en peker

int *pekeren forteller at variabelen pekeren er en peker til en int, slik at den kan inneholde adressen til en int-variabel. Ved å skrive pekeren = &tallet gir vi pekeren en verdi som forteller hvor tallet er lagret. Hvis tallet er lagret på adresse 100, vil pekeren få verdien 100. *pekeren = 6 betyr at den adressen som pekeren viser til (adresse 100, dvs. tallet), skal få verdien 6.

* kalles dereferanseoperatoren, og leses gjerne ”det som variabelen peker til”. I vårt eksempel: Det som pekeren peker til er en int, og den skal få verdien 6.

& er adresseoperatoren og leses "adressen til". I vårt eksempel: pekeren får som verdi adressen til tallet.

& og * er motsatte operatorer, de opphever hverandre. Uttrykket *&pekeren betyr det samme som &*pekeren og det samme som pekeren.

Ved å bruke en peker, kan vi bruke en variabel uten å angi navnet på den. *pekeren = 6 forandrer verdien på variabelen tallet, men gjør ingenting med pekeren. Dette kalles indirekte adressering, og utnyttes i en del situasjoner.

1.4.1. Oppgaver (løsninger bakerst i leksjonen)

Oppgave 1

Følgende er gitt:

int i = 3; int j = 5; int *p = &i; int *q = &j;

Tegn opp datacellene med innhold etter at disse definisjonene er utført.

Lag et lite program som skriver ut både adressen til, og innholdet av disse fire variablene. Ved utskrift av adressen (eksempelvis, adressen til variabelen j: cout << &j) får du skrevet ut adressen i 16-tallsystemet. Dersom du vil ha den ut i 10-tallsystemet, må du caste: (cout << (int) &j).

Stemmer utskriften med tegningen din?

Oppgave 2

Vi går ut fra at datacellene har innholdet fra oppgave 1. Hva blir skrevet ut når følgende programbit utføres?

tallet

pekeren

100

101 100

6tallet

pekeren

6

A: Peker tegnet som datacelle med adresse B: Peker tegnet som datacelle med pil

1. Introduksjon. Pekere og referanser. side 9 av 24

Opphavsrett: Forfatterne og Stiftelsen TISIP

*p = 7; *q += 4; *q = *p + 1; p = q; cout << *p << " " << *q << endl;

Tegn gjerne opp datacellene etter hver enkelt setning.

1.5. Tabell som peker Fra tidligere er det kjent at navnet på en tabell er en adresse.

int tabell[10];

forteller kompilatoren at den skal sette av plass til ti heltall, og at tabell brukes som betegnelse for tabellen.

Merk Når vi skriver bare tabell, tolkes det som adressen til første element i tabellen.

Vi kan la en peker peke til et element i tabellen:

int *heltallspeker = &tabell[4];

eller vi kan la pekeren peke til (begynnelsen av) tabellen: int *heltallsPeker= tabell;

som er det samme som

int *heltallsPeker= &tabell[0];

Når heltallsPeker peker til begynnelsen av tabell, kan heltallsPeker og tabell brukes om hverandre. Vi kan skrive

tabell[2] = 5; heltallsPeker[3] = 8; det samme som tabell[3] = 8 *heltallsPeker = 0; det samme som heltallsPeker [0] = 0 og som tabell[0] = 0 *tabell = tabell[2]; det samme som tabell[0] = tabell[2]

Bruk den skrivemåten du syns er mest naturlig ut fra sammenhengen.

1.6. const-pekere Du er vant med å bruke const for å navngi en konstant, eksempel:

const maks = 100;

Vi vil trenge å bruke const i forbindelsen med pekere. Det er med på å gjøre programmene våre sikrere.

Eksempel: Du er kjent med strlen()-funksjonen for å finne lengden av en streng. Funksjonsprototypen ser slik ut:

size_t strlen(const char *tekst);

1. Introduksjon. Pekere og referanser. side 10 av 24

Opphavsrett: Forfatterne og Stiftelsen TISIP

Når vi angir en peker på denne måten, betyr det at det som pekeren peker til, er konstant. Vi sier at det er en konstantpeker. Kompilatoren vil da passe på at vi ikke prøver å forandre det. Skriver vi tekst[0] = 'A' inni funksjonen, vil vi få kompileringsfeil.

Merk at pekeren ikke er konstant, det er bare det den peker til, som er det. Dermed er det lov å skrive tekst = &tekst[1];

Vi kan ikke la en ikke-konstantpeker peke til det samme som en konstant-peker, for da kunne vi likevel ha forandret på det som konstantpekeren peker til. Det er altså ikke lov å skrive char *pekeren = tekst når tekst er definert som en konstantpeker, mens const char *pekeren = tekst går bra.

Den motsatte veien går også bra. Det kan aldri skje noe galt om vi sier at en ikke-konstant skal betraktes som en konstant, dvs. vi kan la en konstantpeker peke til en variabel. Derfor kan vi godt la en variabel være aktuelt argument når det formelle er en konstantpeker.

char enTekst[81]; cin >> enTekst; cout << "Lengden på teksten er " << strlen(enTekst);

For å angi at pekeren er en konstant, skriver vi

int *const pekeren = &tall;

Leses: Konstanten pekeren er en peker til int. Som andre konstanter, kan en pekerkonstant bare initieres, ikke tilordnes verdi. pekeren = &tall2; er ulovlig. Men det den peker til, kan godt forandres:

*pekeren = 5;

gjør at tall får verdien 5.

Vi kan også lage en peker som både selv er en konstant, og det den peker til er en konstant:

const int *const pekeren = &tall;

Konstanten pekeren er en peker til en konstant heltalls-datacelle.

1.7. Aritmetikk på, og sammenlikning av pekere Å utføre aritmetiske operasjoner på en adresse har bare begrenset interesse. Hva skulle f.eks. resultatet bety hvis vi multipliserer to adresser med hverandre?

Følgende aritmetiske operasjoner er definert:

• Å addere et heltall til, eller subtrahere et heltall fra, en peker.

• Å subtrahere en peker fra en annen.

Dessuten har vi følgende sammenlikningsoperasjoner:

• Å finne ut om to pekere er like eller ulike

• Å finne ut hvilken av to pekere som er størst eller minst

1.7.1. Å addere et heltall til en peker Når vi adderer 1 til en peker, vil den peke til neste datacelle.

1. Introduksjon. Pekere og referanser. side 11 av 24

Opphavsrett: Forfatterne og Stiftelsen TISIP

Eksempel:

double tabell[5]; double *pekeren; pekeren = tabell; pekeren peker til tabell[0] *pekeren = 0.0; tabell[0] får verdien 0.0 *(pekeren + 1) = *pekeren; tabell[1] får samme verdi som tabell[0] pekeren++; pekeren peker til tabell[1]

Merk Når vi adderer et heltall til en peker flytter vi oss så mange dataceller som tallet forteller. Addisjonen regnes i samme enhet som den datatypen pekeren peker til, uavhengig av hvor mange byte denne datatypen bruker.

Uttrykket *(pekeren + N) betyr nøyaktig det samme som uttrykket pekeren[N]

Figur 2: Pekeraddisjon

Figur 2 viser at når pekeren peker til tabell[0], vil pekeren + 4 peke til variabelen fire plasser videre i primærlageret, dvs. tabell[4].

Følgende lille program viser hvordan pekeraddisjon virker:

// // pekeraddisjon.cpp // #include <iostream > using namespace std; int main() { const int m = 5; int heltall[m]; double flyttall[m]; cout << "heltallsadresser:\n"; // Adressene skrives ut på heks.form hvis casting ikke benyttes. for (int i = 0; i < m; i++) cout << (int)(heltall + i) << endl; cout << "\nflyttallsadresser:\n"; for (i = 0; i < m; i++) cout << (int)(flyttall + i) << endl; return 0; } Kjøring av programmet: heltallsadresser:

tabell[0]tabell[1]tabell[2]tabell[3]tabell[4]

pekeren

pekeren + 4

1. Introduksjon. Pekere og referanser. side 12 av 24

Opphavsrett: Forfatterne og Stiftelsen TISIP

1245032 Vi ser at adressene endrer seg med 4 for 1245036 hver gang i øker med 1. En datacelle av 1245040 typen int legger beslag på 4 byte. 1245044 1245048 flyttallsadresser: 1244992 En datacelle av typen double legger beslag 1245000 på 8 byte. Dermed øker adressen med 8 hver 1245008 gang i øker med 1. 1245016 1245024

Pekeraddisjon kan utnyttes til å gå gjennom en hel tabell:

const int lengde = 10; int tabell[lengde]; int *pekeren = tabell; for (int teller = 0; teller < lengde; teller++) { *pekeren = 0; pekeren++; }

Her starter vi med å la pekeren peke til element 0. Inni løkka får den variabelen som pekeren peker til, verdien 0, deretter blir pekeren sin verdi økt slik at den peker til neste element.

Uttrykket *pekeren + 1 tolkes som (*pekeren) + 1 fordi + har lavere prioritet enn *. Ønsker vi verdien av det som pekeren + 1 peker til, må vi skrive *(pekeren + 1). En oversikt over prioritetene til operatorene finner du i vedlegg A i læreboka (side 1228).

1.7.2. Å subtrahere to pekere Vi kan finne antall elementer mellom to posisjoner, ved å ta differansen mellom adressene til dem.

const char *tekst = "Et eksempel"; const char *start = tekst; while (*tekst != '\0') tekst++; int lengde = tekst - start; cout << "Tekstlengden er lik " << lengde << endl;

Eksemplet finner lengden til teksten ”Et eksempel”. tekst og start er begge pekere. Differansen mellom dem forteller hvor langt det er mellom adressene de peker til. Som ved addisjon, regnes differansen i samme enhet som det pekerne peker til. Hvis tekst - start har verdien 2, er det to tegn mellom det som start peker til og det som tekst peker til. Utskriften fra eksemplet blir:

Tekstlengden er lik 11

1.7.3. Sammenlikning To pekere av samme type kan sammenliknes med hensyn på likhet og ulikhet:

if (peker1 == peker2) ....

tester om peker1 og peker2 inneholder samme adresse, dvs. peker til den samme variabelen.

if (*peker1 == *peker2) tester om det de peker til, er likt.

1. Introduksjon. Pekere og referanser. side 13 av 24

Opphavsrett: Forfatterne og Stiftelsen TISIP

Alle pekere kan sammenliknes med 0.

Vi kan også teste om en peker er større eller mindre enn en annen peker. Da vil vi finne ut hvordan det de peker til er plassert i forhold til hverandre i primærlageret. Vanligvis vil dette bare være aktuelt når vi ønsker å teste om et tabellelement er foran eller bak et annet.

pekeren = tabell; slutt = &tabell[aktuellLengde]; // adressen rett etter tabellslutt while (pekeren < slutt) { ... pekeren++; }

Her lar vi pekeren slutt peke til slutten av tabellen. while-løkka går gjennom alle tabellelementene.

1.8. Pekere må behandles med forsiktighet! Når du tar i bruk pekere, vil du fort oppdage at det kan skje mye rart hvis du ikke passer nøye på.

Advarsel Sørg alltid for at en peker peker til noe fornuftig før du bruker den!

Som for enhver annen variabel, vil det alltid stå en eller annen verdi i pekeren når den opprettes. Siden denne verdien tolkes som en adresse, kan den lede hvor som helst i primærlageret.

Det kan ha store konsekvenser å forandre på verdien i denne adressen. Du kan ha havnet i det området av primærlageret som styrer programutføringen. Da kan programmet gjøre merkelige ting "av seg selv", eller maskinen kan gå helt i stå. I heldigste fall kan du få en feilmelding. Det vil avhenge av hvilken kompilator du bruker.

Eller du kan ha havnet i dataområdet for programmet ditt, slik at du forandrer verdi på en variabel uten å være klar over det.

I en del tilfeller kan du være "heldig", slik at det ikke skjer noen synlig skade. Men da kan problemet dukke opp på et seinere stadium, f.eks. når du skal forandre litt på programmet, eller når du skal installere det hos oppdragsgiver ...

For å unngå å ødelegge noe på denne måten, er det en god regel å la alle pekere initieres til 0 hvis de ikke straks får en annen verdi. Adresse 0 brukes ikke til noe fornuftig, slik at du ikke kan ødelegge noe om du legger en verdi her. Men gjør du det, vil du kanskje få en feilmelding som lyder "Null pointer assignment" ved slutten av programutføringen. Og da er det helt sikkert en feil i programmet.

0 kan ikke betraktes som et vanlig tall i denne forbindelsen, andre tall enn 0 gir kompileringsfeil.

Advarsel Ikke tro at du kan få en tabell ved å definere en peker!

1. Introduksjon. Pekere og referanser. side 14 av 24

Opphavsrett: Forfatterne og Stiftelsen TISIP

En tabell krever mange lagerplasser. Kompilatoren må ha beskjed om hvor mange lagerplasser som skal brukes. Hvis du derimot bare definerer en peker, setter kompilatoren bare av plass til pekeren.

Eksempel: Skriver du

char *linje; strcpy(linje, "Dette er farlig!!");

vil det settes av plass til pekeren linje. Teksten vil bli lagret på den plassen linje peker til, som kan være hvor som helst siden linje ikke har fått noen verdi. Derfor kan den ødelegge andre ting, og selv bli ødelagt seinere i programmet.

Skriver du derimot

char *linje = "Dette går ganske bra!";

eller helst

const char *linje = "Dette går helt bra!";

vet kompilatoren hvor mye plass den skal reservere, slik at tekststrengen blir lagret på et trygt sted, og linje satt til å peke til den. Men det blir reservert bare akkurat nok plass, så du kan få problemer hvis du forlenger teksten seinere i programmet, derfor er det lurt å la den være const.

1.8.1. Oppgaver, forts.

Oppgave 3

Hva vil skje hvis du skriver

char *linje = 0; strcpy(linje, "Dette er en tekst");

Oppgave 4

Finn ting som kan gå galt i følgende programbit.

char tekst[5]; char *pekeren = tekst; char letEtter = 'e'; cin >> tekst; while (*pekeren != letEtter) { *pekeren = letEtter; pekeren++; }

1.9. Referanser En referanse er et ekstra navn på en allerede eksisterende variabel.

int tallet; int &ref = tallet; ref = 6; tallet++;

1. Introduksjon. Pekere og referanser. side 15 av 24

Opphavsrett: Forfatterne og Stiftelsen TISIP

&ref betyr at ref er en referanse. int &ref kan leses "ref er en referanse til en int". Ved å skrive int &ref = tallet angir vi at ref skal være et annet navn på tallet. Da kan vi etterpå bruke både ref og tallet som betegnelse på den samme lagerplassen, slik at begge ender med å ha verdien 7 i eksemplet.

Når &-tegnet står på venstre side av = i en erklæring, får vi en referanse (eksempel: int &ref = tallet;). En referanse kan ikke erklæres uten at vi samtidig forteller hvilken variabel den refererer til.

1.9.1. Oppgaver, forts.

Oppgave 5

Finn alle syntaksfeil i følgende programbit:

int a = 5; int &b; int *c; c = &b; *a = *b + *c; &b = 2;

Oppgave 6 Vi har definert variabelen double tall

a) Definer en peker som skal peke til tall. b) Definer en referanse som skal referere til tall. c) Vis tre måter å få tilordnet verdi til tall på.

1.10. Argumentoverføring Vi har to typer argumentoverføring i C++:

- Verdioverføring: Funksjonen jobber med en kopi av det aktuelle argumentet. Denne metoden brukes ved overføring av enkle variabler, dersom disse variablene er inn-argumenter. Metoden brukes også ved overføring av tabeller uansett om disse er inn-, inn-/ut- eller ut-argumenter. Da kalles det ofte adresseoverføring.

- Referanseoverføring: Funksjonen får en referanse til den aktuelle variabelen. Det vil si at funksjonen jobber med den samme variabelen som den kallende funksjonen, eventuelt under et annet navn. Denne metoden brukes for enkle variabler som er kombinerte inn-/ut-argumenter eller bare ut-argumenter.

(Referanseoverføring eksisterer ikke i C. Der bruker man adresseoverføring også for enkle variabler dersom de skal være ut-argumenter.)

7tallet ref

Figur 3: En referanse

1. Introduksjon. Pekere og referanser. side 16 av 24

Opphavsrett: Forfatterne og Stiftelsen TISIP

Vi skal se nærmere på argumentoverføring i lys av det nylig gjennomgåtte stoffet om pekere og referanser.

1.10.1. Verdioverføring

Eksempel 1

const int maksLengde = 10; int main() { int enTabell[maksLengde]; nullstill(maksLengde, enTabell); ... } void nullstill( int antall, // Inn int *tabell) { // Ut for (int teller = 0; teller < antall ; teller++) { tabell[Teller] = 0; } }

Inni funksjonen nullstill() er tabell erklært som en peker, men brukes som en tabell.

I main() er enTabell en adresse, og den brukes som aktuelt argument der funksjonen krever en peker. Bak kulissene skjer følgende ved kallet:

int antall = maksLengde; int *tabell = enTabell;

Det vil si at formelt argument settes lik aktuelt argument. I begge tilfellene har vi verdioverføring, det er verdiene til de aktuelle argumentene som sendes over. Vi ser at adresseoverføring er et spesialtilfelle av verdioverføring. De formelle argumentene vil fungere som lokale variabler inni funksjonen. Og her er tabell en peker. På grunn av at *(tabell + teller) er det samme som tabell[teller] kan vi inni funksjonen bruke []-notasjonen. Se figur 4.

Siden tabell er erklært som en peker, vil det bare settes av plass til denne pekeren inni funksjonen. Tar vi sizeof(tabell) inni funksjonen, får vi beskjed om hvor mye plass en peker til int tar, i vår kompilator 4 byte. Hvis vi derimot tar sizeof(enTabell) i main(), får vi vite hvor mye plass hele tabellen tar, i vårt eksempel 10 * 4 byte. Prøv det gjerne selv!

Det at en tabell og en peker kan brukes om hverandre, kan vi utnytte til å "lure" funksjonen til å tro at tabellen starter et annet sted enn den i virkeligheten gjør.

int *pekeren = &enTabell[3]; nullstill(5, pekeren);

gjør at enTabell[3], enTabell[4],...,enTabell[7] blir nullstilt, mens de andre elementene blir stående urørt. Siden funksjonen får overført adressen til enTabell[3], vil den tolke det som om tabellen starter der. tabell[0] vil derfor være enTabell[3] og tabell[1] vil være enTabell[4], osv.

Vi trenger ikke å gå veien om pekeren. Vi kan også skrive

nullstill(5, &enTabell[3]);

1. Introduksjon. Pekere og referanser. side 17 av 24

Opphavsrett: Forfatterne og Stiftelsen TISIP

Figur 4: Verdioverføring

Eksempel 2

Her er et eksempel på hvordan vi kan kopiere en tekststreng:

void kopier(const char *fra, char *til) { while (*fra != '\0') { *til = *fra; fra++; til++; } *til = '\0'; }

Vi utnytter at adresseoverføring er verdioverføring. Adressene fra og til er verdioverført. Inni funksjonen kan de godt endres uten at det har noen innvirkning på de aktuelle argumentene. Bare når vi endrer det som pekerne peker til, får det varig virkning.

Her ser vi også at vi bruker const foran argumentet fra. Det betyr at funksjonen ikke kan forandre på det denne pekeren peker til. Det er i samsvar med at dette argumentet er et inn-argument. Den som bruker funksjonen kan føle seg trygg på at tekststrengen fra ikke blir forandret.

1.10.2. Referanseoverføring Vi bruker referanser ved ut-argumenter i funksjoner:

// // byttVerdi.cpp // #include <iostream> using namespace std; void byttVerdi(int &tallA, int &tallB) { int hjelp= tallA; tallA = tallB; tallB = hjelp;

? ? ? ? ? ? ? ? ? ?enTabell

10maksLengde

I hovedprogrammet:

I nullstill():

10antall tabell

int antallnullstill() = maksLengdemain();int *tabellnullstill() = enTabellmain();

1. Introduksjon. Pekere og referanser. side 18 av 24

Opphavsrett: Forfatterne og Stiftelsen TISIP

} int main() { int tallEn; int tallTo; cout << "Skriv to tall: "; cin >> tallEn >> tallTo; if (tallEn > tallTo) { byttVerdi(tallEn, tallTo); } cout << "I rekkefølge: " << tallEn << " " << tallTo; cout << endl; return 0; }

Det som skjer idet funksjonen kalles, er at de formelle argumentene initieres til de aktuelle argumentene. Det fungerer som om vi hadde skrevet setningene

int &tallA = tallEn; int &tallB = tallTo;

tallA og tallB blir referanser, og det de refererer til, er tallEn og tallTo. Så når tallA får verdien tallB inne i funksjonen, så vil også tallEn få denne verdien, siden det er den samme variabelen. (I motsetning til ved verdioverføring, da ville vi fått setningen int tallA = tallEn, slik at det ble to forskjellige variabler.)

1.11. Løsninger til oppgavene foran

Oppgave 1

Svar:

I 3 J 5 P

100 102 104

100 Q

106

102

adresser:

På figuren er adressene til datacellene skrevet på (dette er selvfølgelig eksempler på adresser). Pekeren p er vist både med datainnhold (som er adressen til i) og med en pil til i. På samme måte for q.

Programmet som viser dette ser slik ut:

#include <iostream> using namespace std; int main() { int i = 3; int j = 5; int *p = &i; int *q = &j; cout << "Variablene: " << endl; cout << "i: " << i << endl; cout << "j: " << i << endl; cout << "p: " << (int) p << endl; cout << "q: " << (int) q << endl << endl; cout << "Adressene: " << endl;

1. Introduksjon. Pekere og referanser. side 19 av 24

Opphavsrett: Forfatterne og Stiftelsen TISIP

cout << "&i: " << (int) &i << endl; cout << "&j: " << (int) &j << endl; cout << "&p: " << (int) &p << endl; cout << "&q: " << (int) &q << endl; return 0; } /* Utskrift: Variablene: i: 3 j: 3 p: 1245052 q: 1245048 Adressene: &i: 1245052 &j: 1245048 &p: 1245044 &q: 1245040 */

Her ser vi at p og q inneholder adressene til i og j.

Oppgave 2

Kodelinjene nummereres a), b), c), osv.

J

J

J

JI 7 5 P Q

a) Det P peker til skal settes lik 7. Det vil si at I får verdien 7.

I 7 9 P Q

b) Det Q peker til skal økes med 4. Det vil si at J økes med 4 til 9.

I 7 8 P Q

d) P skal settes lik Q. Men innholdet i P og Q er adresser. Det betyr altså at P skal settes til å peke på det samme som Q.

I 7 8 P Q

c) Det Q peker til skal settes lik det P peker til + 1. P peker til I, som har verdien 7. Q peker til J, og J får dermed verdien 7+1=8.

e) Tallet 8 blir skrevet ut to ganger.

1. Introduksjon. Pekere og referanser. side 20 av 24

Opphavsrett: Forfatterne og Stiftelsen TISIP

Oppgave 3

Du vil få kjørefeil "Null pointer assignment". Du prøver å lagre teksten i adresse null.

Oppgave 4

...

cin >> tekst; Det innleste ordet kan være for langt while (*pekeren != letEtter) { Ikke sikkert det finnes i det hele tatt ...

I begge tilfeller forsetter maskinen videre i primærlageret, slik at andre data blir ødelagt. Uforutsigbare ting kan skje hvis vi prøver å kjøre programmet.

Oppgave 5

int a = 5; int &b = a; Må initieres int *c; c = &b; a = b + *c; a og b er ikke pekere b = 2; Adressen til en variabel kan ikke forandres

Oppgave 6

a) double *peker = &tall;

b) double &ref = tall;

c) tall = 3; ref = 3; *peker = 3;

1.12. Enkel filbehandling Repetisjon.

Følgende program leser tall fra en datafil. Programmet leser fram til filslutt. Studer kommentarene.

//-------------------------------------------------------------------- // // tallfil.cpp // // Programmet leser tall fra fil og skriver summen av tallene til skjermen. // #include <fstream> #include <iostream> #include <cstdlib> using namespace std; const char filnavn[] = "tallfil.dat"; int main() {

1. Introduksjon. Pekere og referanser. side 21 av 24

Opphavsrett: Forfatterne og Stiftelsen TISIP

ifstream innfil; // definerer filvariabel innfil.open(filnavn); // åpner filen if (!innfil) { // innfil kan brukes som et logisk uttrykk cout << "Feil ved åpning av innfil." << endl; exit(EXIT_FAILURE); // uthopp fra programmet } int tall; int sum = 0; innfil >> tall; // leser et "ord" fra filen while (!innfil.eof()) { // leser fram til filslutt sum += tall; innfil >> tall; } cout << "Summen er " << sum << endl; innfil.close(); return 0; }

Neste program viser utskrift til fil. Formateringen setw(4) betyr at tallet etterpå skrives ut høyrejustert over fire kolonner.

//------------------------------------------------------------------- // // toerpot.cpp // // Programmet skriver toerpotenser til fil. // Alle toerpotenser mindre enn halvparten av // maksimalverdien til "long int" skrives ut. // #include <climits> #include <cstdlib> #include <fstream> #include <iomanip> #include <iostream> using namespace std; const char filnavn[] = "toerpot.dat"; int main() { ofstream utfil; utfil.open(filnavn); if (!utfil) { cout << "Feil ved åpning av utfil." << endl; exit(EXIT_FAILURE); } long int toerPotens = 1L; int potens = 0; while (toerPotens <= LONG_MAX / 2L) { potens++; toerPotens *= 2L; utfil << "2 opphøyd i " << setw(4) << potens << " er " << toerPotens << endl; } utfil.close(); return 0; }

1. Introduksjon. Pekere og referanser. side 22 av 24

Opphavsrett: Forfatterne og Stiftelsen TISIP

Vi ser at vi leser og skriver mot fil akkurat på samme måte som vi leser og skriver mot tastatur og skjerm.

1.13. Å bruke << og getline() om hverandre Repetisjon.

Vi bruker << til å lese inn ett ord (fra tastatur eller fra datafile). Ordet tolkes som tall eller tekst i henhold til datatypen til variablene vi leser inn i.

Vi bruker funksjonen getline() for å lese inn en linje. Eksempel:

char Tekst[100]; cout << ”Skriv en tekst: ”; cin.getline(Tekst, 100); // maks 100 tegn leses inn, inkl. ’\0’

Inndataene vil gjerne være en blanding av tall og tekst. Tallene, ofte flere pr. linje, ønsker vi å lese inn med cin >>, mens vi vil lese inn teksten linjevis med cin.getline( ).

>> hopper over ordskiller, inkludert linjeskift. Funksjonen getline( ) søker etter linjeskift og avslutter innlesingen når et slikt påtreffes. Linjeskiftet leses altså ikke inn.

Øverst på figur 5 er det vist hva som skjer hvis programmet etter å ha lest inn et tall (Alder) med cin >>, leser inn en tekst (Stilling) med cin.getline( ). Pilene viser hvor langt i inn-bufferet programkontrollen er kommet etter at de to setningene er utført. Stilling blir tom.

5 ‘\n’ ‘F’ ‘r’ ‘i’ ‘r’Inn-buffer

Etter cin >> Alder;

Etter cin.getline(Stilling, Linjelengde);

‘s’ ‘ø’ ‘\n’

Uten bruk av ignore( ) :

5 ‘\n’ ‘F’ ‘r’ ‘i’ ‘r’Inn-buffer

Etter cin >> Alder;

Etter cin.getline(Stilling, Linjelengde);

‘s’ ‘ø’ ‘\n’

Med ignore( ) :

Etter cin.ignore( Linjelengde, ‘\n’);

1. Introduksjon. Pekere og referanser. side 23 av 24

Opphavsrett: Forfatterne og Stiftelsen TISIP

Figur 5: Effekten av ignore()-funksjonen

Funksjonen ignore( ) gjør det mulig å hoppe over tegn i inn-bufferet. Funksjonskallet

cin.ignore(10, '#');

betyr at vi vil at programmet skal hoppe over tegn i inn-bufferet fram til og med tegnet '#', men ikke mer enn 10 tegn.

Første argument angir maksimalt antall tegn som skal hoppes over. Andre argument er stopptegnet. Datatypen er int, men det går bra å sende inn char her. Prototypen til ignore( ) ligger på <iostream>.

I eksemplet på figuren vil vi at programmet skal hoppe over tegn fram til og med linjeskift.

Funksjonskallet

cin.ignore(Linjelengde, '\n');

medfører at programmet hopper over resten av linja (maksimalt Linjelengde tegn). Alt som står fram til og med linjeskift hoppes over (kanskje er det ingenting, kanskje noen blanke, kanskje tall og tekst).

Programmet nedenfor viser eksemplet fra figuren satt inn i en sammenheng.

//------------------------------------------------------------------- // // person.cpp // // Prøver getline() og ignore() // #include <iostream> using namespace std; const int Linjelengde = 81; int main() { char Navn[Linjelengde]; char Adr[Linjelengde]; int Alder; char Stilling[Linjelengde]; cout << "Navn: "; cin.getline(Navn, Linjelengde); cout << "Adresse: "; cin.getline(Adr, Linjelengde); cout << "Alder: "; cin >> Alder; cin.ignore(Linjelengde, '\n'); cout << "Stilling: "; cin.getline(Stilling, Linjelengde); cout << Navn << endl << Adr << endl << Alder << endl << Stilling << endl; return 0; } /* Kjøring av programmet: Uten ignore() Navn: Kari Ås Adresse: Storgt 17, 7000 Trondheim

1. Introduksjon. Pekere og referanser. side 24 av 24

Opphavsrett: Forfatterne og Stiftelsen TISIP

Alder: 26 Stilling: Kari Ås Storgt 17, 7000 Trondheim 26 Med ignore() Navn: Kari Ås Adresse: Storgt 17, 7000 Trondheim Alder: 25 Stilling: Frisør Kari Ås Storgt 17, 7000 Trondheim 25 Frisør */

Programmet er kjørt med og uten ignore( )-setningen. Det første eksemplet viser kjøring uten ignore( ). Etter ledeteksten Stilling: begynner utskriften. Setningen

cin.getline(Stilling, Linjelengde);

fører bare til at tegnet '\n' i inn-bufferet hoppes over. (Se øverst på figuren). Stilling blir en tom tekststreng, noe vi ser av at kjøreutskriften ikke viser noe etter at alderen (26) er skrevet ut.2

2 Vi må bruke ignore( ) foran getline( ) hvis >> er brukt. Bruker vi ignore( ) en gang for mye risikerer vi å miste ei hel linje med data. Løsningen på dette er å kontrollere hvilket tegn som er det neste og så hoppe over det bare hvis det er en blank. Løkka while (isspace(cin.peek())) cin.ignore(); sørger for at blanke (og ikke andre tegn) hoppes over.