1. Introduksjon. Pekere og referanser. · 2010-11-26 · 1. Introduksjon. Pekere og referanser....

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 25.01.2011 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:

Transcript of 1. Introduksjon. Pekere og referanser. · 2010-11-26 · 1. Introduksjon. Pekere og referanser....

Page 1: 1. Introduksjon. Pekere og referanser. · 2010-11-26 · 1. Introduksjon. Pekere og referanser. side 4 av 24 Opphavsrett: Forfatterne og Stiftelsen TISIP 1.2.5. Nytt om for-setningen

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

25.01.2011

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:

Page 2: 1. Introduksjon. Pekere og referanser. · 2010-11-26 · 1. Introduksjon. Pekere og referanser. side 4 av 24 Opphavsrett: Forfatterne og Stiftelsen TISIP 1.2.5. Nytt om for-setningen

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

Opphavsrett: Forfatterne og Stiftelsen TISIP

H. M. Deitel & P. J. Deitel: C++ How to Program, 7.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 H og I (på nettet) 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å.

Page 3: 1. Introduksjon. Pekere og referanser. · 2010-11-26 · 1. Introduksjon. Pekere og referanser. side 4 av 24 Opphavsrett: Forfatterne og Stiftelsen TISIP 1.2.5. Nytt om for-setningen

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 152 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.

Det vil si, slik:

std::cout << ”Velkommen til C++!\n”; // kvalifiserer navnet der det brukes

eller slik:

using std::cout; // spesifiserer eksakt hvilke navn som

using std::endl; // skal brukes i programmet

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

using namespace std;

i begynnelsen av programmet.

Detaljer angående namespace, se kapittel 24.4 i Deitel & Deitel (på nettet, fra side XLV og

utover), eventuelt 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.

Page 4: 1. Introduksjon. Pekere og referanser. · 2010-11-26 · 1. Introduksjon. Pekere og referanser. side 4 av 24 Opphavsrett: Forfatterne og Stiftelsen TISIP 1.2.5. Nytt om for-setningen

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

Opphavsrett: Forfatterne og Stiftelsen TISIP

1.2.5. Nytt om for-setningen

Se eksempel side 202 i Deitel & Deitel, 416 i Lervik og Ljosland. Dersom du, slik som her,

definerer løkketelleren inne i for-setningen, er det for-setningen som beskriver rekkevidden

av denne variabelen. Dette er nytt i 1998-standarden av C++. Tidligere var rekkevidden

blokken rundt for-setningen. Dette var fortsatt tilfelle i for eksempel versjon 6 av Microsoft

Visual Studio C++. Nå har jeg ikke sjekket versjonene innimellom, men i Microsoft Visual

Studio 2005 er iallfall dette rettet opp.

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?

Page 5: 1. Introduksjon. Pekere og referanser. · 2010-11-26 · 1. Introduksjon. Pekere og referanser. side 4 av 24 Opphavsrett: Forfatterne og Stiftelsen TISIP 1.2.5. Nytt om for-setningen

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.19 (side 60-63) 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++.

Page 6: 1. Introduksjon. Pekere og referanser. · 2010-11-26 · 1. Introduksjon. Pekere og referanser. side 4 av 24 Opphavsrett: Forfatterne og Stiftelsen TISIP 1.2.5. Nytt om for-setningen

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.10, 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.

Page 7: 1. Introduksjon. Pekere og referanser. · 2010-11-26 · 1. Introduksjon. Pekere og referanser. side 4 av 24 Opphavsrett: Forfatterne og Stiftelsen TISIP 1.2.5. Nytt om for-setningen

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.10 dekker temaet relativt 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.14 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. (Slik er det ikke i Java.) Dette er en av grunnene til at

Deitel&Deitel har ”Portability Tips”, se f.eks. Tip 1.3 side 52 …

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. En stjerne * 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;).

Page 8: 1. Introduksjon. Pekere og referanser. · 2010-11-26 · 1. Introduksjon. Pekere og referanser. side 4 av 24 Opphavsrett: Forfatterne og Stiftelsen TISIP 1.2.5. Nytt om for-setningen

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

Page 9: 1. Introduksjon. Pekere og referanser. · 2010-11-26 · 1. Introduksjon. Pekere og referanser. side 4 av 24 Opphavsrett: Forfatterne og Stiftelsen TISIP 1.2.5. Nytt om for-setningen

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);

