Komme i gang med C++, del 2: Grunnleggende programmering

Her er en (ganske) kort guide til å komme i gang med programmering. Vi forutsetter at du allerede har installert et utviklingsmiljø; hvis ikke, les artikkelen som det er linket til. Det tar tid å lære seg programmering, og det beste er å ha en skikkelig lærebok eller en god tutorial på nettet, så vi oppfordrer til å finne informasjon på egen hånd etter å ha lest denne artikkelen. Uansett er det viktig at du prøver deg frem og eksperimenterer med hva du kan få til med det du lærer – man kan få til utrolig mye gøy med programmering, og det er bare fantasien som setter grenser!

Hva er en datamaskin?

“Datamaskin” heter “computer” på engelsk, og det betyr “beregner”. En datamaskin er nettopp det – en beregningsmaskin. Alt vi ser på skjermen og hører på høyttalerne er et resultat av enormt mange matematiske utregninger. Datamaskinen eier ikke et fnugg av intelligens; den er kun i stand til å følge instruksjoner om hva den skal regne ut. Programmering går ut på å gi datamaskinen de instruksjonene som må til for at den skal gjøre det vi ønsker.

Programmeringsspråk og kildekode

Internt jobber datamaskinen bare med tall – selv instruksjonene om hva den skal gjøre har forskjellige tallkoder. Dette er veldig tungvindt for mennesker å forholde seg til, og derfor bruker man programmeringsspråk for å skrive programmer på mer lettforståelige måter, og så finnes det egne programmer, såkalte kompilatorer, som oversetter det vi har skrevet til noe datamaskinen kan forstå. Det finnes hundrevis av forskjellige programmeringsspråk; i konkurranseprogrammering er C++ det mest utbredte, og vi skal derfor bruke det. Det er dessverre ikke det enkleste språket å komme i gang med, men følger de samme prinsippene som de fleste andre språk.

All programkoden skrives i kildekodefiler, som i utgangspunktet er vanlig tekstfiler. Det vil si at de ikke inneholder formatering (farger, skrifttyper, fet, kursiv osv.) slik man kan lage i f.eks. Word. Dermed kan man lage kildekodefiler med svært enkle programmer, f.eks. Notepad. Men ofte bruker man en mer avansert teksteditor, som er et skriveprogram som vet om forskjellige programmeringsspråk og kan gi oss hjelp til å skrive kode i disse språkene – f.eks. ved å automatisk fargelegge koden på en slik måte at den blir lettere å lese. I stedet for en vanlig teksteditor går det også an å bruke en IDE (Integrated Development Environment), som er et mer avansert program som kombinerer en teksteditor med mer avanserte verktøy, f.eks. en kompilator og en debugger (et program som kan hjelpe en med å finne feil programmet man lager).

Overordnet programstruktur og output

Her er det enkleste C++-programmet som det går an å lage:

int main() {
	return 0;
}

(For å lage alle mellomrommene foran return trykker du Tab-tasten (til venstre for Q). Dette kalles for innrykk, og selv om C++ ikke bryr seg om hvorvidt det er der eller ikke, er det viktig for at mennesker skal kunne lese koden lettere.) Du kan lage et nytt prosjekt i IDE’en din, skrive inn denne koden og kjøre (starte) programmet – men da vil du merke at dette programmet ikke gjør noe som helst – det starter opp og avslutter rett etterpå, så det er jo ikke så veldig spennende. At det likevel er tre linjer der skyldes at C++ har en del regler for hvordan programmer skal struktureres (alle språk har sine egne strukturregler, som ofte fører til en del ekstra kode som ikke har så mye formål annet enn at det “må være der”) – vi skal straks se litt på disse.

Alle nyttige programmer gir output, dvs. at de viser noe på skjermen, lager en fil, spiller av lyd eller gir en eller annen form for resultat vi kan se eller høre. Det skal en del mer til for å spille av lyd, vise bilder eller å lage 3D-grafikk, men når man først har litt programmeringserfaring, er det ikke så vanskelig å lære seg hvordan man gjør det. Men siden konkurranseprogrammering stort sett handler om å lage programmer som regner ut tallsvar og tekstsvar på oppgavene, skal vi holde oss til konsollprogrammer, altså programmer som kommuniserer med brukeren via tekst som vises i et konsollvindu (også kalt terminal).

