Programmering i ANSI-C


Källkoden redigera

 Källkoden är själva grunden för hur det blivande programmet kommer att gestalta sig. Källkoden innehåller främst preprocessordirektiv¹ och instruktioner som utför operationer på data eller anrop till funktioner som i sin tur utför operationer på data. Det här kapitlet beskriver hur det genomförs med hjälp av ANSI-C.

¹ Se delkapitelt om preprocessorn för närmare beskrivning.

Typisk källkod:

01  #define TITEL "Animera rev 0,1"
01  #include <stdio.h>
01  #include "Animera.h"

02  void visa( int index )
03  {
04      kod.
03  }

05  int main(int argc, char** argv)
03  {
06      int index;

07      /* Visa animation: */
08      for ( index = 0; index < 8; index++)
03      {
09          visa ( index );
03      }

04      kod.

10      return ( 0 );
03  }

O.B.S. radnumreringen tillhör inte den egentliga källkoden.

  • 01; Preprocessordirektiv.
  • 02; Funktionsdefinition.
  • 03; Programdel, alla operationer inom parentesen { ... } ligger på samma nivå.
  • 04; Utrymme för funktionens programkod.
  • 05; Huvudfunktion, kallas alltid "main".
  • 06; Variabledeklaration, reserverar minne och skapar en pekare dit.
  • 07; Kommentar, all text inom markeringen /* ... */ ignoreras av kompilatorn.
  • 08; Villkorsloop.
  • 09; Funktionsanrop.
  • 10; Returvärde till den anropande funktionen / progammet.

Funktioner redigera

En funktion i ANSI-C har formatet:

"returparameterns typ" Funktionens_namn ( "lista på anropsparametrarnas typer" )

Funktioner som ingår i ett program definieras i inledningen av källkoden, det kan vara i något av standardbiblioteken som då inkluderas eller i en huvudfil (filnamn.h) men ofta skrivs definitioner för funktioner direkt i källkoden, då måste definitionen skrivas någonstans före den del av programtexten där den sedan används. Vad som sker om programmet försöker använda en odefinerad funktion beror på kompilatorn som används. Dom flesta retunerar ett felmeddelande i stil med "odefinerad funktion" och en anvisning till vilken rad i källkoden som orsakat felet så att man kan rätta till det.

Funktionsdefinition redigera

Ursprungligen så definierades funktioner på formatet:

1  typ Namn ( variabelnamn, ... )
2  typ variabelnamn, ... ;
3  {
4      funktionens programtext
5  }
  • 1; Funktionshuvud, anger parametertyp för retur, funktionens namn och inom parentesen, anropsparametrarnas namn.
  • 2; Variabeldeklarationer för anropsparametrarna, reserverar minne och skapar pekare till variablerna. Utelämnas deklarationen (vilket är tillåtet) så sätts typen automatiskt till "int".
  • 3; Funktionens start.
  • 4; Mellan parenteserna { och } skrivs funktionens programkod.
  • 5; Funktionens slut.

Men det finns ett alternativt, kortare skrivsätt som kommit att bli standard med tiden. Då utesluts den andra raden och anropsvariablerna behöver inte deklareras specifikt utan det görs genom att ange typen direkt i parameterlistan på det här sättet:

1  typ Namn ( typ variabelnamn, ... )
2  {
3      funktionens programtext
4  }
  • 1; Funktionshuvud, anger returparameterns typ, funktionens namn samt namn och typ för funktionens anropsparametrar.
  • 2; Funktionens start
  • 3; Mellan parenteserna { och } skrivs funktionens programkod.
  • 4; Funktionens slut.

Funktionsanrop redigera

Ett funktionsanrop är det vanligaste kommandot inom C programmering, anropet görs antingen separat på en ensam rad, som en del av en beräkning eller så används funktionens returparameter som ingångsparameter till en annan funktion i ett nästlat anrop.

Funktionsanrop:

resultat = funktionens_namn ( lista på anropsparametrar );

Om funktionens returparameter är av typen "void" eller om resultatet är ointressant av någon anledning så behöver ingen resultatvariabel anges. Om funktionen inte tar några anropsparametrar så lämnas parentesen tom. Extremfallet är då ett anrop utan anrop eller resultatparametrar.

Funktionsanrop utan parametrar:

funktionens_namn ( );

