Det här är ett avsnitt i en webbkurs om databaser som finns fritt tillgänglig på adressen http://www.databasteknik.se/webbkursen/. Senaste ändring: 16 oktober 2005.

Av Thomas Padron-McCarthy. Copyright, alla rättigheter reserverade, osv. Skicka gärna kommentarer till webbkursen@databasteknik.se.

Vi lär oss det där med data: Grunder om programspråket C

eller

C för hjärnkirurger (och andra som inte är dataexperter)

En gubbe med en spade

De flesta som använder datorer idag gör det genom att peka på skärmen med en mus, och klicka på olika ställen. Man klickar här, och vips får man upp Aftonbladets hemsida. Man klickar där, och vips har man raderat alla sina filer. Ibland skriver man lite på tangentbordet också, till exempel när man ska skicka e-post till någon. Kanske ett hatbrev till Bill Gates, för det var säkert hans fel att filerna försvann.

Men ska man göra något riktigt avancerat, måste man programmera datorn, eller skriva ett program som man också säger. Att programmera går för det mesta ut på att tala om för datorn vad den ska göra, steg för steg och i detalj. Man använder ett särskilt programspråk, eller programmeringsspråk som det också heter.

Det finns många olika programspråk, faktiskt tusentals. En del av dem är avsedda för en specifik uppgift, till exempel språket SQL (det betyder Structured Query Language), som man använder för att söka efter data i databaser. Andra språk är tänkta att kunna användas till lite av varje, till exempel språken Ada, Java, C++ och C.

Just C är ett av de mest kända programmeringsspråken. Den första versionen av språket hittade man på omkring 1972, och sen har språket både utvecklats och fungerat som grund och inspiration för många andra språk. Till exempel kommer alla som tittar på ett program skrivit i Java eller C++ att känna igen mycket som är hämtat från C. Och för språket C++ ser man ju redan på namnet att det har med C att göra.

Målgruppen för den här introduktionen till C

Det är inte så vanligt numera att man lär sig C som första programmeringsspråk, utan det är vanligare att man börjar med språk som många tycker är lättare att lära sig. Java är ett populärt val. Men det finns fortfarande många som behöver lära sig C utan att ha särskilt stor tidigare erfarenhet av programmering (som att ha lärt sig Java), och i den här introduktionen till C förutsätter vi inte några förkunskaper i programmering.

Syftet med den här introduktionen till C

Vi vill ge en introduktion till C för den som inte sett det språket förut. (Och, förstås, för den som sett det men inte förstod så mycket). Tanken är inte att man ska bli en fullfjädrad C-programmerare, för det är något som kräver en riktig lärobok och månader eller år av övning. Tanken är i stället att läsaren ska lära sig att läsa och förstå enkla C-program, och (kanske med viss möda) även skriva dem.

Vårt första C-program

När man ska lära ut ett programspråk brukar man börja med ett litet enkelt program som bara skriver ut något på skärmen. Det brukar vara "Hello, world!", men för att så gott det nu går underlätta förståelsen så översätter vi det till svenska. Här är programmet:
#include <stdio.h>

int main(void) {
  printf("Hej, världen!\n");
  return 0;
}
Det var mycket bara för att skriva ut en sak! Uppenbarligen är det den där raden i mitten,
"printf("Hej, världen!\n");",
som är det centrala i programmet. (Haha. Raden i mitten är det centrala. Jättekul!) Det som står runt omkring behövs bara för att det ska bli ett riktigt program av alltihop. Ungefär som med kontoutdraget jag får från banken, där det står hur mycket pengar jag har på mitt konto. Där är det summan 23,50 som är det centrala, men sen står det en massa saker runt omkring för att det ska bli ett riktigt kontoutdrag.

Men hur kör vi vårt första C-program?

Hur man gör för att skriva in, och köra, programmet beror väldigt mycket på vad det är för system man sitter vid. Det beror på om datorn kör Windows, Linux, Mac OS eller något annat. Det beror också på vad det är för C-system som datorn använder. Eftersom det alltså är så väldigt olika, säger vi inte mer om det.

I vilket fall som helst så kallar det som vi skrev ovan, det där med #include och printf och allt det där, för programkoden eller källkoden.

Programmeraren skriver in källkoden i datorn, men källkoden kan inte köras direkt av datorn. Den måste först översättas, eller kompileras, till en form som datorn kan förstå och köra, så kallad körbar kod eller maskinkod. Den översättningen görs av ett särskilt program, en så kallad kompilator. Om man köper ett vanligt program i en låda, som till exempel ett dataspel, så är det körbar kod som man får med sig hem på CD-skivan.

