9. Strukturer

 

1.        Strukturer.

2.        Strukturvariabler och pekare.

 

1. Strukturer.

 

Som vi sett finns ett begränsat antal datatyper avsedda att representera vissa data i våra program. Datatypen 'int' är alldeles utmärkt när man handskas med heltal, medan 'float' och 'double' mycket väl täcker de flesta behov av flyttals­lagring och bearbetning.

 

Om vi däremot skulle vilja beskriva en person finner vi att vi inte på långa väg­ar när har en datatyp som klarar detta. En lista av typen char skulle kanske kun­na hjälpa oss med rena textuppgifter, men vi kanske även har ett behov av att kunna skriva in till exempel födelseår som heltal. Om man till exempel har en ideell fören­ing, behöver man varje år redovisa vilka medlemmar som är under 25 år, för bi­dragens skull. Då vill man gärna ha årtalet som ett heltal, så att det går att utföra beräkningar med det. Om vi lagrar alla uppgifter i en sträng kom­mer vi att behöva speciella metoder för att hantera de separata uppgifterna i strängen, speciellt de numeriska. Detta blir alldeles för klumpigt.

 

Vi skulle alltså behöva en ny datatyp där vi själva kan bestämma hur data ska lagras. Det finns möjlighet till detta, vilken baseras på de existerande da­ta­typ­erna, och det kallas strukturer. Man deklarerar dessa med hjälp av nyckel­ordet struct. Det gör i själva verket ingenting att strukturerna baseras på de red­an exi­sterande datatyperna, eftersom de allra flesta situationerna kan lösas gen­om att man åstadkommer en lämplig kombination av datatyper.

 

Exemplet med en datatyp som beskriver en person skulle kunna innehålla ett antal listor av typen char, för att lagra olika textuppgifter, till exempel namn, adress etcetera, medan en int tar hand om födelseåret. Man kan se det som att man slår samman flera variabler under ett samlingsnamn. När man sedan dek­larerar en variabel baserad på den nya datatypen kommer det nya variabel­nam­net att fungera som ett gruppnamn för alla de variabler som finns beskrivna i strukturen.

 

Definition: En struktur är en ny datatyp vilken baseras på flera existerande datatyper av varierande slag. Man kan se det som att man slår samman flera variabler under ett samlingsnamn, ett gruppnamn.

 

Nu ska vi titta på ett exempel, vilket belyser hur man skriver en struktur­deklara­tion för personuppgifter:

 

struct person {

               char Namn[21];

               char Adress[26];

               char PostNr[7];

               char Ort[11];

               int iFodelseAr;

              };

 

Syntaxen för detta är:

 

struct nytt typnamn { deklarationssatser };

 

Vi skapar alltså en ny datatyp, och ger den ett nytt typnamn. När vi i fortsätt­ningen refererar till detta namn ingår nyckelordet 'struct' i den nya datatypens namn. De olika deklarationssatserna i strukturdeklarationen kallas strukturens element.

 

Observera att den nya datatypen bara är en ny datatyp. Inga variabler har dekla­r­er­ats än. Strukturen är alltså en mall för hur variabler baserade på vår nya data­typ ska se ut. Det går därför inte att initiera de olika elementen i struktur­dekla­r­a­tionen.

 

Observera även att deklarationen avslutas med ett semikolon. Glömmer man det kan man få de mest märkliga kompileringsfel. Anledningen är att man kan an­vända själva strukturdeklarationen direkt som datatyp i en variabeldeklarations­sats, och en sådan avslutas ju med ett semikolon. Har vi inget semikolon kom­mer kompilatorn att uppfatta allt som kommer efter slutklammern som förslag på variabelnamn, och då får vi kompileringsfel. Om denna deklaration står i en headerfil, och/eller om en  headerfil öppnas direkt efter strukturdeklarationen, får vi kompileringsfelet i en helt annan fil än den där felet finns.

 

