Programmering i C

Begynnelsen

/*******************************************************/
/* Vi ønsker å få maskinen til å skrive 'Hello world!' */
/*******************************************************/

#include <stdio.h>

int main() {
  printf("Hello world!");
}
Hello world!

Forsøk å skrive koden over inn i kompilatoren du finner på jdoodle.com (slett det som står skrevet fra før av). Skriv inn alle setningene (kommentaren er ikke nødvendig) og trykk Execute.

Merk, i det følgende kommer vi ikke til å skrive #include <stdio.h> i alle eksemplene, ei heller pakke koden inn i en main-blokk.

Koden over kommer vi da til å skrive som følger:

printf("Hello world!");

Kommentarer

Når du skal kommentere kode i C, for eksempel når du skal fortelle hva en funksjon gjør, skriver du dette mellom /* og */. Maskinen ser da bort fra dette når den skal kjøre programmet ditt.

Preprosessor

C er et språk som kommer uten batterier inkludert … i det aller minste uten batterier satt inn. På linja #include <stdio.h> forteller vi at vi ønsker å ha muligheten for å gi eller få skrevet ut data til og fra programmet vårt. Uten denne linja får vi her ikke tilgang til printf-funksjonen.

I oppgaven Løsning av andregradslikninger skal du eksempelvis bruke et annet bibliotek.

main-funksjonen

Tilnærmet ethvert C-program har en main funksjon hvor vi samler all koden som kjøres i løpet av programmet.

Konstanter og variabler

For å opprette en konstant verdi i C, altså en verdi som ikke kan endres gjennom kjøringen av programmet, bruker vi nøkkelordet const.

Ønsker vi for eksempel å lage konstanten π, kan det gjøres som følger:

const float pi = 3.14;

Her har vi disse elementene:

Symbol Type Effekt
const nøkkelord det som følger på linjen skal være en konstant
float nøkkelord konstanten skal være et desimaltall (se Tall og typer)
pi navn navnet til konstanten
= - gir konstanten verdi
3.14 verdi verdien konstanten skal ha, kan ikke endres
; - avslutter setningen
const float pi = 3.14;
pi = 3.5;

Gir følgende:

~/filename.c: In function 'main':
~filename.c: error: assignment of read-only variable 'pi'
 pi = 3.5;

Vi har altså ikke mulighet til å endre en verdi satt til å være konstant.

Tall og typer

Når vi programmerer, er det ofte viktig å skille mellom spesielt desimaltall, heltall og tekst.

Vi definerer desimaltall1 som:

float pi = 3.14;
float c = 3.00E8;

Legg merke til at vi nå ikke har satt at disse må være konstante verdier, så det er nå ikke noe i veien for seinere i programmet å gjøre følgende:

float pi = 4.0;

Vi definerer heltall som:

int n = 5;

Vi definerer tekststrenger2 som:

char navn[] = "Dagros";

Inn- og utdata

Vi er ofte interessert i å kunne få verdier programmet vårt har regnet ut skrevet på skjermen eller å kunne gi programmet nye data å regne med.

Formatering av strenger

#include <stdio.h>

int heltall = 5;
float desimaltall = 5.5;
char tekststreng[] = "Noe å skrive ut";

int main() {
  printf("Heltallet er: %d\n", heltall);
  printf("Desimaltallet er: %f\n", desimaltall);
  printf("Tekstrengen er: %s\n", tekststreng);
}

Vi har blant andre følgende former for formateringsspesifkasjoner:

Tegn Brukes til Eksempel Resultat
%d Heltall printf("Tallet er %d", 4); Tallet er 4
%f Desimaltall printf("Tallet er %f", 3.5); Tallet er 3.500000
%s Tekststreng printf("Strengen er %s", "en streng") Strengen er en streng
%e Tall på standardform printf("Lysfarten er %e", 3E8); Lysfarten er 3.000000e+008

Vi har også noen tilleggstegn:

Tegn Effekt
\n linjeskift
\" dobbelt anførselstegn uten at det avslutter tekststrengen
\' enkelt anførselstegn uten at det avslutter tekststrengen

Justering av antall desimaler

Spesielt når vi formaterer tall via %f, ønsker vi å spesifisere hvor mange desimaler tallet skal vises med. Dette kan vi gjøre ved å skrive for eksempel %3.2f. Her spesifiserer vi at vi ønsker at tallet skal være 3 siffer langt totalt, med to siffer etter komma.

printf("I oslo er tyngdeakselerasjonen på %3.2f m/s^2.", 9.819);
I oslo er tyngdeakselerasjonen på 9.82 m/s^2.

Legg merke til at tallet da også blir avrundet for oss.

La brukeren taste inn data - avansert