Som redan nämnts så är även nästlade anrop är möjliga. När en funktion används som källa för en ingångsvariabel så behöver den inte anges utan kompilatorn ser till att resultatet läggs till parameterlistan. Om en variabel ändå anges kommer resultatet av funktionsanropet hamna både i variablen och i parameterlistan till den underliggande funktionen. Vid nästlade anrop så är anropsordningen sådan att funktinsanrop som ligger inom en annan funktions parenteser anropas först. På det sättet kan resultatet av anropet användas till den underliggande funktionen.

Nästlat funktionsanrop:

resultat = fn_X ( fn_Y ( parametrar till fn_Y ), fler parametrar till fn_X );

resultat_X = fn_X ( resultat_Y = fn_Y ( parametrar till fn_Y ), fler parametrar till fn_X );

Även anrop i beräkningar eller jämförelser är möjliga.

Funktionsanrop i beräkningar:

resultat = ( funktion ( parametrar ) + x ) * y;

Funktionsanrop i jämförelser:

if ( funktion ( parametrar ) == SKOKRÄM ) 
{
    kod som utför skoputsning.
}

Om funktionen retunerar felkoder så kan ett villkor användas för att testa om något gått fel vid anropet.

Anrop med resultattest:

if ( ( resultat = funktion ( parametrar ) ) != OK )
{
    Visa_felmeddelande ( resultat );
}

Det sista kodexemplet ovan kan läsas som, "Om resutatet av funktionsanropet inte är lika med ok, visa då ett felmedelande."

Variabler och konstanter redigera

Vid deklaration av variabler och konstanter så anges först typen sedan namnet, samtidigt så kan den även tilldelas ett värde. Variabler kan deklareras globalt i källkodens huvud, (till exempel i en huvudfil, *.h), eller lokalt i funktionshuvudet. Variabler i funktioner som deklareras som static kommer att behålla sina värden mellan funktionsanropen (funktioner som använder sådana behöver i allmänhet initieras på något sätt innan dom brukas), övriga variabler måste på något sätt ges ett värde i funktionens kod eller i deklaratioen. Konstanter avges med nyckelordet const, dessa minnesceller kommer att få samma egenskaper som ett ROM och går sedan alltså endast att läsa från men inte skriva till.

/* Deklaration av en variabel: */
typ variabelnamn [ = värde ];

/* Exempel: */
int index = 99;                /* skapar en heltalsvariabel */
const float pi = 3.141592654;  /* deklaration av en konstant */
float vinkel = 123.456;        /* flyttal med deimalpunkt */
float liten  = 1.2E-34;        /* flyttal på exponentform */

Vad som sker är att tillräckligt med minne reserveras för en variabel av vald typ. Sedan används varibabelns namn som en symbol för adressen till minnet där den lagrats av kompilatorn när objektkoden skapas. På sådant sätt länkas rätt minnescell till alla dom ställen där sedan symbolen/variabelnamnet förekommer i källkoden.

/* I funktionsdeklarationer */
void funktion ( int flagga )
{
    char ett_a = 'A';
    static int antal;

    if ( flagga )
        antal = 0;
    else
        antal++;
 
    ... övrig kod ...
}

Funktionens parameter, flagga, används för att nollställa variabeln, antal, som är deklarerad som static. Ett sätt att låta en funktion välja olika vägar genom att anropa den med antingen "funktion ( SANT )" eller "funktion ( FALSKT )".

Strängar är en lite speciell typ av data. En sträng är egentligen inget annat än en pekare till den första i en rad minneceller av typen char.

Deklaration av strängar:

1  const char    titel [ 18 ]    = "Variabler i ANSI-C";
2  const char    hej_0 []        = "Hej";
3  const char   *hej_1           = "världen";
4  char         *pText_0;
5  char          pText_1 [ 256 ];

  • 1; Skapar en strängkonstant med 18 stycken celler som texten mellan dom dubbla citattecken "" placeras i och sätter pekaren "titel" att peka på den första cellen i raden/strängen.
  • 2; Om strängen skapas på det sättet så behöver inte antalet celler heller anges utan hakparentesen [...] kan lämnas tom.
  • 3; Det går på samma sätt även att utelämna parentesen helt och i stället ange att det är en pekare som deklareras med pekaroperatorn *, resultatet är detsamma.
  • 4; Skapar en pekare till typen char men den är ännu inte satt att peka på någon minnescell
  • 5; Skapar en pekare som sätts att peka på en tom sträng om 256 celler som vardera rymmer ett tecken av typen char.
/* Operationer på strängpekare */
pText_0 = hej_1;

"pText_0" pekar nu på strängen "hej_1", variablerna har blivit identiska i allt utom namnet.