I dette kapittelet skal vi ta for oss hvordan man bruker variabler i C. En variabel er en primær del av et hvert programmeringsspråk. Det er mange måter å se på variabler. Grovt sett bruker man variabler som lagringsplasser der vi lagrer midlertidig informasjon mens programmet kjører. Vi bruker også variabler til å generalisere uttrykk, slik som i matematikken. I matematikken har vi at kvadratet av et tall er tallet multiplisert med seg selv. Vi har altså f.eks:

2² = 2 * 2

Men dette uttrykket gjelder bare for tallet 2. For andre tall som f.eks. 9 blir dette feil. Med variabler, derimot, kan vi uttrykke den generelle regelen:

x² = x * x

Der x er hvilket som helst (reelt) tall.

Nok om matematikk. La oss gå over på C. Tenk deg at vi har et program som skal hente et tall fra brukeren (dvs. et tall som brukeren skal skrive inn på tastaturet.) Hvor skal vi gjøre av dette tallet slik at vi senere kan f.eks. multiplisere det med seg selv? Vi trenger et navngitt sted å lagre det, slik at vi kan finne det igjen -- vi trenger en variabel.


Datatypene

rediger

I motsetning til en del andre programmeringsspråk, har C forskjellige typer variabler, avhengig av hva du skal lagre i variabelen. Vi sier at C har forskjellige datatyper. Det er ikke alle som er like mye brukt, så jeg tar bare en liste med de vanligste datatypene:

 +--------------+-------+-------------------------------+
 |Type          | Bytes | Beskrivelse                   |
 +--------------+-------+-------------------------------+
 |              |       |                               |
 | char         | 1     | Bokstaver eller småtall       |
 | int          | 4     | Heltall                       |
 | float        | 4     | Desimaltall                   |
 | double       | 8     | Dobbelpresisjon-desimaltall   |
 |              |       |                               |
 +------------------------------------------------------+

Antall bytes en datatype bruker sier noe om hvor store verdier en variabel av den typen kan holde. I en variabel av typen char, kan man f.eks. bare lagre verdier mellom 0 og 255 eller -128 og 127. Derfor egner denne datatypen seg lite når man vil lagre tall. Da er int som regel mest brukt. Men vil man lagre desimaltall, altså tall med komma, kan man hverken bruke int eller char; da må man bruke float eller double.

Navnet til char, som er en forkortelse for "character" eller "tegn" på norsk, kommer av at denne datatypen er ment for å holde bokstaver og tegn -- selv om den strengt tatt kan brukes til å holde på små tall. God programmeringsskikk sier at man alltid, hvis man ikke må, skal unngå å bruke char til å lagre tall.

Modifikatorer og subtyper

rediger

De fire "hoved-datatypene" over, finnes også i litt andre størrelser og typer. Om man definerer en variabel av typen int, kan denne holde verdier mellom -2147483648 og +2147483647. Men dersom man ikke har behov for å lagre negative tall, og samtidig har behov for å lagre tall som er større enn +214783647, kan man definere int som en unsigned int. Da vil man kunne lagre verdier fra og med 0 og til +4294967295 -- altså like mange forskjellige verdier som en signed int, men fra og med 0 istedet for -2147483648.

Vi sier at variabler som kan holde negative tall er signed (engelsk), og at variabler som ikke kan det, er unsigned.

Vi bruker disse to ordene, unsigned og signed i C for å spesifisere hvilken subtype av datatypen vi vil definere -- altså om vi vil definere den som unsigned eller signed. Vi skriver dem alltid foran datatypen i definisjonen. Dersom man utelater å skrive unsigned eller signed, vil kompilatoren anta at vi mener signed.

Tilleggsopplysninger som unsigned og signed, kaller vi modifikatorer. Dette er fordi de modifiserer hvilken subtype av datatypen man definerer. I tillegg til unsigned og signed, har vi også to andre: short og long.

short lager en kortversjon av datatypen, som opptar mindre plass i minnet. Dette innebærer da selvsagt at variabelen som defineres ikke kan holde like store verdier som en variabel av en vanlig datatype.

long er det stikk motsatte av short. Den lager, for noen datatyper, en versjon av datatypen som kan lagre enda større verdier. I realiteten er det bare double som har en long-versjon som faktisk er større enn den vanlige datatypen double. int har også en spesiell subtype: long long int som er en int som kan holde veldig store tall.