Tekstoutput gjøres ved hjelp av cout, som er en kommando for å skrive ut (printe) ting på skjermen – programmerere bruker altså disse ordene ikke bare om å sette tekst på papir, men også om å sette tekst på skjermen. Etter cout kan man gjentatte ganger skrive << fulgt av en ting man vil printe. Dette kan være tall, for eksempel 42, 3.14 eller -1234567890 (merk at man må bruke punktum som desimaltegn, siden C++ er engelsk, og at det er begrenset hvor store tall man kan skrive inn - prøv å skrive større og større tall inntil du får en feilmelding). Man kan også printe tekst; programmeringsordet for tekst er streng (string på engelsk). En streng angis med gåseøyne, f.eks. "Hello world!". Prøv å bytte ut koden du skrev i sted med dette:

#include <iostream>

using namespace std;

int main() {
	cout << "Hello world!" << endl;
	return 0;
}

Tips til de som bruker Visual Studio: For at konsollen ikke skal forsvinne med en gang programmet er ferdig, start programmet ved å trykke Ctrl-F5 på tastaturet. Eventuelt kan du legge inn linjen system("pause"); før linjen return 0;.

Et program utføres én linje av gangen, ovenfra og ned. Noen av linjene er ikke direkte instruksjoner om hva programmet skal gjøre, men er ment som opplysninger om programmet selv. Den første linjen, #include <iostream>, forteller at vi ønsker å benytte ett av de mange bibliotekene (samling med ferdigskrevet kode) som følger med C++, nemlig det som heter iostream. Dette biblioteket inneholder funksjonalitet for input og output. Hva using namespace std; egentlig betyr er ikke så viktig for oss, men det gjør at det blir litt mindre å skrive når vi skal bruke den innebygde funksjonaliteten i C++ (uten dette måtte man ha skrevet std::cout i stedet for cout). Disse to linjene vil man nesten alltid ha med. int main() { markerer starten av selve programmet vårt, og return 0; } markerer slutten - disse to linjene må alltid være der, og i første omgang skal all den interessante koden vår ligge mellom disse linjene. Merk at de fleste kodelinjene må slutte med semikolon.

Prøv å skrive ut flere ting i samme program, både innenfor samme kodelinje og ved hjelp av flere kodelinjer. Eksperimenter også med plasseringen av endl, som er det som skaper nye linjer.

Variabler og input

Et program som viser akkurat det samme hver gang er ikke spesielt spennende. For at et program skal kunne gjøre forskjellige ting hver gang det kjøres, må det lese input, dvs. at de som bruker programmet skal kunne mate opplysninger inn i programmet. Programmet kan så gjøre beregninger basert på inputen og gi svaret som output. Programmer lagrer opplysninger, for eksempel dataene som er blitt gitt som input, eller mellomresultater programmet har regnet ut, i variabler. Vi skal først se litt på hvordan variabler fungerer. En variabel oppfører seg omtrent som x og y i matematikk: den har et navn, den inneholder en verdi, og verdien kan endre seg underveis. Før man kan bruke en variabel må man deklarere den: da oppgir man datatypen til variabelen og hva den skal hete. En datatype bestemmer hva slags opplysninger variabelen kan inneholde. Det finnes en del innebygde datatyper i C++, men den vi skal bruke først heter int - det er en forkortelse for integer, som betyr heltall. En variabel av typen int kan inneholde heltall mellom -2147483648 og 2147483647 (det er en teknisk årsak til at grensene blir akkurat disse tallene, som har å gjøre med at tall lagres i det binære tallsystemet inni datamaskinen - int-variabler lagres med 32 bits, og da kan man representere 232 forskjellige tall). Slik deklarerer vi en variabel x med typen int:

int x;

Å putte en verdi i en variabel kalles for tilordning. Dette gjøres med likhetstegnet, som har en veldig annerledes betydning i programmering enn i matematikk. La oss først gjøre selve tilordningen:

x = 1;

Den første tilordningen til en variabel kalles initialisering. Det går an å deklarere og initialisere på én gang:

int x = 1;

er det samme som

int x;
x = 1;

Når du har deklarert en variabel, kan du skrive den ut:

int x = 42;
cout << x << endl;

Merk forskjellen på å skrive x, som vil gi deg verdien i den variabelen, og å skrive "x", som vil gi teksten x. Merk også at det er forskjell på store og små bokstaver - prøv å endre én av x'ene til X og se hva som skjer (deklarasjonen vil bestemme hva som er "riktig").

