7. Strukturer och unioner

 

1.     Strukturer: egna typer.

2.     Pekare till strukturelement.

3.     Unioner.

4.     Fördefinitioner i headerfiler.

 

1. Strukturer: egna typer.

 

En struktur är en sorts gruppering av variabler som hör ihop. Man kan t.ex. göra en lista med personuppgifter. Uppgifterna i sig är kanske inte av samma typ, men varje personuppgift är lik varje annan i deklarationen. Då är det på sin plats att gruppera ihop uppgifterna under ett gruppnamn, en struktur. Denna struktur kan vi sedan betrakta som en ny datatyp: vi kan skapa en eller flera variabler av den typen.

 

struct person {

               char Namn[21];

               char Adress[26];

               char PostNr[6];

               char Ort[11];

               short Alder;

              };

 

// Deklarera en list-variabel kallad ‘Medlem’:

struct person Medlem[25];

 

Detta ger en lista som rymmer 25 medlemmars namn, adress, postnummer, ort och ålder.

 

Om vi har ett index (medlemsnummer) kan vi nå vilken uppgift som helst för den medlemmen genom att använda elementoperatorn (en punkt):

 

// Flytta en adress från listan till en temporär variabel:

strcpy(TempAdress, Medlem[index].Adress);

 

// Skriv ut en medlemsuppgift:

printf("Namn......: %s\n",Medlem[index].Namn);

printf("Adress....: %s\n",Medlem[index].Adress);

printf("Postadress: %s",Medlem[index].Postnr);

printf(" %s\n",Medlem[index].Ort);

 

2. Pekare till strukturelement.

 

Man kan också använda pekare tillsammans med den indirekta operatorn (->):

 

// Vi använder oss av samma struktur som ovan:

struct person {

               char Namn[21];

               char Adress[26];

               char PostNr[6];

               char Ort[11];

               short Alder;

              };

 

// Deklarera en list-variabel kallad ‘Medlem’:

struct person Medlem[25];

 

// Först behöver vi också en pekare till strukturen:

struct person *pMedlem;

pMedlem = &Medlem[0]; //(Eller: pMedlem = Medlem)

 

// Flytta en adress från listan till en temporär variabel:

strcpy(TempAdress, pMedlem->Adress);

 

// Skriv ut en medlemsuppgift:

printf("Namn......: %s\n",pMedlem->Namn);

printf("Adress....: %s\n",pMedlem->Adress);

printf("Postadress: %s",pMedlem->Postnr);

printf(" %s\n",pMedlem->Ort);

 

Fördelen med att använda en pekare är densamma som förut: Vi kan låta en funktion ändra innehållet i vårt programs lokala variabler.

 

3. Unioner.

 

Varje gång du deklarerar en variabel sker följande:

 

·     Kompilatorn reserverar minnesutrymme, så mycket utrymme som en variabel av angiven typ behöver. Om det är en lista reserveras utrymme för listans alla element.

·     Kompilatorn gör en egen anteckning som knyter samman det namn du givit variabeln med den fysiska adressen till det reserverade minnesutrymmet.

·     Kompilatorn tillför anteckningen information om hur data ska kodas vid skrivning respektive tolkas vid läsning i minnet.

 

Observera att detta innebär att varje variabel har sitt eget minnesutrymme, och att kompilatorn använder det enligt en viss tolkningsmall (typ).

 

När du skriver mer avancerade program kommer du förr eller senare att tycka att det skulle vara bra om du kunde tolka data på en viss adress på mer än ett sätt. En metod att göra detta är att skapa en union. Man kan säga följande om en union:

 

·     En union är en ny datatyp som du definierar.

·     En union definierar mer än en typ för samma minnesutrymme.

·     En union tilldelas tillräckligt utrymme för att rymma en variabel i listan - den som kräver mest utrymme.

·     Alla variabler i listan använder samma startadress i minnet.

·     Kompilatorn tillåter dig att fritt använda valfri typ av de som listats för unionen, genom att använda motsvarande namn p.s.s. som vid struktur.

 