Her er den komplette listen av datatypene i lista over, inkludert subtypene deres:


                Type  Bytes  Bits                Range

           short int    2      16          -32,768 -> +32,767          (32kb)
  unsigned short int    2      16                0 -> +65,535          (64Kb)
        unsigned int    4      32                0 -> +4,294,967,295   ( 4Gb)
                 int    4      32   -2,147,483,648 -> +2,147,483,647   ( 2Gb)
            long int    4      32   -2,147,483,648 -> +2,147,483,647   ( 2Gb)
       long long int    8      64                veldig langt
         signed char    1       8             -128 -> +127
       unsigned char    1       8                0 -> +255
               float    4      32
              double    8      64
         long double   12      96

Definering av variabler. Tilordning av verdier. Variabelnavn

rediger

Før en variabel kan brukes, må den defineres. Dette er nødvendig for at C-kompilatoren skal kjenne igjen variabelnavnet når det brukes i koden, og at den skal sette av plass i minnet til variabelen når programmet kjøres.

For å definere en variabel, bruker i følgende basisuttrykk:

type navn;

Der type er datatypen man vil lage en variabel av, og navn er navnet man vil gi variabelen. F.eks:

int temperatur;

Her defineres en variabel av typen int, med navnet "temperatur".

I tillegg til å oppgi datatypen vi ønsker at variabelen skal være, kan vi også velge om den skal være unsigned -- det vil si at den ikke skal kunne holde negative verdier. For å gjøre dette, skriver vi unsigned foran datatypen:

unsigned int alder;

Her får vi altså en variabel med navn "alder", som er av typen int, og som er unsigned. Dersom vi ikke oppgir unsigned, er det opp til kompilatoren og systemet hvilken subtype vi får. Merk: det er kun int og char som kan være av typen unsigned.

I tillegg til å oppgi om datatypen skal være signed eller unsigned, kan vi også oppgi short og long. Da må vi huske på å sette long/short foran datatypen, men ikke foran en eventuell unsigned:

unsigned short int alder;

Her definerer vi altså variabelen "alder" av typen unsigned short int.

Dersom vi vil definere flere variabler av samme type, kan vi bruke følgende triks:

float tall1, tall2, tall3;

Her definerer vi tre variabler, tall1, tall2, og tall3, av typen float.


Tilordning av verdier

rediger

En variabel er ikke godt for noe hvis den ikke kan brukes. For å gi en variabel en verdi, må vi bruke en operatør. Operatører er spesielle funksjoner som er innebygd i språket for å gjøre helt grunnleggende og nødvendige ting, som f.eks. å addere to tall eller, som vi skal se på nå: gi variabler en verdi. For å tilordne en verdi til en variabel, bruker vi tilordningsoperatøren; =. Tilordningsoperatøren brukes slik:

variabel = verdi

Et eksempel på bruk av tilordningsoperatøren kan se slik ut:

int alder;
alder = 20;

Her definerer vi først variabelen "alder", deretter bruker vi tilordningsoperatøren til å gi den verdien 20. Husk alltid å definere variabler før du tar dem i bruk (f.eks. slik som over, med tilordningsoperatøren), ellers vil kompilatoren gi en feilmelding fordi den ikke gjenkjenner variabelnavnet!

Tilordningsoperatøren kan også gi variabler verdier fra andre variabler, fra funksjoner (når vi kommer så langt), resultatet fra andre operasjoner, o.l.

int a;
int b;
a = 5;
b = a;

Her definerer vi først to variabler, a og b av typen int. Deretter gir vi a tallet 5, og til slutt gir vi b innholdet av a.

Men denne koden kan forenkles kraftig. Vi kan korte alle linjene ned til én linje. Siden både a og b skal være av typen int, kan vi sette begge inn i samme variabeldefinisjonsuttrykk, som nevnt i et av eksemplene over. I tillegg vil vi gi a verdien 5 med en gang. Da kan vi like godt også sette dette inn i definisjonsuttrykket. Og jammen meg så vil vi jo gi b verdien av a -- da setter vi også dette inn i uttrykket! Vi får følgende uttrykk:

int a = 5, b = a;

Merk at a her må defineres før b kan få verdien dens:

int b = a, a = 5;

Dette uttrykket blir da ugyldig; kompilatoren begynner fra venstre, og evaluerer b = a først. Da vil den anta at a allerede er definert, men som du ser, skjer ikke dette før etter b defineres og får verdien til a.

La oss nå si at vi vil ha en variabel med navn "svar", som skal holde summen av to variabler, "a" og "b":

int svar, a = 5, b = 5;
svar = a + b;

Her ser du en ny operatør i bruk, nemlig addisjonsoperatøren, +. Det er ganske rett frem hva den gjør; den adderer venstre og høyre side (mer om den og andre aritmetiske operatører om litt). I eksempelet over, blir a og b addert, og deretter tilordnet variabelen svar, som nå inneholder 10 (5 + 5). Dersom vi vil, kan vi også skrive denne kodesnutten enda mer forenklet, på én linje:

int a = 5, b = 5, svar = a + b;

Dette viser hvor mye man i grunn kan gjøre i et definisjonsuttrykk. Men det blir fort rotete om man har for mye på i en og samme linje. I dette tilfellet ville det vært mer ideelt å bruke den første kodesnutten.


Type casting

rediger

I C må begge argumentene til, eller sidene av, tilordningsoperatøren være av samme type. Logisk nok går f.eks. ikke følgende uttrykk:

int a = 3.4;

... siden a defineres til å være av typen int, mens 3.4 implisitt er av typen float, vil kompilatoren klage på et slikt definisjonsuttrykk. For å løse et slikt problem må vi konvertere, eller gjøre om, 3.4 til typen int. Dette gjør vi ved hjelp av en såkalt type cast:

(type)verdi;

Typen vi vil caste verdien til spesifiserer vi i paranteser, foran verdien som skal konverteres. Et eksempel på en type cast kan være følgende:

float b = 3.32;
int a = (int)b;

Her defineres b av typen float og får verdien 3.32. For at a, som defineres av typen int skal kunne få denne verdien, må den castes om til en int, ved å sette typen foran verdien (i dette tilfellet verdien til b). En type cast fra et flyttall til heltall vil alltid resultere i at delen bak komma ignoreres; a vil altså få verdien 3 i eksempelet over. Casting er ikke spesielt nyttig nå, annet enn til konvertering mellom flyttall og heltall, men når vi skal se på pekere, vil det være hendig å kunne.

Bokstaver og strenger

rediger

En annen type form for variabler er strenger. En streng er en samling av bokstaver og tegn etter hverandre, f.eks. et ord eller en hel setning. I den første kodesnutten i denne guiden, var "Hallo, verden!\n" for eksempel en streng. I C, skrives alltid strenger omgitt av et hermetegn, ", i hver ende.

Strenger, er egentlig bare en rekke char-er etter hverandre i datamaskinens minne. Grunnen til at vi bruker char som datatype, er at en char er en byte stor, som er akkurat så stor plass et tegn skal ta, i følge ASCII-standarden.

ASCII-standarden definerer en måte å lagre tekst og tegn på, i datamaskiner. Verdien til en char representerer et tegn:

[1]

Det vil si at en char som inneholder tegnet 'J', egentlig inneholder tallet 74. Heldigvis trenger vi ikke huske på alle verdiene og hvilke tegn de representerer -- dette tar kompilatoren seg av. For å lage en variabel med navn "a", av typen char, som skal holde bokstaven "J", kan vi skrive slik:

char a = 'J';

Merk at vi her bruker apostrof rundt J for å fortelle kompilatoren at vi mener tegnet J, og ikke en streng med tegnet J, som ville vært noe annet, som vi snart skal se. Men hvordan lagrer vi en hel streng (rekke) med bokstaver? Dette gjøres nesten på samme måte:

char streng[] = "Hallo, verden!";

Her vil det lages en streng, kalt "streng". Merk bruken av [] etter navnet for å signalisere at vi vil lage en streng, og bruken av "hermetegn" i stedet for '.

(Merknad: Senere vil vi se at [] egentlig ikke er noe spesifikt for strenger.)

Det vi gjør her, er å definere en rekke char-variabler etter hverandre, som hver holder hvert sitt tegn i setningen, i alt 14. Som du kanskje har talt, er det bare 13 tegn i strengen. Den siste char-variabelen, inneholder nemlig et sluttegn. Sluttegnet forteller at strengen slutter der. Hadde vi ikke hatt sluttegnet, ville f.eks. printf() ikke vite hvor den slutter, og dermed vise eventuelle andre data som tilfeldigvis ligger i minnet etter strengen.

Nå kan vi redigere litt på "Hallo, verden!"-programmet vårt, nemlig ved å bruke vår egne streng i form av en variabel:

#include <stdio.h>

int main(void)
{
	char hallo[] = "Hallo, verden!\n";
	printf("%s", hallo);
	return 0;
}

Her definerer vi altså en variabel hallo, av typen char, med strengen "Hallo, verden!\n". Som forklart i det originale "Hallo, verden!"-programmet, forårsaker "\n" et linjeskift i konsollvinduet. Etter at strengen hallo er definert, kan vi sende den til printf, som vil vise den på skjermen. Hvorfor vi bruker printf på denne måten, med "%s", kan du lese om i neste kapittel.

Hva kan brukes som variabelnavn?

