Till KTH:s startsida Till KTH:s startsida

Något om struct:ar

Följande kod definierar typen IntItem som representerar ett listelement med ett heltal, samt funktionen sumList som summerar en lista av IntItem:s:

struct IntItem {
  int num;
  struct IntItem *tail;
};

typedef struct IntItem IntItem;

int sumList (IntItem *list) {
  int sum = 0;
  while (list != NULL) {
    sum += list­>num;
    list = list­>tail;
  }
  return sum;
}

Hur ska vi förstå definitionen av en IntItem? Vi börjar med hur man deklarerar en variabel x av typen heltal:

int x;

x är variablens namn. "int" är typen (heltal). Men hur gör vi om vi vill att x ska innehålla två tal? Det finns inga sådana typer i C, men vi kan bygga en ny typ:

struct Int2 {
  int a;
  int b;
};

Nu har vi en *typ* som heter "struct Int2". Vi kan nu deklarera vår variabel:

struct Int2 x;

x innehåller nu två heltal som vi kan sätta till 1 respektive 2 såhär:

x.a = 1;
x.b = 2;

Vi kan komma åt tex det andra talet med "x.b". Men vi kanske vill införa det kortare namnet "Int2" för typen "struct Int2". Det kan vi göre med typedef:

typedef struct Int2 Int2;

Notera att detta ser ut som deklarationen av x ovan. Faktum är att om vi inte hade haft "typedef" först hade ovanstående rad varit en vanlig variabeldeklaration där variablens namn är "Int2". typedef gör alltså om en variabeldeklaration till en typdeklaration. Vi har nu infört förkortningen "Int2" för typen "struct Int2" så att vi nu kan deklarera x med:

Int2 x;

Precis som förut kan vi skriva x.a respektive x.b för att komma åt innehållet i x. Vi kan också införa en pekare p som pekar på variabler av typen Int2:

Int2 *p;

För att låta p peka på x ovan måste vi sätta p till adressen av x:

p = &x;

Nu kan vi komma åt innehållet i x via p:

p->a = 17;

Ovanstående ställer om a-fältet i x till 17 så att x.a nu är lika med 17. Vi kan inte skriva p.a eftersom p inte är en strukt utan en pekare. Om p pekare på x är "*p" ekvivalent med "x", dvs "*" betyder att vi följer pekaren till det den pekar på, dvs omvändningen av "&" ovan. "p->a" ovan är alltså en förkortning av "(*p).a". Nu vill vi ha en länkad lista av heltal. Varje element i listan måste dels innehålla talet på den platsen (vi kallar det fältet "num") och dels en pekare till nästa element ("tail"). Förut hade vi en struct med två heltal. Nu har vi istället en struct med ett heltal samt en pekare:

struct IntItem {
  int num;
  ... tail;
};

Men vad ska "..." vara? "tail" ska peka på ett element i listan, dvs en "struct IntItem", så vi skriver:

struct IntItem *tail;

(Jämför med deklarationen av p ovan.) Vi har nu:

struct IntItem {
  int num;
  struct IntItem *tail;
};

och vi kan deklarera en variabel x som består av ett listelement med:

struct IntItem x;

(Det är dock ganska oanvändbart med sådana variabler. Vi arbetar vanligtvis med pekare till listelement, dvs "struct IntItem *".) Slutligen vill vi kunna skriva bara "IntItem" för typen av ett listelement istället för "struct IntItem", så vi tar hjälp av typedef:

typedef struct IntItem IntItem;