Man deklarerar en union enligt följande syntax:

 

union <nytt typnamn> {

                      int iTal;

                     char cBokstav;

                     long lTal;

                    }[<variabel>];

 

Observera att man får deklarera en variabel av den nya typen genom att ange dess namn direkt efter listan enligt ovan. Man kan deklarera variabler av den nya typen som vanligt:

 

union <ovanstående typnamn> <variabel>;

 

Vi kunde t.ex. lösa problemet med ASCII-koderna på ett mera ‘rumsrent’ sätt. Vi gjorde ju faktiskt så att vi ‘lurade’ printf() genom att ange två olika typer för samma variabel. Eftersom printf() bara får värdet av en variabel så kan vi göra så. Detta fungerar dock inte i andra fall. Vi kan t.ex. inte lura våra egna funktioner med variabler som de känner. Då använder vi en union i stället:

 

#include <stdio.h>

void main()

{       

   union ascii {

               int sSiffra;

               char cBokstav;

               } AsciiKod;

   AsciiKod.cBokstav = 'A';                        

   while (AsciiKod.cBokstav <= 'Z')

   {

      printf("ASCII-koden för %c = %d\n", AsciiKod.cBokstav, AsciiKod.sSiffra);

      AsciiKod.sSiffra += 1;

   }

}

 

I detta enkla fallet kan man naturligtvis även använda typkonverteringsoperatorn, men du kommer förr eller senare att finna ett exempel där du hellre använder en union.

 

4. Fördefinitioner i headerfiler.

 

När man använder operativsystemets headerfiler finns kompilerade objektsfiler med diverse funktioner. Ofta behöver man använda datastrukturer till detta, och för att man ska slippa definiera dessa själv finns de fördefinierade i headerfilen. Man behöver bara veta vad strukturen heter, och vad de strukturelement man behöver använda heter. I time.h hittar vi följande:

 

 

struct tm  {

    int tm_sec;  /* seconds after the minute - [0,59] */

    int tm_min;   /* minutes after the hour - [0,59] */

    int tm_hour;  /* hours since midnight - [0,23] */

    int tm_mday;  /* day of the month - [1,31] */

    int tm_mon;   /* months since January - [0,11] */

    int tm_year;  /* years since 1900 */

    int tm_wdag;  /* days since Sunday - [0,6] */

    int tm_yday;  /* days since January 1 - [0,365] */

    int tm_isdst; /* daylight savings time flag */

    };

 

typedef long time_t;

 

Längre ner står s.k. prototyper för funktionerna (funktionerna finns bara som objektskod, kompilatorn kan inte läsa objektskod, men behöver veta detaljer om funktionens namn, returtyp samt inparametrar, detta står i prototyperna):

 

/* function prototypes */

 

_CRTIMP time_t__cdecl time(time_t*);

 

_CRTIMP struct tm*__cdecl localtime(const time_t);

 

Man deklarerar egna variabler (i sitt eget program) av de typer som deklarerats i headerfilen, t.ex:

 

struct tm* idag;   // Min variabel ‘idag’, typ tm*

 

time_t tid;       // Min variabel ‘tid’, typ time_t

 

Sedan kan man anropa funktionerna:

 

time(&tid);

 

idag = localtime(&tid);

 

Funktionen har fyllt i min variabel ‘idag’, och nu kan jag läsa de olika elementen,

 

if (idag->tm_min < 10)

{

         printf("Klockan är nu %d:%d%d\n",idag->tm_hour, 0, idag->tm_min);

}

else

{

         printf("Klockan är nu %d:%d\n",idag->tm_hour, idag->tm_min);

}

printf("I dag är det den %d/%d-%d\n", idag->tm_mday, idag->tm_mon+1 ,idag->tm_year+1900);

 

 

 

Övningsuppgift:

·      Skriv ett program som ger dig datum och klockslag.

·      Kalla programmet tiddatum.

·      Utskriften ska se ut enligt nedan.