Gå til innhold

Oppgaver til kapittel 1

Det er to slags oppgaver på denne siden.

Oppvarmingene kommer først: korte programmer du skal lese, der du forutsier hva de skriver ut og velger et svar i nettleseren. Ikke noe prosjekt, ingen skriving, omtrent ett minutt hver. Hver eneste av dem er bygd rundt en feil kapittel 1 advarer spesifikt mot — langt billigere å møte her enn klokka ett om natta i din egen kode.

Så kommer programmene, fra oppgave 1 og utover: ting du skal skrive selv. Prøv hver enkelt selv før du avslører løsningen — du lærer langt mer av et ærlig forsøk, og feilene underveis, enn av å lese et ferdig program. Skriv koden inn i CLion og kjør den; ikke bare les den.

Når du åpner en løsning vises den uskarp — klikk én gang til for å avsløre den, slik at du ikke ser svaret ved et uhell.


Oppvarming: forutsi utskriften

Finn ut hva hvert program skriver ut før du velger et alternativ. Å svare låser spørsmålet og avslører forklaringen, så en gjetning koster deg oppgaven. Er du usikker, spor programmet for hånd, linje for linje, slik en debugger ville gjort det — den vanen er hele poenget.

W1. Del og skriv ut

#include <iostream>

int main() {
    int a = 7;
    int b = 8;
    int c = 10;

    double average = (a + b + c) / 3;

    std::cout << average << "\n";
}

Hva skriver dette ut?

  • 8.33333
  • 8
  • 8.3
  • Det kompilerer ikke — du kan ikke legge et int-resultat i en double

8. double-en på venstre side endrer ingenting ved divisjonen på høyre side.

a + b + c er 25, en int. 3 er også en int. Dermed er 25 / 3 heltallsdivisjon: den regner ut 8 og kaster resten, akkurat der, før noe som helst blir tilordnet. Først da blir denne 8-en konvertert til double og lagret i average.

Løsningen er å gjøre den ene siden til en double, slik at selve divisjonen beholder brøkdelen: (a + b + c) / 3.0. Se Operatorer og uttrykk.

W2. En funksjon som ikke endrer noe

#include <iostream>

void addTen(int value) {
    value += 10;
}

int main() {
    int count = 5;
    addTen(count);
    std::cout << count << "\n";
}

Hva skriver dette ut?

  • 15
  • 5
  • 10
  • Ingenting — addTen returnerer void, så programmet har ingen utskrift

5. count ble aldri rørt.

En parameter er en kopi av argumentet. addTen fikk sin egen int med verdien 5, la 10 til den, og kastet den da funksjonen returnerte. count i main er en annen variabel og ble aldri endret.

Dette er den enkeltoverraskelsen nybegynnere oftest møter, og derfor bruker Funksjoner et helt avsnitt på den. For å la en funksjon endre kallerens variabel sender du en referanse — verktøyet for det kommer i Verdier, referanser og pekere. Inntil da: skal en funksjon gi noe tilbake, return-er du det.

W3. To ord inn, ett ord ut

Brukeren skriver Ada Lovelace og trykker Enter.

#include <iostream>
#include <string>

int main() {
    std::string name;

    std::cout << "Name: ";
    std::cin >> name;

    std::cout << "Hello, " << name << "!\n";
}

Hva skriver dette ut etter ledeteksten?

  • Hello, Ada Lovelace!
  • Hello, Ada!
  • Hello, !
  • Det venter for alltid på mer inndata

Hello, Ada!Lovelace blir liggende igjen.

>> leser ett ord adskilt av blanktegn. Den stopper ved mellomrommet etter Ada, så name inneholder bare Ada; resten av linja blir liggende ulest i inndatabufferet.

For å lese en hel linje, mellomrom og alt, bruker du std::getline(std::cin, name) i stedet. Se Strenger og vektorer — og merk advarselen der om hva som skjer når du blander de to på samme strøm.

W4. En switch uten break

#include <iostream>