Etter at du har deklarert en variabel, men før du initialiserer den, er den uinitialisert. Da vil den inneholde en mer eller mindre tilfeldig verdi. Prøv å bare gjøre

int x;
cout << x << endl;

Hva en variabel heter er fullstendig likegyldig; C++ bryr seg ikke. Den eneste regelen er at de må starte med en bokstav eller en understrek, at det må være et sammenhengende ord uten mellomrom, og at det ikke må krasje med nøkkelordene i språket (reserverte ord som har spesialbetydning, blant annet int, if og for). Så programmet ovenfor hadde vært helt likt hvis det så sånn ut:

int OlaErKul = 1;
cout << OlaErKul << endl;

Variabelnavn bør velges slik at de gir mening for dem som leser programmet - navnet bør beskrive hva variabelen inneholder eller brukes til.

Man kan lage flere variabler, og bruke operatorer for å utføre de fire regneartene:

int x = 4;
int y = 3;
int z = (x + y) * 2;
cout << z - 1 << endl;

Presedensen (reglene for hvilke operatorer som skal regnes ut først) og parentesene fungerer akkurat som i vanlig matematikk - forsøk å skrive x + y * 2 i stedet og se hva svaret blir. Merk igjen at likhetstegnet ikke er en ligning - det er en utregning som gjøres der og da av høyre side av likhetstegnet, og en tilordning av resultatet til venstre side. Så hvis vi etterpå endrer x eller y, har fortsatt z samme verdi:

int x = 4;
int y = 3;
int z = x + y;
cout << z << endl;
x = 8;
y = 15;
cout << z << endl;

Dette betyr også at vi kan bruke den samme variabelen både på venstre og høyre side av likhetstegnet; det er da den gamle verdien som blir brukt for å regne ut den nye:

x = x + 1;

vil ha som effekt å øke verdien av x med 1.

Divisjonsoperatoren er litt spesiell, for den vil alltid runde ned til nærmeste heltall dersom man dividerer to heltall på hverandre. Hvis minst ett av tallene man dividerer på hverandre er desimaltall, vil resultatet bli et desimaltall:

cout << 15 / 4 << endl;
cout << 15.0 / 4 << endl;
cout << 15 / 4.0 << endl;
cout << 15.0 / 4.0 << endl;

Desimaltall kan ikke lagres i int-variabler, men de kan lagres i double-variabler ("double" står for "double-precision floating-point number", som er en gammel databetegnelse på desimaltall med ca. 16 desimaler):

double pi = 3.14;

Når vi mater faste tall inn i variablene våre, gjør programmet altså det samme hver gang vi kjører det, noe som er litt kjedelig. Vi som programmerere kan jo sitte og endre tallene som vi vil, men hvis vi skulle gitt det ferdigkompilerte programmet til noen andre, kan ikke de se koden, og de vil tro at programmet bare kan regne ut svaret på det samme regnestykket hver gang. Det er mye bedre hvis brukerne av programmet får lov til å mate inn verdiene. Dette kalles lesing av input, og gjøres slik:

cin >> x;

Denne linjen vil få programmet til å stoppe og vente på at brukeren skriver inn et tall og trykker enter. Når man skriver programmer som vanlige folk skal bruke, er det lurt å bruke cout før cin for å printe en beskjed om at brukeren skal skrive inn noe - ellers tror brukeren bare at programmet har hengt seg. Husk også at variabelen du leser inn i må være deklarert først. Hvis brukeren skriver inn noe som ikke passer med datatypen til variabelen, vil programmet gi mystiske resultater (det går an å korrigere dette, og det bør man vanligvis også gjøre - men i konkurranseprogrammering antar vi at all inputen blir skrevet inn rett).

Merk at det ikke går an lese input inn i en variabel samtidig som man deklarerer den, så cin >> int x; er ikke lov. Derimot er det lov å lese inn i flere variabler etter hverandre:

cin >> x >> y >> z;

(ingen endl til slutt). Variablene husker ikke hvor opplysningene kom fra, og du kan bruke en variabel som har mottatt input akkurat på samme måte som om du hadde initialisert variabelen i koden. Dersom variabelen inneholdt en verdi før man leser input til den, forsvinner den gamle verdien.

Matematiske funksjoner