rediger

Vi kan ikke kalle variablene våre hva vi vil. Noen ord er reservert, dvs. at de brukes av interne C-funksjoner, operatører, kommandoer, eller funksjoner vi allerede har definert. Det sier for eksempel seg selv at vi ikke kan kalle en variabel for "=", da "=" er navnet, eller rettere sagt tegnet, til tilordningsoperatøren!

Gyldige variabelnavn i C kan inneholde bokstaver, tall, og tegnet "_". Alt annet enn det, blir sett på som ulovlig. En annen regel, er at variabelnavnet må begynne på en bokstav, eller "_". Innleder man variabelnavnet med et tall, er det ulovlig (utenom det, er tall som sagt lovlige i variabelnavnet). Det er også normalt å ikke innlede variabelnavnet med "_", selv om det er lov.

Her er alle ordene som ikke er lovlige som variabelnavn:

asm, bool, break, case, char, const, continue, default, do, double, else, enum, extern, false, float, for, goto, if, inline, int, long, protected,
register, return, short, signed, sizeof, static, struct, switch, true, typedef, union, unsigned, void, volatile, while 

Det vil si at kompilatoren vil gi deg en feilmelding dersom du f.eks. prøver å definere en variabel med navnet "goto", enkelt nok fordi det allerede er definert. Merk at det kan være flere reserverte ord fra kompilator til kompilator. Heldigvis får du beskjed dersom variabelnavnet du prøver å lage er i bruk fra før.

En viktig ting å merke seg, er at C skiller mellom store og små bokstaver i variabelnavnene. Det vil si at følgende definisjonsuttrykk er fullt lovlig:

char variabel1, vAriabel1;

Her får vi da to forskjellige variabler, selv om de heter det samme, bare med forskjellig type "a"! Dermed blir det også lovlig å f.eks. definere en variabel med navn "Case", da det ikke er et reservert ord i C, i motsetning til case.


Operatører og aritmetiske uttrykk

rediger

Grunnspråket, dvs. selve språket uten funksjonene definert i eksterne headerfiler som f.eks. stdio.h, består i hovedsak av to typer syntaktiske former: operatører og nøkkelord. Operatørene i C er så å si alle tegnene vi skriver i koden (utenom inni strenger). Noen av operatørene er så selvsagte for oss at vi kanskje ikke tenker på dem: Det er jo enkelt å skjønne hva tegnene + og = betyr i uttrykket a = 2 + 2, siden vi er så vant med dem fra matematikken.

C har en rekke aritmetiske operatører:

*  -  multiplikasjon
/  -  divisjon
+  -  addisjon
-  -  subtraksjon
%  -  modulus (rest av divisjon)

Det er også operatører for andre ting enn matematikk

Operatørprioritet

rediger

I matematikken lærer man at, i matematiske uttrykk der det er både addisjon, multiplikasjon o.l. skal multiplikasjon og divisjon utføres før eventuelle subtraksjoner og addisjoner. Det samme gjelder i C: * og / har høyere prioritet enn +, - og %. Det vil si at de operatørene blir evaluert først:

a = 1 + 3 * 5;
a = 1 + 15;
a = 16;

Dersom dette ikke hadde vært tilfellet, kunne vi likevel ha fått det til ved å bruke den operatøren med høyest prioritet av alle: (). Ved å sette paranteser rundt forskjellige uttrykk sikrer vi at det evalueres først. Vi kan bruke () til å tvinge addisjonen til å skje før multiplikasjonen:

a = (1 + 3) * 5;
a = 4 * 5;
a = 20;

Som vi ser har = mindre prioritet enn de aritmetiske operatørene igjen. Etter hver som vi ser på flere operatører, vil vi se at prioritetslisten er viktig å forholde seg til for å unngå feil i programmet.


Kombinasjonsoperatører

rediger

I C er uttrykk som a = a + 1; og a = a + 32, altså uttrykk som øker eller minsker en variabel med 1 eller en annen verdi, så mye brukt at man har egne operatører for slike operasjoner. Disse utfører en aritmetisk operasjon på et argument (en variabel), og tilordner så svaret til variabelen igjen.

x++     -  øker x med 1 og tilordner svaret til x
x--     -  minsker x med 1 og tilordner svaret til x
x += y  -  legger y til x og tilordner svaret til x
x -= y  -  trekker y fra x og tilordner svaret til x
x *= y  -  multipliserer x med y og tilornder svaret til x
x /= y  -  dividerer x på y og tilordner svaret til x
x %= y  -  tar resten av x / y og tilordner svaret til x

Disse operatørene vil vi snart se er svært hendige.