int main() {
    int gear = 2;

    switch (gear) {
        case 1: std::cout << "First\n";
        case 2: std::cout << "Second\n";
        case 3: std::cout << "Third\n";
        default: std::cout << "Unknown\n";
    }
}

Hva skriver dette ut?

  • Second
  • Second, så Unknown
  • Second, så Third, så Unknown
  • Det kompilerer ikke — hver case trenger en break

Alle tre: Second, Third, Unknown.

En case-etikett er et inngangspunkt, ikke en selvstendig blokk. Kjøringen hopper til case 2: og fortsetter så rett gjennom hver etikett under den helt til den treffer en break eller den avsluttende krøllparentesen. Her finnes ingen break noe sted, så den faller helt gjennom — også inn i default.

Det kompilerer uten en eneste innvending, fordi bevisst gjennomfall av og til er nyttig. Nettopp det gjør en glemt break til et så godt gjemmested for en feil. Se Kontrollstrukturer.

W5. Riktige grener i feil rekkefølge

#include <iostream>

int main() {
    int celsius = -5;

    if (celsius < 25) {
        std::cout << "Comfortable\n";
    } else if (celsius < 15) {
        std::cout << "Cold\n";
    } else if (celsius < 0) {
        std::cout << "Freezing\n";
    } else {
        std::cout << "Hot\n";
    }
}

Minus fem grader. Hva skriver dette ut?

  • Freezing
  • Comfortable
  • Cold
  • Ingenting — ingen gren passer

Comfortable, ved −5 °C.

Bare den første grenen som passer kjører; resten hoppes over uansett hvor mye bedre de passer. -5 < 25 er sant, så kjeden stopper der og vurderer aldri Cold eller Freezing i det hele tatt. De to grenene er uoppnåelige for enhver verdi: alt som er under 15 eller 0 er også under 25.

Ingenting her er en syntaksfeil, så kompilatoren sier ikke et ord. En else if-kjede over et verdiområde må ordnes fra den ene enden til den andre — kaldest først, slik oppgave 9 nedenfor gjør det. Se Kontrollstrukturer.

W6. Dobling som ikke fester seg

#include <iostream>
#include <vector>

int main() {
    std::vector<int> readings = {1, 2, 3};

    for (int value : readings) {
        value *= 2;
    }

    for (int value : readings) {
        std::cout << value << " ";
    }
    std::cout << "\n";
}

Hva skriver dette ut?

  • 2 4 6
  • 1 2 3
  • 1 2 3 2 4 6
  • 6 12 18

1 2 3. Vektoren er urørt.

for (int value : readings) gir deg en fersk kopi av hvert element. value *= 2 dobler kopien, og kopien kastes ved slutten av den runden — samme regel om verdioverføring som i W2, i løkkeforkledning.

For å endre elementene på plass tar du en referanse, slik at value er elementet i stedet for en kopi av det:

for (int& value : readings) {
    value *= 2;
}

Dette ene &-tegnet er hele forskjellen. Se Kontrollstrukturer.

Når du har svart på et spørsmål, lim programmet inn i Compiler Explorer og kjør det — å se utskriften bekrefte (eller motsi) resonnementet ditt er det som får det til å feste seg.


Hvor du skal legge koden din

Hver oppgave er sitt eget lille program med sin egen main(), og et CLion-prosjekt kjører én main() om gangen. Du har to muligheter:

Enklest — hold ett prosjekt åpent og bytt ut innholdet i main.cpp for hver oppgave. Kjør det, og lim så inn det neste. (Du mister det forrige forsøket, noe som er greit for rask øving.)

Beholder hver oppgave (anbefalt) — gi hver oppgave sin egen fil i ett enkelt prosjekt (ex1.cpp, ex2.cpp, …) og legg til én linje per fil i CMakeLists.txt:

add_executable(ex1 ex1.cpp)
add_executable(ex2 ex2.cpp)

Velg så hvilket program som skal kjøres fra rullegardinmenyen for kjørekonfigurasjon ved siden av den grønne ▶-knappen. Du trenger ikke forstå CMakeLists.txt ennå — CMake-introduksjon forklarer det i kapittel 2; for nå, bare kopier mønsteret.