Det körbara programmet kan köras (eller, på mer svengelsk svenska, exekveras). När ett program körs kan det förstås åstadkomma en massa olika saker, men vårt lilla program producerar bara utskrifter (det där med Hej, världen!), även kallat utdata eller utmatning.

Vi sa ovan att man skriver in källkoden i datorn, och då kan man antingen använda en så kallad editor (på mindre svengelsk svenska redigerare), som bara är ett program för att skriva in och redigera text, eller så använder man en så kallad programmeringsomgivning, där man byggt ihop editorn med kompilatorn och en del andra praktiska verktyg.

Om man använder en fristående editor och kompilator, får man ofta redigera, kompilera och köra programmet i flera steg. I en programmeringsomgivning, som har en egen editor för inmatningen av källkoden, finns det ofta en knapp man kan klicka på med musen för att både kompilera och köra programmet. (Man jobbar också på att skapa en knapp som kan skriva programmet. Bill Gates hälsar att det snart är klart.)

Vi dissekerar vårt första C-program: raden i mitten

Vi börjar från mitten, med den där centrala raden vi pratade om:
  printf("Hej, världen!\n");
printf är en så kallad funktion, vilket egentligen betyder att den är en liten programsnutt som finns någonstans. Egentligen är den en biblioteksfunktion, vilket betyder att någon annan (kan det vara Bill Gates?) har skrivit den, och varit vänlig nog att låta oss använda den. Vad programsnutten printf gör är just att skriva ut saker.

"Hej, världen!\n" är en textsträng, eller bara sträng, nämligen en följd av bokstäver och andra tecken. Tecknet " (eller dubbelfnutt, som vi utbildade experter säger) används för att markera början och slutet på strängen. De två tecknen \n har en speciell betydelse: de kommer nämligen att översättas till ett radslut, så efter utskriften av Hej, världen! så kommer nästa utmatning inte på samma rad, utan på nästa.

printf följt av "Hej, världen!\n" inom parenteser är ett funktionsanrop, eller bara anrop, dvs vi säger åt den där funktionen printf att hörru, nu ska du göra din grej, och det du ska göra den med är strängen "Hej, världen!\n". Det som funktionen ska göra något med, och som man alltså skickar till den i anropet, kallas argument.

Semikolonet på slutet av raden betyder att funktionsanropet bildar en programsats, eller bara sats. En programsats är en instruktion till datorn om att den ska göra något. Eftersom programmering (för det mesta) går ut på att tala om för datorn vad den ska göra, steg för steg. så består programmering till stor del av att skriva programsatser efter varandra.

Då var raden klar!

  printf("Hej, världen!\n");

Raden består alltså av en sats, som i sin tur består av ett funktionsanrop till funktionen printf, med strängen "Hej, världen!\n" som argument. Resultatet när programmet körs är att Hej, världen! skrivs ut på skärmen.

(En detalj som vi hoppar över är exakt var den där utskriften kommer. Förr i världen, innan datorerna hade möss, surroundljud och Aftonbladet, skrevs all utmatning på en pappersremsa som matades fram från en rulle. Då var det förstås på den pappersremsan som Hej, världen! dök upp, ackompanjerat av slammer från skrivverket. Numera, när man har en skärm full med fönster, och Aftonbladet, kan man inte vara lika säker. Det kan komma i ett särskilt fönster, eller försvinna nånstans. Men vi struntar i de detaljerna just nu, och återkommer till saken senare.)

C skiljer på stora och små bokstäver

C skiljer på stora och små bokstäver, så printf är inte alls samma sak som Printf eller PRINTF.

Vi fortsätter att dissekera vårt första C-program: main-funktionen

En programsats, som den med printf-anropet, får inte stå ensam för sig själv i ett C-program, utan den måste bakas in i en sån där programsnutt som vi pratade om, en funktion. En funktion kan innehålla en eller flera satser, eller ingen. Man skulle förstås ha kunnat gjort C så att programsatser kunde stå för sig själva, och i en del andra språk går det bra att göra så, men just i C måste man alltså stoppa in alla programsatser i funktioner.

Varje funktion har en kropp, och det är den som innehåller programsatserna. För att visa var kroppen börjar och slutar skriver man fiskmåsklamrar: { och }. Den första fiskmåsklammern ska förstås stå före den första satsen i funktionen, och den avslutande fiskmåsklammern ska stå efter den sista satsen.

Vi bygger kroppen:

{
  printf("Hej, världen!\n");
}

Varje funktion har ett namn, som till exempel printf. Men just printf var ju upptaget, så vi får kalla vår funktion för något annat. Vi skulle kunna kalla funktionen för olle, kurt eller kanske PRINTF (kom ihåg att C skiljer på stora och små bokstäver!) men i stället väljer vi att kalla den för main. Det gör vi av två skäl:

Funktionen har inte bara en kropp, utan den har också ett huvud. Funktionshuvudet innehåller bland annat namnet, och vårt funktionshuvud kommer att se ut så här:
int main(void)
Funktionshuvudet består av tre delar: Om vi börjar med parameterlistan, så sa vi tidigare att i ett funktionsanrop kan man skicka med argument, till exempel den där textsträngen till printf. Men om man skickar saker till funktionen, så måste funktionen också ha ett sätt att ta emot dem, och göra något med dem. Det är just det man använder parameterlistan till. Vi kommer att förklara mer om parameterlistor senare, men just nu räcker det att säga att void helt enkelt betyder att main inte vill ha några argument.

Sen tittar vi på den där returtypen som står före namnet. En funktion kan nämligen returnera saker. När man anropar en funktion kan man alltså skicka med något som funktionen ska göra något med, till exempel en textsträng som den ska skriva ut på skärmen. Men en funktion kan också skicka tillbaka saker, eller som dataexperterna säger, returnera dem. printf skickar till exempel tillbaka antalet tecken som den skrev ut, ifall man nu skulle behöva den informationen till något. (Vi brydde oss inte om att göra något alls med den, i vårt printf-anrop.) För att ha lite koll på exakt vad det är som funktionen ska skicka tillbaka, så anger man vilken sorts saker den kan skicka tillbaka, eller typen på dem. Returtypen är alltså den typ av saker som funktionen kan skicka tillbaka. int är en förkortning av integer, vilket i sin tur är det engelska ordet för heltal. Det betyder att main kan skicka tillbaka ett heltal. Även printf skickar tillbaka ett heltal: antalet utskrivna tecken kan knappast bli sju och en halv, utan antalet är alltid ett heltal.

Heltalet som funktionen main skickar tillbaka är ett litet meddelande om hur programmet lyckades med det som det skulle göra. Till exempel ska ju vårt program skriva ut Hej, världen! på skärmen, och det kan man ju i alla fall tänka sig att det skulle kunna misslyckas med. Program kan starta andra program, och för att det första programmet (som startade det andra) ska få reda på hur det andra programmet (som det startade) lyckades med sin uppgift, så kan det andra programmet alltså skicka ett heltal till det första programmet, genom att returnera det från funktionen main. Om programmet inte startades av ett annat program, utan direkt från operativsystemet, så är det till operativsystemet som informationen (alltså heltalet) skickas.

Man har bestämt sig för att talet 0 (noll) betyder att det gick bra.

Nu har vi återskapat så här mycket av programmet:

int main(void) {
  printf("Hej, världen!\n");
}
Fast attans också, vi glömde ju det där med att returnera noll. Det finns en speciell sats i C som kallas retur-satsen, eller return-satsen för den som hellre vill prata svengelska än svenska, och vi lägger in en return-sats som returnerar talet noll:
int main(void) {
  printf("Hej, världen!\n");
  return 0;
}

void main är fel

Nu har vi lärt oss att man ska skriva int main när man deklarerar main-funktionen, för den ska returnera ett heltal. Men man ser ganska ofta C-programmerare som skriver void main. Även om det ofta fungerar ändå, så är det egentligen fel att skriva så.

Det är fel nästan jämt i alla fall. När man programmerar tvättmaskiner, och det enda som finns där inne i tvättmaskinens styrdator är vårt C-program, utan sällskap av några andra program eller något operativsystem, så finns det ingenstans som main kan returnera sitt heltal. main är kanske överhuvudtaget inte tänkt att någonsin avslutas och returnera något. Då kan void main vara rätt.

Vi dissekerar färdigt: #include-direktivet

Nu är det bara en rad kvar innan vårt program är färdigt, nämligen den allra första:
#include <stdio.h>
Rader som börjar med #, eller brädgård som datanördar och orienterare säger, är direktiv till preprocessorn. Preprocessorn, eller för-processorn som man skulle kunna säga på svenska, är en helt egen del av C. Innan kompilatorn börjar jobba med att översätta programmet till maskinkod, så hanterar den nämligen preprocessordirektiv som #include. Det som det här #include-direktivet gör är att "inkludera" (eller "stoppa in", eller "göra tillgänglig") diverse information som finns i stdio.h. stdio.h är en header-fil, eller bara header. Om man letar runt på datorn kan man ofta hitta en fil som heter just stdio.h, och det är det som står i den filen som stoppas in av #include. stdio.h innehåller information om det så kallade standard-I/O-biblioteket, förkortat stdio.

