TDIU01 - Programmering i C++, grundkurs Pekare och Listor Eric Elfving Institutionen för datavetenskap 31 oktober 2014
Översikt 2/41 Internminne Pekare Dynamiska datastrukturer (Enkellänkade) listor Arbeta med flera filer
Internminne - RAM 3/41 Datorns internminne (RAM, random access memory) består av en ordnad sekvens bitar Vi kan normalt sätt adressera (komma åt) en byte i taget (8 bitar)
Minne 4/41
Minne 5/41 Vad händer i minnet? int x 3421; x: int 3421
Variabler 6/41 En variabel har alltid: Ett namn En datatyp Ett värde En adress
Pekare I En pekare lagrar en adress till en given position i minnet I Deklareras med en asterisk (*) int * ip ; 7/41
Pekare I Man kan ta fram adressen till en variabel med adressoperatorn (&) int x 3421; int * ip & x ; 8/41 int x: ip: 3421
Pekare 9/41 För att komma åt det värde en pekare pekar på används avrefereringsoperatorn (*) # include <iostream > using namespace std ; int main () int x 3421; int *ip &x; cout << ip << endl ; cout << * ip << endl ; return 0; 14 3421 Vi får bara avreferera pekare som pekar på adresser som vårt program äger
Pekare I Pekare används oftast för att skapa dynamiskt minne I Vi allokerar nytt minne med new-operatorn I Vi kan endast nå det nya utrymmet med hjälp av pekaren int * ip ; ip = new int ; * ip = 5123; int * ip new int 5123; 10/41
Pekare 11/41 Minnesläcka Om vi redan pekar på en allokerad minnesadress och sedan ber om nytt utrymme tappar vi bort det vi pekade på. Detta fenomen kallas minnesläcka Minnesläckor är det vanligaste felet när vi jobbar med pekare Vi äger fortfarande utrymmet, men har inget sätt att nå det int * ip new int 44; ip = new int 123; int 44 int ip: 123
Pekare 12/41 Om vi vill lämna tillbaka minne till operativsystemet gör vi det med delete-operatorn int *ip new int 345; int ip: 345 delete ip; int ip: 345 OBS, delete-operatorn ändrar inte på pekarens värde!
Pekare 13/41 Värdet nullptr betyder inget värde för pekare int * ip ; // ip sätts till nullptr ip = new int ; // det nya heltalet får värdet 0 delete ip; ip = nullptr ; kodrad: 1 2 3 4 int int Minnet: ip: ip: 0 ip: 0 ip:
Pekare 14/41 void f( int * p) delete p; p = new int 2; int main () int * ptr new int 12; f( ptr ); cout << * ptr << endl ; delete ptr ; Vad händer?
Pekare 15/41 Precis som vanligt måste vi ta emot referenser om vi vill ändra på värden: void f( int *& p) delete p; p = new int 2; int main () int * ptr new int 12; f( ptr ); cout << * ptr << endl ; delete ptr ;
Alias 16/41 Om man tycker det är jobbigt med alla asterisker kan man skapa ett nytt namn för en typ med aliasdefinition Skrivs på formen using NAMN = BESKRIVNING; using int_ptr = int *; void f( int_ptr & p); int main () int_ptr ptr new int 4 ; delete ptr ;
Pekare till poster 17/41 För att komma åt fälten i poster kan man använda sig av piloperatorn (även kallad medlemsåtkomstoperatorn) (->) struct Book string title ; string author ; int pages ; ; Book * bp new Book ; bp- >title = " C++ Direkt "; bp->author = " Skansholm "; bp- >pages = 665; Man kan använda avreferering också, men då krävs parenteser cout << (*bp ). title << endl ;
Dynamiska datastrukturer 18/41 En dynamisk datastruktur kan ändra storlek under programkörningen Vi har redan använt två dynamiska datastrukturer, string och vector Det finns många fler inbyggda i std
Dynamiska datastrukturer 19/41 Tänk er att vi vill stoppa in ett värde sorterat i en vector. Då behöver vi: Hitta rätt position 7 0 4 12 17 0 1 2 3
Dynamiska datastrukturer 19/41 Tänk er att vi vill stoppa in ett värde sorterat i en vector. Då behöver vi: Hitta rätt position Utöka storleken 7 0 4 12 17? 0 1 2 3 4
Dynamiska datastrukturer 19/41 Tänk er att vi vill stoppa in ett värde sorterat i en vector. Då behöver vi: Hitta rätt position Utöka storleken Flytta alla värden som ligger efter hittad position 7 0 4 12 17? 0 1 2 3 4
Dynamiska datastrukturer 19/41 Tänk er att vi vill stoppa in ett värde sorterat i en vector. Då behöver vi: Hitta rätt position Utöka storleken Flytta alla värden som ligger efter hittad position 7 0 4 12 17 17 0 1 2 3 4
Dynamiska datastrukturer 19/41 Tänk er att vi vill stoppa in ett värde sorterat i en vector. Då behöver vi: Hitta rätt position Utöka storleken Flytta alla värden som ligger efter hittad position Stoppa in värdet 7 0 4 12 12 17 0 1 2 3 4
Dynamiska datastrukturer 19/41 Tänk er att vi vill stoppa in ett värde sorterat i en vector. Då behöver vi: Hitta rätt position Utöka storleken Flytta alla värden som ligger efter hittad position Stoppa in värdet 7 0 4 7 12 17 0 1 2 3 4
Enkellänkade listor 20/41 En enkellänkad lista består av noder. En nod är en post som innehåller två saker, ett värde av någon datatyp samt en pekare till nästa nod i listan. Detta gör en pekare till en nod till en lista. Tom lista (eller slutet av listan) markeras med pekarvärdet nullptr. 3 7 9
Enkellänkade listor 3 21/41 7 9
Enkellänkade listor 22/41 Deklaration av en listtyp: struct List_Node int data ; List_Node * next ; ; Alternativ lösning med alias: struct List_Node ; using List = List_Node *; struct List_Node int data ; List next ; ;
Viktigt till labben 23/41 Initiera din lista till nullptr! using List = struct List_Node *; struct List_Node int data ; List next ; ; int main () List l ;
Exempel 24/41 Antag att vi vill lägga till ett värde först i en lista Vi ser (förhoppningsvis) två fall: 1. Vi har en tom lista (inga element) 2. Vi har ett eller flera element
Exempel 25/41 insert_first 1. Tom lista l: Skapa en ny nod och stoppa in värdet l = new List_Node ; l- >data = value ; l- >next = nullptr ; l: value
Exempel 26/41 insert_first 2. Finns värden l: 4 123 Skapa en ny nod med en temporär pekare, stoppa in värdet och få in noden i listan List tmp new List_Node ; tmp - >data = value ; tmp - >next = l; l = tmp ; tmp: value l: 4 123
Exempel 27/41 insert_first void insert_first ( List & l, int value ) if ( l == nullptr ) l = new List_Node value, nullptr ; else List tmp new List_Node value, l ; l = tmp ;
Exempel 28/41 insert_first Vi gör egentligen samma sak i de två fallen! Om l är nullptr stoppar vi in nullptr som next, annars l. Därför kan vi även skriva funktionen såhär: void insert_first ( List & l, int value ) List tmp new List_Node value, l ; l = tmp ;
Exempel 29/41 insert_first Vi behöver inte ens temporären: void insert_first ( List & l, int value ) l = new List_Node value, l; Vi skapar en ny nod vars next-pekare pekar på den nod l pekar på Därefter ändrar vi l så att den pekar på den nya noden
Exempel 30/41 member Tänk er att vi har en sorterad lista Hur gör vi för att ta reda på om ett värde finns i listan? Fyra fall: 1. Tom lista! 2. Eftersökt värde är först i listan. 3. Värdet på första positionen är större än det vi söker efter 4. Värdet på första positionen är lägre än det vi söker efter
Exempel 31/41 member 1. Tom lista: l: Vi vet svaret: false!
Exempel 32/41 member 2. Värdet är först i listan: value: 4 l: 4 123 Vi vet svaret: true!
Exempel 33/41 member 3. Första värdet är större än det vi söker efter: value: 4 l: 32 123 Vi vet svaret: false!
Exempel 34/41 member 4. Första värdet är lägre än det vi söker efter: value: 35 l: 6 123 Värdet kan vara längre bak i listan...
Exempel 35/41 member bool member ( List l, int value ) if ( l == nullptr ) return false ; else if ( l- >data == value ) return true ; else if ( l- >data > value ) return false ; else // Kontrollera om värdet finns i resten av listan return member ( l- >next, value ); // rekursivt anrop!
Exempel 36/41 member Det var ett exempel på en rekursiv lösning, här kommer en iterativ: bool member ( List l, int value ) List cur = l; while ( cur!= nullptr ) if ( cur - >data == value ) return true ; else if ( cur - >data > value ) return false ; cur = cur ->next ; return false ;
Exempel 37/41 Vi måste alltid tänka på fallet tom lista! Om vi försöker komma åt en del av noden när pekaren har värdet nullptr får vi ett sementation fault Listing 1: test.cc # include <iostream > using namespace std ; struct List_Node int data ; List_Node * next ; ; int main () List_Node *l ; cout << l- >data ; $ g++ - std =c ++11 test.cc $./a. out Segmentation fault ( core dumped )
Att arbeta med flera filer 38/41 Hittills har vi skapat våra program med all kod i en cc-fil. Detta kan ställa till det ibland Det blir svårt att hitta i filen Vi måste kopiera kod om vi vill använda listan i flera program I C++ kan man dela upp sin kod i inkluderingsfiler (headerfiler) och implementationsfiler Headerfilen innehåller deklarationer Implementationsfilen innehåller definitioner (all kod)
Att arbeta med flera filer 39/41 # ifndef LIST_H # define LIST_H Listing 2: List.h using List = struct List_Node *; void insert_first ( List &, int ); bool member ( List, int ); # endif Listing 3: List.cc # include " List.h" struct List_Node int data ; List next ; ; void insert_first ( List & l, int value )... bool member ( List l, int value )...
Att arbeta med flera filer 40/41 # include " List.h" Listing 4: list-test.cc # include <iostream > using namespace std ; int main () List l ; for ( int i : 9,5,3,1 ) insert_first (l, i); if ( member (l,3) ) cout << " 3 fanns i listan!" << endl ; $ g++ - std =c ++11 list - test.cc List.cc $./a. out 3 fanns i listan!
www.liu.se