1. Presenter deg selv

Øver på: Grunnstruktur, Variabler og grunntyper

Deklarer en std::string for navnet ditt og en int for alderen din (akkurat som kapittelets int age = 25). Skriv ut én linje:

My name is Ada and I am 36 years old.
Vis løsning
#include <iostream>
#include <string>

int main() {
    std::string name = "Ada";
    int age = 36;

    std::cout << "My name is " << name << " and I am " << age << " years old.\n";
}

Hver variabel får riktig type og initialiseres idet den deklareres; << kjeder bitene sammen til én linje.


2. Gjennomsnittskarakter

Øver på: Operatorer og uttrykk

Du har tre prøvekarakterer: 7, 8 og 10. Skriv ut gjennomsnittet deres. Sørg for at det kommer ut som et desimaltall — 8.33…, ikke et avkortet 8.

Vis løsning
#include <iostream>

int main() {
    int a = 7;
    int b = 8;
    int c = 10;

    double average = (a + b + c) / 3.0;   // 3.0 er en double, så desimalene beholdes

    std::cout << "Average: " << average << "\n";
}

Del på 3 (en int) og C++ gjør heltallsdivisjon — den kaster bort brøkdelen og du får 8. Å skrive 3.0 gjør den ene siden til en double, så desimalene overlever. Det er kapittelets 10 / 3-regel i praksis.


3. Sensoravlesninger

Øver på: Strenger og vektorer, Kontrollstrukturer

Lagre fem sensoravlesninger — 42, 17, 99, 8, 56 — i en std::vector<int>. Skriv ut hvor mange det er, gjennomsnittet deres (som et desimaltall), og det største.

Hint: gå gjennom vektoren for å summere verdiene og holde styr på det største; readings.size() er antallet.

Vis løsning
#include <iostream>
#include <vector>

int main() {
    std::vector<int> readings = {42, 17, 99, 8, 56};

    int sum = 0;
    int largest = readings[0];
    for (int r : readings) {
        sum += r;
        if (r > largest) {
            largest = r;
        }
    }

    double average = static_cast<double>(sum) / readings.size();

    std::cout << "Count:   " << readings.size() << "\n";
    std::cout << "Average: " << average << "\n";
    std::cout << "Largest: " << largest << "\n";
}

En områdebasert for besøker hvert element: vi legger hvert til sum og beholder det største som er sett så langt. static_cast<double> holder divisjonen desimal (heltallsdivisjon-regelen igjen), og readings.size() gir antallet elementer.


4. Partall eller oddetall

Øver på: Kontrollstrukturer

Bruk en for-løkke til å skrive ut tallene 1 til 10, og merk hvert enkelt som even eller odd.

Hint: et tall er partall når n % 2 == 0.

Vis løsning
#include <iostream>

int main() {
    for (int i = 1; i <= 10; ++i) {
        if (i % 2 == 0) {
            std::cout << i << " even\n";
        } else {
            std::cout << i << " odd\n";
        }
    }
}

En tellerbasert for-løkke som den i kapittelet, med en if/else inni som avgjør hva som skal skrives ut.


5. Kvadrater

Øver på: Funksjoner

Skriv en funksjon int square(int n) som returnerer n * n (du så akkurat denne funksjonen i kapittelet). Bruk så en for-løkke til å skrive ut kvadratene av 1 til og med 5.

Vis løsning
#include <iostream>

int square(int n) {
    return n * n;
}

int main() {
    for (int i = 1; i <= 5; ++i) {
        std::cout << i << " squared is " << square(i) << "\n";
    }
}

En liten funksjon med én klar oppgave, kalt fra en løkke. Å definere square én gang og gjenbruke den slår å skrive i * i overalt.


6. Trafikklys

Øver på: Enumerasjoner

Definer en enum class TrafficLight med Red, Amber og Green. Skriv en funksjon som skriver ut handlingen for hver enkelt — Stop, Get ready, Go — ved hjelp av en switch, og kall den for alle tre.