Nu ska vi i alla fall deklarera en lista baserad på den nya strukturen. Vi behöver kanske en medlemslista, och vilken datatyp passar då bäst? Antagligen vår ny­deklarerade 'struct person'. Lägg märke till att man använder små bokstäver, allt i enlighet med att alla andra datatyper skrivs med mså bokstäver. Så här kan vi skapa vår medlemslista:

 

struct person Medlem[25];

 

Detta ger en lista som rymmer 25 medlemmars namn, adress, postnummer, ort och ålder. Listans namn, variabelnamnet, är 'Medlem'.

 

Om vi har ett index, till exempel ett medlemsnummer, kan vi nå vilken uppgift som helst för den medlemmen genom att använda den så kallade elementopera­torn, vilken består av en punkt:

 

// Fyll i födelseår för medlem nummer 12:

cin >> Medlem[12].iFodelseAr;

 

// Skriv ut en medlemsuppgift angiven av ett iIndex:

cout << "Namn........: " << Medlem[iIndex].Namn << '\n';

cout << "Adress......: " << Medlem[iIndex].Adress << '\n';

cout << "Postadress..: " << Medlem[iIndex].PostNr

     << ' ' << Medlem[iIndex].Ort << '\n';

 

Övningsuppgift 9.1.1:

·      Skapa ett nytt projekt Medlem.

·      Skriv en strukturdeklaration enligt ovan.

·      Deklarera en lista som kan rymma medlemmarna i din grupp.

·      Skriv en programslinga där du kan mata in alla uppgifter. Om någon inte vill uppge sin adress kan du fingera den.

·      Skriv en adresslista enligt ovan. Varje post i listan ska skiljas från föregående med en blankrad.

·      Så här kan det se ut:

 

                

 

Och så här blir utskriften:

 

                

 

2. Pekare till strukturelement.

 

Man kan också använda pekare tillsammans med den indirekta operatorn (->) när man vill använda en medlem i en strukturvariabel:

 

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

struct person {

               char Namn[21];

               char Adress[26];

               char PostNr[6];

               char Ort[11];

               short iFodelseAr;

              };

 

// 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:

cout << "Namn......: " << pMedlem->Namn << '\n';

cout << "Adress....: " << pMedlem->Adress << '\n';

cout << "Postadress: " << pMedlem->Postnr

     << ' ' << pMedlem->Ort << '\n';

 

Övningsuppgift 9.2.1:

·      Öppna projektet Medlem om det inte redan är öppet.

·      Deklarera en pekare, och använd den till att peka på medlem nummer 0.

·      Låt användaren fylla i uppgifterna, men använd pekaren denna gång.

·      När första medlemmens uppgifter är klara ökar du pekarens värde med hjälp av ökningsoperatorn (++), så att den pekar på nästa element och så vidare.

·      Före utskrift återställer du pekaren till att peka på medlem nummer 0.

·      Använd pekaren och ökningsoperatorn även i utskriften.

 

Man kan även använda en teknik med pekare kallad indexerad pekare. Det går ut på att man inte flyttar pekaren, det vill säga man ändrar inte adressen i pekar­en som vi gjorde ovan, utan man anger ett index till pekaren. Syntaxen säger att man gör detta genom att ta bort stjärnan före pekarnamnet och lägger till ett index mellan hakparenteser. Vill vi till exempel ange födelseåldern för en medlem på tredje plats i listan till 78 kan man skriva så här:

 

pMedlem[2].iFodelseAr = 78;

 

Man skulle kunna fylla i namnet på detta sätt:

 

cin >> pMedlem[2].Namn;

 

...eller, eftersom vi redan lärt oss att hantera problemet med blanktecken:

 

cin.getline(pMedlem[2].Namn);

 

Övningsuppgift 9.2.2:

·      Öppna projektet Medlem om det inte redan är öppet.

·      Ändra i programmet så att pekaren används indexerat i stället för att flytta den för varje ny medlem.

·      Använd denna teknik vid såväl inmatning som utskrift. Observera att du alltså inte behöver återställa pekaren före utskriften. Den påverkas inte under indexeringen.