I/O står för input/output eller inmatning och utmatning. stdio-biblioteket innehåller (bland annat) funktioner för in- och utmatning av text. Funktionen printf finns i stdio-biblioteket, och för att vi ska vara säkra på att programmet fungerar, måste vi alltså inkludera stdio.h i programmet. (Det kan hända att det fungerar i alla fall, men då beror det mest på tur.)

Själva printf-funktionen finns inte i header-filen, utan det som finns i header-filen är bara lite information om att printf finns, vad den har för returtyp, och vad den kan ta emot för argument. Själva funktionen, alltså den körbara kodsnutt som gör det printf ska göra, finns inte i header-filen, utan i en särskild biblioteksfil.

Så nu är vi klara med hela programmet:

#include <stdio.h>

int main(void) {
  printf("Hej, världen!\n");
  return 0;
}

Data: värden och datatyper

I vårt första C-program såg vi hur man kan skicka saker till en funktion: strängen "Hej, världen!\n" som vi skickade till funktionen printf. Vi såg också att man kan returnera saker från en funktion: heltalet noll, som vi returnerade från main.

Dessa saker som vi skickar fram och tillbaka i programmet brukar man kalla data.

Data består av värden. Till exempel så är talet noll ett värde. Varje värde har en viss, bestämd typ, eller datatyp som man också säger. Talet noll som vi arbetade med ovan har datatypen heltal, eller int som den stavas i C.

Variabler

Vi har sett att man kunde skicka data till en funktion, och tillbaka igen från funktionen. Men man kan göra mer saker med data, till exempel lägga dem i en liten låda.

En "låda" i programmeringssammanhang kallas variabel. Varje variabel har en särskild datatyp, precis som värdena. När man placerar data i en variabel, så ska typen på värdet och typen på lådan stämma överens, annars går det inte. Här är ett nytt program:

#include <stdio.h>

int main(void) {
  int x;
  int y = 17;
  x = 5;
  y = x;
  x = x + y*10 + 3;
  printf("x innehåller %d och y innehåller %d.\n", x, y);
  return 0;
}
En rad som
  int x;
kallas en variabeldefinition. Det betyder att man talar om för C-kompilatorn att hörrudu, jag vill ha en variabel som jag kan lägga data i, den ska heta x, och den sorts data som man ska kunna lägga i den är heltal. Med andra ord ska den ha datatypen int.

Variabeln y, som definieras på nästa rad, får dessutom ett initialvärde, eller ursprungligt värde. Eftersom vi skrivit = 17 efter variabelnamnet i definitionen, kommer variabeln från början att innehålla värdet 17.

Det som händer på raden

  x = 5;
är en så kallad tilldelning. Man säger att variabeln x tilldelas värdet 5. Det betyder att värdet 5 placeras i variabeln.

Även nästa rad,

  y = x;
är en tilldelning. Man kan också se det som en kopiering: i variabeln y placerar vi en kopia av det värde som finns i variabeln x. Efter den tilldelning innehåller både x och y (varsin kopia av) värdet 5.

Man kan räkna också. Det som händer på raden

  x = x + y*10 + 3;
är att vi (eller rättare sagt datorn) först beräknar värdet av x + y*10 + 3 (som blir 58), och sen placerar vi (en kopia av) det värdet i variabeln x. Efter den här tilldelning innehåller x alltså värdet 58.

Raden

  printf("x innehåller %d och y innehåller %d.\n", x, y);
innehåller en lite mer avancerad användning av funktionen printf än vad vi sett förut. Genom "procent-specificerarna" (%d) kan man skriva ut tal, och utskriften från programmet blir:
x innehåller 58 och y innehåller 5.
Eftersom variabeln x är deklarerad inuti kroppen på funktionen main, så är den lokal i main, dvs man kan bara komma åt den variabeln i satser som finns i main, inte i andra funktioner. Motsatsen är en global variabel, som man kommer åt från hela programmet.

Det finns inget som heter C/C++

Man ser ofta uttrycket C/C++, men det finns det egentligen inget som heter. Det som finns är två olika programspråk: ett som heter C, och ett som heter C++.

Mer om C

Än så länge beskriver det här avsnittet bara de absoluta grunderna, men kanske kommer det så småningom lite mer.

De viktigaste begreppen

De viktigaste begreppen från det här avsnittet:

programmera, program, programspråk, C, C++, källkod, körbar kod, kompilator, printf, funktion, biblioteksfunktion, textsträng, funktionsanrop, argument, programsats, funktionskropp, main, parameterlista, void, returtyp, int, retur-satsen, return-satsen, preprocessor, preprocessordirektiv, #include, stdio, stdio.h, data, värde, datatyp, variabel, variabeldefinition, tilldelning, %d, lokal variabel, global variabel


Webbkursen om databaser av Thomas Padron-McCarthy.