Vis løsning
#include <iostream>

enum class TrafficLight {
    Red,
    Amber,
    Green
};

void act(TrafficLight light) {
    switch (light) {
        case TrafficLight::Red:   std::cout << "Stop\n";      break;
        case TrafficLight::Amber: std::cout << "Get ready\n"; break;
        case TrafficLight::Green: std::cout << "Go\n";        break;
    }
}

int main() {
    act(TrafficLight::Red);
    act(TrafficLight::Amber);
    act(TrafficLight::Green);
}

Et fast sett med navngitte verdier håndtert av en switch. Uten default advarer kompilatoren deg hvis du legger til en farge senere og glemmer den her.


7. Fortsett å spørre

Øver på: Kontrollstrukturer

Spør brukeren om et positivt tall, om og om igjen, til de faktisk gir deg et. Skriv det så ut. Bruk en do-while-løkke, så du spør minst én gang.

Hint: dette er do-while-mønsteret fra kapittelet — les inne i løkken, og gjenta så lenge verdien ennå ikke er positiv.

Kjør det — du skal se:

Enter a positive number: -4
Enter a positive number: 0
Enter a positive number: 12
Thanks — you entered 12
Vis løsning
#include <iostream>

int main() {
    int number = 0;

    do {
        std::cout << "Enter a positive number: ";
        std::cin >> number;
    } while (number <= 0);

    std::cout << "Thanks — you entered " << number << "\n";
}

En do-while kjører kroppen sin før den tester betingelsen, så ledeteksten vises alltid minst én gang. Løkken gjentar så lenge number <= 0, så den slipper deg først ut når verdien er ekte positiv — nettopp det "fortsett å spørre til det er gyldig" trenger.


8. Hils med fullt navn

Øver på: Strenger og vektorer

Spør om brukerens fulle navn (fornavn og etternavn, med mellomrommet), hils på dem, og fortell hvor mange tegn navnet har. Fordi navnet inneholder et mellomrom, trenger du std::getline, ikke std::cin >>.

Hint: std::getline(std::cin, name) leser hele linjen; name.length() teller tegnene.

Kjør det — du skal se:

Enter your full name: Ada Lovelace
Hello, Ada Lovelace
Your name has 12 characters.
Vis løsning
#include <iostream>
#include <string>

int main() {
    std::string name;

    std::cout << "Enter your full name: ";
    std::getline(std::cin, name);

    std::cout << "Hello, " << name << "\n";
    std::cout << "Your name has " << name.length() << " characters.\n";
}

std::getline leser hele linjen, mellomrom inkludert, så Ada Lovelace kommer inn helt — std::cin >> name ville stoppet ved mellomrommet og beholdt bare Ada. Tallet 12 inkluderer mellomrommet, fordi det er ett av tegnene i strengen.


9. Temperaturklassifisering

Øver på: Kontrollstrukturer

Les en temperatur (et helt antall grader Celsius) og skriv ut en beskrivelse ved hjelp av en if / else if / else-kjede: under 0 er Freezing, 0 til 14 er Cold, 15 til 24 er Comfortable, og 25 eller over er Hot.

Hint: test det kaldeste tilfellet først og arbeid oppover, så hver else if bare trenger å sjekke sin øvre grense.

Kjør det — du skal se:

Enter the temperature in Celsius: 18
Comfortable
Vis løsning
#include <iostream>

int main() {
    int celsius = 0;

    std::cout << "Enter the temperature in Celsius: ";
    std::cin >> celsius;

    if (celsius < 0) {
        std::cout << "Freezing\n";
    } else if (celsius < 15) {
        std::cout << "Cold\n";
    } else if (celsius < 25) {
        std::cout << "Comfortable\n";
    } else {
        std::cout << "Hot\n";
    }
}

Bare den første grenen som passer kjører, så det å ordne testene fra kaldest og oppover lar hver else if anta at alt under den allerede er utelukket: når celsius < 15 sjekkes, vet vi at den ikke er under 0, så den grenen betyr 0 til 14. Den siste else fanger alt som er igjen — 25 og over.