Vi har ofte lyst til å hente data fra brukeren av programmet vårt. For eksempel kan programmet be om to tall som skal ganges sammen eller vi må la brukeren bestemme hva det neste programmet skal gjøre er.

Der vi bruker printf for å få programmet til å skrive ut verdier til oss, kan vi bruke scanf for å hente verdier fra brukeren.

int tall;
printf("Skriv inn et tall og trykk Enter: ");
scanf("%d", &tall);                /* &-tegnet er viktig */
printf("\nDu skreiv inn tallet %d.", tall);

Merk dette fungerer ikke om du kjører C-programmet ditt via jdoodle.com.

Funksjoner

Definere

Vi ønsker nå å lage oss en funksjon3 som tar inn en radius og returnerer en sirkels areal.

float pi = 3.14;

/* En funksjon som returnerer arealet av en sirkel */
float Areal(float radius) {
  return pi * radius * radius;
}
Symbol Type Effekt
float nøkkelord forteller at funksjonen skal returnere et desimaltall
area navn setter navn på funksjonen
() tegn viser at dette er en funksjon og inneholder eventuelle parametere
float radius parameter forteller at funksjonen tar inn én parameter, og at denne er et desimaltall
{} tegn angir «kroppen» til funksjonen
return nøkkelord forteller at det følgende er det funksjonen skal returnere
; tegn avslutter retursetningen

I «mattespråk»

Vi er vant til å skrive funksjonen over som \(A(r) = \pi r^2\).

Her kan vi si at \(A\) er navnet til funksjonen, den tar inn parameteren \(r\) og returnerer \(\pi r^2\).

Her, og i en del programmeringsspråk, trenger vi ikke fortelle funksjonen hva slags tall \(A\) og \(r\) er, hvorvidt disse er heltall, brøker eller desimaltall. Det må vi altså gjøre i C.

Kalle funksjonen

Når vi nå ønsker å kalle denne funksjonen fra programmet vårt, gjør vi dette ved å skrive funksjonsnavnet og oppgi parameteren vi vil sende til funksjonen.

const float pi = 3.14;

float Areal(float radius) {
  return pi * radius * radius;
}

/* the area of a circle with radius 2.5 */
float beregnet_areal = Areal(2.5);

printf("Det beregnede arealet er %.2f.", beregnet_areal);

Kontrollstrukturer

Logiske tester

I C har vi følgende måter å sammenligne to verdier:

Tegn Eksempel Returnerer True hvis
== a==3.0 om to verdier er like
!= i != 4 om to verdier ikke er like
< h < height om verdien til venstre er mindre enn verdien til høyre
> h > height om verdien til venstre er større en verdien til høyre
<= n <= 15 om verdien til venstre er mindre eller lik verdien til høyre
>= n >= 19 om verdien til venstre er større eller lik verdien til høyre

Ulike logiske tester kan også settes sammen ved hjelp av logiske operatorer. Eksempel her kan være om du ønsker å gjøre noe dersom det er søndag eller det er mer enn fire dager siden sist du gjorde det, til dømes å ringe hjem.

Operator Eksempel Uttales Beskrivelse
&& True && False gir False «and» eller «og» Returnerer kun True dersom både venstre og høyre side er True
       
