NIO-logo

Norsk informatikkolympiade

Finalemaskiner

På internasjonale konkurranser får man ikke bruke sin egen datamaskin, men får en maskin tildelt. NIO har pleid å gjøre det samme i finalen, både for å øve til internasjonale konkurranser, og for å gjøre det rettferdig. Har du ønske om å bruke eget tastatur er det lov å ta med.

Enten du er med i en internasjonal konkurranse, eller bruker maskiner fra Universitet i Bergen, vil du kunne forvente å bruke en Linux-maskin med følgende programmer:

Resten av denne siden beskriver:

Sette opp egen maskin

Dersom du skal bruke din egen maskin, vil nok mye allerde være på plass. Du har en nettleser, og antageligvis en teksteditor du liker.

Teksteditorer

Dersom teksteditoren din har dusinvis med plugins og snippets, eller er et stort og tungt IDE, kan det være lurt å installere en smidig og enkel teksteditor til konkurranseprogrammering. Husk at du ikke kan ta med ferdigskrevet kode på finalen, med mindre den er skrevet ut på papir. Det er altså ikke lov å bruke templates du har lagt inn i teksteditoren.

Vi kan anbefale Notepad++ til Windows. Til macOS er TextMate et lignende alternativ. På Linux er Gedit et trygt valg. Et alternativ som finnes til alle platformene er Kate.

Programmeringsspråk

Det er lurt å installere programmeringsspråket sitt på en slik måte at du kan bruke terminalen for å kompilere og teste. På Windows er WSL en veldig rask måte å få en svært kraftig Linux-terminal. Her kan du installere programmer med Linux-distribusjonens pakkehåndterer, som for eksempel på Ubuntu:

# Kjør denne først
sudo apt update

# C++
sudo apt install gcc gdb valgrind

# Java
sudo apt install default-jdk

# Python3 (sannsynligvis allerede installert!)
sudo apt install python3

På macOS får du en C++-kompilator ved å åpne terminalen og skrive g++. Hvis den ikke allerede er installert vil du bli spurt om å installere.

Dokumentasjon

Dokumentasjon til de tillatte programmeringsspråkene vil ligge på konkurranseserveren, så dere trenger ikke laste ned dokumentasjon.

Bruk av terminalen

Det er veldig kjekt å kunne navigere i terminalen, slik at du finner frem til de filene du vil kompilere, teste og debugge.

Dersom du er på Windows vil denne guiden kun gjelde dersom du bruker WSL.

Praktiske kommandoer:

Merk at filbaner som begynner med /, for eksempel /bin/bash er relative til rot-mappen.
Filbaner som ikke begynner med /, for eksempel mappe/fil.txt, er realative til mappen du befinner deg i.
Mappen . er til enhver tid mappen du befinner deg i, så du kan også bruke ./mappe/fil.txt.

For å avbryte ut av et program du har startet, trykk Ctrl-C.

Kompilering og kjøring av kode

C++
For å kompilere, og deretter kjøre, bruk:

# Kompilerer
g++ -std=c++11 -Wall -g traktor.cpp -o traktor

# Kjører
./traktor

Den øverste kommandoen tar inn en fil ved navn traktor.cpp, og spytter ut et kjørbart program traktor. Det er ikke nødvendig, men ofte praktisk at filene heter det samme som oppgavene man jobber med.

Flagget -std=c++11 gjør at du får C++11-funksjonalitet, flagget -Wall skrur på “alle” advarsler, og flagget -g gjør at programfilen inneholder ekstra informasjon for debugging.

Java
I Java er det viktig at filnavnet passer til oppgaven, f.eks. traktor.java, og at den filen inneholder en klasse ved navn traktor, med main-metoden inni.

# Kompilerer
javac traktor.java

# Kjører
java traktor

Den første kommandoen lager en fil traktor.class, som den andre kommandoen kjører. Merk at den andre kommandoen ikke skal ha med .class-endelsen.

Python
Python trenger ikke kompileres, så bare skriv

python3 traktor.py

Mating av testinput

Når et program kjører må du skrive inn testinput, enten med tastaturet eller ved å kopiere og lime inn. For å slippe å bruke mye tid på dette, kan du lagre en av testinputene som en fil, f.eks. sample1.txt. Deretter kan du kjøre programmet med den filen som input:

# C++
./traktor < sample1.txt

# Java
java traktor < sample1.txt

# Python
python3 traktor.py < sample1.txt

For å slippe å skrive dette hver gang, kan du bruke pil opp for å bla i tidligere kommandoer.

Debugging av C++

Når du skriver C++ er det fort gjort at det sniker seg inn kodefeil der du overskriver eller leser fra steder i minnet som egentlig ikke tilhører den listen du tror det tilhører. Når slike ting skjer, er det to mulige utfall: Enten så krasjer programmet med én gang med en Segmentation fault, eller så skjer det tilsynelatende ingenting. En annen måte å krasje programmet sitt på er å forsøke å dele på 0.

Kompilatoradvarsler

Før vi tar i bruk noen tilleggsverktøy, er det lurt å få så mye bemerkninger fra kompilatoren som mulig. Som tidligere nevnt finnes -Wall, for å skru på advarsler. Det finnes også -Wextra, som skrur på enda flere. Da vil kompilatoren advare deg dersom du bruker variabler på rare måter.

GDB

Det første verktøyet vi har er gdb, en kraftig debugger. Vi går bare såvidt inn på alt det du kan gjøre med gdb, så gjerne les videre andre steder.

Det viktigste når du bruker gdb, er at programmet ble kompilert med -g-flagget. Da vil informasjon om koden bli lagret sammen med programmet, slik at gdb kan referere til linjenummer og variabelnavn i kildefilen.

For å debugge programmet ditt, bruk

gdb ./traktor

Dette vil åpne gdb, men ikke starte programmet. For å gjøre det skriv

run

eventuelt gi en input-fil ved å skrive

run < sample1.txt

Dette vil starte programmet, og det vil kjøre helt til det krasjer. Når det krasjer vil gdb ta over, og du kan skrive

bt

for å printe ut rekken med funksjonskall som til slutt endte opp på den linjen som krasjet.

Du kan også bruke

p <variabel>

for å printe ut verdiene til ulike variabler. Både globale og lokale variabler kan printes. Man kan også printe mer avanserte uttrykk. Hvis man for eksempel har en liste med noder kalt nodes, og hver node har en child_count:

p nodes[5].child_count

Valgrind

Som nevnt over er det ikke sikkert programmet ditt krasjer i det øyeblikket det gjør en ulovlig minneaksess. Programmet kan i stedet rulle videre, som gjør det vanskelig å oppdage hvor feilen ligger.

Har du lest inn fra feil adresse har du kanskje lest en verdi som er ulik hver gang du kjører programmet, som kan gi skikkelig hodebry. Hvis du har skrevet til en adresse du egentlig ikke er ment å røre, kan det hende at programmet krasjer på et senere tidspunkt, når det prøver å bruke det som lå der. Da vil krasjen inntreffe på et annet sted i koden enn der selve buggen ligger.

For å oppdage slike feil finnes valgrind. Det er en emulator som kjører programmet ditt og sjekker hver eneste minneaksess. Det går altså mye tregere, men vil ofte stoppe om programmet prøver å gjør noe rart. For å bruke det, skriv valgrind foran kommandoen du vanligvis bruker for å kjøre programmet. Også her er det veldig lurt å ha kompilert med -g-flagget.

Address Sanitizer & Undefined Behaviour Sanitizer

Valgrind er veldig tregt, men oppdager svært mye. Et lignende verktøy som ikke oppdager like mye, men som til gjengjeld kjører veldig mye raskere, kalless Address Sanitizer. Dersom programmet ditt prøver å gjøre noe dumt med minnet, slik som å bruke en peker til noe som ikke finnes lenger, vil programmet krasje, og fortelle deg om feilen der og da.

En nær slektning er Undefined Behaviour Sanitizer, som stopper programmet dersom det gjør noe udefinert, slik som å la en int bli så stor at verdien ikke lenger får plass i en int.

Det beste med disse to verktøyene er at du ikke trenger noen ekstra programmer for å aktivere dem. De kjører også så fort at du ofte kan inkludere kommandoene fra begynnelsen av, og glemme at de er aktivert, før de plutelig rapporterer en feil du hadde oversett.

Inkluder følgende når du kompilerer koden:

g++ -Wall -g -fsanitize=address,undefined traktor.cpp -o traktor