Page 10: 1. Introduksjon. Pekere og referanser. · 2010-11-26 · 1. Introduksjon. Pekere og referanser. side 4 av 24 Opphavsrett: Forfatterne og Stiftelsen TISIP 1.2.5. Nytt om for-setningen

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.

Page 11: 1. Introduksjon. Pekere og referanser. · 2010-11-26 · 1. Introduksjon. Pekere og referanser. side 4 av 24 Opphavsrett: Forfatterne og Stiftelsen TISIP 1.2.5. Nytt om for-setningen

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

Page 12: 1. Introduksjon. Pekere og referanser. · 2010-11-26 · 1. Introduksjon. Pekere og referanser. side 4 av 24 Opphavsrett: Forfatterne og Stiftelsen TISIP 1.2.5. Nytt om for-setningen

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 Deitel & Deitel (side 1042). I Lervik og

Ljosland finner du oversikten i vedlegg 2, side 619.

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.

Page 13: 1. Introduksjon. Pekere og referanser. · 2010-11-26 · 1. Introduksjon. Pekere og referanser. side 4 av 24 Opphavsrett: Forfatterne og Stiftelsen TISIP 1.2.5. Nytt om for-setningen

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

Opphavsrett: Forfatterne og Stiftelsen TISIP

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

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!

Page 14: 1. Introduksjon. Pekere og referanser. · 2010-11-26 · 1. Introduksjon. Pekere og referanser. side 4 av 24 Opphavsrett: Forfatterne og Stiftelsen TISIP 1.2.5. Nytt om for-setningen

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++;

Page 15: 1. Introduksjon. Pekere og referanser. · 2010-11-26 · 1. Introduksjon. Pekere og referanser. side 4 av 24 Opphavsrett: Forfatterne og Stiftelsen TISIP 1.2.5. Nytt om for-setningen

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

Page 16: 1. Introduksjon. Pekere og referanser. · 2010-11-26 · 1. Introduksjon. Pekere og referanser. side 4 av 24 Opphavsrett: Forfatterne og Stiftelsen TISIP 1.2.5. Nytt om for-setningen

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]);

Page 17: 1. Introduksjon. Pekere og referanser. · 2010-11-26 · 1. Introduksjon. Pekere og referanser. side 4 av 24 Opphavsrett: Forfatterne og Stiftelsen TISIP 1.2.5. Nytt om for-setningen

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();

Page 18: 1. Introduksjon. Pekere og referanser. · 2010-11-26 · 1. Introduksjon. Pekere og referanser. side 4 av 24 Opphavsrett: Forfatterne og Stiftelsen TISIP 1.2.5. Nytt om for-setningen

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;

Page 19: 1. Introduksjon. Pekere og referanser. · 2010-11-26 · 1. Introduksjon. Pekere og referanser. side 4 av 24 Opphavsrett: Forfatterne og Stiftelsen TISIP 1.2.5. Nytt om for-setningen

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.

Page 20: 1. Introduksjon. Pekere og referanser. · 2010-11-26 · 1. Introduksjon. Pekere og referanser. side 4 av 24 Opphavsrett: Forfatterne og Stiftelsen TISIP 1.2.5. Nytt om for-setningen

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() {

Page 21: 1. Introduksjon. Pekere og referanser. · 2010-11-26 · 1. Introduksjon. Pekere og referanser. side 4 av 24 Opphavsrett: Forfatterne og Stiftelsen TISIP 1.2.5. Nytt om for-setningen

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;

}

Page 22: 1. Introduksjon. Pekere og referanser. · 2010-11-26 · 1. Introduksjon. Pekere og referanser. side 4 av 24 Opphavsrett: Forfatterne og Stiftelsen TISIP 1.2.5. Nytt om for-setningen

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’);

Page 23: 1. Introduksjon. Pekere og referanser. · 2010-11-26 · 1. Introduksjon. Pekere og referanser. side 4 av 24 Opphavsrett: Forfatterne og Stiftelsen TISIP 1.2.5. Nytt om for-setningen

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

Page 24: 1. Introduksjon. Pekere og referanser. · 2010-11-26 · 1. Introduksjon. Pekere og referanser. side 4 av 24 Opphavsrett: Forfatterne og Stiftelsen TISIP 1.2.5. Nytt om for-setningen

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.