Kvadratrøtter kan regnes ut ved hjelp av en innebygd funksjon som heter sqrt, som befinner seg i biblioteket cmath. En funksjon kalles (startes) ved å skrive navnet dens fulgt av en start- og sluttparentes. Parentesene inneholder argumentene til funksjonen, dvs. opplysningene funksjonen må ha for å gjøre jobben sin. sqrt trenger ett argument, nemlig tallet man skal finne kvadratroten av. Resultatet, eller returverdien, fra sqrt er et desimaltall, og har dermed typen double:

#include <iostream>
#include <cmath>

using namespace std;

int main() {
    double x;
    cin >> x;
    double result = sqrt(x);
	cout << result << endl;
	return 0;
}

Nå kan vi f.eks. løse annengradsligninger ved hjelp av abc-formelen. Merk at det går an å deklarere flere variabler av samme datatype på én gang, og å lese inn i flere variabler etter hverandre på samme linje. I koden nedenfor må man skrive inn tre tall: det første kommer inn i a, det andre i b og det tredje i c. Det finnes ingen operator for å opphøye - det finnes en funksjon for det som heter pow, men den enkleste måten å opphøye en variabel i to er å gange den med seg selv. Merk at i koden under deklarerer vi flere variabler på samme linje.

double a, b, c;
cout << "Enter a, b, and c: ";
cin >> a >> b >> c;
double result = (-b + sqrt(b * b - 4 * a * c)) / (2 * a);
cout << result << endl;

Hvis man skal gjøre lignende utregninger flere ganger, kan det kan ofte lønne seg å trekke ut de felles delene av utregningen i egne variabler (både slik at man slipper å gjenta seg selv, og slik at datamaskinen slipper å regne ut det samme flere ganger), sånn som her, hvor vi regner ut begge løsningene av annengradsligningen:

double a, b, c;
cin >> a >> b >> c;
double root = sqrt(b * b - 4 * a * c);
double firstResult = (-b + root) / (2 * a);
double secondResult = (-b - root) / (2 * a);
cout << firstResult << " " << secondResult << endl;

If og else

Nesten alle programmer vil ha behov for å ta avgjørelser basert på input eller verdier som er regnet ut basert på inputen, for å kunne reagere forskjellig på forskjellige situasjoner. Til dette kan vi bruke if/else. Til en if oppgir man en matematisk betingelse som programmet skal sjekke om er sann eller usann. Hvis betingelsen viser seg å være sann, vil det som står i krøllparentesene etter if bli kjørt; hvis ikke vil det som står etter else bli kjørt. Uansett hvilken av grenene som velges, fortsetter kjøringen med første linje etter avslutningskrøllparentesen til else etterpå.

int age;
cout << "How old are you? ";
cin >> age;
if (age < 11) {
	cout << "You're too young to watch The Hobbit." << endl;
}
else {
	cout << "You're old enough to watch The Hobbit." << endl;
}

< betyr "mindre enn", akkurat som i matematikk. Mindre enn eller lik er <=, større enn eller lik er >=, ulik er != og lik er ==. Det er vikitg at det er to likhetstegn når man skal sjekke om to ting er like; det går an å skrive if (a = b), men dette vil ha en annen effekt enn if (a == b), som er riktig måte å sjekke om a og b er like.

Prøv å lage følgende:
- Et program som ber brukeren om å skrive inn to tall, og så viser summen, differansen, produktet og kvotienten av de to tallene. Prøv først å lagre tallene som brukeren skriver inn i en int, og deretter i en double.
- Et program hvor brukeren kan mate inn enkle regneuttrykk som f.eks. 4 + 5, 23 * 4.2 og 19.2 / 2.4 . Til å ta vare på operatoren trenger du å bruke datatypen char, som kan inneholde ett enkelt tegn. Hvis du har en char-variabel c og vil sjekke om den inneholder et plusstegn, kan du gjøre slik: if (c == '+').
- Et program som regner ut og printer begge løsningene av en annengradsligning, men som bare printer én løsning hvis tallet under roten blir 0, og som printer "There are no solutions" hvis tallet under roten blir negativt.

I neste artikkel kommer vi til å lære om løkker, som gjør at programmet kan gjenta de samme handlingene på mange forskjellige opplysninger etter hverandre - dette kan man bruke til å lage enkle spill, eller til å regne på større samlinger med tall.