|| True || False gir True «or» eller «eller» Returnerer True dersom venstre eller høyre side er True
! !True gir False «not» eller «ikke» Returner False hvis True inn og motsatt
() ((5 > 2) eller !(a == 3)   Brukes til å gruppere logiske tester

Merk spesielt forskjellen mellom == og =. Det er en svært typisk feil i C-programmer at man blander disse to.

if-blokk

Collatz' formodning sier at for et hvilket som helst positivt heltall, dersom det er et partall, deler du det på to; dersom det er et oddetall, ganger du med tre og legger til én. Om du velger tallet 7, får du for eksempel da \(7\cdot 3 + 1 = 22\).

Hvordan ville du ha programmert en funksjon som skal ta inn et positivt heltall n og returne det neste tallet i Collatz' formodning?

Du ser at du trenger en måte å hvorvidt et tall er partall eller oddetall, for så å gjøre noe avhengig av tallet. Dette kan vi gjøre via en if-blokk.

I C har en if-blokk følgende struktur:

if (logisk_test) {
  /* det du ønsker å gjøre dersom den logiske testen er sann */
}
#include <stdio.h>

int Collatz (int n) {
  if (n % 2 == 0) {
    return n / 2;
  }
  else {
    return 3*n + 1;
  }
}

int main() {
  printf("Om du bruker tallet %d, vil det neste tallet i Collatz-rekken være %d.",
         5, Collatz(5));
 }
Om du bruker tallet 5, vil det neste tallet i Collatz-rekken være 16.

Merk, her ser dere at vi også har brukt strukturen else, denne gjennomføres kun om den øvrige if-setningen ikke kjøres.

Tillegg: if-else

Om du vil ha flere if-tester hvor kun én kjøres, kan du bruke else if. Strukturen ser da ut som følger:

if (logisk-test-1) {
  /* kjøres hvis logisk-test-1 er ~True~ */
}
else if (logisk-test-2) {
  /* kjøres hvis logisk-test-2 er ~True~ og
logiske-test-1 ikke er ~True~*/
}
else {
  /* kjøres hvis hverken logisk-test-1 eller logisk-test-2
er ~True~*/
}

Hvorfor kan vi ikke her bare bruke flere if-setninger etter hverandre?

for-løkke

I de fleste tilfeller ønsker vi å gjenta oss selv så lite som mulig. Dess flere ganger vi skriver nesten det samme i et program, jo lettere er det at det sniker seg inn feil … og jo kjedeligere blir det.

Om vi skal skrive et program som finner summen av alle tall fra 1 til 100, kan det gjøres med rå muskelkraft som

#include <stdio.h>
int sum;

int main() {
  sum = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 +
    11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 +
    21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 +
    31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 +
    41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 +
    51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 +
    61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 +
    71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 +
    81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 +
    91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 + 100;

 printf("Summen av alle tallene fra 1 til 100 er %d.", sum);

}
Summen av alle tallene fra 1 til 100 er 5050.

Vi ser dog her at vi gjør mer eller mindre det samme stykket hundre ganger. Dette kan vi forenkle.

#include <stdio.h>

int sum;

int main() {
  for (int i = 1; i <=100; ++i) {
    sum += i;
  }
  printf("Summene av alle tallene fra 1 til 100 er %d.", sum);
}
Summene av alle tallene fra 1 til 100 er 5050.

Tillegg for matemematikerne

Og så sier matematikeren: «Og hvorfor bruker dere ikke at summen av alle tallene fra 1 til n er følgende?

\(S = \frac{(n+1)n}{2}\)

Vel … fordi.

Oppgaver

Omkrekts (funksjoner)

Lag en funksjon circumfrence som regner ut omkretsen av en sirkel avhengig av radiusen r.

Konvertering fra grader farhenheit til celsius (funksjoner, formatering og for-løkker)

Hvis \(T_C\) er en temperatur målt i grader celsius, og \(T_F\) er den samme temperaturen målt i grader fahrenheit, kan gå fra grader celsius til farhenheit ved å bruke at

\(T_F = 1,8\cdot T_C + 32\).

Lag et program som gjør følgende:

  • Skriver ut en overskrift
  • Skriver ut to kolonner med tall
  • Den første kolonnen viser temperatur i grader celsius
  • Den andre kolonnen viser temperatur i grader fahrenhet
  • Konverter tallene fra -30 °C til 100 °C med intervaller på 10 °C.

Overskriften formateres som:

Temp. grader C    Temp. grader F
--------------------------------

Summen av alle tall fra 1 til 100 som er delelig på 3 (for-løkker, if-setninger)

Skriv et program som regner ut summen av alle tall som er delelig på 3 fra og med 1, til og med 100.

Løsning av andregradslikninger (funksjoner, formatering og kvadratrøtter)

I math-biblioteket får vi tilgang til flere nyttige funksjoner, for eksempel finner vi kvadratrotfunksjonen, sqrt, der.

#include <math.h>
printf("Kvadratroten av %2.1f er %.2f.\n", 4.0, sqrt(4.0) );
printf("Kvadratroten av %2.1f er %.2f.\n", 5.0, sqrt(5.0) );
Kvadratroten av 4.0 er 2.00.
Kvadratroten av 5.0 er 2.24.

Lag en funksjon AbcLoeser som tar inn heltallene a, b og c, og skriver ut x-verdiene til eventuelle løsninger.

Bruken skal være som følger:

AbcLoeser(2, -4, 2);
AbcLoeser(1, 1, 1);
AbcLoeser(3, 1, -2);
Likningen har én løsning, x = 0.00.
Likningen har ingen løsning.
Likningen har to løsninger, x = 0.67 and x = -1.00.

Introduksjon til doble for-løkker (doble for-løkker)

Hva vil skrives ut av følgende program?

int i;
int j;

for (i = 1; i <=5; ++i) {
  printf("i er %d.\n", i);

  for (j = 1; j <=5; ++j) {
    printf("j er %d.\n", j);
  }
}

Gangetabellen (doble for-løkker)

Skriv et program som skriver gangetabellen til skjermen.


1

Når vi skal skrive desimaltall inn i et program, kaller vi de ofte flyttall eller float.

2

I en datamaskin vil en serie med bokstaver (en streng) representeres som en og en bokstav i arbeidsminnet til maskinen.

3

I programmering er funksjoner gjenbrukbar kode som kan ta inn parametere for å returnere noe til der den blei kalt fra.