hogkvalite - vagen till c, 4de - jan skansholm, ulf bilting

269
Vagen till C ULF BILTING JAN SKANSHOLM Studentlitteratur

Upload: h1742283

Post on 05-Jan-2016

770 views

Category:

Documents


173 download

DESCRIPTION

C programming book in swedish

TRANSCRIPT

Page 1: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Vagen till C

ULF BILTING

JAN SKANSHOLM

Studentlitteratur

Page 2: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Kopieringsforbud

Dettaverk ar skyddat avlagen omupphovsratt. Kopiering,utover larares ratt att kopiera forundervisningsbruk enligt

BONUS-Presskopias avtal, ar forbjuden.Sadant avtal tecknas

mellan upphovsrattsorganisationer och huvudman for

utbildningsanordnare t.ex.kommuner/universitet. For

information omavtalet hanvisas till utbildningsanordnarenshuvudman ellerBONUS-Presskopia.

Den som bryter mot lagen om upphovsratt kan italas av

allmaniklagareochdomas till boterellerfangelse i upptill tvk ki samt bli skyldig att erlaggaersattning till

upphovsman/rattsinnehavare.

Denna trycksakar miljoanpassad, bade nar det galler

papper och tryckprocess.

Art.nr 2673

ISBN 978-91-44-07606-5

Upplaga 4:4

© Forfattarna och Studentlitteratur 1987,2011

www.studentlitteratur.se

Studentlitteratur AB, Lund

Omslagslayout: Jakob Meijling

Omslagsbild: Sura Nualpradid/Shutterstock

Printed by Eurographic Danmark A/S, Denmark 2014

Page 3: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Innehall

Forord 1

Inledning 31.1 C:s foregangare och historia 31.2 Standardisering41.3 Framtida utveckling av C 51.4 Bokens upplaggning 61.5 Ytterligare information 6

C i ett notskal 72.1 Det forsta programmet 72.2 Anvandning av variabler 102.3 Programmed repetition 122.4 Program med altemativa vagar 162.5 Egna funktioner 182.6 Lasning och skrivning av texter 192.7 Funktionema getchar och putchar 212.8 Ovningsuppgifter 24

C frSn grunden 253.1 Identifierare och nyckelord 253.2 Variabeldeklarationer 26

3.3 Heltalstyper 283.3.1 De fbrdefinierade heltalstypema 283.3.2 Heltalskonstanter 31

3.4 Texter 32

3.4.1 Teckenkoder 32

3.4.2 Teckenvariabler och teckenkonstanter 33

3.4.3 Textstrangar ochteckenfalt 353.5 Flyttalstyper 36

3.5.1 De fbrdefinierade flyttalstypema 373.5.2 Flyttalskonstanter 38

3.6 Aritmetiska typomvandlingar 383.6.1 Automatiska typomvandlingar 383.6.2 Explicita typomvandlingar 39

3.7 Upprakningstyper 39

Page 4: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Innehall

3.8 Typenvoid433.9 Utskrift med funktionen printf 433.10 Inlasning med funktionen scanf 483.11 Typdeklarationer (typedef) 533.12 Ovningsuppgifter 54

4 Uttryck och operatorer 574.1 Aritmetiskauttryck 584.2 Oknings- ochminskningsoperatorer 614.3 Jamfbrelseoperatorer634.4 Logiska operatorer 654.5 Villkorsoperatom 674.6 Bit-operatorer 684.7 Tilldelningsoperatorer 714.8 sizeof-operatom 734.9 Operatorprioriteter 744.10 Ovningsuppgifter 75

5 Satser 775.1 Uttryckssatser 775.2 Sammansatta satser 785.3 if-satsen 78

5.4 switch-satsen 815.5 while-satsen 835.6 do-satsen 855.7 for-satsen 865.8 NSstlade repetitionssatser 895.9 Hoppsatser905.10 Ovningsuppgifter 92

6 Funktioner och programstruktur 956.1 Funktionsdefmitioner 956.2 Funktionsdeklarationer 1006.3 Funktionsanrop 1036.4 Aldre syntax 108

6.4.1 Aldre funktionsdefmitioner 1086.4.2 Aldre funktionsdeklarationer 109

6.5 inline-funktioner 1096.6 Deklarationsomrade och synlighet 1106.7 Lagringsklasser 112

6.7.1 Lagringsklassen auto 1136.7.2 Lagringsklassen register 113

) Studentlitteratur

Page 5: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6.7.3 Lagringsklassen extern 1146.7.4 Lagringsklassen static 1206.7.5 Flyktiga objekt - volatile 122

6.8 Modular programutveckling 1236.9 Ovningsuppgifter 128

7 Pekare och fait 131

7.1 Pekare och adresser 131

7.2 Fait och pekarvarden 1347.2.1 Faltstorlek 134

7.2.2 Faltindexering 1357.3 Aritmetik pa pekarvarden 1377.4 Pekarstegning i stallet for indexering 1387.5 Pekarvarden som parametrar 1397.6 Pekare och const 142

7.7 Initiering av fait 1447.8 Teckenfalt och textstrangar 1447.9 Flerdimensionella fait 148

7.10 Fait av pekare samt pekare till pekare 1507.11 Pekare till dynamiskt minne 1537.12 Pekare till funktioner 154

7.13 Komplicerade deklarationer och typedef 1567.13.1 restrict pa pekare 157

7.14 Ovningsuppgifter 158

8 Sammansatta typer 1598.1 Poster (struct) 159

8.1.1 Organisation av s truct-deklarationer 1628.1.2 Initiering av poster 1638.1.3 Pekare till poster 163

8.2 Lankade datastrukturer 166

8.2.1 Lankade listor 168

8.2.2 Sokning i tabeller 1738.2.3 Trad 180

8.3 Bitfaltl84

8.3.1 Bitmasker 186

8.4 Variabla typer (union) 1878.5 struct/union och typedef 1898.6 Ovningsuppgifter 190

9 Preprocessorn 1919.1 Makron 191

Innehall

) Studentlitteratur 111

Page 6: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Innehall

9.2 Makronmedparametrar 1929.3 Filinkludering 1939.4 Villkorligkompilering 194

9.4.1 Skydd mot flergangsinkluderande 1959.5 Andra direktiv 195

9.6 Fordefinierademakron 196

9.7 Ovningsuppgifter 196

10 Standardfunktioner och standardbibliotek 19710.1 Inkluderingsfiler 19810.2 Gemensammadefinitioner<stddef.h> och <errno.h>199

10.3 Teckentyper<ctype.h> 19910.4 Lokala konventioner <locale . h> 200

10.5 Matematiska funktioner <math. h> 200

10.6 Standard in- och utmatning <stdio. h> 20110.6.1 Filer 202

10.6.2 In- och utmatning av oformaterad text 20310.6.3 Formaterad in- och utmatning av text 20610.6.4 Direkt in-och utmatning 20710.6.5 Felhantering 208

10.7 Generellafunktioner<stdlib.h>209

10.7.1 Strangkonvertering 20910.7.2 Slumptalsgeneratorer 21010.7.3 Minneshantering 21010.7.4 Interaktion med omgivningen 21110.7.5 Funktioner for sokning och sortering 212

10.8 Stranghantering <string. h> 21310.8.1 Kopiering och sammanfogning 21310.8.2 Jamfbrelse 214

10.8.3 Langdberakning 21410.8.4 Sokning 21410.8.5 Operationer i generellt minne 215

10.9 Datum och tid<time.h> 216

10.9.1 Processortid 216

10.9.2 Kalendertid 216

10.10 Icke-lokalahopp <setjmp.h>21910.11 Mjukvaruavbrott <signal .h> 21910.12 Variabelt parameterantal <stdarg. h> 22110.13 Bibliotek i C99 och framtiden 222

10.13.1 Matematiska funktioner 223

10.13.2 Unicode och wchar_t 22310.13.3 Okad sakerhet 223

10.13.4 Nasta C-standard 224

IV © Studentlitteratur

Page 7: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10.14 Ovningsuppgifter 224

11 C i olika omgivningar 22711.1 Parametrar till programmet 22811.2 Operativsystemmiljo, Unix 229

11.2.1 Systemanrop 23011.2.2 In- och utmatning 23011.2.3 Styming av processer 231

11.3 Operativsystemmiljo, persondatorer 23211.4 Programmeringsverktyg 233

11.4.1 Statisk avlusning, lint 23311.4.2 Syntaxanalys, yacc och lex 23411.4.3 Programunderhall, make 23411.4.4 Programprofllering, effektivitet 23511.4.5 Symbolisk avlusning 235

11.5 Naken omgivning 23611.5.1 Att anvanda en bestamd minnesplats 23611.5.2 Att omtolka data godtyckligt 23811.5.3 Anrop av assemblerkod 23911.5.4 Avbrottshantering 24011.5.5 Att gora belt vansinniga saker i C 241

11.6 Flyttbarhet av C-program 24211.6.1 Identifierare 243

11.6.2 Heltalstyper 24311.6.3 Poster 243

11.6.4 Funktioner 244

11.7 Ovningsuppgifter 244

Appendix A Reserverade ord och operatorer 245

Appendix B LATIN_1 koder 247

Appendix C Binar lagring av tal 249

Appendix D Aritmetiska typomvandlingar 253

Appendix E printf 257

Appendix F scanf 259

Sakregister 261

© Studentlitteratur

Innehall

Page 8: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Inledning 1

Detta kapitel tar upp nagra amnenkring spraketC, forklararvarfor och hur det kom tillsamt diskuterar standardisering och framtida utveckling.

1.1 C:s fbregangare och historiaC konstmerades i borjan pa sjuttiotalet av Dennis Ritchie vid Bell Laboratories. Detdirekta skalet till att skapa C var att kollegan Ken Thompson nagra ar tidigare gjortspraket B for att skriva om Unix fran assemblerkod till ett mer flytt- och lasbart hog-nivasprak. B visade sig ha en del brister som borde rattas till. Bland annat farms detegentligen inga typer i B, bara operationer pa maskinord. Da skapades C for ytterligareen omskrivning av Unix och C har sedan dess anvants som huvudsprak i Unix. Bbyggde i sin tur pa spraket BCPL, som var avsett for i stort sett samma sak: att manskulle fa ett hognivaspraks fordelar men anda ha kvar kontakten med maskinvaru-verkligheten nar sa behovdes.

Dessa sprak (liksom t.ex. Pascal och Ada) har sina rotter i Algol 60 vad galler denovergripande strukturen. Man ville dock bort ffan Algols totala maskinoberoende foratt kunna gora de saker man normalt anvander assembler till utan att behova gdraomskrivningar och anpassningar.

Mycket av C:s utveckling ar naturligtvis direkt kopplad till utvecklingen av Unix. C-kompilatorema for Unix skrevs noggrant uppdelade i maskinberoende och maskinoberoende delar sa att de med relativt liten anstrangning kunde modifieras till att gene-rera kod for nya processorer. Att C snabbt blev tillgangligt for nya processorer sa attUnix kunde flyttas till dessa ar ett mycket viktigt skal till sprakets populariteten.

Praktiskt taget hela Unix, saval operativsystem som anvandarprogram (till och medassemblem) ar helt och hallet skrivna i C. Undantagen ar ett antal hundra rader i opera-tivsystemet som hanterar avbrott pa allra lagsta niva samt specialinstruktioner for vissaprocessorer (t.ex. minneshantering och processbyte).

© Studentlitteratur

Page 9: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

1. Inledning

Foren C-programmerare finns detnastan aldrig anledning attkanna till assemblerpro-grammering. Det enda tillfallet da det eventuellt kan behovas ar nar man behover smaanpassningar till vissa belt maskinspecifika instruktioner och mekanismer.

C bar under aren genomgatt en viss utveckling. Man bar bland annat lanat en delmekanismer fran andra sprak. Andringar avexisterade konstmktioner barvarit mycketfa ocb i fbrekommande fall bar det rort sig cm tillrattalaggande av tvetydigbeter. Defiesta ovriga andringarbar varit rena tillagg dar inget gammaltandrats.

1.2 StandardiseringDet ar naturligtvis av synnerlig vikt att ett programmeringssprak ar val definierat.Malet ar att ett korrekt skrivet program som fiingerar i en viss miljo, skall vara likakorrektocb aven fungera i en annanmiljo. Detta kravertill att borja med att det pa ettotvetydigt vis definieras vad som ar grammatiskt tillatet ocb vad olika operationerskall betyda. Standardisering ar naturligtvis ocksa fomtsattningen for att programoverbuvudtaget skall kunna flyttas Mn en miljo till en annan.

Den traditionella standarden ar den referensmanual som ingar i boken "Tbe C Programming Language" (Prentice-Hall, 1978) som skrevs av Dennis Ritcbie ocb BrianKemigban. Traditionell C brukar kallas K&R C efter forfattamas eftemamn. Boken ar

i ovrigt en larobokocb inneballermangaexempelpa C-program ocb programmering iallmanbet. SpraketC bar emellertidutvecklats sedan dess. Den ursprungliga upplaganav boken ar darfor foraldrad ocb en ny upplaga gavs ut 1988.

Den amerikanska standardiseringsorganisationen ANSI tog 1989 fram en standard forC. Man var mycket noga med att folja det som bade bant i den naturliga utvecklingenav spraket. Denna standard bar traditionellt gatt under beteckningenANSI-C, men denkallas ocksa C89. Vi kommer i boken att anvanda beteckningen C89 nar vi refererartill denna standard.

En ny standard for C antogs 1999. Denna standard gar under beteckningen C99.Manga av de nya delama i denna standard anvands dock fortfarande inte av mangaprogrammerare ocb ar inte ens alltid fullt implementerade av dagens kompilatorer.Forklaringen ligger nog i att andra sprak (C++, Java ocb C#) bar tagit over i majorite-ten av storre programvaruprojekt. C anvands dock fortfarande till det som det traditio

nellt ar riktigt bra pa, ntoligen att bantera de sma detaljer som bebovs i maskinnarakod. C ger programmeraren full kontroll (ocb fullt ansvar) over programmets bete-ende. Bade C++ ocb C# bar kvar C som en del av respektive sprak, just av detta skal.

Denna bok banterar C99 genom att, i respektive avsnitt, beskriva det som denna stan-Idard infort. Detta markeras med en speciell C99-markor. Vi skiljer ocksa pa de merobskyra tillaggen ocb de delar som slagit rot ocb ar i anvandning.

4 © Studentlitteratur

Page 10: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

1.3 Framtida utveckling av C

Derma bok ar inte avsedd att vara en referensmanual men den ar en fullstandig intro-duktion till spraket och dess anvandningar i olika miljoer. Vi bar belt ocb ballet utgattfran standarden i all terminologi ocb exempel. Standarden tillater manga aldre kon-struktioner ocb vi beskriver aven dessa. I de fiesta programmerares uppgiffer ingar juatt lasa ocb modifiera aldre kod.

1.3 Framtida utveckling av CC som det ser ut i dag kommermed stor sarmolikbet att fiimas kvar ocb vara populartlang tid framover. Det ar latt att inse detta om man tanker pa de omodema sprak somutvecklades for lange sedanmen som fortfarande anvands ocb stods i dag. Det skapasju tyvarr ett inbyggtmotsatsforballande mellan standardisering ocb vidareutveckling.Det markstydligt i C99-standarden dar man inte gjort specielltmycketat C efter tio ar,utan varit mest intresserad av att bebMla C som det ar ocb endast gora enkla ocb bakat-kompatibla tillagg samt klarlagga otydligbeter ocb tvetydigbeter.

Det firms mangasprakutvecklade utifranC. Ett exempel ar C++ som ffanborjan(medmycket fa undantag)var en ren pabyggnadpa C. Numera stammerdetta inte belt efter-som en del konstmktioner inforts i C99 som inte firms i C++. For det mesta kan man i

alia fall saga att ett C-program ocksa ar ett program i C++. C++ ar konstruerat avBjame Stroustrupvid Bell Laboratories.Det viktigaste tillagget i C++ ar att man infortklasser, en typkonstruktionsom bade irmefattar beskrivning av ett dataobjekt ocb ope-rationema pa det. Man kan dynamiskt skapa nya objekt av en viss klass ocb sedan app-licera de operationer som ar definierade for objektet. Mekanismen leder till en pro-grammeringsstil som pa ett tydligt satt stoder uppdelning av ett program i banterbaradelar. Man brukar kalla denna programmeringsstil objektorienterad. Sattet att dekla-rera fimktioner med prototypersom infordes i C-standardenkommerjust fran C++, darman varit extra noggrarm med att ta bort mojligbeter till feldeklarationer ocb felan-vandning av funktioner.

Java ar ett armat exempel, som i stor utstrackning lanat bade sin syntax ocb mycket avsina mekanismer fran C ocb C++. Den viktigaste skillnaden ar att Java ar processor-

ocb maskinoberoende ocb i sina standarder omfattar mycket stora bibliotek for grafik,anvandargranssnitt, databaser, kommunikation m.m. C# foljer samma ideer som Javaocb ar Microsofts buvudsprak for programmering i .NBT-miljo.

Ytterligare ett sprak som bor namnas ar Objective-C som ar en utvidgning av C, inspi-rerad av det objektorienterade spraket Smalltalk. Objective-C konstruerades redan pa1980-talet men bar idag fatt ett uppsving tack vare att det anvands i Apples operativ-system Mac OS X ocb iOS. Det senare anvands iPbone ocb iPad.

) Studentlitteratur

Page 11: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

1. Inledning

Ckommer attbesta som ettsprak for dem som behover uttrycka sig padeltaljniva ochsom villkunna utfora vaddevill, bortom skyddande begransningar.

1.4 Bokens upplaggningKapitel 2 ar en oversikt av spraket darvi direkt studerar enklamenkompletta exempel,sa att man kan se hur C ser ut utan att forsta alia detaljer.

Kapitel 3-9 ar en ingaende beskrivning av spr&kets alia delar. Materialet ar ordnat saatt man kan lasakapitlen i sekvens om man vill fa en komplett oversiktav C.

Kapitel 10ar en genomgang av de standardfunktioner som finns i standarden. Vipre-senterar dem med atfoljande exempel.

Kapitel 11 beskriverhur C kan se ut i olika programmeringsmiljoer. Ett storre avsnittagnas at att beskriva C for en "naken" miljo, en fristaende dator utan operativsystem.Detta avsnitt fomtsatter viss kunskap om assemblerprogrammering och nagon processors principiella funktion.

1.5 Ytterligare informationTillbokenfinns en webbsida med diverse information, bl.a. losningar till ovningsupp-giftema. Man kan na denna sida via www. studentiitteratur. se.

) Studentiitteratur

Page 12: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

C i ett notskal

I delta kapitel skall vi studera nagra exempel pa C-program. Avsikten ar alt lasarenskall fa en forstauppfattningom hur program skrivna i C ser ut. Nagra olika sprakkon-struktioner demonstreras och kommenteras men langt ifran alia mojligheter 1 Cbehandlas och beskrivningen ar bara oversiktlig. De ovriga kapitlen i boken kommeralt ge detaljerade beskrivningar av alia delar av C.

2.1 Det forsta programmet

Som forsta exempel skall vi studera ett C-program som ger en enkel utskrift. Vi borjarmed att skriva sjalva programtexten, den s.k. kdlltexten eller kdllkoden. For att kurmagora delta kan vi anvanda ett enkelt textredigeringsprogram,en texteditor. Exempel paen sadan ar Anteckningar {Notepad) i Windows. Programmet ser ut pa foljande salt:

tinclude <stdio.h>

int main()

printf("Hej hopp!\n");

return 0;

Denna programtext sparas sedan som en textfil. Det brukar kravas att en textfil sominnehaller kallkoden till C-program bar namn som slutar med .c. Lat oss anta att visparar vart program i en textfil som vi kallar exi. c.

Innan programmet kan koras, eller exekveras, maste det oversattas, kompileras. Somexempel visar vi hur man skriver nar man bar ett kommandofdnster {Kommandotolkeni Windows, Terminalfdnster i Linux) och anvander den allmant spridda gratiskompila-tom fran Gnu^ Vi ger kommandot

' Densenaste versionen av denna kan hamtas fran www. gnu. org. ForWindows-anvandarekan det vara enklare att ladda ner kompilatom som en del av programutvecklingssystemetDev-C++ fran www.bloodshed, net/devcpp . html.

© Studentlitteratur

Page 13: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

2. C i ett ndtskal

gcc exl.c

och vi far en exekverbar fil som brukar beta a. exe i MS-DOS och a. out i Linux. Vill

vi att filenmed det exekverbara programmet skallbeta nagot annat, t.ex. hej. exe, kanvi i stallet skriva

gcc -o hej.exe exl.c

Efter -o angerman det namnman vill att det exekverbara programmet skallba. For attkoraprogrammet skriverman sedanbelt enkeltprogrammets namn:

hej.exe

Nar programmet kors kommer det att skriva ut texten

Hej hopp!

Nar man skriver program ar det ofta lampligt att dela upp dem i flera filer. Da skallmanraknaupp alia filema i kommandot till gcc. Om man t.ex. hade haft ett programsom var uppdelat i filema deii. c, dei2. c och dei3. c sa skulle man skrivit

gcc dell.c del2.c del3.c

Vilka kommandon sombebovs for att redigera, kompilera ocbexekvera ett C-programvarierar fran system till system ocb omfattas inte beller av standarden. Numera anvan-dermanoftaett integrerat programutvecklingssystem ett s.k. IDE (Integrated Development Environment) som t.ex. Microsofts Visual Studio aven vid utveckling av C-program.

Lat OSS nu studera vart C-programlite narmare. Ett C-programbestar av en eller fleraseparata delar som kallas funktioner. Man ger varje funktion ett unikt namn. I ett C-program maste exakt en av funktionema beta main ocb nar man kor programmetkommer exekveringen att starta i denna funktion. I vart program borjar ftinktionen main paandra raden som bar utseendet:

int main()

Ordet int som star forst anger att main som resultat ger ett varde av typen int. Paren-tesema efter ordet main anger att main ar en funktion ocb att den saknar parametrar. (Isjalva verket bar main ett par parametrar, men om man inte ar direkt intresserad av demkan man skriva pa det enkla satt vi gjort bar. Vi kommer att anvanda detta skrivsatt pade fiesta stallen i boken. Parametrama till main beskrivs i kapitel 11.)

Klamrama { ocb } som star pa radema 3 resp. 6 i programmet anger var funktionensinnanmate borjar ocb slutar.

I ett C-system finns ett "bibliotek" med diverse anvandbara standardflinktioner, t.ex.funktioner for in- ocb utmatning av data. Filen stdio. h ar en s.k. inkluderingsfil. Den

8 © Studentlitteratur

Page 14: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

2.1 Detforsta programmet

innehaller deklarationer av standardfunktioner for in- och utmatning. Den forsta radeni programmet

#include <stdio.h>

gor att filen stdio. h inkluderas i vartprogramsa att vi far tillgang till informationen ifilen. Det ar inte alltid denna rad behover vara med i ett program for att det skall fim-gera. (I detta programexempel hade det formodligen gatt bra utan att man haft denmed.)Om denbehovseller inteberorpa vilka standardfunktioner man anroparoch hurde anropas. Detar i alia fall en godregelatt alltidinkludera stdio. h nar mananvandernagon av standardfunktionema for in- och utmatning. Det ger namligen kompilatommojlighet att upptacka om nagon av dessa funktioner anvands pa fel satt.

Inne i en funktion finns ett antal satser som anger vad programmet skall gora. I wkriprogram fmns bara tva satser, den forsta ar

printf("Hej hopp!\n");

Detta ar just ett anrop av en standardfunktion som heter printf. Funktionen printfkan anvandas for att redigerautskrifterpa en mangfaldolika satt men vi anvanderdenbar i sin enklaste form for att skriva ut en text.

En foljd av tecken som omges av citationstecken, som t.ex.

"Hej hopp!\n"

kallas en textstrdng. Citationstecknen ingar inte i sjalva textstrangen. De anger baravar textstrangen boijar och slutar. Tecknet \ som star inne i textstrangen har en speciellbetydelse i C. Det anger att efterfoljandetecken inte skall tolkas som ett vanligt teckenutan som ett styrtecken. Kombinationen \n skall tolkas som ett nyradstecken vilketinnebar att efter det att foljande text skrivits ut

Hej hopp!

sa flyttas utskriftspositionen fram till en ny rad.

Observera att det ar var \n star i textstrangen som avgor var en ny rad paborjas iutskriften. Var man borjar pa ny rad i programtexten saknar betydelse. Satsen

printf("Hej\nhopp!\n");

skulle givit utskriften

Hej

hopp !

Sist i programmet har vi lagt in satsen

return 0;

© Studentlitteratur 9

Page 15: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

2. C i ett ndtskal

Denna gorattprogrammet avslutas ochattdetlamnar ifran sigreturvardet 0. Man bru-kar anvanda vardet 0 for att indikera att programmet avslutats pa ett normalt satt.Denna return-sats kan oftast utelamnas, vilket vi gor i de fiesta av bokens exempel.Vissa kompilatorer utfardar docken vamingomman intebar med retum-satsen.

2.2 Anvandning av variablerI nasta exempel skall vi syssla med berakning av tider i en slalomtavling. Som bekantkors enslalomtavling i tva separata omgangar, s.k. ak. Tidema i de tva aken laggs ihopoch den som bar den lagsta totaltiden blir segrare. Vi skall studera ett program somberaknar totaltiden ocb den genomsnittliga tidenper ak for en tavlande. Tidema i debada aken skall vara indata till programmet ocb matas in fran tangentbordet narprogrammet kors. Tidema skallanges i sekunder ocbnoggrannbeten skallvarabundradelssekimder. Programmet bar utseendet:

#include <stdio.h>

int main() /* Tidsberakning */

{

float tidl, tid2;

printfC'Tid i forsta aket? ");

scanf("%f", &tidl);

printfC'Tid i andra aket? ");

scanf ( "%f", &tid2) ;

printf ("Total tid: %f\n", tidl + tid2);

printf("Genomsnittlig tid: %f\n", (tidl+tid2)/2);

}

Nar man kor programmet kan det se ut pa foljande satt:

Tid i forsta aket? 54.25

Tid i andra aket? 55.75

Total tid: 110.000000

Genomsnittlig tid: 55.000000

Talen 54.25 ocb 55.75bar skrivits in avdensomkorprogrammet ocbprogrammet barproducerat ovriga utskrifter.

Om man kor detta programunder Windows ocb texten visas i Kommandotolken (detsvartafonstret) sa kommerbokstavemaa, a ocb 6, bMe sma ocb stora,att se underligaut. Detta beror pa att dessa bokstaver i Kommandotolken kodas pa ett annorlunda, aid-re satt an vad som normalt anvands i den texteditor som man skrivit programmetmed.(Se ovningsexempel 5 pa sidan 54.) Om man vill fa korrekta utskrifter kan man anvanda entexteditor som kan anvanda denna aldre kodning^

I texteditom TextPadkan man t.ex. utfora Save as... ocb valja kodningen DOS.

10 © Studentlitteratur

Page 16: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

2.2 Anvdndning av variabler

Pa andra raden i programmet finns en kommentar. Kommentarerinleds i C med teck-enkombinationen /* och avslutas med */. Allt som star inne i en kommentar ignoreras

av C-kompilatom. Kommentareranvands for att fortydligaprogrammet. Det ar tillatetatt lagga in kommentarer varsomhelst i ett program (utom i textstrangar, i tecken-konstanter och inne i en annan kommentar).

I C99 finns ett altemativt satt att skriva kommentarer. Detta inleds med dubbla sned-1streck //. Resten av raden blir da en kommentar. Andra raden kan i C99 skrivas som

int mainO // Tidsberakning

Pa fbljande rad deklareras tva variabler som ges namnen tidi och tid2.

float tidl, tid2;

Alia variabler man anvander maste deklareras. Deklarationema placeras fore de exe-kverbara satsema i en C-funktion. (C99 tillater dock att man blandar kommentarer ochexekverbara satser.) Ordet float anger att variablema tidi och tid2 har typen float,vilket innebar att de skall anvandas for att lagra reella tal i. (Reella tal ar tal som kan habade en heltalsdel och en decimaldel.)

Ordet float har skrivits med fet stil eftersom det ar ett s.k. nyckelord eller reserveratord. Det ar ord som har en speciellbetydelse, t.ex. som har for att ange en typ. Man farinte anvanda nyckelord for nagot annat andamal an det de ar reserverade for. Man fart.ex. inte ge en variabel namnet float. Observera att alia nyckelord skall skrivas medsma bokstaver. I fortsattningen kommer vi att markera alia nyckelord med fet stil.

Foljande satser innebar att varden lases in till variablema tidi och tid2. scanf ar lik-som printf en standardfiinktion och den ar deklarerad i stdio. h.

printf("Tid i forsta aket? ");

scanf("%f", &tidl);

printf("Tid i andra aket? ");

scanf("%f", &tid2);

Parametrama till scanf ser kanske lite underliga ut. Den forsta parametem ar alltid enformatstrdng som anger hur de tecken som matas in fran tangentbordet skall tolkas.Formatstrangen inleds och avslutas med citationstecken. I formatstrangen star tecken-kombinationen %f. Det ar en s.k. omvandlingsspecifikation som anger att det som lasesskall omvandlas till ett reellt tal och placeras i en variabel av typen float. Den andraparametem till scanf har har formen

&tidl

Detta anger att det varde som lases in skall placeras i variabeln tidi. Att man masteskriva ett &-tecken framfor en variabels namn for att kunna lasa in ett varde till den ar

lite speciellt for C. Nagot motsvarande bmkar inte behovas i andra sprak. Vi skall inte

© Studentlitteratur 11

Page 17: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

2. C i ett notskal

har ga narmare in pa skalet till detta (det forklaras i kapitel 7). Lat oss tills vidare beltenkelt acceptera att det skall vara sa.

For att skriva ut summan av tidema anvander vi funktionen printf.

printf ("Total tid: %f\n", tidl + tid2);

Den forsta parametem ar en formatstrang. Denna anger att texten Total tid: skallskrivasut foljd av ett reellt tal (markeras med omvandlingsspecifikationen %f) och attman darefter skall borja en ny rad (anges med \n).

Som vi ser ovan kommer utskriftema av summanav tidema och den genomsnittligatiden att ske med sex decimaler. Dettaar inte riktigtmeningsfiillt eftersom tidema intemats med storre noggrannhetan tva decimaler. I omvandlingsspecifikationen i format-strangen till printf kan man om man vill ange bur manga decimaler man vill ba iutskriften. Om man t.ex. skriver %. 2f betyder detta att man vill skriva ut ett reellt talmed tva decimaler. Om vi gor justeringama

printf ("Total tid: %.2f\n", tidl + tid2);

printf("Genomsnittlig tid: %.2f\n", (tidl+tid2)/2);

kommer i utskriftema i vart komingsexempel att avrundas till tva decimaler:

Total tid: 110.00

Genomsnittlig tid: 55.00

Villvi dessutom snyggatill utskriftensa att decimalpunktema kommerrakt undervar-andra kan vi komplettera omvandlingsspecifikationen i den forsta printf-satsen medett tal som anger bur manga utskriftspositioner (inklusive ev. minustecken, decimal-punkt ocb decimaler) som skall anvandas for det reella talet. Om vi skriver

printf ("Total tid: %13.2f\n", tidl + tid2);

innebar det att utskriften skall goras med totalt 13 positioner. Om som bar inte aliapositionema bebovs sker utfyllnad med blanka tecken framfor talet. Utskriften blir da

Total tid: 110.00

Genomsnittlig tid: 55.00

2.3 Program med repetitionLat OSS studera ett ranteberakningsproblem. Antag att vi disponerar ett visst beloppsom vi kan satta in pa banken. Vi vill nu veta bur detta kapital forrantar sig, med rantapa ranta, under de narmaste tio aren ocb skriver ett C-program som raknar ut detta.

12 © Studentlitteratur

Page 18: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

2.3 Program med repetition

#include <stdio.h>

#define ANTAL_AR 10

int mainO /* Ranteberakning */

{

float kap, ranta;

int ar;

printf("Insatt kapital? "); scanf("%f", &kap);

printf("Ranta? "); scanf("%f", &ranta),

printf("\n Ar Saldo\n == =====\n");

for (ar = 1; ar <= ANTAL_AR; ar++) {

kap = kap * (1 + ranta / 100);

printf("%3d%13.2f\n", ar, kap);

Nar man kor programmet fragar det efter insatt kapital och skriver sedan ut en tabellsom visar hur stort kapital man har efter varje ar for de narmaste tic aren. Om man t.ex.satter in 10000 kr med 2,5% ranta ser en koming av programmet ut pa foljande satt:

Insatt kapital? 10000

Ranta ? 2.5

Ar Saldo

1 10250 .00

2 10506.25

3 10768.91

4 11038 .13

5 11314 .08

6 11596.93

7 11886.86

8 12184.03

9 12488 . 63

10 12800 .84

Lat OSS studera programmet. Det innehaller en del nyheter. Innan en programtext tasom hand av den egentliga C-kompilatom behandlas den forst av en s.k. preprocessorsom gor diverse redigeringar i programtexten. De rader som borjar med #include och#def ine ar direktiv till denna preprocessor och inte till sjalva C-kompilatom. Raden

#define ANTAL AR 10

deflnierar ett s.k. makro med namnet antal ar. Makrot ges "vardet" lo. Detta betyderatt varje gang texten antal ar forekommer i programtexten skall den ersattas med tex-ten 10. Man brukar skriva makronamn med stora bokstaver for att latt kunna skilja demfran namn pa ovriga ting. Makron bmkar ocksa kallas symboliska konstanter.

) Studentlitteratur 13

Page 19: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

2. C i ett notskal

Variabeln ar anvands for att hMla reda pa aktuellt arsnummer (ett heltalmellan 1 och10). Derma variabel ges typenint, vilketinnebar att denbarakanirmehalla heltal (intar en forkortning av det engelska ordet"integer" sombetyderheltal). Radema

for (ar = 1; ar <= ANTAL_AR; ar++) {kap = kap * (1 + ranta / 100);

printf("%3d%13.2f\n", ar, kap);

}

utgoren for-sats. Vad somhanderhar ar att de tva indragna satsemautfors 10ganger.(Att skriva satser en bit in pa raden pa detta satt kallas att indentera. Man anvanderindentering enbart for att oka ett programs lasbarhet. Hurkompilatom uppfattar pro-grammet paverkas inte av indenteringen.) Forsta gangen har variabeln ar vardet 1,andra gangen vardet 2 o.s.v. Att det ar just dessa tva satser som skall upprepas angerman genom att omge dem med klamrar { }.

I formatstrangen till printf anger omvandlingsspecifikationen %3d att ett heltal skallskrivas ut ochatt 3 positioner skall anvandas i utskriften. Efter formatstrangen kom-mer en lista med de variabler som skall skrivas ut. Dessa variabler kopplas tillomvandlingsspecifikationema i formatstrangen, d.v.s. ar kopplas till %3d och kap till%13.2f.

Nar for-satsen exekveras hander foljande: Forstberaknas det forsta uttrycket

ar = 1

Detta ar ett s.k. tilldelningsuttryck som ger ar vardet 1. Darefter beraknas det andrauttrycket

ar <= ANTAL_AR

Om detta uttryck ar sant (vilket det har ar forsta gangen) utfors satsema inom klam-rama en gang. Slutligen beraknas det tredje uttrycket

ar + +

Dettabetyderatt vardet av variabelnar okas med 1.Nu beraknasuttrycket

ar <= ANTAL_AR

ytterligare en gang. Om det fortfarande ar sant utfors satsema inom klamrama annu engang och ar okas med 1. Detta forfarande upprepas tills sa smaningomar blir storre anANTAL_AR.

Det ar en god vana att anvandamakronsom antal ar i program. Behoverman nagongang andra konstantema behover man bara andra pa ett enda stalle i programtexten.Hade man skrivit det konstantavardet inne i programmet, kanske pa flera stallen,hademan behovt leta reda pa alia dessa stallen och andra dar.

14 © Studentlitteratur

Page 20: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

2.3 Program med repetition

Villman gora ranteberakningsprogrammet mer flexibelt kan man anvanda en variabelantai ar i stallet for makrot antal ar.

int antal_ar;

Man maste da ocksa lagga in en inlasning till denna variabel:

printf("Antal ar? "); scanf("%d", &antal_ar);

Observera att det i formatstrangeni anropet av scant star omvandlingsspecifikationen%d som betyder att det som lases skall omvandlas till ett heltal och placeras i en variabel av typen int. Med denna forandring kan en koming av programmetse ut pa foljan-de satt:

Insatt kapital? 5000

Rantesats ? 2.2

Antal ar? 6

Ar Saldo

1 5110.00

2 5222.42

3 5337.31

4 5454.73

5 5574.74

6 5697.38

For att demonstrera ett annat satt att astadkomma repetition skall vi nu vanda pa frage-stallningen. I stallet for att berakna vilket kapital man bar efter ett visst antal ar med engiven ranta fragar vi oss hur lange pengama maste sta inne for att man skall fa ett visstonskat kapital. Vilket kapital man satter in fran borjan, vilket belopp man onskar samtrantan anges som indata.

#include <stdio.h>

int mainO /* Berakning av hur lange */

{ /* kapitalet maste sta inne */

float kap, onskat, ranta;

int ar;

printf("Insatt kapital? "); scanf("%f", &kap);

printf ( "Onskat belopp? "); scanf("%f", &onskat);

printf("Ranta? "); scanf("%f", &ranta);

ar = 0;

while (kap < onskat) {

ar = ar + 1;

kap = kap * (1 + ranta / 100);

printf("Kapitalet maste sta inne %d ar\n", ar);

}

) Studentlitteratur 15

Page 21: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

2. C i ett ndtskal

I programmet firms en whiie-sats. Den fimgerar sa att om uttrycket inomparentes

kap < onskat

ar sant sa utfors satsema inom klamrama en gang. Darefter testas ater om uttrycketinomparentesema ar sant. I sa fall utfors satsemaytterligare en gango.s.v. Varje varvi whiie-satsen motsvarar ett ar och man haller reda pa antalet varv i variabeln ar.

En koming av programmet skulle kunna se ut pa foljande satt:

Insatt kapital? 5000

Onskat belopp? 10000

Ranta ? 2.5

Kapitalet maste sta inne 29 ar

2,4 Program med alternativa vagarI nasta exempel skall vi studeraett programsom laser in ett godtyckligt antal reella talfran kommandofonstret. Som resultat skriverprogrammetut medelvardetav de inlastatalen samt det storsta av talen.

#include <stdio.h>

int main() /* Berakna medelvarde och max-varde */

{

float tal, sum, storsta;

int ant;

sum = 0;

storsta = 0;

ant = 0;

printfC'Skriv in talen.\n"),•

printf("Avsluta med END OF FILE.\n");

while (scanf("%f", &tal) == 1) {

ant++;

sum += tal;

if (tal > storsta) {

storsta = tal;

}

}

printf("Medelvardet: %f\n", sum / ant);

printf("Storsta talet: %f\n", storsta);

}

Den centrala delen av programmet ar en whiie-sats som upprepas sa lange uttrycket

scanf("%f", &tal) == 1

ar sant (== betyder lika med). Funktionen scanf ar sa konstmerad att den som resultat

av ett anrop ger ett heltal som ar lika med antalet lyckade inlasningar (d.v.s. antalet

16 © Studentlitteratur

Page 22: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

2.4 Program med alternativa vdgar

variabler den lyckats lasa in varden till). Har skall varje anrop av scanf normalt gevardet 1 som resultat eftersom vi forsoker lasa in ett varde till en enda variabel.

Nar man anropar funktionen scanf for att lasa indata stannar programmet tillfalligt ivantan pa att nagot skall matas in fran tangentbordet. I de fiesta fall skriver man daindata och programmet fortsatter pa normalt satt, men man kan ocksa tala om for programmet att man inte tanker mata in mer indata. Man gor detta genom trycka pa enspeciell kombination av tangenter (i Linux Ctrl-D och i Windows Ctrl-Z). Man sagerda att end offile uppstaroch funktionen scanf ger som resultatett specielltvarde,kall-lat EOF. Namnet eof ar deklarerat som ett makro i inkluderingsfilen stdio. h.

I vart program innebar detta att uttrycket

scanf("%f", &tal) == 1

blir falskt nar end offile uppstatt.

En komingav programmet kan se ut pa foljande s^tt. (Observera att mankan skrivaetteller flera tal pa varje rad.)

Skriv in talen.

Avsluta med END OF FILE.

3.5 2.4

1 . 2

8.4 -4.1 6.5

har skriver man en teckenkombination som markerar END OF FILE

Medelvardet: 2.983333

Storsta talet: 8.400000

Lat OSS sa studera de satser som star inne i whiie-satsen i programmet. Satsen

a n t + + ;

ar ett kompakt satt att ange att variabeln ant skall okas med 1 och satsen

sum += tal;

betyder att vardet av variabeln tal skall adderas till vardet av variabeln sum och place-ras i sum. Satsen hade ocksa kunnat skrivas

sum = sum + tal;

Nyckelordet if inleder en s.k. if-sats. Om uttrycket

tal > storsta

ar sant kommer satsen

storsta = tal;

att utforas, annars gors inget.

© Studentlitteratur 17

Page 23: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

2. C i ett ndtskal

2.5 Egna funktionerFor att demonstrera hur mankan konstruera och anvanda sig av egnafunktioner skallvi visa en annanversionav programmet i foregaende avsnitt.

#include <stdio.h>

float max(float x, float y) /* ger max-varde */

{

if (X > y) {

return x;

}

else {

return y;

int main 0 /* Berakna medelvarde och max-varde */

{

float tal, sum, storsta;

int ant ;

sum = 0;

storsta = 0;

ant = 0;

printfC'Skriv in taien.Xn");

printf("Avsiuta med END OF FILE.\n");

while (scanf("%f", &tal) == 1) {

a n t + + ;

sum += tai;

storsta = max(storsta, tal);

}

printf("Medelvardet: %f\n", sum / ant);

printf("Storsta taiet: %f\n", storsta);

}

Programmet bestar av tva separata funktioner, max och main och i main har if-satsenersatts med ett anrop av funktionen max

storsta = max(storsta, tal);

Funktionen anropas med de tva argumenten storsta och tai. Resultatet av funktionentilldelas sedan variabeln storsta.

Pa andra raden i programmet borjar definitionen av funktionen max. Av denna radframgar att max ar en funktion som retumerar ett varde av typen float och att max hartva parametrar, x och y, bada av typen float. Dessa kallas funktionens parametrar.Nar funktionen anropas kommer vardena av de tva uttryck som anges som argument ianropet att kopieras till x respektive y. (I anropet fran main kommer alltsa vardet avstorsta att kopieras till x och vardet av tai att kopieras till y.)

18 © Studentlitteratur

Page 24: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

2.6 Ldsning och skrivning av texter

Inom klamrama i funktionsdefinitionen star de satser som beskriver vad funktionen

gor. Iimanmatetav max bestar av en if-sats med else-del. Beroende pa om x ar storrean y eller inte retumeras vardet av x eller y. Exekvering av en return-sats innebar attfunktionen avslutas och att vardet av uttrycket efter ordet return ges som resultat.

For att man skall kunna vara saker pa att en funktion anropas pa ratt satt maste dessegenskaper varakandainnanden anropas. Narmanbarett program sombestarav flerafimktioner maste man alltsa tanka pa i vilken ordningfunktionemaplaceras i program-texten. (Om en fimktion fi anropar en annan funktion f2 bor alltsa f i placeras efterf 2.) Detta innebar att huvudprogrammetmain alltid bor placeras sist. (Om man anvan-der separata funktionsdeklarationer kan man placera funktionema i en friare ordning.Detta fbrklaras i kapitel 6.)

2.6 Lasning och skrivning av texterNastaprogram fragarefter operatorens namn och skriverut en liten halsning.

#include <stdio.h>

int main() /* Lasning och skrivning av namn */

{

char namn [20] ;

printfC'Vad heter du?\n");

scanf("%s", namn);

printfC'Hej %s\n", namn);

}

Vid en koming av programmet kan det se ut pa foljande satt:

Vad heter du?

Peter

Hej Peter

I de tidigare exemplen har vi sett hur man kan skrivaut text i kommandofonstret. Vadsom ar nytt har ar att vi kan ha texter som kan andras, s.k. textstrdngar. Deklarationen

char namn [20] ;

sager att namn ar ett teckenfdlt som bestar av 20 st tecken. (Nyckelordet char ar en for-kortning av engelskans "character" som betyder "tecken".) En bild av hur teckenfaltetser ut visas i figur 2.1. Vad som finns i de enskilda elementen efter deklarationen arodefinierat.

For att lasa in en text till namn anropas scant,

scant ("%s", namn);

)Studentlitteratur 19

Page 25: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

2. C i ett notskal

1 2 3 4 5 6 7

Figur 2.1

18 19

Omvandlingsspecifikationen %s anger att man vill lasa in en textstrang till ett tecken-falt. Narvi i tidigare exempel laste invarden tillvariabler avtypen int ochfloat hadevi ett &-tecken framfor variabelnamnet. Lagg marke till att man inte skall ha det narman laser in varden till variabler som ar teckenfalt. Varfor det ar olika kommer att for-

klaras senare. Lat oss tills vidare tillampa foljande regel: Om man laser in varden tillen variabel sa skall man ha ett &-tecken framfor variabelns namn, savida inte dennavariabel ar ett teckenfalt.

Vid anropet av scanf lases ett tecken i taget ochplaceras i namn med borjan i detforstaelementet. Inlasningen avslutas da ett mellanslag (blankt tecken), ett tabulatorteckenellerett nyradstecken patraffas. Funktionen scanf placerar da ett speciellt nolltecken,som betecknas med \o, i nasta element i namn. Detta tecken markerar slutet pa text-strangen. Hur det ser ut efler inlasningenvisas i figur 2.2.

p e t e r \0

0 1 2 3 4 5 6 7

Figur 2.2

18 19

Det sker ingenkontroll av att alia de inlasta tecknen samtnolltecknet farplats i tecken-faltet. Detta ar forstas farligt eftersom den som kor programmet inte vet hur langanamnhan ellerhon far skriva. Lite sakrare kan man gora programmet genom att goraenjustering i anropet av funktionen scanf. Vilagger till 19 i omvandlingsspecifikationen, vilket innebar att scanf kommer att lasa hogst 19 tecken.

scanf ("%19s", namn); /* sakrare inlasning */

Utskrift av textstrangen sker med printf.

printf("Hej %s\n", namn);

Omvandlingsspecifikationen %s anger att en textstrang skall skrivas ut och dennaspe-cifikation kopplas vid utskriften till namn. Funktionen printf anvander nolltecknet itextstrangen for att veta hur manga tecken den skall skriva ut. (Sjalva nolltecknetskrivs inte ut.)

20 ) Studentlitteratur

Page 26: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

2.7 Funktionerna getchar och putchar

2.7 Funktionerna getchar och putchar

I alia exempel hittills har vi anvant scanf och printf for att lasa och skriva. Dessaflinktioner ar generella och kan anvandas for att lasa och skriva data av manga olikatyper. Tva mer grundlaggande standardfunktioner ar getchar och putchar. Lat oss stu-dera ett programsom anvanderfunktionen getchar. Programmets uppgift ar att lasa inen text och undersoka hur manga av tecknen i texten som ar siffror.

#include <stdio.h>

int mainO /* Berakning av antal siffror 1 en text */

{

int c, ant;

ant = 0;

while ((c = getchar ()) != EOF) {

if (c >= '0' && c <= '9') {

a n t + + ;

}

}

printf("Antal siffor: %d\n", ant);

}

Funktionen getchar saknar parametrar. (Observera att man i C maste skriva ut paren-tesema () nar man anropar en funktion som inte har nagra parametrar.)Vid varje anroplaser getchar ett tecken fran tangentbordet och ger detta tecken som resultat. Om endoffile har uppstatt ger getchar som resultat vardet eof. Tilldelningsuttrycket

c = getchar()

placerar resultatet fran getchar i variabeln c. En sak som skiljer C fran flera andrasprak ar att en tilldelning betraktas som ett uttryck och har ett vdrde. Vardet avuttrycket ovan ar det varde variabeln c far. Jamforelsen

(c = getchar ()) != EOF

undersoker alltsa om end offile har uppstatt. Observera att de extra parentesema runttilldelningsuttrycket behovs. (Teckenkombinationen != betyder icke lika med.)

Variabeln c har givits typen int och inte typen char, vilket kanske hade verkat naturli-gare. Detta beror pa att variabler av typen char i manga C-versioner endast kan inne-halla teckenkoder. Vardet av eof ar inte en teckenkod och kan darfor inte lagras i enchar-variabel, varfor man maste ge c typen int.

whiie-satsen kommer alltsa att upprepas tills end offile uppstar. Pa varje varv testasom det inlasta tecknet som finns i c ar en sifffa. && betyder "och", vilket innebar att

c >= ' Q' && c <= '9'

ar sant om c innehaller teckenkoden for en siffra.

© Studentlitteratur 21

Page 27: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

2. C i ett ndtskal

Programmet i forra avsnittet laste in ett namn och skrev ut en halsning. Om vi hadeskrivit namnet Peter Anders son nar programmet kordes hade vi fattutskriften

Vad heter du?

Peter Andersson

Hej Peter

Problemet ar att om man laser in en text med hjalp av funktionen scant sa avslutasinlasningen sa snart ett blankt tecken patraffas, d.v.s. efter ordet Peter. Hela radenkommer inte att lasas in. Med hjalp avfimktionen getchar skriver vi enny version.

#include <stdio.h>

int main() /* Lasning och skrivning av hel rad */

{

char namn[20], c;

int i;

printfC'Vad heter du?\n");

i = 0;

while ((c = getchar()) != '\n') {

namn[i] = c;

i + + ;

}

namn[i] = '\0';

printfC'Hej %s\n", namn) ;

I whiie-satsen lases ett tecken i taget till variabeln c och placeras i teckenfaltet namn.Detta pagar tills getchar som resultat ger ett nyradstecken. Hela den inmatade raden(utom nyradstecknet) kommer alltsa att lasas in. Variabeln i haller redapa var i namnde inlasta tecknen skall placeras. Uttrycket

namn[i] = c;

innehMler en s.k. indexering och innebar att tecknet i c tilldelas det i:te elementet inamn. Numreringen av elementen i ett fait boijar alltid med 0. Innan det inlasta namnetskrivs ut placeras ettnolltecken \o i namn efterdet inlasta namnet. Detta ar nodvandigtfor att printf skall veta var textstrangen slutar.

Vi kan gora vart program lite elegantare genom att placerainlasningen av en rad i enspeciell funktion som vi kallar las rad. Huvudprogrammet blir da:

int main() /* Lasning och skrivning av hel rad */

{

char namn[20];

printfC'Vad heter du?\n");

las_rad(namn);

printfC'Hej %s\n", namn);

}

22 © Studentlitteratur

Page 28: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

2.7 Funktionerna getchar och putchar

Sjalva definitionen av las rad far utseendet

void las_rad(char rad[]) /* laser en hel rad */{

char c;

int i;

i = 0;

while ((c = getchar0) != '\n') {

r a d [ i ] = c ;

i + + ;

}

rad[i] = '\0';

}

Nyckelordet void anger att funktionen integernagot resultat nardenanropas. Funktio-nen bar en parameter, rad, med specifikationen

char rad[]

Detta betyder att rad innehaller minnesadressen (en pekare) till ett teckenfalt. Laggmarke till att man inte bar skall ange hur manga tecken det finns plats for i faltet. Dettabetyder att man kan anropa las rad med ett teckenfalt av godtycklig langd. Anropet

las_rad(namn);

i main innebaratt parametem rad kommer att peka till teckenfaltet namn. Funktionenlas rad kommer darfor att placera de inlasta tecknen i faltet namn.

Vi avslutar kapitlet med en funktion som anvander sig av standardfimktionen putchar.

void skriv_rad(char rad[]) /* skriver en hel rad */

{

int 1=0;

while (rad[i] != '\0') {

putchar(rad[i]);

i + + ;

}

putchar ('\n' ) ;

}

Funktionen lamnar inget resultatvarde. Som enda parameter bar den en pekare till entextstrang av godtycklig langd. skriv_rad antar att det i teckenfaltet finns ett noll-tecken \ o som markerar textstrangens slut. Raden

int 1=0;

visar bur man kan initiera en variabel direkt vid deklarationen. whiie-satsen upprepas

tills ett nolltecken patrafFas. Pa varje varv skrivs ett tecken ut.

) Studentlitteratur 23

Page 29: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

2. C i ett ndtskal

2.8 dvningsuppgifter1. Skriv ett C-program som skriver ut ditt namn och din adress. Namnet, gatu-

adressen och postadressen skall sta pa tre olika rader.

2. Skriv ett program som beraknar hur manga mil en bil gatt under det senaste aretoch hur stor den genomsnittligabensinforbrukningen per mil bar varit. Indata tillprogrammet ar dagens matarstallning, matarstallningen for ett ar sedan och hurmanga liter bensin som forbrukats under aret.

3. Skriv ett program som produceraren tabell med foljande utseende:

11 1

2 4 8

3 9 27

Som indataskallprogrammet ha ett heltal n somangerdet hogstavardetpa i.

4. En man erbjuds ett ovanligt riskfyllt arbete. Lonesattningen ar ocksa ovanlig. Forforsta dagen erbjuds ban 1 ore, for andra 2, for tredje 4 o.s.v. Lonen fordubblasalltsa varje dag. Skriv ett program som beraknar hur manga dagar mannen mastearbeta for att tjana en miljon kronor.

5. En firma erbjuder sina kunder 10 procents rabatt om man handlar for minst 1000kr.Antagfor enkelhets skull att manbara handlarvaror av ett visst slag.

a) Skriv ett program som beraknar vad en kund skall betala. Indata till program-met skall vara antalet kopta enheter och priset per enhet.

b) Generalisera programmet genom att lata rabattprocenten och gransen for attman skall fa rabatt vara makron.

6. Skriv om uppgift 3 sa att berakningama av kvadraten och kuben pa varje tal utforsi tva separata funktioner kvad och kub.

7. Indata till ett program fa^r forutsattas vara ord separerade med blanktecken, tabula-tortecken eller nyradstecken. Skriv ett program som laser indata och skriver ut deinlasta orden sa att varje ord hamnar pa en egen rad i utskriften. (Strunta i att dentext som matas in och den text som programmet skriver ut kan blandas i komman-dofonstret.) Tips. Anvand scanf och printf.

8. Skriv ett program som laser in en text fran kommandofonstret och beraknar hurmanga rader texten bestar av.

24 © Studentlitteratur

Page 30: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

C fran grunden 3

Detta kapitel behandlar de grandlaggande byggstenar ett C-program ar uppbyggt av.Efter en inledande diskussion om hur man namnger olika saker och ting i ett program,behandlas deklarationer av variabler och konstanter. Darefter foljer en genomgang avde grandlaggande fordefmierade datatypema. Avenomvandlingar mellanolika dataty-per diskuteras. Kapitlet innehaller ocksa en beskrivning av standardfunktionemaprintf och scanf som anvands for skrivning och lasning.

3.1 Identifierare och nyckelordI ett program behover man kunna namnge olika ting, t.ex. variabler och funktioner. Ettnamn kallas en identifierare. En identifierare i C bestar av en foljd av bokstaver (storaeller sma), siffror och understrykningstecknetDe svenska bokstavema a, a och ofar inte anvandas. Det forsta tecknet i en identifierare far inte vara en sifFra. Nagra

exempel pa identifierare ar:

i y5 tal_l ANTAL hogsta_vardet

Vissa ord, t.ex. while, ar s.k. nyckelord och far inte anvandas som identifierare. Manfar alltsa inte kalla en variabel for while. 1 appendix A visas alia nyckelorden i C99.Vissa av nyckelorden finns inte med aldre versioner av C.

1 den fortsatta diskussionen om hur identifierare far se ut skall vi skilja mellan interna

namn och externa namn. Nagot forenklat kan man saga att variabler som ar deklare-rade utanfor en fiinktion samt funktioner normalt bar extema namn (savida derasdeklaration inte innehaller ordet static). Andra ting man deklarerar, t.ex. variablerdeklarerade inne i funktioner, bar normalt intema namn (savida deras deklaration inteinnehaller ordet extern). 1 nedanstaende program ar t.ex. namnen ii, 14, fi och mainextema medan namnen 12 och 13 ar intema. (Mer om deklarationemas innebord ges ikapitel 6.)

© Studentlitteratur

Page 31: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

3. Cfran grunden

#include <stdio.h>

int il = 1;

static int i2 = 2;

int main ()

{

int 13;

extern int 14;

extern int f1();

For interna namn galler att atminstone de forsta 31 tecknenar signifikanta. (For C99galler63 tecken.) Dettabetyderatt identifierare som skiljersig at i nagonav de 31 forsta positionema betraktas som olika. Sma och stora bokstaver betraktas som olika,d.v.s. identifierama Kaiie och kaiie ar olika.

For externanamnbehoveri bara de 6 forstatecknen vara signifikanta. (For C99 galler31 tecken.)Dessutomkan i vissa implementeringar stora och sma bokstaverbetraktassom lika. De tva extema namnen medeivikt och medelvarde kan t.ex. uppfattas somlika. For extema namngaller vidare att man inte skalldeklarera egna variabler, fimk-tioner och annat med samma namn som de standardfunktioner som finns i C. Man

skall inte helleranvanda sig av namnsomborjarpa understrykningstecknet, eftersomsadana ofta anvands intemt av C-systemet.

Ett programs tydlighet okar vasentligt om man anvander beskrivande och inte alltfbrkorta namn pa saker och ting. Om man t.ex. har en variabel som innehaller medelvar-det av ett antal matningar bor man inte kalla variabeln mutan hellre medei temp.

3.2 Variabeldeklarationer

Data lagras i en dators minne i form av bitar, dar varje bit kan ha vardet 0 eller 1. Manbmkar dela in minnet i enhetenbytes. En byte i minnet kan adresseras direkt (den haren viss minnesadress) och ar stor nog att kunna innehalla representationen av etttecken. I de fiesta datorer ar en byte en gmpp bestaende av 8 bitar men det kan avenforekomma datorer dar en byte bestar av fler an 8 bitar. I C-standarden definieras ettobjekt^ som ett minnesutrymme som bestar av ett antal sammanhangande bytes. Detminsta objekt som kan forekomma ar alltsa en byte langt. Ett objekthar i sig ingen spe-ciell typ, det ar bara ett minnesutrymme.

Man kan emellertid deklarera en variabel. Vad som da sker ar att det reserveras plats idatoms minne for ett objekt och att ett visst namn kopplas till detta objekt. Man sager

^Ordet objekt i Cbetecknar inte samma sak som i de objektorienterade spraken, t.ex. C++.

26 © Studentlitteratur

Page 32: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

3.2 Variabeldeklarationer

ocksa att variabeln skall ha en viss typ. Typen avgor hur langt objektet blir och burbitama i objektet skall tolkas. Pa raden

char teck;

deklareras t.ex. en variabel teck som bar typen char. Ett annat satt att uttrycka detta aratt saga att man reserverar plats for ett objekt som ar en byte langt, identifieraren teckbanvisar till objektet ocb man tolkar bitama i objektet som en teckenkod.

Pa motsvarande satt innebar

int tal;

att man deklarerar en variabel tai av typen int. Hur langt motsvarande objekt blirberor pa den aktuella implementeringen men standarden sager att en int maste varaatminstone tva bytes lang.

En variabels varde kan andras genom en tilldelning.

teck = 'X';

tal = 5;

Man kan ocksa initiera en variabel i samband med deklarationen.

char teck = 'A' ;

int tal = 0;

Om man inte initierar en variabel kommer dess varde att vara odefmierat. (Ett undan-

tag ar s.k. extema ocb statiska variabler. Dessa far vardet 0 om de inte initieras. Sevidare avsnitt 6.7.) Nar det galler vanliga enkla variabler som deklareras inne i ftinktio-ner bebover inte initieringsvardet vara ett konstant uttryck. Det kan vara ett mer all-mant uttryck som t.ex. ocksa kan inneballa variabler eller funktionsanrop

float maxtal = max (a, b) ;

Flera variabler av samma typ kan man raknas upp i samma deklaration.

int i, j , k ;

Det gar ocksa bra att initiera variablema i en sadan deklaration.

int 1=1, j=2, k=3;

Observera att en initiering bara galler en enda variabel. I deklarationen

int m, n = 5;

kommer alltsa variabeln n att initieras till 5 medan m blir oinitierad.

Man kan tillfoga det reserverade ordet const i en deklaration.

const char slut = '\0';

const int max_antal = 1000;

const int storlek = n * k;

© Studentlitteratur 27

Page 33: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

3. Cfrangrunden

I stallet for att deklarera en variabel deklareras da en konstant. En konstants varde kaninte andras i programmet. Man far t.ex. inte tilldela ett varde till en konstant. For att enkonstant skall ha ett valdefinierat varde maste den darfor alltid initieras.

Det ar tillatet att utelamna typen nar man deklarerar en konstant. I sa fall antas att kon-stanten bar typenint. I C99kravs dockatt konstantens typ alltidanges.

Att anvanda sig av konstanter ar ett elegantare altemativ an att definiera makron somvi demonstrerade i avsnitt 2.3. Konstantens typ ffamgar direkt av deklarationen, vilketintear fallet nar mandefmierar makron. Kompilatom har darfor mojlighet att kontrol-lera att konstanten anvands pa ratt satt.

3.3 HeltalstyperI C spelarheltalstypema en speciellt viktigroll. De anvands namligen for att represen-tera data av flera olika slag:

• Heltal utan tecken.

• Heltal med tecken.

• Grafiska tecken i texter. Varje tecken representeras med ett imikt bitmonster. Mankan tolka detta bitmonster som ett heltal och kalla det for det aktuella tecknets hod.

Forr anvande man oftast ASCII-standarden for att koda tecken men nu anvands for

det mestamer omfattande standarder i vilka ASCII-kodema ingar somen del.

• Logiska varden. I C anvander man sig normalt av typen int och later vardet noilbetydafalskt och alia andra varden betyda sant, men i C99 kan man ocksa anvandatypen __boo1.

• Ett bitmonster. Man kan belt enkelttolka ett objektsom en foljd av nolloroch ettor.Det ar mojligtatt paverkade enskildabitama i en sadan foljdmed hjalp av ICKE-,OCH-, och ELLER-operationereller att skifta bitama at hoger eller vanster.

For den "vanliga" programmeraren ar det kanske inte nodvandigt att kanna till exaktbur data lagras binart, men det kan anda vara bra att kanna till principema. C ar dess-utom ett maskinndra programmeringssprak och det anvands ofta i tillampningar somkraver ingaende kunskaper om den aktuella datoms speciella egenskaper. Da ar detkanskenodvandigt att kannatill datomssatt att lagradata.For den som vill sattasig ini detta finns darfori Appendix C en beskrivning av burnumeriska tal lagras i en dator.

3.3.1 De fordefinierade heltalstypema

Det fmns heltalstyper av fem storlekar. Dessa ar char, short int, int, long int ochlong long int. Den sista av dessa finns dock bara i C99. Varje storlek finns i tva ver-sioner, med tecken (signed) eller teckenlos (unsigned). I C99 finns dessutom typen

28 © Studentlitteratur

Page 34: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

3.3 Heltalstyper

_Booi somanvands for att lagra logiskavarden(nolieller ett). I faktamtan ges en sam-manstallning av alia heltalstypema.

Fordefinierade heltalstyper

_Bool minst 1 bit

signed char minst 8 bitar

unsigned char minst 8 bitar

[signed] short [int] minst 16 bitar

unsigned short [int] minst 16 bitar

[signed] int minst 16 bitar

unsigned [int] minst 16 bitar

[signed] long [int] minst 32 bitar

unsigned long [int] minst 32 bitar

long long int minst 64 bitar

unsigned long long int minst 64 bitar

Att vissanyckelord skrivits inomhakparenteser innebar att dessa kan utelamnas i entypangivelse. Dettaar dockinte tillatet i C99 darmanalltidmasteskrivaut ordet int.

Hur langa objekt av de olika typema ar beror pa den aktuella implementeringen. Deminimilangder som ar angivna i faktamtan ar hamtade ur C99-standarden. Dessutomgalleratt en short int alltidar minst lika lang somen char, att en int alltid ar minstlika langsomen short int ochatt en long int alltidar minstlika langsomen int. Detva variantema signed och unsigned ar alltid lika langa. En unsigned int ar t.ex. alltid lika lang som en int.

Det finns en fil limits.h som innehaller uppgifter om de olika heltalstypemas egen-skaper i den aktuella implementeringen. Om man lagger raden

#include <limits.h>

fbrst i sittprogram far mantillgang till denna fil. I filen finns ett antalmakron definie-rade. Makrot char bit anger hur manga bitar som ingar i en byte. MakronaSCHAR_MAX, CHAR_MAX, SHRT_MAX, USHRT_MAX, INT_MAX, UINT_MAX, LONG_MAX OChULONG MAX anger det storsta tal som kan lagras i en variabel av typen signed char,char, short int, unsigned short int, int, unsigned int, long int respektiveunsigned long int. Pa motsvarande satt anger makrona schar min, char min,SHRT MiN, iNT MiN och LONG MiN det minsta tal (mest negativa) som kan lagras i envariabel av typen signed char, char, short int, int respektive long int.

Typen char maste kommenteras speciellt. Huvudavsikten med typen char ar somnamnet antyder att man skall anvanda den for att representera tecken i texter.Detta dis-kuteras i avsnitt 3.4. Man kan emellertid ocksa anvanda typen char for att representeraheltal. Om typen char da skall uppfattas som en teckenlos heltalstyp eller som en hel-talstyp med tecken ar implementeringsberoende. Vill man undvika denna osakerhet

) Studentlitteratur 29

Page 35: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

3. Cfrangrunden

kan man explicitdeklarera en variabel som signed char ellerunsigned char i stalletfor att bara ange typen char.

Nar skall man da anvanda de olikaheltalstypema? Dennormala "arbetstypen" i C arint. I flera sammanhang ar det underforstatt att man menar int cm man inte sagernagot annat. Nar man t.ex. utfor aritmetiska operationer pa heltal omvandlas operan-dema i de fiesta fall automatiskt till typen int. Detnaturliga valet av typ for att repre-sentera heltal ar darfor typen int.

Omman villkunna representera heltal somi denaktuella implementeringen interymsi en int farmanvalja long int. Ommanintebehover lagrasa storatal kan det rackamednagon av typema signed char ellershort int. Omdetbaragaller nagra enstakavariabler lonardet sig emellertid inteatt anvanda kortare typeran int eftersom varde-na andai defiesta operationer omvandlas till int. Skall mandaremot lagratabeller ochliknande med manga sma heltal kan man spara utrymme genom att anvanda nagonkortare typ an int.

De teckenlosa typema har den egenskapen attderaknar modulo 2^. {N ar den aktuellatypens langd.) Detta innebar att om man gor en aritmetisk operation med teckenlosaoperander kan man aldrig fa s.k. "overflow". Resultatet ar alltid valdefinierat. Antagt.ex. att vi har deklarerat en variabel k

unsigned int k;

ochatten int i den aktuella kompilatom bestar av 16bitar. Detstorsta tal som ryms i kblir da 65535 och vi kan gora tilldelningen

k = 65535;

Satsen

k = k + 1;

orsakar nu inte "overflow" utan resultatet ar valdefinierat och blir alltid 0.

Man valjer alltsa teckenlosa typer narman pa detta sattvillrakna modulo 2^. Ettannattillfalle ar nar man vill anvandaen heltalstyp enbart for att representera ett bitmonster.Det finns da ingenanledning att betraktaden vanstrabiten i ett objektsom en teckenbitoch nagon av de teckenlosa heltalstypema ar det naturligavalet.

IEtt problem nar man skall portera program ar att heltalstypemas storlekar ar systembe-roende. I C99 finns darfor en uppsattning namn for olika heltalstyper, t.ex. ints t,uint32_t, int ieastie t och int fast32_t. Dessa namn ger programmeraren merkontroll. Man kan med dem ange heltal av exakt storlek, heltal av viss minsta storleksamteffektivaste heltal for en viss storlek. Dessanamnar deklarerade i inkluderingsfi-len stdint.h. Namnen betecknar inte egna typer eller reserverade ord, utan de over-satts till existerandeheltalstyper som uppfyller angivna krav. (Se avsnitt 3.11.)

30 © Studentlitteratur

Page 36: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

3.3 Heltalstyper

3.3.2 Heltalskonstanter

Ibland behover man ange konstanta varden i ett program. I satsen

i = j + 13;

anvands t.ex. det konstanta vardet 13. SMana konstanta varden brukar i programme-

ringssprak ofta kallas literaler, men i C anvands belt enkelt termen konstanter. Dettaskall inte forvaxlas med konstanta objekt som deklareras med hjalp av nyckelordetconst.

Heltalskonstanter i C kan anges i decimal, oktal eller hexadecimal form. En decimalheltalskonstant bestar av sifffoma 0 till 9 och borjar alltid med en siffra som inte ar 0.Nagra exempel ar;

4 33 9 100000

En heltalskonstant i oktal form borjar med siffran0 och far inneh^la sif&oma 0 till 7.Konstanten 035 betyder t.ex. 29 decimalt. Exempel pa oktala heltalskonstanter ar:

01 077 02653271

Om konstanten 0 skall tolkas som en decimal eller oktal konstant spelar ingen rolleftersom vardet anda ar samma.

Hexadecimala heltalskonstanter inleds med tecknen Ox eller ox foljda av ett antal hex-adecimala siffror. De hexadecimala siffroma ar sifffoma 0 till 9 samt bokstavema A,

B, C, D, E och F. Altemativt kan sma bokstaver a till f anvandas. Den hexadecimalakonstanten 0x3c betyder t.ex. 60 decimalt. Foljandear exempel pa hexadecimala heltalskonstanter:

0x1 0X75 OXff 0xD7A02

Oberoende om en heltalskonstant har angivits i decimal, oktal eller hexadecimal formlagras den forstas intemt i datom pa samma satt.

Vilken typ har en heltalskonstant? En decimal heltalskonstant far normalt typen int.Skulle den emellertid vara sa stor att den inte kan representeras i en int far den i stallettypen long int. En decimal heltalskonstant som inte ens ryms i en long int far typenunsigned long int.

En oktal eller hexadecimal heltalskonstant far ocksa normalt typen int. Skulle den interymmas i en int far den typenunsigned int. Ar aven en unsigned int for kort blirtypen long int och om inte heller long int racker till far konstanten typen unsignedlong int.

Om man vill kan man sjalv styra vilken typ en heltalskonstant far genom att skrivanagra av bokstavema u, u, i och l sist i konstanten. For heltal av typen long long int |anvands ii eller ll. Nagra exempel:

© Studentlitteratur 31

Page 37: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

3. Cfrdn grunden

45L far typen long int

0653U far typen unsigned int

0x4a651 far typen long int

0 50 lu far typen unsigned int

8239775LL far typen long long int

3.4 Texter

3.4.1 Teckenkoder

Den kanske storsta mangden data som behandlas av datorer bestar inte av numeriskatalutanavtexterochtecken. Ett tecken kanvaraenbokstav, ensiffra, ett specialtecken(t.ex. fragetecken, punkt eller kolon) eller ett "icke skrivbart styrtecken". Icke skriv-bara styrtecken anvands bl.a. nar datom kommunicerar med en skarm for att fa den attgora vissa saker, t.ex. borja pa en ny rad. For att lagra ett tecken i datom bmkar manoftast anvanda en byte bestaende av 8 bitar. Fdrr i tiden anvandes bara sju av dessabitar. Det finns en gammal, allmant accepterad standard som bestammer vilka olikatecken som skall kunna kodas med sju bitar. Denna standard kallas ASCII-standarden.Med 7 bitar kan man bara koda hogst 128 olika tecken. Uppsattningen bokstaver iASCII-standarden omfattar darfor bara bokstavema 'A' till 'Z'. Det saknas alltsa bok

staver som anvands i flera romanska och germanska sprak, t.ex. n, e, a, a, och 6.

Numera anvands ofta 8-bitars koder. Det finns en intemationell ISO-standard kalllad

LATIN_1 som utnyttjar 8 bitar. Denna overensstammer med ASCII-standarden nar det

gallerde forsta 128tecknen(de med nummer0 till 127). LATIN_1 anvands ofta i Windows men inte i MS-DOS, dar man kodar de tecken som bar nummer 128till 255 paannat satt. I appendix B finns en tabell med kodema i LATIN_1. Notera att stora ochsma bokstaver inte betraktas som samma och att den normala alfabetiska ordningeninte galler for de svenska bokstavema a, a och o.

Att anvanda LATIN_1 istallet for ASCII loser forstas bara problemet for de lander darde vasteuropeiska spraken anvands. DMor finns numera den s.k. L^w/coJ^-standarden.Teckenkodema i Unicode kallas code points och varje tecken har alltsa ett unikt sadantnummer. Kodema 0 -255 overensstammer med LATIN_1 standarden. I den urspmng-liga versionen av Unicode anvands 16 bitar for att koda de olika tecknen. Med 16 bitarkan inte mindre an 65 536 tecken kodas. Den mangd tecken som kan kodas med 16bitar i UnicodekallasBasic MultilingualPlane (BMP).Det visade sig emellertidatt 16bitar inte rackte for att koda alia tecken som forekommer i olika sprak i varlden. Isenare versioner av Unicode har man darfor infort mojligheten att definiera mer an enmiljon olika tecken. Detta gor man genom att tilllata att vissa tecken, s.k. supplementary characters, representeras med 16 extra bitar, fomtom de 16 normala. En forteck-ning over alia tecken i Unicode finns pa www.Unicode.erg.

32 © Studentlitteratur

Page 38: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

3.4 Texter

Unicode-standarden stodjer tre olika satt att koda symbolemasnummer: UTF-8, UTF-16 och UTF-32. UTF-32 ar enklast. Dar placerar man belt enkelt vaije symbols nummer i ett 32-bitars tal. Om man bar en text som ar kodad pa detta satt kommer alltsavaije tecken i texten att uppta fyra bytes i en foljd.

I Unicode ligger de "vanligaste" tecknen i borjan. De forsta 128 tecknen overensstam-mer t.ex. med ASCII-koden. Detta betyder att om man lagrar en text som bara innebM-ler ASCII-tecken, sa slosar man med utrymmet om man anvander mer an en byte pertecken. I UTF-8 kodning anvands en enda byte om tecknet bar ett nummer som ligger iintervallet 0 till 127, dvs. om det ar ett ASCII-tecken. Skulle tecknet bar ett bogrenummer anvands ytterligare en, tva eller tre bytes. Olika tecken kan alltsa kodas medolika antal bytes. Iden ar att de vanligaste tecknen ligger forst ocb darfor kodas med eneller tva bytes, medan de mer ovanliga kodas med tre eller fyra bytes. UTF-8 kodningar darfor ett kompakt satt att lagra text. UTF-8 kodning borjar bli allt vanligare. I C99anvands termen multibyte character for att beteckna en foljd av en eller flera bytessom pa detta satt representerar ett tecken.

Kodningstekniken UTF-16 ar en kompromiss mellan UTF-32 ocb UTF-8. I dennaanvands antingen ett ellertva 16-bitars ordforatt lagra tecknet. Aven barar tanken attde vanligaste tecknen bara bebover 16 bitar, medan de mer ovanliga kraver 32 bitar.UTF-16 fmns i ett par olika varianter, men vi avstar fran att ga narmare in pa detta.

3.4.2 Teckenvariabler och teckenkonstanter

Den enklaste typen for att representera tecken i C ar typen char som normalt bestar av8 bitar. Denna kan anvandas for att representera tecken som kan kodas med en endabyte, t.ex. ASCII ocb LATIN l. Den kan dessutom anvandas for UTF-8 om man laterde tecken som kraver flera bytes respresenteras av en foljd av variabler av typen char.Vi kan t.ex. gora deklarationen

char teck;

I en sadan variabel kan man placera en teckenkod bestaende av bdgst 8 bitar. I normalafall bebover man inte ocb skall inte beller skriva kodema direkt. Kompilatom vet nam-ligen sjalv vilka koder de olika tecknen bar. Istallet skriver man det tecken man villrepresentera omgivet av apostrofer. Detta kallas en teckenkonstant eller en tecken-literal. Vill vi t.ex. lagga ett plustecken i variabeln teck kan vi skriva

teck = '+';

En variabel av typen char kan, om inte UTF-8 anvands, inneballa alia tecken somingar i LATIN_1, bl.a. de svenska bokstavema a, a ocb 6. Foljande ar t.ex. tillatet:

teck = 'A' teck = ' q' ; teck = ' i' ;

) Studentlitteratur 33

Page 39: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

3. Cfran grunden

Om UTF-8 anvands kan bara de tecken som ingar i ASCII-standarden, dvs. tecknenmed koder i intervallet 0 till 127, lagras i en enstaka variabel av typen char. For attrepresentera ovriga tecken behovs ytterligare en, tva eller tre char-variabler.

Teckenliteraler for de skrivbara tecknen som finns pa tangentbordet kan latt anges sasom visats bar. Undantag ar tecknen ' (apostrof), " (citationstecken) och \ (backslash). Narman skallange dessa maste man anvands s.k. escapesekvenser. En escape-sekvens inleds med tecknet \ och skall tolkas som ett enda tecken. For att t.ex. tilldela

variabeln tecki en apostrof och variabeln teck2 ett backslash-tecken kan man skriva:

teckl = teck2 = 'W;

Escape-sekvenser anvands ocksa for att ange icke skrivbara styrtecknen. I faktarutanvisas de speciellaescape-sekvenser som finns for att ange sadanatecken.

Escape-sekvenser for icke skrivbara tecken

\ 0 (null-character) Ett tecken dar alia bitar bar vardet 0.Anvands for att markera slut pa teckenstrangar.

\ a (alert) Ger en ljud- eller ljussignal.

\b (backspace) Flyttar utskriftspositionen ett steg till vanster.

\ f (form feed) Flyttar utskriftspositionentill bdrjan pa nasta sida.

\ n (new line) Flyttar utskriftspositionen till borjan pa nasta rad.

\ r (carriage return) Flyttar utskriftspositionen till boijan pa aktuell rad.

\ t (horisontaltab) Flyttar fram utskriftspositionen till nasta tabulatorstopp.

\ V (vertical tab) Flyttar utskriftspositionen till boijan paden rad som ar markerad som nasta vertikala tabulatorstopp.

Vill man ange ett tecken som man inte kan skriva direkt och som saknar speciellescape-sekvens kan man anvanda en escape-sekvensdar man anger tecknets kod. Eftertecknet \ skriverman ett oktalt tal som bestar av hogst tre siffror. Nagra exempel ar

'\0' '\33' 'Mil' '\2 66' \'311'

Altemativt kan man ange teckenkoden i hexadecimal form. Man skriver da ett x eftertecknet \. Samma exempel som ovan kan da skrivas

'\xO' '\xlb' '\xlf' '\xh6' '\xff'

En teckenkonstant bar faktiskt typen int (inte typen char). Observera att vardet av enteckenkonstant kan bli negativt om man bar en kompilator dar typen char ar lika medsigned char. Antag t.ex. att vi bar teckenkonstanten ' \xf f' och att en byte ar 8 bitar.Om typen char ar lika med typen unsigned char kommer denna konstant att ha vardet

34 © Studentlitteratur

Page 40: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

3.4 Texter

+255. Om daremot typen char ar lika med signed char kommer teckenkonstanten attha vardet -1 (om vi antar att tvakomplementsform anvands).

Behover man representera tecken vilkas koder kraver mer an 8bitar kan man anvanda |variabler av typen wchar t som brukar vara 16 eller 32 bitar. Med wchar t kan man(atminstone) representera alia de tecken som ingar i BMP Unicode, wchar t finnsdefinierad i inkluderingsfilen stddef .h vilken alltsa maste inkluderas i programmet(direkt eller indirekt). Vi kan t.ex. deklarera en variabel cw:

wchar_t cw = L'\u03Bl'; /* lilla alfa */

Tecknet l som star forst anger att sjalva teckenliteralen ocksa skall ha typen wchart.Vi har har anvant skrivsattet \u for att ange teckenkoden. Man kan skriva antingen \ueller \u foljt av teckenkoden i hexadecimal form. Teckenkoden skrivs sa som den arangiven i Unicode-standarden. Om man skriver \u skall koden besta av fyra hexa-decimala siffror och har man skriver \u skall den besta av atta hexadecimala siffror.

I det senaste standardforslaget finns ocksa typema charie t och char32_t. Dessa ardeklarerade i inkluderingsfilen uchar .h. En variabel av en sadan typ innehaller direkttecknets kod (dess code point) enligtUnicode. (Typen chari6_t kan dock bara inne-hMla sadanatecken som kan lagras med 16bitar.)Vi kan t.ex. gora deklarationema

charl6_t cl6 = u'\u20AC'; /* euro-tecknet */char32_t c32 = U'\U0001D121'; /* C-klav */

Tecknet u fore apostrofen betyder att en teckenliteral far typen charie t och tecknetu att den far typen char32_t.

3.4.3 Textstrangar och teckenfalt

Vi har i vara programexempel tidigare sett flera exempelpa hur man kan skriva ut texter - foljder av flera tecken. Sadanafoljderkallas textstrangar. En konstant text, en s.ktextstrdngsliteral, anges genom att man omger den med citationstecken, som t.ex.

"A string literal"

Notera att enkla tecken, s.k. teckenliteraler som vi diskuterat hittills i detta avsnitt,omges med enkla apostrofer, medan texter, med av flera tecken, omges av citationstecken. Vill man lagga in icke skrivbara tecken i en textstrangsliteralkan man anvandaescape-sekvenser. Satsen

printfC'Rad l\nRad 2\tJag piper nu\a\n");

ger t.ex. utskriften

Rad 1

Rad 2 Jag piper nu

© Studentlitteratur 35

Page 41: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

3. Cfrdn grunden

i kommandofbnstret. Dessutom hors ett pip eller nagot annat ljud. Efter utskriftenkommer markoren att flyttas framtill borjanpa nasta rad.

Observera att backslash-tecknet \ markerar starten pa en escape-sekvens. Vill man attsjalva backslash-tecknet skall inga i texten maste man lagga in ett extra \ framfor. Ettexempel ar om man skall ange ett filnamn:

"C:\\Cpp3\\Ex\\kap3\\temp.txt"

Detta galler ocksa om man vill lagga in citationstecken i texten:

"Han ropade: \"Stopp!\""

Vanliga textstrangar aruppbyggda somen foljdav element av typenchar. Detgar ocksa att bildatextstrangar dar elementen ar av typenwchar_t. Man skriverda ett stort Lframfor literalen:

I L"Price: 10\u20AC" /* 20AC ar koden for euro-tecknet */

I avsnitt2.6 pa sidan 19visadevi hur man kunde lasa in och redigeratexter. Eftersomvariabler av typema char, wchar t, charie t och char32_t bara innehaller ett endatecken kan de inte anvandas for att lagrahela texter. Man anvander man sig darfor avett teckenfdlt. I normala fall ar komponentema i ett sadant av typen char. I figurema2.1 och 2.2 visade vi t.ex. ett teckenfalt med foljande deklaration:

char namn[20];

Anvander vi LATIN_1 anvands alltid en komponent per tecken i faltet. Om textendaremot lagras sommultibyte characters, enligtUTF-8, kan vissasymboler, t.ex. bok-stavema a, a och 6, i UTF-8 lagras i flera intilliggande komponenter. Tecknens num-mer kommer darfor da inte att vara samma som komponentemas nummer.

IVill man att varje tecken skall lagras i en enda komponent i faltet kan man anvandateckenfalt i vilkakomponentema ar av typenwchar t. Vikan t.ex. goradeklarationen

wchar_t wnamn[20];

Vikommer att diskutera textstrangar ochteckenfhlt merutforligt i kapitel 7.

3.5 FlyttalstyperI datorsammanhang bmkar man skilja mellan heltalstyper och s.k. flyttalstyper. Desenare anvands for att representera reella tal. Heltal kan lagras exakt men de fiestareella tal kan bara lagras i approximativ form. For detaljer se Appendix C.

36 © Studentlitteratur

Page 42: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

5.5 Flyttalstyper

3.5.1 De fBrdefinierade flyttalstyperna

Det finns tre flyttalstyper, float, do\ibie och long double. Exakt hur manga bitar somskall anvandas for att representera objekt av dessa typer anges inte i standarden menfor alia tre typema galleratt de bar en noggrannhet motsvarande minst 6 decimalasiff-ror, att det till beloppet storsta tal som kan representeras ar atminstone 10^^ och att dettill beloppet minsta tal som kan representeras (forutom talet 0 som brukar specialbe-handlas) inte ar storre an 10' ^. Dessutom galler att typen double bar minst sammanoggrannhet och talomrade som typen float och att typen long double bar minstsamma noggrannhet och talomrMe som typen double. Det ar alltsa mojligt att tva avdessa typer (eller eventuellt alia tre) anvander likamangabitar om den aktuella datominte stodertre flyttalstyper med olika langd. Ett typisktexempel ar att 32 bitar anvandsfor typen float, 64 bitar for double och 128 bitar for long double.

Fordefinierade flyttalstyper

float

do-uble

long double

Vilkenav de tre typemaman skall anvandabor i varje enskilt fall bestammas av vilkennoggrannhet man kraver. I den urspmngliga versionen av C var det sa att alia vardenav typen float automatiskt omvandlades till typen double sa fort man anvande dem iett uttryck. Detta innebar att man inte tjanade sa mycket pa att anvanda typen floatutom nar det gallde samlingar av manga varden, t.ex. tabeller.Numera bar denna auto-matiskaomvandling tagitsbort och varden av typen float kan alltsa utan att omvand-las inga i uttryck. Darfor finns det ingen anledning att anvanda typen double om dennoggrannhet som float erbjuder racker till.

Filen float.h innehaller diverse makron som ger information om de fordefinieradeflyttalstypemas egenskaper i den aktuella implementeringen. Man kan inkludera dennafil i sitt program och far da tillgang till dessa makron. Nagra av de aktuella makrona arFLT DiG, DBL DiG och LDBL DiG som ger autalet decimala siffrors noggrannhet fortypema float, double respektive long double, flt max, dbl max och ldbl max gerdet till beloppet storsta tal som kan representeras och flt min, dbl min och ldbl minger det till beloppet minsta tal (fomtom talet 0) som kan representeras for de tre olikaflyttalstypema.

I C99 bar man lagt till tva nya reserverade ord for att kunna hantera komplexa tal |direkt i spraket: _compiex och ^imaginary. Utover detta finns en stor utvidgning avbiblioteken for numeriska berakningar, i syfte att gora C till ett tillfdrlitligt verktyg fornumeriker. Det ligger dock utanfor ramama for denna bok att diskutera detta narmare.

) Studentlitteratur 37

Page 43: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

5. Cfran grunden

3.5.2 Flyttalskonstanter

Konstanta reella vardeni ett program anges somflyttalskonstanter. En sadankonstantuttrycks alltid som en decimal konstant, d.v.s. medbasen 10. Man kan skriva en flyt-talskonstant antingen som "vanligt" elleri exponentform. Den"vanliga" formen bestarav ett antal heltalssiffror, en decimalpunkt och ett antal decimaler. Det ar tillatet att ute-lamna antingen heltalssiffroma eller decimalema men decimalpunkten maste alltidvara med. Exempel pa flyttalskonstanter i "vanlig" form ar:

567.7 0.0041 5.0 57.

0.3 .003 .0 0.

Foratt ange mycket stora ellermycket smatalkandetvarapraktiskt att anvanda exponentform. Denna form bestar antingen avenkonstant i "vanlig" form foljd avenexpo-nentdel ellerett heltal foljt av en exponentdel. Sjalva exponentdelen bestarav boksta-vene ellere foljd avenexponent somkanvarapositiv ellernegativ. Omexponenten arpositiv farplustecknet utelamnas. Exempel pa flyttalskonstanter i exponentform ar:

0.98765el5 3.11E-5 l.e+12

.543E-17 249e25 OeO

Exponentdelen anger hur talet skall skalas. Konstanten o.3ei4 betyder t.ex. 0.3 x 10^"^.En flyttalskonstant far typen do\ibieommaninteanger nagot speciellt. Vill manatt enflyttalskonstantskall ha typen float kan man skriva ett f eller ett f efter konstanten.

7.45f 0.624e-7f O.OF

Pa motsvarande satt angernagonav bokstavema i och l att en flyttalskonstant skallhatypeniong double.

7 . 1234567 68 656574L 0.93432e50L

3.6 Aritmetiska typomvandlingarEtt vardeav en aritmetiskt typ (heltalstyp eller flyttalstyp) kan omvandlas till ett vardeav en annan aritmetisk typ. Har beskriver vi bara nar detta kan ske. For en detaljeradbeskrivning av exakt vad som bandervid typomvandlingar hanvisas till Appendix D.Rent generellt kan sagas att man skall vara forsiktig med typomvandlingar, specielltom omvandling sker ffan en langre typ till en kortare typ eller om den ena typen arunsigned och den andra inte. Da kan namligen vardet i varsta fall forvanskas.

3.6.1 Automatiska typomvandlingar

Typomvandlingar kan ske automatiskt vid vissa tillfallen:

• Operandema till en operator kan omvandlas.

38 © Studentlitteratur

Page 44: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

5.7 Upprdkningstyper

• Vid tilldelning omvandlas uttrycket i hogerledet till den typ som operanden tillvanster har.

• Ett argument till en fiinktion kan ibland omvandlas. (Se kap 6.)

• Vardet som retumeras fran en funktion omvandlas till den typ som funktionen skallge som resultat.

Nar man har operatorermed tva operander (som t.ex. +-operatom och *-operatom) arhuvudprincipen att den operand som har kortast typ omvandlas till den andra operan-dens typ. Berakningen kommeralltsaatt utforasmed den langre typen. Ett bivillkorardock att berakningen inte skall utforasmed en kortaretyp an int. DetaljemabeskrivsiAppendix D.

Har fbljernagraexempel pa "ofarliga"automatiska typomvandlingar. Vi antaratt vari-abeln i har typen int och variabeln d typen doiibie.

i + d // i:s varde omvandlas till double

i = d // d:s varde omvandlas till int, decimaler kapas

sin(i) // i:s varde omvandlas till double om sin ar deklarerad

// sa att kompilatorn vet att parametern har denna typ

3.6.2 Explicita typomvandlingar

Man kan ocksa gora explicita typomvandlingar (eng. casts). En sidan har formen

{typnamn) uttryck

Inom parentesema anges vilken typ man vill att uttrycket skall omvandlas till, t.ex.

(unsigned int) i

(long double) (x + y)

(char) k

Explicit typomvandling ar tillaten mellan alia skalara typer. Med skaldra typer menastyper som beskriver enstaka varden, vilket innebar att aritmetiska typer och pekartyperraknas som skalara typer. Det ar alltsa tillatet att explicit omvandla ett uttryck av enaritmetisk typ till en annan aritmetisk typ.

Om det ar nodvandigt att gora en "farlig" typomvandling ar det lampligt att lataomvandlingen vara explicit for att tydligt markera att man vet vad man gor.

3.7 Upprakningstyper

I verkligheten finns manga slag av data som man beskriver med ord i stallet for medsiffror och tal. Vi kallar t.ex. oftast manadema "januari", "februari" etc. och inte"manad 1", "manad 2" o.s.v. Ett annat exempel ar fargema i en kortlek. For vaije

© Studentlitteratur 39

Page 45: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

3. Cfran grunden

sadant slag av data finns endast ett visst antal mojliga varden. Ett spelkorts farg kant.ex.baraantanagotav vardena "klover", "ruter, "hjarter"och "spader".

IC kanmananvanda s.k. upprdkningar dar manraknaruppaliade mojliga vardena avett visst slag. Fargema i en kortlek kan beskrivas med deklarationen

enum kort_farg {klover, ruter, hjarter, spader};

Vi har givit upprakningen namnet kort farg. Upprakningens namn kan man sedananvanda i sitt program t.ex. for att deklarera variabler.

enum kort_farg fargl, farg2;

Har har vi deklarerat tva variabler med namnen fargi och farg2. Vi sager att dessavariabler har typen enum kort farg. Dettabetyder att de baraskall antanagot av vardena klover, ruter, hjarter eller spader. Variabeldeklarationer kan ocksa gorasdirekt i samband med deklarationen av upprakningen.

enum {klover, ruter, hjarter, spader} fargl, farg2;

Har har vi inte givit sjalva upprakningen nagot namn men det gar bra att deklarerabade ett upprakningsnamn och variabler pa sammagang.

enum kort_farg {klover, ruter, hjarter, spader}

fargl, farg2;

Variabler av upprSkningstyper kan behandlaspi samma satt som andra variabler. Mankan t.ex. gora tilldelningar

fargl = farg2;

fargl = spader;

och jamforelser

if (fargl == ruter)

if (fargl == farg2)

Upprakningstyper implementeras genom att kompilatom associerar vissa heltalsvar-den med vaije identifierare i en upprakning. Om man inte anger nagotannat kommerden forsta identifieraren att associeras med vardet 0, den andra med vardet 1 o.s.v. Iupprakningen kort farg kommer t.ex. klover att motsvaras av vardet 0, ruter avvardet 1, hjarter av vardet 2 och spader av vardet 3.

Man har mojlighet att sjalv ange vilka varden som skall associeras med de olika identi-flerama i en upprakning.Vi kan t.ex. ha en upprakning

enum op {add = 20, sub = 25, mul = 13, div = 18};

add har da vardet 20, sub vardet 25 etc. Man far ange vilka konstanta heltalsuttrycksom heist i en upprakningsdeklaration och man behover inte ange varden for samtligaidentifierare. Om man utelamnar vardet kommer en identifierare att ha foregaende

40 © Studentlitteratur

Page 46: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

5.7 Upprdkningstyper

identifierares varde plus 1 eller, om den ar fdrst, vardet 0. Det ar tillatet att lata tvaolika identifierare associeras med samma v^de men detta kan inte rekommenderas. I

foljande exempel bar a vardet 0, b vardet 10, c vardet 11,d vardet -5 och e vardet -4.

enum upp {a, b = 10, c, d = -5, e};

Avsiktenmed att anvandaupprakningstyper ar att man vill kunna arbeta med variableroch uttrycksombara far anta vissabestamdavarden. Om man t.ex. bar deklarerat vari-abeln fargi enligt ovan sa skall denna variabel bara fa ba nagot av vardena kiover,ruter, hjarter eller spader. Om man forsoker ge variabeln nagot annat varde villman att kompilatom atminstone skall ge en vaming.

Enligt standarden uppfattas upprakningstyper som beltalstyper. En upprakningstyp arbelt enkelt lika med typen int (aven om en kompilator kan valja att lagra ett upprak-ningsvarde i ett kortare beltalsobjekt).Med detta synsatt ar t.ex. deklarationen

enum {kiover, ruter, hjarter, spader};

ekvivalent med deklarationema

const int klover=0, ruter=l, hjarter=2, spader=3;

Fordelen med att betrakta upprakningstyper som typen int ar att alia operationer somkan goraspa beltal ocksa finns defmierade for upprakningstyper. Nackdelenar forstasatt man inte far samma kontroll av att olika upprakningstyper ocb beltalstyper inteblandas pa ett oonskat satt. (En kompilator kan forstas skriva ut vamingar.)

Som fbrsta exempel skall vi studera ett program som beraknar bur manga dagarssemester man tagit ut under ett ar. Som indata till programmet ger man bur mangasemesterdagar man tagit ut under var ocb en av arets manader.

/* Berakning av antal semesterdagar */

#include <stdio.h>

int main ()

{

enum manad {jan=l, febr, mars, april, maj, juni,

juli, aug, sept, okt, nov, dec};

enum manad akt_man;

int n, ant_dagar = 0;

printf("Ange semesterdagar for varje manad\n");

for (akt_man = jan; akt_man <= dec; akt_man + 4-) {

s c a n f ( " %d " , &n) ;

ant_dagar += n;

}

printf ("Totala antalet semesterdagar: %3d\n", ant_dagar);

)Studentlitteratur 41

Page 47: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

3. Cfrdn grunden

Observera att nar manbar upprakningstyper darde olikavardena inte ligger intill var-andra, som i typen enum op ovan, ger inte operatom ++ nasta upprakningsvarde utanokar heltalsvardet med ett.

I nasta exempel skall vi visa ett program som "komponerar"melodier. For att beskrivade olika tonema anvandervi en upprakningstyp enum ton.

enum ton [c, ciss, d, diss, e, f, fiss, g, giss, a, b, h};

Somindatatill programmet ges ett heltal som angerbur mangatoner vi vill att melodinskallbesta av. Programmet plockardarefter slumpmassigt ut sa mangatonerocb skri-ver ut tonemas namn.

/* Slumpmassig melodikomposition */

#include <stdio.h>

#include <stdlib.h>

#include <time.h>

int main ()

enum ton {c, ciss

enum ton ny_ton;

int ant_toner, i;

srand(time(0)); /'

printf("Antal toner?

printf("Melodin blir:\n");

for

d, diss, e, f, fiss, g, giss, a, b, h}

initiera slumtalsgeneratorn

"); scanf("%d", &ant toner);

' (i == 1; i '<= ant_toner; i + +) {

ny_ton = rand () % 12;

if (ny_

rto

== c) { printf( "c

else if (ny_ ton == ciss) { printf( "c#

else if (ny_ ton d) { printf( "d

else if (ny__ton == di s s) { printf( "d#

else if (ny_

fto

== e) { printf( "0

else if (ny_ ton == f) { printf( "f

else if (ny__ton == f iss) { printf( "f#

else if (ny__ton == g) { printf( "gelse if (ny__ton == giss) { printf( "g#

else if (ny_ ton == a) { printf( " a

else if (ny_

o-p

1

== b) { printf( "b

else if (ny__ton == h) { printf( "h

For att fa fram en slumpmassig ton anvander vi en slumptalsfunktion rand som ardeklarerad i filen stdiib.h. Varje gang funktionen anropas ger den som resultat ettslumpmassigt tal av typen int. Om vi tar detta resultat, dividerar det med 12 ocb tarvara pk resten vid divisionen far vi alltsa ett slumpmassigt beltal i intervallet 0 till 11.Detta sker i foljande uttryck dar operatom %betecknar "resten vid beltalsdivision".

42 ) Studentlitteratur

Page 48: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

3.8 Typen void

rand() % 12

De tal man far vid anrop av rand verkar slumpmassiga men egentligen ar de inteslumpmassiga utan utgor en lang sekvens av tal, dar varje tal beraknasutgaende franforegaende tal. Detta gor att slumptalsgeneratom alltid skulle generera samma slump-talsfbljd om man alltid borjade med samma varde. (Varjegang man korde programmetskulle det alltsa komponera samma melodi.) For att undvika detta kan man ge ettstartvarde, s.k. fro, till slumptalsgeneratom. Detta gor man genom att anropafiinktio-nen srand som skall ha en parameter av typen unsigned int. Som argument ger viresultatetav anropet time (O). Funktionen time ger som resultat den aktuella tidpunk-ten, oftast uttryckt i antal millisekunder.Detta varde fungerar fint som fro.

I programmet ser vi att det inte finns nagot enkelt satt att direkt skrivaut uppraknings-varden. Vi bar dMor anvant oss av en foljd av if-satser. I avsnitt 7.10 kommer vi attvisa en elegantare losning.

3.8 Typen voidTypen void ar ingen vanlig typ. Man kan t.ex. inte deklarera variabler av typen voideller berakna nagra varden som bar denna typ.

Typen void anvands ffamfor allt i samband med deklarationer av funktioner for attmarkera att varden saknas. Man anger att en funktion inte lamnar nagot resultat genomatt saga att funktionen ger ett resultat av typen void.

void skriv_resultat();

Typen void anvands ocksa for att markera att en fimktion saknar parametrar:

int rand(void);

Man kan slutligenba anvandning for typen void i sambandmed pekare.Det ar tillatetatt deklarera "pekare till objekt av typen void". En sadan pekare far peka till dataob-jekt av godtycklig typ.

3.9 Utskrift med funktionen printf

Standardfimktionen printf kan anvandas for att astadkomma redigerad utskrift.printf kan skriva ut beltal, flyttal, tecken ocb textstrangar. Den forsta parametem tillprintf skall alltid vara enformatstrdng. Denna innebMleromvandlingsspecifikationersom anger bur de varden som skrives ut skall redigeras. Alia omvandlingsspecifikationer inleds med tecknet %ocb avslutas med en typspecifikation, en bokstavskombina-tion som anger for vilken typ omvandlingen galler. Formatstrangen kan fomtom

) Studentlitteratur 43

Page 49: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

3. Cfrdn grunden

omvandlingsspecifikationer aven iimehMla text som skall skrivas ut. De ovriga para-metrama (om det fiims nagra) till printf ar varden som skall skrivas ut.

Lat OSS studeraett exempel. Antagatt vi bar variablema moch i med typemalong intrespektive int. Antag vidare att minnehaller v^det 5000000 och i vardet 27. Satsen

printf ("Storsta tal: %ld, antal tal: %d", m, 1*100);

kommer da att skriva ut texten

Storsta tal: 5000000, antal tal: 2700

I formatstrangen finns tva omvandlingsspecifikationer, %id och %d. TypspecifikationenId anger att det varde som skall skrivas ut bar typen long int och att utskriften skallske pa decimal form. Typspecifikationen d anger att vardet bar typen int och attutskriflen skall ske i decimal form.

Om man vill att ett %-tecken skall inga i den text som skall skrivas ut och inte tolkassom inledningen till en omvandlingsspecifikationfar man skriva dubbla %-tecken.

Lat OSS borja med att diskutera utskrift av heltal. I faktarutan visas de typspecifikatio-ner som kan anvandas vid utskrift av heltal.

Typspecifikationer vId utskrift av heltal

d, i Vardet tolkas som int, decimal utskrift

u Vardet tolkas som unsigned int, decimal utskrift

0 Vardet tolkas som unsigned int, oktal utskrift

X, X Vardet tolkas som unsigned int, hexadecimal utskrift

med sma resp. stora bokstaver

1 Kan sta fore nagon av ovanstaende, t.ex. id och luDa tolkas vardet som long int, unsigned long int, etc

11 Kan sta fore nagon av ovanstaende, t.ex. iid och iiu.Da tolkas vardet som long long int, unsigned long long int, etc.

Alia heltalsargument till printf som ar av kortare typ an int kommer automatiskt attomvandlas till typen int vid anropet. Darfor kommer printf aldrig att ha nagra para-metrar av dessa korta heltalstyper. Utskrift kan ske aven i oktal eller hexadecimalform. Observera att om detta sker tolkas det varde som skall skrivas ut som teckenlost.

Om variablema i och k bar vardena 27 respektive -1 kommer t.ex. satsen

printf("%o %o %x", i, k, k);

att skriva ut texten

44 © Studentlitteratur

Page 50: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

3.9 Utskrift medfunktionen printf

33 mill ffff

(Vi har har antagit att en int lagras med 16 bitar.)

Om man vid utskrift i oktal eller hexadecimal form skriver tecknet # efter %-tecknet

kommer alia oktala tal att skrivas ut med en extra nolla framfor och alia hexadecimala

tal med tecknen Ox eller ox framfor. Satsen

printf("%#o %#o %#X", i, k, k);

ger t.ex. utskriften

033 0177777 OXFFFF

Man har mojlighet att i en omvandlingsspecifikation ange antalet positioner som skallanvandas i utskriften. Det gor man genom att skriva ett heltal efter %-tecknet. Satsen

printf("%6d", i);

skriver t.ex. ut texten

27

(Det ar fyrablankateckenframfor tvaan.) I normalafall ar det sa att om det vardesomskall skrivas ut inte behover alia de angivna utskriftspositionema, sa placeras vardethogerjusterat i utskriftsfaltet och utfyllnad skermed blankatecken. Skulle vardet interymmas i de angivnapositionema sker anda utskriften med sa manga positioner sombehovs. Om man inte angivit antalet utskriftspositioner vid utskrift av ett heltal farman precis sa manga positioner som behovs for att rymma talet.

Lat OSS sa studera utskrift av flyttal. Vi borjar med ett exempel. Antag att variablemaminoch sum har typen double och att de har vardena 153.4respektive 5953.76 samt attheltalsvariabeln n har vardet 100. Satsen

printf("Minimum: %.5e\nMedelvarde: %.3f", min, sum / n);

ger da utskriften

Minimum: 1.53400e+02

Medelvarde: 59.538

Som vi ser ger typspecifikationen e utskrift i exponentform och typspecifikationen futskrift i "vanlig" form. Utskrift i exponentform sker alltid med en heltalssiffra. Forbada utskriftsformema galler att man kan bestammahur manga decimalerman vill ha iutskriften genom att ange en s.k. precision. En precisionsangivelsebestar av en punktfoljd av ett tal. Anger man inte precisionen vid utskrift av flyttal kommer utskriften attske med 6 decimaler. Vid utskriften kommer avrundning att ske till det onskade antaletdecimaler.

Funktionen printf kommer aldrig att ha nagra parametrar av typen float eftersomalia sadana automatiskt omvandlas till typen double vid anropet. De enda slag av flyt-

© Studentlitteratur 45

Page 51: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

3. Cfrangrunden

talsparametrar som kan forekomma ar darfor double och long double. I faktarutanvisas detypspecifikationer som ar aktuella forutskrift av flyttal.

Typspecifikationer vid utskrift av flyttal

f, If Vardet tolkas som double. Utskrift sker i "vanlig" form medminst en heltalssiffra. Antaletdecimaler bestamsav precisions-angivelsen. Om precision inte anges skrivs 6 decimaler ut.

e, le Vardet tolkas som double. Utskrift sker i exponentformmeden heltalssiffra. Antalet decimalerbestams av precisions-angivelsen. Om precision inte anges skrivs 6 decimaler ut.

E, IE Samma som formen e men med den skillnaden att ett stort E

anvands i stallet for ett litet e i utskriften.

g, ig Vardettolkassomdouble. Utskriftsker antingen i "vanlig"form ellerpa exponentform. Om vardetar "stort" (exponenten arstorre an precisionen) eller "litet" (exponenten ar mindre an -4)sker utskriften i exponentform, annars i "vanlig" form.

G, iG Samma som formen g men med den skillnaden att ett stort E an

vands i stallet for ett litet e om utskriftensker i exponentform.

L Kan sta fore nagon av ovanstaende enkla, t.ex. Lf och Le.Da tolkas vardet istallet som long double.

Aven nar det galler flyttal kan man i en omvandlingsspecifikation ange det totala antalet tecken som skall inga i utskriften. Detgormani sa fall forst, fore precisionsangivel-sen. Med samma variabler som i exemplet ovan ger t.ex. satsen

printf("Minimum:%15.5e\nMedelvarde:%12.3f", min, sum / n);

Utskriften

Minimum: 1.53400e+02

Medelvarde: 59.538

Funktionen printf erbjuder en mangd ytterligare mojligheter. Bl.a. kan man ge ettspeciellt redigeringstecken forst i en omvandlingsspecifikation. Vibar tidigare sett hurtecknet # kunde anvandas vid utskrift av oktala och hexadecimala tal. De ovrigaredigeringstecken ar minustecken, plustecken, och blankt tecken. Ett minustecken angeratt ett tal skall vansterjusteras. Med ett plustecken anger man att tecknet for ett tal all-tid skall skrivas ut (avenom det ar positivt). Ett blanktteckenanger att om ett positivttal skrivs ut sa skallett extrablankttecken skrivas ut framfor taletpa tecknets plats.

46 © Studentlitteratur

Page 52: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

3.9 Utskrift medfunktionen printf

Normalt skerutfyllnad medblanka tecken om ett tal somskallskrivas ut inte fyller uthela utskriftsfaltet. Man kan andra detta sa att utfyllnad i stallet sker med nollor,genomatt lata det tal somangerantaletutskriftspositioner borjamed siffrannoil.

De olikamojlighetema framgar bastav nagraexempel. Antag i foljande satser att vari-ablema i och j bar typen int och att de innehMlervardena 27 resp. -48.

printf("%d", i); ger utskriften:27

printf("%d", j); ger utskriften:-48

printf("% d", i); ger utskriften: 27

printf("% d", j); ger utskriften:-48

printf("%5d", i); ger utskriften: 27

printf("%5d", j); ger utskriften: -48

printf("%05d", i); ger utskriften:00027

printf("%05d", j); ger utskriften:-0048

printf ("%-5d", i); ger utskriften:27

printf ("%-5d", j); ger utskriften:-48

printf("%+5d", i); ger utskriften: +27

printf("%+5d", j); ger utskriften: -48

Nar man anger bur manga tecken som skall finnas i utskriftsfaltet eller nar man angerprecisionen kan man istalletfor att ange ett tal skrivatecknet*. Dettabetyderatt nastaparameter till printf inneballer det aktuella vardet. (Denna parameter kommer alltsainte att skrivas ut.) Antagt.ex. att variablemai ocb x bar vardena 134respektive 5.27ocb att beltalsvariablema tot ocb dec bar vardena 7 respektive 3. Da ger satsema:

printf("%*d\n", tot, i);

printf("%*.*f", tot, dec, x);

Utskriften

134

5.270

Utskrift av enstaka tecken kan goras med standardfunktionen putchar, men printf arocksa anvandbar. Man anvander da omvandlingsspecifikationen %c. (Se faktarutan).

printf("Tecknet ar %c", teck);

Vid utskrift av textstrangar ocb teckenfalt anvands omvandlingsspecifikationen %s.

printf("Hej %s", namn);

Tecken i teckenfaltet namn skrivs ut tills ett nolltecken \o patraffas. Detta ar det nor-mala sattet att markera slutet pa textstrangar.

Vill man skriva ut enstaka tecken av typen wchar t anvander man omvandlings-1specifikationen %ic.

printf("Det breda tecknet ar %lc", wc) ;

© Studentlitteratur 47

Page 53: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

3. Cfran grunden

Typspecifikationer vid utskrift av text

c Vardet tolkas som en int innehallande en teckenkod.

Motsvarande tecken skrivs ut.

s Vardet tolkas som en pekare till ett fait med komponenter avtypen char. Alia tecknen i faltet, fram till nolltecknet, skrivs ut.

ic Vardet tolkas som en wchar t innehallande en teckenkod.

Tecknet omvandlas till en foljd av bytes (UTF-8) och skrivs ut.

Is Vardet tolkas som en pekare till ett fait med komponenter avtypen wchar t. Alia komponentema i faltet, fram till nolltecknet,omvandlas till en foljd av multibyte characters (UTF-8) och skrivs ut.

Textstrangar och teckenfalt i vilka de ingaende komponentema ar av typen wchar tkan skrivasut med omvandlingsspecifikationen %is.

printfC'Hej %ls", wnamn);

Det ar dock inte sakert att implementeringenskriver ut tecken med teckenkoder storrean 255 pa ett korrekt satt.

3.10 Inlasning med funktionen scanf

For att lasa in tal, tecken och textstrangar fran tangentbordet kan man anvanda stan-dardfunktionen scant. Den forsta parametem till scant skall alltid vara enformat-strdng, Denna innehaller omvandlingsspecijikationer som anger bur den text sommatas in fran tangentbordet skall tolkas. Vaije omvandlingsspecifikation inleds medtecknet %ochavslutas meden typspecifikation. Argumenten efterformatstrangen skallvara adresserna till de variabler dar de inlasta vardenaskall laggas. Darfor skall manom man har vanliga enkla variabler (heltal, flyttal eller tecken) skriva ett &-teckenframfor variabelnamnet. Tecknet &betecknar namligen en operator som ger adressentill sin operand.

Exempel pa inlasning till heltalsvariabler ges i satsen

scant("%d%hd%u", &s, &k);

Vi antar att i har typen int, s typen short int och k typen unsigned int. De olikatalen kan nar de matas in separeras med ett eller flera blanka tecken, med tabulator-tecken eller med nyradstecken. Om man t.ex. matar in

154 -11 64000

far i vardet 154, s vardet -11 och k vardet 64000.

48 © Studentlitteratur

Page 54: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

3.10 Inldsning medfunktionen scanf

I faktarutan visas de typspecifikationer som kan anvandas vid lasningav heltal.

Typspecifikationer vid lasning av heltal

d De inlasta tecknen skall tolkas som ett decimalt heltal och

placeras i en variabel av typen signed int.

u De inlasta tecknen skall tolkas som ett decimalt heltal och

placeras i en variabel av typen unsigned int.

0 De inlasta tecknen skall tolkas som ett oktalt heltal och

placeras i en variabel av typen unsigned int.

X De inlasta tecknen skall tolkas som ett hexadecimalt heltal och

placeras i en variabel av typen unsigned int.

1 De inlasta tecknen tolkas pa samma satt som en heltalskonstant(se avsnitt 3.3.2) och placeras i en variabel av typen signed int.

hh Kan sta fore de ovanstaende, t.ex. hhd och hhi.

Talet placeras da i en char istallet for en int.

h Kan sta fore de ovanstaende, t.ex. hd och hi.

Talet placeras da i en short int istallet for en int.

I Kan sta fore de ovanstaende, t.ex. id och ii.

Talet placeras da i en long int istallet for en int.

II Kan sta fore de ovanstaende, t.ex. iid och ill.

Talet placeras da i en long long int istallet for en int.

Vi ser att det ocksa ar mojligt att ange heltal pa oktal eller hexadecimalform vid inlas-ningen. Om vi t.ex. har satsen

scanf("%d%o", &1, &j);

dar bade i och j har typen int och vi skriver

23 23

pa tangentbordet, sa kommer i att fa vardet 23 och j vardet 19.

Inlasning av flyttal gar till pa motsvarande satt. Satsen

scanf("%f%lf%Lf", &x, &y, &z);

laser t.ex. in varden till variablema x, y och z som vi antar har typema float, double

respektive long double. Om talen

4.67 -1.985e2 14

© Studentlitteratur 49

Page 55: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

3. Cfrangrunden

matas in Mn tangentbordet far x vardet 4.67, y vdrdet -198.5 och z vardet 14.0.

Flyttal som matas in ffin tangentbordet far ha samma form som flyttalskonstanter,d.v.s. de kan skrivas antingen i "vanlig" form elleri exponentform. Dessutom fSr mansom i exemplet ovan skriva ett tal i heltalsform. Framfor eft tal kan man ocksa skrivaeft plus- eller minustecken.

De omvandlingsspecifikationer som ar aktuella vi inlasning av flyttal ar e, f och g,eventuellt medtecknet i ellerl framfor. (Sefaktarutan.)

Typspecifikationer vid lasning av flyttal

e, f, g De inlasta tecknen skall tolkas som ett reellt tal

och placeras i en variabel av typen float.

le, If, ig De inlasta tecknen skall tolkas som ett reellt tal

och placeras i en variabel av typen doiibie.

Le, Lf, Lg De inlasta tecknen skall tolkas som ett reellt tal

och placeras i en variabel av typen long double.

Funktionen scanf kan ocksa utnyttjas for att lasain varden till variabler av typencharoch till teckenfalt. (Se faktarutan).

[text]

[^text]

Typspecifikationer vid lasning av text

Argumentet skall vara en pekare till en variabel avtypen char. Ett tecken lases in och laggs dit pekaren pekar.Nolltecken laggs inte till.

Argumentet skall vara en pekare till ett teckenfalt. Inledande vitatecken hoppas forst over. Efterfoljande tecken som inte ar vita lasessedan in och laggs dit pekarenpekar. Ett nolltecken laggs till sist.

Som specifikationen s, men matchar bara tecken som ingar i text.

Som specifikationen s, men matchar bara tecken som inte ingar i text.

Kan sta fore de ovanstaende, t.ex. ic och is.

Argumentet skall vara en pekare till en variabel avtypen wchar t resp. till ett fait med komponenter av denna typ.De inlasta tecknen tolkas da som multibyte characters och gors omtill typen wchar t innan de laggs dit pekaren pekar.

For att lasa ett enstaka tecken anvander man omvandlingsspecifikationen c. Satsen

50 © Studentlitteratur

Page 56: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

3.10 Inldsning medfunktionen scanf

scanf("%c", &teck);

laser in ett tecken till variabeln teck. Nar man anvander omvandlingsspecifikationen choppas inga blanka tecken over utan nasta tecken som star i tur lases, aven cm detta arett blanktecken, en tabulator eller ett nyradstecken. Antag t.ex. att vi bar satsen

scanf ("%c%d%c%d",&cl, &i, &c2, &j);

dar ci och c2 bada bar typen char. Om vi skriver

-45

20

sa far ci vardet' , i vardet 45, c2 vardet' \n' ocb j vardet 20.

Aven textstrangar kan lasas in. Da anvander man omvandlingsspecifikationen %s.Antag att vi bar foljande programrader:

char text [ICQ] ;

scanf("%s", text);

Da lases en textstrang ocb den placeras i teckenfaltet text. Inlasningen avbryts sa fortett blanktecken, tabulatortecken eller nyradstecken patraffas. Da placeras ett noll-tecken i nasta element i text. Som vi tidigare papekat skall man vid lasning till ettteckenfalt inte ba nagot &-tecken framfor variabelnamnet. Detta beror pa att variabel-namnet for ett fait faktiskt redan betecknar adressen till faltet. (Se vidare kap 7.)

Av faktarutan framgar att scanf aven bar omvandlingsspecifikationema ic ocb is |vilka mojliggor inlasning fran multibyte characters till variabler resp. fait med kompo-nenter av typen wchar t.

Man kan lagga in annan text an omvandlingsspecifikationer i formatstrangen tillscanf. Lagger man in vita tecken, t.ex. mellanslag, matcbar detta ett eller flera inlastavita tecken ocb lagger man in andra tecken kommer dessa att matcbas direkt vid inlasningen. Vi kan t.ex. gora anropet

scanf("\ninput=%d output=%d", &i, &j);

Anvandaren kan da t.ex. skriva

input=56 output=78

Funktionen scanf lamnar ett resultatvarde, aven om vi inte visat det bittills i exemplen

i detta avsnitt. Som resultat av ett anrop far man antalet lyckade omvandlingar. Omingen omvandling skett innan end offile uppstatt ges vardet eof som resultat. I exem-plet bar skulle man lasa in varden till tva variabler. Da skall man fa vardet 2 som resultat om indata ar inmatade pa ett korrekt satt. Man kan alltsa i programmet kontrolleraatt indata matats in ratt genom att se pa resultatvardet.

© Studentlitteratur 51

Page 57: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

3. Cfrangrunden

Foljande programavsnitt forsoker lasain ett heltal fran tangentbordet. Omett felaktigtheltalmatas in ges en felutskrift ochhela den inmatade raden hoppas over.

/* Forsok lasa heltal */

if ( scanf("%d", &i) != 1) {

printf ("Felaktigt heltal\n");

while (getchar() != '\n')

; /* torn sats */

}

En omvandlingsspecifikation far ha ett heltal direkt efter %-tecknet. Om det finns ettsadant betyder det att hogst sa m4nga tecken fSr lasas i denaktuella omvandlingen. Iavsnitt 2.6sig vi t.ex. att vi kunde fa en sakrare inlSsning av textmedhjalpav detta.

char namn[20];

3canf ( "%19s", namn); /* sakrare inlasning */

Det ar ocksa tillatet att skriva tecknet * efter %-tecknet. Det betyder att texten skallmatchas i inlasningen men att inget skall placeras i nagot argument. Har foljer ettexempel som anvanderheltal, * och [] i omvandlingsspecifikationema:

int j ;

double z;

char a [ 50];

scanf("%3d%lf%*d%[0123456789]&i, &z, a);

Antag att vi ger foljande indata

1234567 987 43x6

Da far variabeln i vardet 123, z vardet 4567.0 och a kommer att inneh^la texten "43".

Nasta tecken som star i tur att lasas ar tecknet x.

Inlasning av en textstrang med scanf kan sluta innan en rad tar slut om det fmns ettblanktecken eller ett tabulatortecken pa raden. Ibland vill man lasa en hel rad i taget,oberoende av vilka tecken som star pa raden. En annan standardfunktion som da aranvandbar ar fgets. Satsen

fgets(text, n, stdin);

laser en hel rad, dock hogst n-i tecken, och placerar de lasta tecknen i teckenfaltettext. Om nyradstecknet ryms bland de n-i tecknen finns det med i text. Ett noll-teckenplaceras alltid i text efter den inlastateckenstrangen. Om lasningen misslyckasger fgets som resultat en tom pekare (vardet null).

52 © Studentlitteratur

Page 58: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

3.11 Typdeklarationer(typedef)

3.11 Typdeklarationer (typedef)

I de fiesta modema programmeringssprak kan man deklarera egna typer och sattanamn pa dem. Denna mojlighetfinns aven i C, men den inte ar sa flexibel som i andraprogrammeringssprak. Typdeklarationen har dessutom en ganska egendomligsyntax.

For att deklarera ett typnamn anvandsprecis samma syntax som for en variabeldekla-ration men man skall skriva det reserverade ordet typedef framfor. Det som var varia-belnamn blir nu i stallet typnamn. Vi tar ett exempel:

typedef float vikt, volym;

typedef unsigned char text [100] ;

Har har vi deklarerat tre typnamn: vikt och voiym ar nu samma sak som float ochtext betecknar teckenfaltmed 100platser.Nu kan vi anvanda dessa typnamn i varia-beldeklarationer som t.ex.

volym veil, vol2;

vikt vil, vi2, vi3;

text ifl, if2;

Har deklareras fem flyttalsvariabler och tva teckenfalt.

I en del programmeringssprak anses de egendefinierade typema vara helt nya typermen sa ar det enligt standarden inte i C, vii blir t.ex. en helt vanlig float-variabel.

Det fmns flera goda skal att anvanda typedef:

• Det forbMrar flyttbarheten av program. Man kan t.ex. namnge heltalstyper medvili langd och endast andra i typdeklarationen om sa behovs nar programmet flyt-tas. Ett exempel pa detta ar typema int st, intiot, int32_t etc. i C99.

• Det okar lasbarheten. Om man t.ex. ser att en variabel har typen voiymsa sager detmer an att man bara ser att den har typen float.

• Det gor programmet mer andringsbart. Om vi t.ex. kommer pa att typen double

kravs for att beskriva vikter sa behover vi bara andra defmitionen av typen vikt. Vibehover inte andra float till double pa flera stallen i programmet och kanskemissa nagot.

• Det kan forenkla krangliga deklarationer. C:s regler for hur man anger typer kanibland leda till att deklarationer blir mycket krangliga och svarlasta. I sadana fallkan man forenkla och fortydliga deklarationema genom att anvanda typedef. Vikommer att se exempel pa detta i senare kapitel.

Vi far daremot tyvarr ingen extra sdkerhet. Anledningen till detta ar att typedef inteskapar nagon ny typ, utan bara ger ett altemativt namn pa en redan existerande typ:

voil = vil; /* Helt OK, bada ar float */

© Studentlitteratur 53

Page 59: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

3. Cfrdn grunden

Flera av de typer som anges i standarden ar i sjalva verket inga"riktiga" typer utandear definierade med hj^lp av typedef. Vi bar redan namnt int st, intie t, int32_tetc. i C99. Ett annat exempel pa en sadan typ ar wchar t som t.ex. skulle kunna varadefinierad pa foljande satt i filen stddef. h:

typedef unsigned short int wchar t;

3.12 Ovningsuppgifter1. Vilka av nedanstaende ar identifierare i C.

nr4 NR4 nr_4 nr-4

_nr4 IDENT ident identi

union vardet adam&eva identifierare

Vilka av dembetraktas somsamma identifierare om det ror sig om intemarespek-tive extema namn.

2. Ange med hjalp av beskrivningen i Appendix C bur talen 175 ocb -4 kan skrivaspa tvakomplementsform med 16 bitar.

3. Tag reda pa bur manga bitar som anvands for att representera de fordefinieradebeltalstypema i den C-version du anvander.

4. Ommananvander en skrivande terminal kanmanibland, precis sompa en vanligskrivmaskin, stryka under ett ord genom att backa tillbaka pa raden ocb skrivaunderstrykningstecken _ under ordet. Skriv ett programavsnitt som skriverut dentextstrang som firms i variabeln s understruken.

5. I Kommandotolken i Windows anvands normalt i vasteuropa den aldre kodningenCP-850. Teckenkodema 0 till 7F (bexadecimalt) overenssttomer med ASCII-koden ocb teckenkoder som ar storre an eller lika med 80 (bexadecimalt) anvandsfor att representera tecken som saknas i ASCII-koden. Bokstavema a, a ocb orepresenteras t.ex. med foljande koder. (Kodema ar angivna bexadecimalt.)

a 86 A 8F

a 84 A 8E

6 94 0 99

Skriv ett program som laser en text som matats in fran Kommandotolken ocb somskriverut texten med alia a, a ocb 6 oversattatill motsvarande LATIN_1-tecken.

6. Visa bur talet -17.625 kan lagras som ett flyttal med 32 bitar. Antag att exponent-delen lagras i tvakomplementsform ocb upptar 8 av bitama. (Se Appendix C.)

7. Forsok att med bjalp av reglema for typomvandlingar i Appendix D forklara var-for foljande program bamnar i en "evig snurra". Antag att typen char i den aktu-ella implementeringen betraktas som unsigned ocb representeras med 8 bitar ocb

54 © Studentlitteratur

Page 60: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

3.12 Ovningsuppgifter

att typen int representeras med 16 bitar. Det antas vidare att getchar retumerarett varde av typen int och att makrot eof bar vardet -1.

#include <stdio.h>

int main()

{

char c;

int antal_tecken = 0;

while ((c = getcharO) != EOF) {

antal_tecken++;

}

}

8. Deklarera en upprakning veckodag som beskriver veckans dagar. Skriv sedan ettprogram som laser de temperaturer som uppmatts kl 13 under en vecka. Program-met skall berakna den hogst uppmatta temperaturen och medelvardet av tempera-turema. Den variabel som anger aktuell dag skall ha typen enumveckodag.

9. Skriv ett program som laser in dagens datum som: 20dd-mm-dd. Darefter skallprogrammet lasa in ett personnummer och skriva ut texten: Grattis! om den aktu-ella personen har fodelsedag. Personnummer anges med minustecken.

10. Undersok vilket teckenkodning din texteditor anvander for att lagra program-texten. Kan man valja kodning? (Utforska kommandotSpara som ...) Forsok ock-sa ta reda pa vilket teckenkodning kommadofonstret anvander. Ardetsamma kodning som programtexten lagrats med? Gar det ev. att andra?

11. Definiera egna heltalstyper myintie och myint32 som garanterat har 16 resp. 32bitar i den kompilator du anvander.

12. Undersok om den kompilator du anvander klarar av C99. Finns det kanske nagonoption som kan skrivas pa kommandoraden?

) Studentlitteratur 55

Page 61: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Uttryck och operatorer 4

Ett uttryck ar en sprakkonstruktion dar man satter ihop ett eller flera varden med hjalpav en operator och pa sa satt far ett nytt varde. I C finns en ovanligt rik repertoar avolika operatorer. Ett uttryck bar nagon av formema

X Op

primart uttryck

dar Xoch y kallas operander och op betecknar operatom. En operand kan i sin tur varaett uttryck. Det finns som syncs operatorer med en eller tva operander. (Det finns fak-tiskt i C dessutom en operator, den s.k. villkorsoperatom, som bar tre operander. Merom detta foljer i avsnitt 4.7.)

Den enklaste formen av uttryck ar de s.k. primdra uttrycken. Ett sMant bestar av enidentifierare (t.ex. ett namn pa en variabel), ett konstant varde eller ett uttryck omgivetav parenteser. Exempel pa primara uttryck ar:

(i + j)

Exempel pa uttryck ar:

1+3 P = q ++X r -= S

n-- a + b * c 381 k == n

Viktigt att observera ar att ett uttryck alltid bar ett varde av en viss typ, vilket ar bero-ende av de ingaende operandemas typ och varde.

I detta kapitel skall vi studera de fiesta av operatorema i C. Vi skall ocksa bebandla deolika operatoremas prioriteter och i vilken ordning olika uttryck beraknas. Vi kommerbar ffamst att diskutera de olika operatorema i samband med aritmetiska typer menflera av operatorema kan ocksa anvandas for andra slag av typer, t.ex. pekartyper.Detta genomgas i kapitel 7.

© Studentlitteratur

Page 62: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

4. Uttryck och operatorer

4.1 Aritmetiska uttryckMedaritmetiska uttryck menarvi uttryck somhar operander av aritmetisk typ och somger ett resultat av aritmetisk typ. Aritmetiska uttryck kan t.ex. innehalla de vanligaoperatorema + - * och / for addition, subtraktion, multiplikation respektive division

tl + t2 vardet + 5.2 1 - n x - y

p * q 100 * antal i / 10 x / y

Resultatetav ett aritmetiskt uttryck far sammatyp som de ingaende operandema. Omoperandema ti och t2 ovan t.ex. bMa har typen int sa far resultatet av uttrycket ti +t2 ocksa typen int.

Innan ett aritmetiskt uttryck beraknas omvandlas automatiskt operandema sa att de farsamma typ. (Se avsnitt 3.6.1).

Operatorema +och - finns ocksa i s.k. undra variantersom saknarvansteroperand.

+6 +n -3.7 +y

Divisionsoperatom / fordrar en speciell kommentar. Om operandema ar av flyttalstypkommer resultatet att fa samma flyttalstyp och bli kvotenmellan de tva operandema.Om Xoch y har vardena 7.0 respektive 2.0 och bada har typen do\ibie sa kommer t.ex.uttrycket x / y att ha typendouble och vardet3.5. Om daremot bkda, operandema arav heltalstyp sa sker heltalsdivision, vilket innebaratt man ser hur manga ganger denhogra operanden "gar i" den vanstra. Om i och j har vardena 7 respektive 2 och hartypen int sa far uttrycket i / j vardet 3 och blir av typen int.

For att fa fram restenvid en heltalsdivision anvands modulo-operatom %. Den ger res-ten da den forstaoperanden heltalsdivideras med den andra. Uttrycket

12 % 5

far t.ex.vardet2. Operatom %kraveratt bMa de ingaende operandema ar av heltalstyp.Den ar inte definierad for flyttalstyper.

Om bada de ingaende operandematill en heltalsdivision eller till %- operatom ar posi-tiva sa kommer ocksa resultatetatt bli positivt. Om nagon av operandemaar negativ arinte resultatet valdefinierat. Det kan bli olika nar man anvanderolika kompilatorer.

Nar man har ett sammansatt uttryck som innehaller flera operatorer kommer de ingaende operatoremasprioriteter att styra hur uttrycket skall tolkas, eller gmpperas. Operatorer med hogre prioritet beraknas fore operatorer med lagre prioritet och (nar detgaller aritmetiska operatorer) sker berakningen fran vanster till hoger om tva operatorer har samma prioritet.

I sjalva verket har kompilatom fnhet att berakna de ingaende deluttrycken i vilken ord-ning den vill. Den hogra operanden i ett uttryck kan t.ex. beraknas fore den vanstra.

58 © Studentlitteratur

Page 63: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

4.1 Aritmetiska uttryck

Vill man styra berakningsordningen exakt far man med hjalp av tilldelningar laggamellanresultat i tillfalliga variabler.

Om ett uttryck innehaller flera forekomster av operatom + eller * kan kompilatomdessutom gruppera om berakningen, detta trots att man skrivit parenteser. Uttrycket a+ (b + c) kan t.ex. komma att beraknas som (a + b) + cellersoma + (b + c) ellert.o.m. som (a + c) + b. For att tvinga fram en viss grupperingav sadanauttryck kanman anvandaden unara + operatom. Uttrycketa + +(b + c) garanterart.ex. att b ochc adderas innan summan av dem adderas till a.

Av de operatorer vi hittills bar sett bar de unara operatorema + ocb - bogst prioritet,darefter foljer operatorema * / ocb %. Lagst prioritet bar de vanliga additions- ocbsubtraktionsoperatorema + ocb - (de med tva operander). Uttrycket

-2 + 4/ 2*3

far t.ex. vardet 4.

Vill man andra gmpperingeni ett uttryck kan man anvanda parenteser. Uttrycket

(-2 + 4) 7 2*3

far exempelvis vardet 3.

Nar man arbetar med aritmetiska uttryck bar man ofta anvandning for matematiskastandardfunktioner av olika slag, t.ex. trigonometriska funktioner. Det fmns en inklu-deringsfil math.h som innebMler deklarationer av ett antal sMana matematiska funktioner. Vill man anvanda de matematiska standardfunktionema maste man ba raden

#include <math.h>

i borjan pa sitt program. I faktamtan visas nagra av funktionema i math.h. Gemen-samt for alia funktionema som listas dar ar att de skall ba ett argument av typen double

ocb att de nar de anropasger ett resultat som ocksa ar av typen double. Det finns ocksamotsvarande funktioner med ett f eller ett i sist i namnet, t.ex. sinf ocb sini. Dessa

bar argument av typen float respektive long double.

Har maste ett par vamingar utfardas. Ett mycket vanligt programmeringsfel ar att mananvander de matematiska standardfunktionema men glommer att inkludera filenmath.h i sitt program. En kompilator som inte foljer C99-standarden ger inte nagonfelutskrifl for detta. I stallet gor den vissa standardantaganden vilket leder till att funktionema ger belt felaktiga resultat nar de anropas. (En mer utforlig beskrivning avfiinktionsdeklarationer ocb funktionsanrop ges i kapitel 6.)

Anrop av matematiska funktioner kan inga i aritmetiska uttryck, som t.ex.

a + sin(x) 2.5 * sqrt(y) exp (p) / log(q)

fabs (x + y) floor (b * c) sqrt(sin(x))

© Studentlitteratur 59

Page 64: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

4. Uttryck och operatorer

Enkia funktioner i math.h

Nedanstaende funktioner barparametrar ochreturvarden av typen double.Det finns motsvarande funktioner med ett f eller ett i sist i namnet som bar

parametrar ocb returvarden av typen float respektive long double.

sin cos tan parametem anges i radianer

asin acos atan ger arcsin etc.

sinh cosh tanh byperboliska funktionerexp ger e uppbqjt till parametemlog ger naturliga logaritmen In

logic ger den vanliga logaritmen (med basen 10)sqrt ger kvadratroten

cell ger minsta bela tal som ar storre an eller lika med parametemfloor ger stdrsta bela tal som ar mindre an eller lika med parametemf abs ger absolutvardet av parametem

Funktionema pow, powf ocbpowi kanocksa namnas. Debaraliatvaparametrar ocbgersom resultat den forsta parametem uppbojt till den andra. Parametrama bar typemadouble, float respektive long double ocb resultaten far ocksadennatyp. Anropet

pow(x, y)

ger alltsa x uppbojt till y. Om x ar negativt kravs att y skall vara ett belt tal.

Funktionena abs, labs ocb iiabs vilka deklareras i stdiib.h ar ocksa intressanta. De

bar en parameter som ar av typen int, long int respektive long long int. Resultatetar av samma typ ocb ger absolutvardet av argumentet.

Som exempel pa anvandning av matematiska standardfunktioner skall vi studera ettprogram som beraknar den tredje sidans langd i en triangel dar man kanner tva avsidoma, a ocb b, ocbmellanliggande vinkel, v. Foljande formel kanutnyttjas:

c = - labcosv

#lnclude <stdlo.h>

#lnclude <math.h>

int main () /* Beraknlng av en slda 1 en triangel */

60

const double pi = 3.1415926536;

float a, b, c, v;

prlntfC'Ange tva av sldorna: ");

scanf ( "%f%f", &a, &b);

) Studentlitteratur

Page 65: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

4.2 Oknings- och minskningsoperatorer

printf("Mellanliggande vinkel (i grader)? ");

scanf("%f", &v);

c = sqrt(a * a + b * b -

2 * a * b * cos(v * 2 * pi / 360));

printf("Den tredje sidan blir %.3f\n", c);

}

Lagg marke till att vinkeln v i programmet maste omvandlas fran grader till radianeriiman funktionen cos anropas. Detta gors enligt formeln

radianer = grader x360

4.2 Oknings- och minskningsoperatorerNar man programmerar visar det sig att satser som

antal = antal + 1;

rest = rest - 1;

ar mycket vanliga. Konstruktionersom okar eller minskar variablersvarde med 1 ingarsom nodvandiga delar i de fiesta program. I C fmns oknings- och minskningsoperatorer som erbjuder ett bekvamt satt att gora detta. Okningsoperatom betecknas med ++och minskningsoperatom med Operatorema har bara en operand och denna operand maste vara av skalar typ samt vara ett objekt som kan andras. Operanden kan t.ex.vara en enkel variabel. Nagra exempel ar:

+ + i

--n

tal + +

kvar--

Daremot ar det inte tillatet att skriva

+ + (i + j) /* FEL! */

255-- /* FEL! */

Minsknings- och okningsoperatorema finns som synes i tva varianter. De kan skrivasfore eller efter sin operand. Den variant som skrivs fore operanden kallas enprefixope-rator och den som skrivs efter operanden kallas en postfixoperator.

Satsema forst i detta avsnitt skulle kunna skrivas

++antal;

--rest;

eller

a n t a 1 + + ;

r 0 s t - - ;

© Studentlitteratur 61

Page 66: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

4. Uttryck och operatorer

I detta fall spelar det ingen roll vilken av variantemaman valjer, resultatet blir andadetsamma. Det finns emellertid en avgorande skillnad mellan prefix- och postfixvari-antema vilken bar betydelse cm operatorema ingar i uttryck. Uttrycken ++k och k++innebarbada att variabeln k okas med 1 men vdrdetav uttrycken ar olika. SkrivsMet++k innebar att okningenav k skall ske innan uttrycketsvarde beraknas och skrivsattetk++ innebar att okningenav k skall ske efter det att uttryckets varde bar beraknats. Omk franborjan bar vardet 5 sa bar alltsauttrycket ++k vardet6 ochuttrycket k++ vardet5. Motsvarande galler for minskningsoperatom. Antagt.ex. att vi bar foljande satser:

i = 4; j = 7;

a = ++i * --j; /* a far vardet 30 */

i = 4; j = 7;

b = i++ * j--; /* b far vardet 28 */

Man skall inte skriva uttryck som

i++ + i * j; /* Felaktigt uttryck! */

dar den forandrade variabeln anvands pa fler stallen. Det ar namligen odefinierat burett sadant uttryck beraknas. Det ar faktiskt inte ens sakert att variabeln andras ochffamfbr allt inte exakt nar.

Som exempel pa anvandning av okningsoperatom studerar vi ett programavsnitt somletar igenom en textstrang och beraknar bur manga blanka tecken som finns i den.Antag att foljande deklarationer ar gjorda

char text [ 100];

int ant_blanka, i;

och att variabeln text inneballer en text som ar bogst 100tecken lang och som avslu-tas med nolltecknet \o. Satsema

/* berakning av antal blanka i en text */

ant_blanka = 0;

i = 0;

while (text[i] != '\0') {

if (text[i++] == ' ') {

++ant blanka;

printf("Antal blanka: %ld\n", ant_blanka);

resulterar da i att antalet blanka tecken i texten skrivs ut. Pa tva stallen anvands

okningsoperatom. Pa raden

if (text[i++] == ' ')

62 © Studentlitteratur

Page 67: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

43 Jdmfdrelseoperatorer

ar det viktigt att postfixvarianten anvands eftersom okningen av i inte skall ske forranefter det att det aktuella tecknet i text bar valts ut. (Numreringen av tecknen i en text-strang borjar ju med 0.) Pa det andra stallet dar okningsoperatom anvands

++ant_blanka;

spelar det ingen roll om man anvander prefix- eller postfixvarianten eftersom vardet avuttrycket anda aldrig anvands.

4.3 JamforelseoperatorerFor att jamfora olika varden kan man anvanda de operatorer som visas i faktarutan.

Jamforelseoperatorer

== lika med < mindre an > storre an

! = icke lika med <= mindre an el. lika med >= storre an el. lika med

Nagra exempel ar:

X < y

X == y

a > 3.5

i != 0

i + j <= 4

a * b + c ==

n >= k /

- y

Dessa operatorer bar alia tva operander som skall ba aritmetisk typ (eller vara pekare).Nar operandema bar aritmetisk typ tilllampas forst "de vanliga aritmetiska typom-vandlingama" som beskrevs i avsnitt 4.1. Operandema omvandlas alltsa sa att de farsamma aritmetiska typ.

Typomvandlingama vallar for det mesta inga problem. Man far de resultat man for-vantar sig men man far se upp med uttryck dar man jamfor beltal med tecken med bel-tal som ar unsigned. Antag t.ex. att variabeln u bar vardet 5 ocb typen unsigned intocb att i bar vardet -1 ocb typen int. Da blir faktiskt uttrycket

u > i

falskt. Detta beror pa att vardet av i omvandlas till typen unsigned int ocb blir ettstort positivt tal.

I tidigare versioner av C finns ingen speciell logisk typ i likbet med typen boolean it.ex. C++ ocb Java, utan resultatet av ett jamforelseuttryck bar alltid typen int. Omjamfbrelsen ar sann blir vardet 1ocb om jamfbrelsen arfalsk blir vardet 0. I C99 bar |man emellertid som vi tidigare namnt introducerat en ny typ med namnet _boo1. Det aren beltalstyp men de mojliga vardena ar bara 0 eller 1. Omvandlingar till ocb frandenna typ sker automatiskt. Vid omvandling till typen __boo1 blir resultatet 0 om vardet

) Studentlitteratur 63

Page 68: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

4. Uttryck och operatorer

somskallomvandlas ar likamed0 och 1 annars. Vidomvandling frantypen_boo1 blirdet resulterande vardet antingen 0 eller 1.

Observera speciellt att operatom som testar likhet skrivs med dubbla likhetstecken ==.Ett av de vanligasteprogrammeringsfelen ar att man forvaxlar likhetsoperatom== medtilldelningsoperatom =. Ett exempel ar programavsnittet

if (i = 0) { /* VANLIGT FEL! */

printf ("Noll") ;

}

else {

printf("Inte noil");

}

Texten inte noii kommer bar att skrivas ut, oberoende av vilket varde i bar. Detta

beror pa att uttrycket

1 = 0

inte ar ett jamforelseuttryck utan en tilldelning. (Problemet ar att det ar belt legalt attskriva pa detta satt ocb man far alltsa inte nagon felutskrift.) Variabeln i tilldelas vardet 0 ocb detta varde blir ocksa resultatet av uttrycket. Detta motsvarar vardet "falskt"ocb if-satsens else-del kommer alltid att utforas. Den fdrsta raden i programavsnittetskulle i stallet sett ut pa foljande satt:

if (i == 0)

I konstruktioner som

while ((c = getchar()) == ' ')

anvander man ibland bade tilldelningsoperator ocb likbetsoperator i samma uttryck.Har vill man lasa in ett tecken till variabeln c ocb darefter testa om det inlasta tecknet

var ett blanktecken eller inte. De extra parentesema runt deluttrycket c = getchar0ar viktiga eftersom operatom == bar bdgre prioritet an tilldelningsoperatom. Hadeparentesema utelamnats skulle jamforelsen getchar o == ' ' utforts forst ocb resultatet av denna (talet 0 eller 1) bade tilldelats till c.

Eftersom de vanliga aritmetiska operatorema bar bogre prioritet an jamforelseoperato-rema kommer uttryck som

a + b > c - d

att ge det resultat man forvantar sig. De beraknas alltsa som om det bade statt

(a + b) > (c - d)

I vanligmatematisk text skriver man oftauttryck som2 <k< 9. Dettakan inte direktoversattas till C. Motsvarande uttryck i C

2 < k < 9 /* FEL! */

64 © Studentlitteratur

Page 69: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

4.4 Logiska operatorer

tolkas namligen som

(2 < k) < 9

Antag t.ex. att k bar vardet 10. Man tycker da att uttiycket 2 < k < 9 borde bli falsktmen det blir det inte. Det blir sant. Det forsta deluttrycket 2 < k ar sant och resultatetav det blir alltsa lika med 1. Detta innebar att hela uttiycket far vardet av i < 9 vilketju ar sant. Ett korrekt satt att skriva detta uttryck hade i stallet varit att anvanda denlogiska OCH-operatom (se vidare nasta avsnitt) och skriva

2 < k && k < 9

4.4 Logiska operatorer

Det films tre logiska operatorer i C: &&, | | och !. Dessa utfor de logiska operationemaOCH, ELLER respektive ICKE. Operatorema && och | | bar bada tva operander ochoperatom ! en operand. Operandema skall vara av nagon skalar typ. Oftast anvandstypen int. Nagra exempel ar:

aktiv && varm

!aktiv

n == 100 && klar

! (n > 7)

x>0 11 !(i==2&&

aktiv I I klar

i < 0 && j == 4

temp <100 II !varm

! (i == 0 && varm)

j < 4)

Resultatet av ett logiskt uttryck bar typen int och vardet 0 eller 1 vilket representerarde logiska vardenafalskt respektive sant. Detta visas i faktarutan.

Logiska operatorer

a b a && b a 1 1 b !a !b

inte 0 inte 0 1 1 0 0

inte 0 0 0 1 0 1

0 inte 0 0 1 1 0

0 0 0 0 1 1

Lagg marke till att alia operandvarden som inte ar lika med 0 tolkas som det logiskavardet sant. Detta innebar att det inte alltid galler att

i == !!i /* Galler inte alltid */

Cm i t.ex. bar vardet 2 blir det vanstra ledet lika med 2 och det hogra lika med 1.

Operatorema && och i | bar bada lagre prioritet an de aritmetiska operatorema ochjamfbrelseoperatorema. Det innebar att uttryck som

a+b>c*d&&e==f

) Studentlitteratur 65

Page 70: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

4. Uttryck och operatorer

tolkas pa det naturliga sMet

( (a + b) > (c * d) ) && (e == f)

&&-operatom bar hogre prioritet an i | -operatom. Uttrycket

i M j && k

beraknas alltsa som

ill (j && k)

Operatom ! bar bog prioritet, bogre an operatorema && ocb i |. Uttrycket

! aktiv && klar

betyder alltsa

( ! aktiv) && klar

Operatorema && ocb i | bar en egenskap som ar vardefull i vissa sammanbang: Denvanstra operanden raknas alltid ut forst ocb bogeroperanden beraknas sedan bara omdet ar nodvandigt for att avgora vardet av bela uttrycket. Om det ar &&-operatom somanvands ocb den vanstra operanden far vardet 0 (d.v.s. motsvarar vardetfalskt) sa vetman att resultatet av &&-operatom alltid blir 0 (falskt) oberoende av bogeroperandensvarde. Darfor beraknas aldrig den bogra operanden. Pa motsvarande satt galler for | | -operatom att om den vanstra operanden blir skild fran noil (d.v.s. sant) sa blir alltidresultatet av | | -operatom 1 (sant). Inte beller i detta fall beraknas bogeroperanden.Har visas en konstmktion dar man bar nytta av detta. Antag att vi bar deklarationema

char rad[51];

int i, c;

For att lasa in en rad fran tangentbordet till textstrangen rad kan vi gora foljande

/* las rad med max 50 tecken */

1 = 0;

while (i < 50 && (c = getchar()) != '\n') {

rad[i++] = c;

}

rad[1] = '\0';

Inlasningen pagar tills man antingen last 50 tecken eller tills ett radslutstecken patraf-fas. Ett nolltecken \o placeras i rad efler det sist inlasta tecknet. Om villkoret

i < 50

ar falskt, d.v.s. 50 tecken bar lasts, kommer aldrig den andra operanden

(c = getchar0) != '\n'

att utfbras. Detta innebar att inlasningen stannar efter exakt 50 tecken ocb nasta teckensom star i tur att lasas ar det 51 :a tecknet.

66 © Studentlitteratur

Page 71: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

4.5 Villkorsoperatom

4.5 Villkorsoperatorn

Antag att vi har variablema x, y och z. Satsema

if (X > y) {

z = X;

}

else {

z = y;

}

beraknarmaximumav x och y och placerardet i z. Ett mer kompaktsatt att gora dettaar att anvanda den s.k. villkorsoperatorn och bilda ett villkorsuttryck. Man skriverda

z= (x>y) ?x : y;

Villkorsoperatorn ar den enda operator som har tre operander. Ett villkorsuttryck harden allmanna formen

uttryck_l ? uttryck_2 : uttryck_3

Berakningen gar till sa att uttryck_l beraknas fbrst. Om dettauttryck far ett varde sominte ar noil (sant) sa beraknas darefter uttryck_2 och resultatetav hela villkorsuttrycketblir lika med vardet av uttryck_2. Om a andra sidan uttryck_l far vardet 0 (falskt)beraknasi stallet uttryck_3 och resultatetav hela villkorsuttrycket blir lika med vardetav uttryck_3. Endast ett av uttryck_2 och uttryck_3kommer alltsa att beraknas.

Den forsta operanden till villkorsoperatom maste ha en skalar typ. Den andra ochtredje operanden kan vara av aritmetisk typ. De far ocksavarapekare,posterelleruni-oner (se kap 8) och i sa fall kravs att de har sammatyp. Om den andra och den tredjeoperanden ar av aritmetisk typ sker "de vanliga aritmetiska typomvandlingama" sa attdessa operander far samma aritmetiska typ. Om vi t.ex. har villkorsuttrycket

(i < 0) ? n : a

dar variabeln n har typen int och a typen double, sa far resultatet av hela uttryckettypQn double aven om i ar mindre an 0.

Som exempel pa anvandningav villkorsuttryckvisar vi ett programavsnitt som skriverut en textstrang s. Textstrangen avslutas med ett nolltecken. Utskriften skall varasadan att alia eventuella stora bokstaver i intervallet A till Z oversatts till motsvarande

sma bokstaver. Alia ovriga tecken skall skrivas ut oforandrade.

/* oversattning fran stora till sma bokstaver */

1 = 0;

while ( (c = s [i + + ] ) != ' \0' ) {

putchar ( (c>='A' && c< = 'Z') ? c + 'a' - 'A' : c);

}

© Studentlitteratur 67

Page 72: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

4. Uttryck och operatorer

I exemplen ovan har vi satt parenteser runt den forsta operanden i villkorsuttrycken.Detta ar egentligen inte nodvandigt men kan vara en god regel for att det skall bli tyd-ligare. Eftersom ett villkorsuttryck behandlas somovriga uttryck i C, kan det ingasomdeluttryck i andra uttryck, t.ex. i ett annat villkorsuttryck. Berakningsriktningen forsadana sammansatta villkorsuttryck ar "fran hoger till vanster", vilket innebar attuttrycket

i ? j : k ? 1 : m

tolkas som

(k m)

Som exempel andrar vi i utskriftsexemplet ovan sa att alia stora bokstaver oversatts tillsma bokstaver, alia sma bokstaver skrivs ut oforandradeoch alia ovriga tecken ersattsmed ett blanktecken.

i = 0;

while ((c = s[i++]) != '\0') {

putchar ( ( c>='A' && c<='Z') ? c

(c>='a' && c<='z') ? c

}

'a'

' ' )

- ' K'

4.6 Bit-operatorer

IC finns en uppsattning operatorer som betraktar sina operander som bitmonster,d.v.s.grupper av nollor och ettor. Dessa operatorer framgar av faktarutan.

Bit-operatorer

negation, bit for bit« vansterskift

» hogerskifl& OCH, bit for bit- exklusiv ELLER, bit for bit

1 ELLER, bit for bit

Gemensamt for dessa operatorer ar att deras operander skall vara av heltalstyp och att"de vanliga aritmetiska typomvandlingama" utfors pa operandema. Alia operatoremautom ~ har tva operander.

Operatom ~ ("tilde") har en operand. Resultatet av en operation ar ett varde dar aliaettor i operanden ersatts med nollor och vice versa. Antag t.ex. att ci och c2 har typenchar och ar atta bitar langa.

68 ) Studentlitteratur

Page 73: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

cl

c2

5;

~cl;

/* cl har bitmonstret 00000101

/* c2 far bitmonstret 11111010

4.6 Bit'Operatorer

* /

Operatom &utfor, bit for bit, en OCH-operation. Detta innebar att cm bada operan-dema har en etta i en viss bitposition sa kommer ocksa resultatet att ha en etta i dennaposition. Annars kommer resultatet att innehMla en nolla i motsvarande position.

Pa samma satt utfor operatom | en ELLER-operation. Om nagon av operandema haren etta i en viss position sa far resultatet en etta i denna position, annars en nolla.

Operatom utfor en s.k. exklusiv ELLER-operation. Om exakt en av operandema(inte bada) innehaller en etta i en viss position blir resultatet en etta i denna position.Annars blir det en nolla.

I faktamtan visas hur vardet av en viss bit beraknas med de olika operatorema.

Berakning av bit nr 1

ai bi (a & b)i (a 1 b)i (a - b)i ~bi

1 1 1 1 0 0 0

1 0 0 1 1 0 1

0 1 0 1 1 1 0

0 0 0 0 0 1 1

Lat OSS studera nagra exempel.

cl=5; /*clhar bitmonstret 00000101 */

c2 = 6; /* c2 har bitmonstret 00000110 */

c3 = cl & c2; /* c3 far bitmonstret 00000100 */

c4 = cl I c2; /* c4 far bitmonstret 00000111 */

c5 = cl ^ c2; /* c5 far bitmonstret 00000011 */

Noteraskillnadenmellande/ogz.s'^aoperatorema&&, i | och ! ochbit-operatorema &, ioch De logiska operatorema ger som vi tidigare sett alltid antingen vardet 0 eller 1som resultat. Med samma varden som ovan pa ci och c2 far vi t.ex.

c6 = cl && c2;

c7 = cl II c2; /'

c 8 = ! c 1; / ^

c9=~cl;

c6 far bitmonstret 00000001 */

c7 far bitmonstret 00000001 */

c8 far bitmonstret 00000000 */

c9 far bitmonstret 11111010 */

Operatorema « och » anvands for att skifta (forflytta) de enskilda bitama at vansterrespektive hoger. Den forsta operanden innehaller det bitmonster som skall skiftas ochden hogra operanden anger hur manga steg som skall skiftas. Vi visar nagra exempel:

) Studentlitteratur 69

Page 74: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

4. Uttryck och operatorer

cl = 5; /* cl har bitmonstret 00000101 V

c2 = cl << 3; /* c2 far bitmonstret 00101000 V

\—1o

II

00o

<< 6; /* c3 far bitmonstret 01000000 V

c3 = cl >> 2; /* c3 far bitmonstret 00000001 */

Som synes skiftas nollor in fran hoger nar operatom « anvands. Ett vansterskift ettsteg motsvarar alltsa en multiplikation med 2. For operatom » galler att nollor skiftasin fran vanster om den forsta operanden har en typ som ar unsigned. Om daremot denforsta operandens typ har tecken ar det implementeringsberoende om nollor skiftas ineller om teckenbiten (biten langst till vanster) kopieras. (Om nollor skiftas in kallas detlogiskt skift och om teckenbiten kopieras kallas det aritmetiskt skift.) Antag t.ex. attvariablema i och j har typen int och att en int har 16 bitar

i = -3; /* i har bitmonstret 1111111111111101 */

j = i >> 1; /* j far bitmonstret 7111111111111110 */

Den andra operanden far inte vara negativ och inte heller storre an eller lika med denforsta operandens langd. Vad som i sa fall hander ar odefmierat.

&-operatom anvands ofta for att maska bort vissa bitar av ett objekt som man inte arintresserad av. Satsen

i = j & Oxff;

ger t.ex. variabeln i ett bitmonster dar alia bitar har vardet noil utom de 8 bitama

langst till hoger. Bitama langst till hoger kommer att fa samma varden som motsva-rande bitar i variabeln j. Skulle man i stallet vilja maska bort de 8 bitama till hoger kanman skriva pa fdljande satt. Da behover man inte veta hur manga bitar variablema ioch j bestar av.

1 = j & ~ Oxff;

Pa motsvarande satt kan i-operatom anvandas for att "sla pa" vissa bitar. Foljande satsslar t.ex. pa den sjatte biten fran hoger i variabeln k.

k = k I 0x20;

Detta skulle ocksa kunna skrivas:

k = k I (1 << 5);

Vi skall studera ett par exempel dar bit-operatorema anvands. I det forsta exempletskall vi visa hur man kan plocka fram en viss bit i ett teckenfalt f. Antag att vi hardeklarationema

char f [ 50], c;

unsigned char mask;

int k, b;

70 © Studentlitteratur

Page 75: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

4.7 Tilldelningsoperatorer

(Man kan tanka sig att vi anvant teckenfaltet f for att lagra ett stort antal logiskavar-den som vardera kan representeras med en bit.) Vi antar att varje tecken i f bestar av 8bitar och vi numrerar bitama inom vaije tecken fran vanster till hoger. Foljande pro-gramavsnitt placerar bit nummer k i teckenfaltet f i variabeln b:

c = f[k >> 3]; /*" valj ut ratt tecken */

mask = 128 >> (k & 7); /* markera ratt bit */

b= (c&mask) !=0;

Denforsta satsen placerar i c det tecken i f sominnehaller densoktabiten. Hogerskif-tet k » 3 motsvarar en division med 8.1 den andra satsen placeras en etta i ratt position i variabeln mask. De tre hogra bitama i k innehaller positionsnumret. I den sistasatsen valjs den aktuella biten i c ut och jamfors med 0.

I nastaexempel skallvi visa hur man omvandlar en textstrang med siffertecken till ettheltal. Antag att variabeln a ar ett teckenfalt som endast innehaller siffror (d.v.s.ASCII-koden for siffror) i intervallet '0' till '7'. Textstrangen kan tolkas som ett oktalttal. Foljande programavsnitt omvandlar foljden av siffertecken till ett heltaloch placerar det i variabeln tai som antas ha typen unsigned long int.

/* omvandla oktala siffror till heltal */

1 = 0;

tal = 0;

while ((c = a[i++]) != '\0') {

c = c & 7 ;

tal = tal << 3 I c;

}

De enskildatecknen i a plockas fram, ett och ett, och placeras i teckenvariabeln c. Forvarje tecken plockas de tre hogra bitama ut. Dessa innehaller heltalsvardet som motsvarar det aktuella siffertecknet. (For tecknet '5' innehaller t.ex. de tre hogra bitamavardet 5.) Dareftervansterskiftas de tidigarebitama i tal tre steg (multiplikation med8) och de tre hogra bitama i c placeras langst till hoger i tal.

4.7 TilldelningsoperatorerI faktamtan visas tilldelningsoperatorerna.

Tilldelningsoperatorer

/ =

>>= & =

Nar operatom = anvands kallas det enkel tilldelning och nar nagon av de ovriga tilldel-ningsoperatoremaanvandskallas det sammansatt tilldelning. Vi har redan sett atskilli-

© Studentlitteratur 71

Page 76: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

4. Uttryck och operatorer

ga exempel pa enkel tilldelning. Vid en sadan omvandlas den hogra operandens vardetill den vanstra operandens typ. Darefter tilldelas den hogra operandens varde till denvanstra operanden. Eftersom den vanstra operanden skall tilldelas ett varde maste denpa nagot satt referera till ett andringsbart objekt. Det vanligaste ar att den vanstra operanden ar naninet pa en variabelmen den kan ocksa referera till objekt pa andra satt. Vibar t.ex. sett att den kan ange ett enskilt elementi ett textfalt. Nagra exempel ar:

i=0 x=y*z rad[i] = 'A'

I en enkel tilldelning far bada operandema ha aritmetisk t)^. Dessutom far de varaposter, unioner eller pekare och i sa fall maste bada operandema ha samma typ. (Ettimdantag ar om bada operandemaar pekare.Da far den ena pekarenvara en pekare tilltypen void och den andra en pekare till nagon annan typ.) I aldre C-versioner tillatsibland inte att operandema ar poster eller unioner.

Till skillnad ffan de fiesta andra sprak betraktas i C en tilldelning som ett uttryck. Entilldelning bar alltsaett varde. Dettabetyderatt mankan lataen tilldelning inga somettdeluttryck i ett storre uttryck. Vi bar t.ex. sett konstruktioner som

(c = getcharO) != '\n'

Vardet av en tilldelning ar det varde som tilldelas den vanstraoperandenoch dess typblir lika med den vanstra operandens typ.

Ett annat satt att utnyttja att en tilldelning betraktas som ett uttryck ar att gora multiplatilldelningar, som t.ex.

i=j =k=l=m=0;

Har far alia variablema vardet noil. Om de olika operander som ingar i en multipel tilldelning bar olika typ, far man emellertid vara lite forsiktig. Tilldelningsoperatoremahar namligenberakningsriktningen "ffan hoger till vanster"vilketbetyderatt uttrycket

a = b = c = d

tolkas som

a = (b = (c = d) )

Om t.ex. a och d har samma aritmetiska typ men b eller c har en lagre aritmetisk typ, sakan a, pa gmnd av de typomvandlingar som skett, fa ett annat varde an om man skrivit

a = d

De sammansatta tilldelningsoperatorema ar mycket praktiska att anvanda i vissa sam-manhang. Ofta vill man gora operationer som "oka i med k" eller "subtrahera x ifrany". I stallet for att da behova skriva

i = i + k;

y = y - x;

72 © Studentlitteratur

Page 77: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

4.8 sizeof-operatorn

kan man anvanda sammansatt tilldelning.

1 += k;

y -= x;

Allmant galler att en sammansatt tilldelning

opl op= op2

ar ekvivalent med uttrycket

opl = opl op op2

dar op kan varanagon av */% + -«»& ^ I. Den endaskillnaden ar att denvanstra operanden opl bara beraknas en gang. (Detta spelar ingenroll cm opl ar enenkel variabel men ar vasentligt cm berakningen av opl ger nagon sidoeffekt, somt.ex. f [i++].)

I en sammansatt tilldelning maste bMa operandema vara av aritmetisk typ som over-ensstammer med de typer som ar tillatna for motsvarande enkla operator.For operato-rema += och -= galler dessutom att den vanstra operanden far varaen pekare. I sa fallskall den hdgra operanden vara av heltalstyp. (Se kapitel 7.)

4.8 sizeof-operatornEn lite speciell operator ar operatom sizeof. Denanvands for att fa redapa storlekenpa ett dataobjekt eller en typ. Operatom finns i tva former:

sizeof uttryck

sizeof itypnamn)

Resultatet av operatom ar operandens langd, uttryckt i antal bytes. Detta betyder attuttrycken

sizeof(unsigned char)

sizeof(signed char)

sizeof c

alia bar vardet 1. (Vi antar att variabeln c bar typen char.) Operanden kan vara ettuttryck, som t.ex.

sizeof(i + j)

(Harbebovs parenteser eftersom sizeof-operatom barbogprioritet.) Resultatet blirdalangden av den typ som operanden skulle fa om den beraknades. Om t.ex. i bar typenshort int ocb j typenint skulleresultatetbli langden av typen int. Sjalvaoperandenberaknas inte. Kompilatom kan ju redan vid kompileringstillfallet avgora vilken typoperanden skulle fa.

Man kan utnyttja sizeof-operatom pa fait. Om rad ar ett teckenfaltger t.ex. uttrycket

© Studentlitteratur 73

Page 78: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

4. Uttryck och operatorer

sizeof rad

antalet bytes, d.v.s. antalet tecken, i rad.

Om man bar fait dar de enskilda elementen inte ar av typen char kan man beraknaantaletelement i ett fait pa foljande satt (antagatt faltets namnar f):

sizeof f / sizeof f[0]

Man dividerar belt enkelt faltets totala storlek med storleken pa ett element (det for-sta).

Envanlig anvandning av sizeof-operatomar i samband medallokering avdynamisktminnesutrymme. Man anvander da ofta standardfunktionen malloc.

malloc(100 * sizeof (int))

(Detta beskrivs vidare i kap 7 ocb 8.)

Operanden till sizeof-operatom far inte ba eller ange en funktionstyp eller typenvoid. Operanden far inte beller vara ett bitfalt.

Vilken typresultatet av ett sizeof-uttryckbarar beroende av den aktuella implemen-teringen. Det ar inte sjalvklart att typen ar int. Om man skall berakna storleken avstora fait kanske inte typenint rackertill. Filenstddef.h innebMler en definition av ettmakro size_t somanger typenpa resultatet av ett sizeof-uttryck.

#include <stddef.h>

size_t storlek;

storlek = sizeof f;

4.9 OperatorprioriteterHurett uttryck skalltolkas, ellergmpperas, berori forstabandpa de ingaende operato-remas prioriteter. Om en operand star mellan tva operatorer med olika prioriteter rak-nas den till den operatorsom bar bogstprioritet. Vi bar t.ex. sett att i ett uttryck som

i + j * k

raknas j till operatom *. Detta innebar att uttrycket gmpperas som om man bade skri-vit det pa foljande satt:

i + (j * k)

I C finns en prioritetsordning definierad for alia operatorer som kan forekomma i ettuttryck. I appendix B ges en sammanstallning av samtligaoperatorer ocb deras prioriteter. Om en operand star mellan tva operatorer med samma prioritet avgor de aktuellaoperatoremas berdkningsriktning om operanden skall raknas till den vanstra eller

74 © Studentlitteratur

Page 79: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

4.10 Ovningsuppgifter

hogra operatom. Berakningsriktningen kan vara antingen"fran vanster till hoger" eller"fran hoger till vanster". Om berakningsriktningen ar "fran vanster till hoger" raknasoperanden till den vanstra operatom, annars till den hogra. Alia operatorer som harsammaprioritethar ocksasammaberakningsriktning, sa det kan inte bli nagra oklarhe-ter. Uttrycket

i - j + k

gmpperas t.ex. som

(i - j) + k

eftersom operatorema - och + har samma prioritet och berakningsriktningen ar "franvanster till hoger".

Nar det galler de unara operatorema (de som bara har en operand) behover man intetala om nagon berakningsriktning, eftersom deras prioriteter belt bestammer gmppe-ringen. Uttrycket

-a [i]

skall t.ex. tolkas som

- (a[i] )

eftersomalia postfixoperatorer (operatorersom star efter sin operand)har hogre prioritet an prefixoperatorer (operatorer som star fore sin operand).

Om man alltsa bortser fran unara operatorer galler att de enda operatorer som harberakningsriktningen "fran hoger till vanster" ar tilldelningsoperatorema och villkors-operatom.

Som diskuterats i avsnitt 4.1 behover den faktiska berakningsordningen inte vara striktfran vanster till hoger eller fran hoger till vanster. Kompilatom har ffihet att beraknaoperandema till en operator i vilken ordning som heist. (Ett undantag ar som vi settoperatorema | | och && som alltid beraknas fran vanster till hoger.) Dessutom kan ettuttryck som vi sett gmpperas om ifall det innehaller flera forekomster av operatom *och +. Detta galler aven for operatorema & ^ och |.

4.10 Ovningsuppgifter1. Vilka varden har foljande uttryck? Antag att variablema i och j har vardena 3 res-

pektive 7 fore varje uttryck.

a. i<=j e.

b. i=j f. (++i<3)?!j:j

c. 2+j%i g. i&j

d. j/i*2 h. j <<= i

) Studentlitteratur 75

Page 80: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

4. Uttryck och operatorer

2. Skriv ett program som laser in ett artal och imdersoker om aret ar ett skottar. Ett arar ett skottar om det ar delbart med4 men inte med 100. Undantag ar ar som ardelbara med 400. De ar skottar.

3. Skriv ett program som beraknar hur mycket vaxel man skall fa tillbaka nar manbarhandlat samti vilkasedlar ochmyntvaxeln skall utbetalas. Indata tillprogram-met skallvara detpris man skallbetalaoch det beloppmanbetalarmed.For enkel-hets skull antas att alia belopp ar ett belt antal kronor. Det antas ocksa att vaxelaldrig betalas tillbaka med sedlar av hogre valor an 100 kr.

Om man t.ex. handlat for 711 kr och betalarmed en tusenkronorssedel skallpro-grammet skriva ut att man skall ha tva hundrakronorssedlar, en femtiokronorsse-del, tre tiokronorssedlar, ett femkronorsmynt och fyra enkronor tillbaka.

4. Vid radioaktivt sonderfall kan man berakna mangden kvarvarande radioaktivtmaterial n efter en viss tid t med formeln:

-ktn = n^e

dar riQ ar mangden radioaktivt material vid tiden t = 0, Konstanten k ar en materi-alkonstant. Man brukar for det mesta ange halveringstiden (den tid det tar innanhalften av det radioaktiva materialet sonderfallit). Om halveringstiden betecknasmed T raknar man latt ut att:

k =T

Halveringstiden for isotopen 14C ar 5730 ar. Skrivett program som skriver ut hurmanga procent av denna isotop som aterstar efter S ar. S ges som indata.

5. Antag att variablema ci och c2 innehaller teckenkodema for tva svenska sma bok-staver. Skriv ett uttryck som ger vardet 1 om ci innehaller en bokstav som kom-mer fore bokstaven i c2 i alfabetet och vardet 0 annars. Tank pa att kodema forbokstavemaa, a och 6 inte ligger i bokstavsordning.

6. Skriv ett program som laser in ett personnummer och somavgorom den aktuellapersonen ar en kvinna eller en man. (Den nast sista siffran i ett personnummer arjamn for kvinnor och udda for man.)

7. Antag att en variabel c av typen char innehaller tva sma heltal i intervallet 0 till15.Den ena heltalet ligger lagrat i de fyra vanstrabitama i c och det andra i de fyrahogra. Konstruera ett programavsnitt som skriver ut summan av de tva sma talen.

8. Antag att variabeln mbar typen unsigned int och lagras med 16 bitar. Skriv ettprogramavsnitt som tolkar innehMlet i msom ett teckenldst tal och omvandlar dettill en textstrang med fyra hexadecimala siffror. Textstrangen skall placeras i ettteckenfalt s.

76 © Studentlitteratur

Page 81: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Satser 5

I C anvander man, liksom i de fiesta vanliga programmeringssprak, s.k. satser for attuttrycka vad ett program skall gora. Huvudregeln ar att satsema utfors i sekvens franborjan till slut, en sats i taget. Det firms emellertid ocksa satser som kan anvandas foratt beskriva altemativa vagar samt repetition. I detta kapitel skall vi studera de olikaslagen av satser. Manga av dem bar vi redan sett exempelpa i tidigare kapitel men barfoljer en mer fullstandig beskrivning.

5.1 UttryckssatserEn uttryckssats bar den allmanna formen

uttryck;

Genom att placera ett semikolonefter ett uttryck far man alltsa en sats. Vad som bander nar en uttryckssats exekveras ar att uttryckets varde beraknas. Darefter "slangs"det beraknade vardet. Det ar naturligtvis darfor bara meningsfullt att utfora satser daruttrycket bar en sidoeffekt. Man kan t.ex. anvanda en tilldelningsoperator,okningsope-ratom eller minskningsoperatom eller gora ett funktionsanrop. Nagra exempel pauttryckssatser ar:

antal = 0;

kostnad == antal * pris;

i <<= k;

a n t a 1 + + ;

printf("Totalkostnad: %3.2 f", kostnad);

Man kan konstruera en s.k. torn sats genom att utelamna uttrycket ocb bara skriva ettsemikolon. Man bar ibland nytta av tomma satser i sadana situationer dar sprakregler-na kraver att det skall sta en sats men ingenting skall utforas. Man kan t.ex. boppa overett antal blanka tecken vid en inlasning genom att skriva

while ((c = getchar()) == ' ')

; /* torn sats */

© Studentlitteratur

Page 82: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

5. Satser

5.2 Sammansatta satser

I flera sprakkonstruktioner, t.ex. i en if-sats, kravsatt det pa ett visst stalle skall sta enendasats. Ommanvid ett sManttillfalle behdver ha flera satser kan mananvanda sigav en sammansatt sats. En sadan kallas ocksa for ett block. Man far en sammansatt sats

genom att omge ett antal andra satser med klamrar, som t.ex.

{

printf("Antal? ");

scanf("%d", &n);

}

En sadan sats raknas som en sats.

En sammansatt sats kan faktiskt ocksa innehalla deklarationer, som t.ex.

{

char namn[20], c;

int i;

printfC'Vad heter du?\n");

1 = 0;

while ((c = getchar()) != '\n') {

namn[i] = c;

i + + ;

}

namn[i] = '\0';

printfC'Hej %s\n", namn);

}

Detta bar man framst anvandning av vid konstruktion av funktioner. Den allmanna for-men pa en sammansatt sats ar

{

foljd av deklarationer och satser

}

11 aldre C-versioner kravdes att alia deklarationer skulle ligga fbrst men iC99 fSr manblanda deklarationer och satser. Observera sarskilt att en sammansatt sats inte avslutas

med semikolon.

5.3 i£-satsen

Det vanligaste sattet att astadkomma altemativavagar i ett programar att anvandaif-satsen. Denna sats finns i tva grundversioner, utan eller med else-del. Dessutom kanvaije del besta av en eller flera satser. Se faktarutan.

Lagg marke till att uttrycket som stkr efter if skall skrivas inom parentes och att detinte finns nagot nyckelord then som i manga andra sprak. Uttrycket inom parentes

78 © Studentlitteratur

Page 83: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

5.3 if-satsen

if-sats, olika former

if {uttryck)

sats;

if {uttryck) {

en eller flera satser

}if {uttryck)

sats;

else

sats;

if {uttryck)

sats;

else {

en eller flera satser

}if {uttryck) {

en eller flera satser

}

else {

en eller flera satser

}

if {uttryck) {

en eller flera satser

}

else

sats;

skallha skalartyp. Om det bar ett varde som inte ar lika med noil (d.v.s. ar sant), utforsden angivna satsen, annars gors inget. Har kommer ett exempel:

if (n > 0)

medel_v = sum / n;

Om man vill utfora mer an en enda sats far man anvanda sig av en sammansatt sats,

d.v.s. omsluta satsema med klamrar, som t.ex. i foljande sats

if (n > 1) {

medel_v = sum / n;

stand av= (kvad sum - sum * sum / n) / (n 1) ;

}

(Man brukar ofta skriva vansterklammem langst till hoger pa forsta raden for att sparaen rad.)Avert omman bara villutfora en enda sats ar det Idmpligt att omslutaden medklamrar. Det ar da mindre risk att det blir fel om man senare skulle vilja lagga till ytter-ligare nagon sats.

Den sats som skall utfbras far vara en godtycklig sats, t.ex. en annan if-sats. Pa dettasatt kan man fa flera nivaer av if-satser inne i varandra, s.k. nastlade if-satser.

if (kommando == las) {

if ((teck = getchar()) == 'A') {

printf("ALARM!");

Det kan finnas en else-del i en if-sats. Denna del kommer naturligtvis att utforas omvillkoret innanfor parentesema efter if visar sig vara falskt. Aven eise-delen kannaturligtvis vara en sammansatt sats. Vi visar ett exempel.

) Studentlitteratur 79

Page 84: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

5. Satser

if (a > 0) {

p r i n t f ( " %f " , a) ;

}

else {

b = X + y ;

a = b * c ;

}

Observera att en uttryckssats skall avslutas med semikolon men inte en sammansattsats.Dettaar speciellt viktigtatt tankapa framfor else. Foljande if-satser ar felaktiga.

if (i >= j)

k = i /* FEL! Semikolon saknas */

else

k = j;

if (a > 0) {

b = a * X ;

c = b + 2;

}; /* PEL! Skall inte vara semikolon */

else

b = a / X ;

I nastlade if-satser maste manhalla redapa vilken if-sats else hor till. Foljande pro-gramavsnitt skriver ut texten Peiaktigt tecken trots att variabeln c bar vardet' / '.

c = ' / ' ;

j = 0;

if (c == '/')

if (j != 0)

k = i / j ;

else /* OBS! Avsiktligt fel */

printf("Peiaktigt tecken");

Detta beror pa att eise-delen inte hor till den yttre if-satsen utan till den inre. E« else-del rdknas alltid till den ndrmaste if-satsen. Detta exempel illustrerar att det ar lamp-ligt att alltid anvanda klamrar, aven om man bara har en sats. Programavsnittet borskrivas sa bar for att bli korrekt:

if {c == ' /' ) {

if (j != 0)

k = i / j;

}

else {

printf("Peiaktigt tecken");

}

Nar man programmerar forekommer ibland flervalssituationer. Ett satt att losa detta aratt ba flera if-satser efler varandra enligt foljande monster.

80 © Studentlitteratur

Page 85: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

5.4 switch-satsen

if {uttryck_l)

sa ts_l

else if {uttryck_2)

sats_2

else if {uttryck_N)

sats_N

else

sa ts_N+l

Som exempel visas ett programavsnittsom skriver ut innehallet i ett teckenfalt text paett sadant sM att alia stora bokstaver och alia siffror skrivs ut oforandrade, alia sma

bokstaver oversatts till stora och alia ovriga tecken till blanka tecken. Samtidigt raknasantalet forekomster av de olika kategoriema av tecken. (Detta exempel fungerar barafor bokstavema a-z. Bokstavema a, a och 6 oversatts inte.)

i = 0;

while ((c = text[i++]) != '\0') {

if {'A' <= c && c <= 'Z') {

antal_stora++;

}

else if ('a' <= c && c <= ' z') {

antal_sma++;

c -= 'a' - ' A' ;

}

else if ('0' <= c && c <= '9') {

antal_siff++;

}

else {

antal andra++;

putchar(c);

5.4 switch-satsen

I vissa slag av flervalssituationer ar det naturligare att anvanda en switch-sats an enif-sats. Som exempel visar vi ett program som simulerar en mycket enkel kalkylator.Programmet skall lasa in och berakna mycket enkla uttryck med formen

x?y

dar Xoch y ar reella tal och ? nagon av de vanliga aritmetiska operatorema.

) Studentlitteratur 81

Page 86: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

5. Satser

#include <stdio.h>

int mainO /* Enkel kalkylator */

{

float X, y;

char c;

while (scanf ("%f%c%f", &x, &c, &y) == 3) {

switch(c) {

case '+':

printf("%f\n", x + y);

break;

case '-':

printf("%f\n", x - y);

break;

case '*':

printf("%f\n", x * y);

break;

case '/' :

if (y != 0)

printf("%f\n", x / y);

else

printf ( "Division med noll\n");

break;

default:

printf("Felaktig operator\n");

break;

Efter ordet switch skall det inom parentes sta ett s.k. styruttryck som skall vara av hel-talstyp. Varje altemativ inleds med nyckelordet case och fdljs av ett uttryck som skallvara konstant och av heltalstyp. Det far inte finnas tva altemativ som inleds sammavarde. Man kan ocksa ha ett altemativ som inleds med ordet default.

Nar switch-satsen exekveras beraknas forst stymttrycket. Nasta steg ar att uttryckenefter case omvandlas till samma typ som stymttrycket och jamfors med detta. Omstymttrycket har samma varde som nagot av dessa uttryck sa utfors satsema i motsva-rande altemativ. Om stymttrycket inte har samma varde som nagot av uttrycken utforsde satser som star efter default. (Skulledefauit-altemativ saknasutfors ingenting.)

break-satsema som star sist i vaije altemativ ar nodvandiga. Exekvering av en break-sats medfbr ett hopp ut ur switch-satsen. Skulle det inte fmnas nagon break-sats fort-satter exekveringen med nasta sats och man "trillar igenom" in i nasta altemativ.

82 © Studentlitteratur

Page 87: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

5.5 while-satsen

switch-sats

switch (heltalsuttryck) {

case konstant vardel:

satser

break;

case konstant varde2:

satser

break;

default:

satser

}

Uttiycken efter case maste ha olika varden.defauit-altemativ kan utelamnas.

Flera case-uttiyck far finnas framfor ett visst altemativ.

Vid exekvering sker hopp till det altemativ for vilket heltalsuttryckar lika med konstant vdrde.

Om inget altemativ stammer sker hopp till defauit-altemativet.Finns inget sMant sker inget.Saknas break hamnar man i nasta altemativ.

Det gar inte att specificera flera olika varden for ett visst altemativ. Vill man astad-komma nagot liknande far man rakna upp flera altemativ och avsiktligt "glomma" attha med break-satser efler altemativen. Detta visas i foljande exempel:

switch(c) {

case ' '

case '\t'

case '\n'

antal_mellanrum++;

breaks-

default :

ant al_andra4-+ ;

break;

5.5 while-satsen

Ett satt att astadkomma repetition ar att anvanda while-satsen. Vi har redan sett atskil-liga exempel pa denna sats. Efter ordet while skall det sta ett uttryck inom parentes.Om uttrycket ar sant, d.v.s. har ett varde som inte ar lika med noil, sa utfors efterfoljan-de sats en gang. Darefter beraknas uttrycket pa nytt. Om det fortfarande ar sant utfors

) Studentlitteratur 83

Page 88: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

5. Satser

while-sats, oMka former

while {uttryck) while (uttryck) {

sats; en eller flera satser

}

satsen ytterligare en gang o.s.v. Exekveringen av whiie-satsen fortsatter tills uttrycketblir falskt, d.v.s. lika med 0. Om man bar flera satser som skall upprepas maste deomges med klamrar, men det ar lampligt att anvanda klamrar aven om man bara villutfbra en enda sats.

Som fbrsta exempel visas bur man kan berakna n! som definieras av formeln:

n\ = Ix2x3x4x...x«

Vi far satsema:

nfak = i = 1;

while (++i <= n)

nfak *= i;

/* berakning av n! */

I nasta exempel skall vi studera omvandling mellan en textstrang ocb ett beltal. Antagatt teckenfaltet text inneballer en textstrang dar de enskilda tecken ar siffror i interval-let '0' till '9'. Vi skall tolka en sadan textstrang som ett decimalt beltal ocb omvandladen till typen int. Resultatet skall placeras i variabeln tai. De enskilda siffertecknen itextstrangen plockas ut fran vanster till boger ett i taget ocb omvandlas till motsva-rande beltal. Detta fortsatter tills nagot tecken som inte ar en siffra patraffas.

/* omvandling fran textstrang till heltal */

t a 1 = i = 0 ;

while ('0' <= text[i] && text[i] <= '9') {

tal = 10 * tal + text[i] - '0';

i + +;

}

Uttrycket i while-villkoret kan som vi tidigare sett ocksa inneballa tilldelningar. Fol-jande exempel visar bur en textstrang som finns i faltvariabeln texti kan kopieras tillfaltvariabeln text2. Man kan inte kopiera bela textstrangen pa en gang, utan mastekopiera tecken for tecken.

/* kopiering av en textstrang */

1 = 0;

while ((text2[i] = textl[i]) != '\0') {

i + + ;

}

84 ) Studentlitteratur

Page 89: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

5.6 do-satsen

5.6 do-satsen

En annan repetitionssats som vi inte studerat tidigare ar do-satsen. Dess form framgarav faktarutan.

do-sats, olika former

do do {

sats en eller flera satser

while iuttryck); } while iuttryck);

Observera att satsen avslutas med ett semikolon. Denna sats ar narbeslaktad med

whiie-satsen. Den enda skillnaden ar att uttrycket inom parentes beraknas och testasefter varje varv i stallet for fore. Satsen utfors alltsa alltid minst en gang.

Man kan ha anvandning av do-satsenar nar man bar programavsnittmed formen

do {

c = getchar();

gor nagot med c

} while (c != '\n');

Har bar vi anvant klamrar for att fa en sammansatt sats inne i snurran.

I nasta exempel skall vi visa bur man kan beraknasin(x) nar man inte bar tillgang tillnagon matematisk standardfunktion. Vi anvander oss av en s.k. Maclaurin-serie somsager att

3 5 7 9• / \ X ,X X ,x

Antalet termer i summan ar oandligt ocb termemas absolutbelopp blir allt mindre. Viskall bara ta med sadana termer vars absolutbelopp ar storre an ett visst litet tal som vikallar epsiion (Detta tal kan t.ex. vara 10' ^.) Den forsta termen arlika medx. Vi kansedan berakna en viss term genom att multiplicera foregaende term med -x? ocb divi-dera med / x (/ - 1) dar i ar lika med termens nummer (3, 5, 7 osv.). Vi far foljandeprogramavsnitt:

/* berakning av sin(x) */

term = x;

i = 1;

sum = 0;

do {

sum += term;

i += 2;

term = -term *x*x/ (i* (i-1));

} while (term > epsiion I 1 term < -epsiion);

) Studentlitteratur 85

Page 90: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

5. Satser

Pa varje varv laggs den aktuella termen till summan och sedan beraknas nasta term.Om nasta term ar storre an epsiion fortsatter man ytterligareett varv.

do-satsen ar den av repetitionssatsema som man anvander minst. De fiesta problembrukar vara sadana att det ar naturligare att anvanda en whiie-sats eller en for-sats.

5.7 fo3>satsen

Den tredje repetitionssatsenar for-satsen. Denna skiljer sig lite fran for-satser i andravanliga programmeringssprak. Den allmanna formen for en for-sats visas i faktarutan.

for {uttryck_l; uttryck_2} uttryck_3)sats;

Ekvivalent med satsema:

uttryck_l;

while {uttryck_2) {

sats;

uttryck_3;

Delama uttryck_i, uttryck_2 och uttryck_3 kan ev. utelamnas,men semikolon skall vara kvar.

Varje del kan besta av flera uttryck med kommatecken mellan.

Det forsta uttrycket innanfor parentesema, uttryck_l, utfors en enda gang fore forstavarvet. Det andra uttrycket, uttryck_2, utfors en gangforst pa varje varv och ar detuttrycksomstyromrepetitionen skallfortsatta. Det sistauttrycket, uttryck_3, utfors engdngsist pa varje varv. Det ar tillatet att utelamna nagot eller nagra av uttrycken. I enextrem form kan vi alltsa skriva

for (;;)

sa ts

vilket blir en oandlig snurra.

For for-satsen galler, liksom for whiie-satsen, att man maste anvanda klamrar om det

ar flera satser som skall upprepas, men att det ar lampligt att anvanda klamrar aven omman bara bar en enda sats.

Ofta brukar man ha en for-sats nar man har nagon form av raknare som raknas uppeller ner pa varje varv. Da later man uttryck_l vara en initiering av raknaren, uttryck_2en test om raknaren har natt sitt slutvarde och uttryck_3 en upp- eller nedrakning av

86 © Studentlitteratur

Page 91: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

5.7 for-satsen

raknaren. I foljande exempel visar vi denna teknik for att beraknavardet av x upphojttill n, dar x ar ett reellt tal och n ett positivt heltal. Variabeln i ar raknaren som raknasupp fran 1 till n.

/* berakning av x upphojt till n */

upp = 1;

for (i = 1; i <= n; i++) {

upp *= x;

}

Aven cm raknaren i en for-sats ar en vanlig variabel bor man inte andra den inne isnurran. Detta kan leda till fel som ar svara att upptacka. Alia andringar av raknarenskall synas pa forsta raden.

Det ar ofta en smaksak om man skall anvanda en whiie-sats eller en for-sats. Berak-

ningen av n! kunde t.ex. lika val ha utforts med en for-sats.

/* berakning av n! */

n f a k = 1 ;

for (i = 1; i <= n; i++) {

nfak *= i;

}

Som vi bar sett i flera exempel anvands ofta for-satser nar man vill stega sig igenomteckenfalt. Raknaren anvands da som index i faltet. For att berakna langden av en text-strang som ligger lagrad i teckenfaltet text kan man t.ex. ha for-satsen

/* berakning av en textstrangs langd */

for (langd = 0; text[langd] != '\0'; langd++)

En textstrang avslutas med ett nolltecken. I detta exempel ar den sats som upprepas pavarje varv en tom sats. (Manbrukar skrivaen tom sats pa en egenrad for att den skallsynas tydligt.)

Av definitionen av for-satsen ser vi att den bara far innehalla tre uttryck, uttryck_l,uttryck_2 och uttryck_3. Vid vissa tillfallen vill man ha flera raknare som raknas uppeller ner parallellt. (Man skall t.ex. jtofora tva olika teckenstrangar med varandraochbehover ett index for vaije.) Man skulle da i uttryck_l vilja initiera alia raknama och iuttryck_3 rakna upp eller ner alia raknama. Det gar inte att t.ex. skriva

for (i = 0; j = 100; s[i]<t[j]; i++; j++) /* FEL! */

eftersom det skulle bli fler an tre uttryck.

Losning pa detta problem ar att anvanda den s.k. kommaoperatorn. Denna bar tva ope-rander. Ett kommauttryck, bar darfor formen

uttryck_a , uttryck_b

© Studentlitteratur 87

Page 92: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

5. Satser

Berakningen sker strikt fran vanster till hoger. Forstberaknas den vanstra operandenoch darefter "slangs" vardet av denna operand. Sedan beraknas den hogra operanden.Den hogra operandens varde blir det varde som hela kommauttrycket far. Man kansaga att en kommaoperator egentligen inte ar en operator i vanlig mening, eftersomden inte kombinerar sina bada operander till nagot varde. Den anvands enbart for attastadkomma att tva separata uttryck beraknas i sekvens fran vanster till hoger. Mankan naturligtvis kombinera flera kommaoperatorer och pa detta satt fa fler an tvauttryck att beraknas i sekvens.

a = 0,b = x*y, i = l, k = n- l

Det skall papekas att kommaoperatom inte far forvaxlas med de kommatecken somstar mellanparametramai ett funktionsanrop. Dar fungerarkommatecknen endast somavgransare och det ar inte sakertatt parametrama beraknas franvanster till hoger.

Omvi atervander till for-satser kanvi nu lata uttryck_l ochuttryck_3 varakommaut-tryck. Den felaktiga for-satsen ovan skriver vi da istallet som

for (i = 0, j = 100; s[i]<t[j]; i++, j++)

Somexempel skall vi visa ett programavsnitt somundersoker om en textstrang ar etts.k. palindrom eller inte. Ett palindrom ar en text somblir samma antingen man laserden framifran eller bakifran (t.ex. madam, C och nitalarbralatin). Vi antar att text-strangen fmns i positionema 0 till n i teckenfaltet text. I exemplet anvands tva index ioch j som loper fran borjan och framat respektive fran slutet och bakat.

/* undersok om en text ar ett palindrom */for (palindrom =1, i = 0, j = n; i < j ; i++, j--) {

if (text[i] != text[j])

palindrom = 0;

}

Variabeln palindrom kommer att fa vardet 1 eller0 beroende pa om textstrangen varett palindrom eller inte.

11 C99 kan deklarationer forekomma var som heist och inte bara fore forsta satsen i ettblock. Detta kan utnyttjas pa ett mycket bra satt just i for-satser dar raknar-variabelnkan deklareras direkt i for-satsens parentes. Vi kan t.ex. skriva:

for (int i = 0; i < 10; i + + ) {

printf("%d, ", i);

}

Har gallerdessutom en regel som sager att for-satsen bildar ett eget block.Detta inne-bar att variabeln i inte ar synlig efter f or-satsens slut.

) Studentlitteratur

Page 93: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

5.8 Ndstlade repetitionssatser

5.8 NMlade repetitionssatserDet ar mycket vanligt att man bar repetitionssatser inne i varandra. Som forsta exem-pel studerar vi ett programavsnitt som sorterar elementen i textstrangen s i bokstavs-ordning sa att de minsta elementen kommer forst och de storsta sist. Vi skall anvandasorteringsmetoden "sok lagsta och byt". Den gar till pa foljande satt: Forst letar manreda pa det minsta elementet i hela textstrangen. Man later sedan detta elementbytaplats med det elementsom finns i den forstapositionen. Darefterletar man reda pa detnast minsta elementet och later det byta plats med det element som finns i den andrapositionen. I nasta steg soker man det tredje minstaelementeto.s.v. Detta leder till tvanastlade repetitionssatser. I den yttre later man raknaren i lopa ffan 0 till textstrangensslut. Denna raknare anger vilket element vi soker. (i = o betyder det minsta, i = i,betyder det nast minsta etc.) I den inre repetitionssatsen loper vi igenom elementeni +1 och framat for att leta reda pa det minsta elementet bland dem.

/* sortera en textstrang */

for ( i = 0; s[i] != '\0'; i++) {

/* sok index for det minsta bland elementen nr i, i+1, ...*/

imin = i;

for (j = i + 1; s[j] != '\0'; j++) {

if ( s [ j ] < s[imin] )

imin = j;

}

/* lagg det minsta elementet i position nr i */

temp = s[i]; s[i] = s[imin]; s[imin] = temp;

}

Det andraexemplet visaren funktion somundersoker om den textstrang somfinns i siinnehallertextstrangen i s2 som delstrang. Som resultat skall funktionen ge index i sidar delstrangen borjar. Om t.ex. si innehaller Vdgen till C och s2 gen sa skall funktionen som resultat ge vardet 2 (indexeringenborjar pa 0). Om textstrangeni s2 inte finnsi si skall funktionen ge vardet -1 som resultat. Funktionen kan skrivas

/* letar efter delstrang */

int delstrang(char sl[], char s2[])

{

int k, i, j ;

for (k = 0; sl[k] != '\0'; k++) {

for (i=k, j=0; sl[i]==s2[j] && s2[j]!='\0'; 1++^ j++)

if (s2 [ j] == '\0' ) {

return ( k) ;

}

}

return (-1);

)Studentlitteratur 89

Page 94: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

5. Satser

I den yttre for-satsen loper k genom alia mojliga startpositionerfor delstrangen och iden inre for-satsen undersoker man cm den delstrang som boijar i position k i si arlika med textstrangen i s2. Undersokningen sker tecken for tecken tills man antingenpatraffar tva olika tecken eller tills man nar slutetpa s2.1 det senare fallet bar man hit-tat delstrangen och vardet av k kan ges som resultat av funktionen. Hittar man inte densoktadelstrangen pa nagotstalle i si avslutas funktionen medatt -1 ges somresultat.

5.9 HoppsatserI C fmns fyra slag av s.k. hoppsatser. Det ar retum-satsen, break-satsen, continue-satsen och goto-satsen. retum-satsen bar vi redan sett exempel pa. Den kommer attbehandlasytterligare i sambandmed funktioner i kapitel 6.

En break-sats bar formen

break;

och far fdrekomma endast inne i en switch-sats eller inne i en repetitionssats. Nar denexekveras avslutas switch-satsen eller repetitionssatsen. Om man bar flera nastladeswitch-satserellerrepetitionssatser innebar exekvering av enbreak-satsbaraett hopput ur den innersta av de nastlade satsema.

Som vi sag i avsnitt 5.4 var det nodvandigt att anvanda break-satser i switch-satser foratt man inte skulle hamna i eflerfoljande altemativ. Att anvanda break-satser i samband med repetitionssatser ar langt ifran lika nodvandigt. Det rekommenderas att mandar anvanderbreak-satsen med en viss forsiktighet. Ofta kan man skrivaom villkoret ifor- eller whiie-satsen sa att det inte behovs nagon break-sats. Programmet kanmanga gangervinna i tydlighet och effektivitet pa detta. Lat oss som exempel studeraett programavsnitt som laser in n st tecken fran tangentbordet och placerardem i teck-enfaltet text, n ar definierad som ett makro.

#define N 20

Vid inlasningen kan det undantagsvis vara sa att end offile intraffar innan n st teckenbar lasts. I sa fall skall forstas inlasningen avslutas. Vi kan lata en break-sats ta handom detta undantagsfall och far da

for (i = 0; i < N; i++) {

c = getchar () ;

if (c == EOF) {

break;

}

s [ i ] = c ;

Man klarar sig emellertid utmarkt utan break-sats. Vi kan belt enkelt skriva

90 © Studentlitteratur

Page 95: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

5.9 Hoppsatser

for (i = 0; i < N && (c = getcharO) != EOF; i + +) {

s [ i ] = c ;

}

I samband med interaktiv inmatning kan en break-sats i vissa fall vara anvandbar.Antag att vi bar ett program som upprepade ganger skall gora en berakning av nagotslag. Efter varje avslutad berakning skall den som kor programme! ha mojlighet attavgora om ban vill gora ytterligare en berakning eller inte. Som exempel visar vi ettprogram som beraknar bypotenusanslangd i en ratvinklig triangel. Som indata till programme! ges langdema av de bMa ovriga sidoma.

/* berakning av hypotenusa */

#include <stdio.h>

#include <math.h>

int main ()

{

float a, b, c;

for (;;) {

printf("Ange kateternas langderXn");

if (scanf("%f%f", &a, &b) != 2) {

break;

}

c = sqrt(a * a + b * b);

printf ( "Hypotenusan blir %f\n", c);

continue-satsen bar formen

continue;

ocb far bara forekomma inne i en repetitionssats. Nar en continue-sats utfors sker ettbopp till ndsta varv i repetitionssatsen. Repetitionssatsen avslutas alltsa inte som narbreak anvands. Bgentligen ar continue-satsen en ganska onodig sats eftersom mankan uppna samma effekt tydligare ocb lika enkelt med bjalp av en if-sats. Foljande tvakonstruktioner ar t.ex. ekvivalenta.

for

sats_l

if {uttryck) {

continue;

}

sats 2

}

) { for {

sats_l

if {[uttryck) {

sats 2

Det rekommenderas darfor att man inte anvander continue-satsen.

) Studentlitteratur 91

Page 96: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

5. Satser

Den sista av hoppsatsema ar goto-satsen. Med hjalp av denna kan man hoppa till engodtycklig sats i den aktuella funktionen. Den sats man skall hoppa till skall forsesmed ett Idge som i foljande exempel:

goto fel;

fel: printfC'Ett allvarligt fel har uppstatt\n") ;

Anvandningen av goto-satser har tidigare debatterats flitigt inomdatavetenskapen ochnumera rader det en allman enighet cm att man inte bor anvanda sig av denna sats.Anvandning av goto-satser leder namligen ofta till dMigt strukturerade program.Genom att istallet anvanda if-satser och repetitionssatser kan man alltid konstrueraprogram som blir lika effektiva och mycket tydligare.

5.10 Ovningsuppgifter

1. Konstruera ett program som skriver ut en tabell med varden for funktionen:

f{x) = + 2x - 20

a) Lat programmet skriva ut vardet avf(x) for alia heltalsvarden jc i intervallet -10till+10.

b) Lat programmet skriva ut vardet avf(x) for alia jc-varden i intervallet -2 till +2med steget 0.1, d.v.s. for vardena -2.0, -1.9,-1.8 1.9, 2.0.

2. En kommunhar gjort foljande prognos for befolkningsutvecklingen:

— Vid borjan av innevarande ar hade kommunen 26000 invanare.

— Antalet fodda och avlidna under ett ar uppskattas vara 0.7 % respektive 0.6 %av befolkningen vid arets borjan.

— Antalet inflyttade och antalet utflyttade uppskattas till 300 respektive 325varje ar.

Skriv ett program som beraknar kommunens uppskattade invanarantal i borjan avett visst ar. Vilket ar det galler skall lasas som indata till programmet.

3. Om man inte har tillgang till standardfunktionen cos kan foljande Maclaurinserieanvandas. Vardet x ar uttryckt i radianer.

2 4 6

cosfjc) = +2! 4! 6!

Skriv ett program som laser in ett tal x och beraknar och skriver ut cos(x) medhjalp av denna serie. Termer som tillbeloppet ar mindre an 10"^ behover inte tasmed.

92 © Studentlitteratur

Page 97: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

5.10 Ovningsuppgifter

4. Skriv ett program som laser in ett personnummer (10 tecken) och som kontrolleraratt personnumret ar korrekt. Lat programmet kontrollera att samtliga tecken arsiffror. Lat programmet dessutom kontrollera att kontrollsiffran (den sista siffran)ar korrekt. Kontrollsiffran beraknas pa foljande satt:

a) Addera siffroma nr. 2, 4, 6 och 8 i personnumret.b) Multiplicera siffroma nr. 1, 3, 5, 7 och 9 i personnumret med 2 och addera

siffrorna i resultaten.c) Addera summoma fran steg 1 och 2.d) Kontrollsiffran bestams nu av att summan av kontrollsiffran och summan fran

steg 3 skall vara jamnt delbar med 10.

5. Skriv ett programavsnitt som vander pa textstrangen i faltet s sa att den kommerbaklanges. Tips. Anvand ett index som raknas upp och ett annat som raknas ner.

6. En enkel (men inte sarskilt effektiv) sorteringsmetod kallas bubble sort. Den gartill pa foljande satt. Man loper gang pa gang igenom faltet som skall sorteras.Varje gang man hittar tva intilliggande element som ligger i fel ordning later mandessa bada element byta plats. Man haller pa detta satt tills man kan lopa igenomfaltet utan att behova gora ndgot byte. Skriv ett program som laser in en textstrangoch sorterar tecknen i strangen med denna metod.

7. Skriv ett program som laser in en text och beraknar det genomsnittliga antalettecken per rad i texten.

8. Ett primtal ar ett tal som inte ar delbart med nagra andra tal an med talet 1 och medsig sjalv. Skriv ett program som skriver ut alia primtal som ar mindre ^ eller likamed n. Talet n ges som indata till programmet. Det enklaste sattet att undersokaom ett visst tal i ar ett primtal ar att dividera det med alia tal mellan 2 och i-1 ochfor varje tal undersoka om resten blir noil.

9. Vid simhoppstavlingarbedoms varje hopp av 7 domare som ger poang pa en skalafran 0 till 10. Berakning av hoppets poang gdrs pa foljande satt: Man bortser franden hogsta och den lagsta domarpoangen. Darefter beraknas medelvardet av deaterstaende 5 domarpoangen. Hoppets poang far man fram genom att multipliceradet erhallna medelvardet forst med 3 och sedan med ett tal som anger hoppets sva-righetsgrad. Skriv ett program som forst laser in hoppets svarighetsgradoch darefter de 7 domarpoangen. Programmet skall berakna och visa hoppets poang.

10. Modifiera programmet i foregaende uppgift sa att det kan anvandas for att beraknaslutpoang for ett godtyckligt antal hopp. Varje ny berakning inleds med att programmet fragar efter hoppets svarighetsgrad. Anvandaren kan da ange att programmet skall avslutas genom att skriva teckenkombinationen for eof (Ctrl-Deller Ctrl-Z).

) Studentlitteratur 93

Page 98: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Funktioner och

programstruktur

For att kunna losa ett stort kompliceratproblem maste man dela upp problemet i fleradelproblem av mer hanterlig storlek och denna teknik anvander man ocksa nar manprogrammerar. Ett stort program bestar av flera delprogram, eller underprogram, darvarje underprogram utfor en val avgransad deluppgift. I varje underprogram kan mangomma undan detaljer som ar ovasentliga for helheten. Programmet far da en klararestruktur och blir lattare att konstruera och imderhalla.

Underprogram brukar i programmeringssprak kallas procedures funktioner eller meto-der. I C anvands bara begreppetfunktion. Ett C-programbestar av en eller flera kdll-kodsfiler. Varje kallkodsfil kan bl.a. innehalla ett antalfunktionsdefmitioner. De olikakallkodsfilema kan kompileras separat och de kompilerade programdelama kan sedanlankas samman till ett korbartprogram. En av funktionema maste ha namnet main ochbetraktas som C-programmets "huvudprogram". Nar programmet kors kommer exe-kveringen att starta i denna funktion.

Ofta kan de enstaka funktionema goras sa generella att de ar anvandbara i flera olikaprogram.Det kan t.ex. rora sig om funktionerfor in- och utmatning. Detta gor det moj-ligt att byggaupp "bibliotek"med fardiga s.k. standardfunktioner. I standardenspecifi-ceras ett stort antal funktioner som maste fmnas i varje C-system. (Savida det inte rorsig om ett "naket" system dar C-programmen skall exekveras pa en dator som saknaroperativsystem.)

1 detta kapitel skall vi diskutera hur funktioner defmieras, deklareras och anropas.Kapitlet behandlar ocksa identifierares deklarationsomrade och synlighet samt olikas.k. lagringsklasser. I samband med detta diskuteras hur man i C kan konstmera mot-svarigheten till moduler (ellerpaket som det ibland kallas).

6.1 Funktionsdefinitioner

Vi boijar med att studera en enkel funktionsdefinition.

© Studentlitteratur

Page 99: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6. Funktioner och programstruktur

float medelv (float x, float y)

{

return (x + y) / 2;

Detta ar en funktion som beraknar medelvardet av tva flyttal. Exempel pa anrop avfunktionen ar

mv = medelv(a, b);

En funktionsdefinition far bara finnas pa den yttersta nivan i en kallkodsfil. Man faralltsa inte som i vissa andra programmeringssprak ha funktioner som definieras inne iandra funktioner. Det finns bara en funktionsniva i C.

Lite forenklat kan man se en funktion som en "svart lada". Nar man anvander funktio

nen stopparman in vissavarden som kallas funktionens parametrar. Somresultatpro-ducerarfunktionen ett varde som bar beraknats med hjalp av parametrama. Exakt burberakningen gar till bebdver man inte veta som anvandare av funktionen. Det bebdverbara funktionskonstruktdren kanna till. Som specialfall finns funktioner som inte barnagra parametrar ocb funktioner som inte ger nagot resultat. Det finns ocksa funktioner som bar sidoeffekter, d.v.s. gdr nagot annat an att bara producera ett resultat. (Enfunktion kan t.ex. andra vardetpa en variabel som ar deklarerad utanfdr funktionen.)Vissa slag av sidoeffekter ar dnskvarda (man kan t.ex. se lasning ocb skrivning somsidoeffekter) men oftast bdr man strava efter att undvika sidoeffekter, eftersom dettakan leda till svarupptackta fel.

En funktionsdefinition bestar av tva delar: ettfunktionshuvud ocb enfunktionskropp.Funktionskroppen beskriverbur funktionen ser ut inuti. Funktionskroppen skall alltidvara en sammansatt sats, d.v.s. en eller flera deklarationer ocb satser omslutna med

klamrar. Funktionsbuvudetbeskriver bur funktionen kommunicerar med sin omvarld,d.v.s. vad den beter, burdana parametrar den bar ocb vilken typ av resultat den ger.Parametrama kallas ocksa ibland funktionensformella parametrar.

Den allmanna formen for en funktionsdefinition visas i faktamtan. Den fdrsta raden

(funktionsbuvudet) kallas ocksa ibland funktionens prototyp. Innanfdr parentesemafinnsparameterlistan i vilkenparametramas namnocb typeranges. Observera att mani parameterlistan maste ange typen for varje parameter, aven om man skulle ba fleraparametrar av samma typ. Om en funktion inte bar nagra parametrar anger man dettagenom att skriva ordet void innanfor parentesema.

Man kan ange att en funktion skall ba ett variabeltantal parametrar. Man anvandersigda av den s.k. ellips-notationen, dar parameterlistanavslutasmed tre punkter.

float f (int a, float b, ...)

96 © Studentlitteratur

Page 100: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6.1 Funktionsdefinitioner

Funktionsdefinition

resultattyp funktionsnamn {typl paraml, typ2 param2, etc.)

{

lokala deklarationer

och satser

Om fiinktionen saknar parametrar skall funktionshuvudet ha utseendet

resultattyp funktionsnamn (void)

I exemplet anges att fiinktionen f har tva kandaparametrar, a och b, samtett godtyck-ligt antal ytterligare parametrar. Nagon information om dessa parametrars typer ochnamn ges inte. I filen stdarg.h finns ett antal makron som man kan anvanda inne ifunktionskroppen for att komma at de variabla parametrama. Exempel pa funktionermed godtyckligt antal parametrar ar standardfunktionema printf och scant.

Forst i funktionshuvudet skall man ange funktionens resultattyp.Vissa funktioner1am-nar inget resultat. Detta markeras genomatt man anger resultattypen void. Utelamnarman resultattypen antas att denna ar int. Detta ar dock inte tillatet i C99. Dar maste |man alltid anger resultattypen.

For att avsluta en funktion och retumera ett varde anvands retum-satsen. (Se faktaru-tan.) Den forsta formen anvands nar fiinktionen skall lamna ett varde. Om uttrycketefter return har en annan typ an den resultattyp man angivit omvandlas uttrycket tillresultattypen. Det ar inte tillatetatt anvanda dennaform om manhar angivitresultattypen void. Den andra formen anvands nar man inte skall lamna nagot varde.

return-sats

return littryck\

Avslutar fiinktionen och ger vardet uttryck som resultat.Uttryckets typ bor vara lika med funktionens resultattyp.Om fiinktionen har resultattypen void skriver man istallet

return;

Man far ha flera retum-satser i en funktion. En funktion kan ocksa sakna return-sat-

ser. Nar exekveringennar den avslutandehogerklammemi funktionskroppen avslutasfiinktionen. (Det ar ekvivalent med att en return-sats utan uttryck utfors.)

Vi visar nagra exempel. Funktionen bokstav undersoker om ett tecken ar en bokstaveller inte. (Det forutsatts att endast bokstavema a-z anvands.)

) Studentlitteratur 97

Page 101: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6. Funktioner och programstruktur

int bokstav (char c) /* ar c en bokstav ? */

{

return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');

}

Detta ar ett exempel pa en fiinktion som lamnar ett sanningsvarde som resultat. Artecknet enbokstav retumeras vardet i annans retumeras vardet o. Returtypen barangi-vitssom int, men omviprogrammerar i C99 kantypen_Booi anvandas istallet:

_Bool bokstav (char c) /* ar c en bokstav ? */

{

return ('a' <= c && c <= 'z') M ('A' <= c && c <= 'Z');

}

I bada fallenkan fimktionen anropas pa t.ex. foljande satt:

if (bokstav (teck) )

(Del kan namnas att det finns en motsvarande standardfunktion med namnet isalpha.Se avsnitt 10.3.)

En funktion som beraknar det storsta av tre tal kan defmieras som

long int max3 (long int i, long int j, long int k)

{

if (i >= j && i >= k) {

return (i);

}

else if (j >= i && j >= k) {

return j;

}

else {

return k;

}

}

Nasta funktion beraknar ett heltals tecken. Som resultat ger funktionen vardet -1,0eller 1 beroende pa om talet ar negativt, ar lika med noil eller ar storre an noil.

int signum(int n) /* ger n:s tecken */

{

return n < 0 ? -1 : n == 0 ? 0 : 1;

}

Som exempel pa en funktion utan parametrar visar vi en funktion som laser in nastaicke vita tecken (mellanslag, tabulatoreller radslut)Mn tangentbordet.

98 © Studentlitteratur

Page 102: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6.1 Funktionsdefinitioner

int las_teck(void) /* laser forsta icke vita tecken */{

int c;

while ((c = getchar()) == ' ' II c == '\t' || c == '\n')

return c;

Det ar viktigt att man skriver void innanfor parentesema i funktionsdefinitionen. Dethade visserligen varit tillatet att skrivabara tommaparenteser, men det hade betytt attfunktionens parametrar varit okanda. (Se avsnitt 6.4.) Exempel pa anrop ar

teck = las_teck();

Observera att man i anropet inte skall skriva ordet void.

Det ar tillatet att ha parametrar av vilken typ som heist och resultattypen kan ocksavaragodtycklig. De endaundantagen ar fait ochflmktioner. Narmananropar en fiink-tion medett fait elleren flmktion somparameter kommerparametem att omvandlas tillen pekare till faltet eller till funktionen. (Detta beskrivs utforligare i kapitel 7.) Narman deklarerar sadanaparametrarbor man alltsa ange att de ar pekare till fait respek-tive funktioner. Nar det galler teckenfalt innebar detta att man gor som vi tidigaredemonstrerat. Da man deklarerar parametem skriver man en tom hakparentes efternamnet. Detta innebar att parametem blir en pekare till teckenfaltet.

Som exempel visar vi en funktion som beraknar langden av en textstrang. Som parameter far funktionen en pekare till det teckenfalt i vilket textstrangen fmns. Vi antarsom fdmt att en textstrang avslutas med ett nolltecken.

int langd(char s[]) /* ger langden av s */

{

int i ;

for (i = 0; s[i] != ' \0'; i + + )

return i;

}

Vi kan t.ex. anvanda funktionen i foljande programavsnitt dar vi forst laser in en radmed hjalp av standardfunktionen fgets och dareffer skriver ut radens langd.

char rad [200 ] ;

fgets(rad, 200, stdin);

printf("%d\n", langd(rad));

) Studentlitteratur 99

Page 103: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6. Funktionerochprogramstruktur

Har foljer slutligen ett exempel pa en funktion med resultattypen void. Fimktionenskriver ut kapitelnummer samt rubrik till ettkapitel. Som parametrar far den rubrikensom skall skrivas ut och kapitelnumret.

void skriv_rubrik(char r[], unsigned int nr)

{

printf ( "\n%u.\t%s\n", nr, r);

}

Man anropar funktionen genom att t.ex. skriva

skriv_rubrik("Funktioner och programstruktur", 6);

6.2 Funktionsdeklarationer

Endeklaration ar enkonstruktion som specificerar vaden identifierare (ettnamn) i ettprogram star for. I C skiljer man noga pa begreppendefinition och deklaration. Mankan ha definitioner avvariabler eller funktioner. Endefinition ar enkonstruktion som,forutom attspecificera vadenidentifierare starfor, aven gorattminnesutrymme reser-veras for den variabel eller funktion som man deklarerar. Detta innebar att en definition alltid ocksa ar en deklaration men att en deklaration inte alltid behover vara en

definition. (En deklaration av en vanlig variabel inne i en funktion ar t.ex. ocksa endefinition av variabeln.)

Vihar i foregaende avsnitt settatt funktionsdefmitioner badespecificerar vad funktio-nens namn star for och dessutom, genom att ge en beskrivning av hur fiinktionenskropp serut, reserverar minnesutrymme for funktionen. I varje program maste det Annas exakt en definition av varje funktion som anvands.

Ett C-program bestaroftast av flerakallkodsfiler somkompileras var for sig.Foratt enfunktion skall kunna anropas fran andra kallkodsfiler maste den goras kand ocksa idessa. Losningen pa problemet ar att lata funktionen vara extern och anvanda funktionsdeklarationer. I normala fall (om man inte anvander nyckelordet static) blir enfunktion extern nar man definierar den.

I de andra filema lagger man en funktionsdeklaration som talar om vad funktionenheter och vilka egenskaperden har.En deklaration kan antingenliggapa ytterstaniva ifilen eller sa kan den placeras inne i den funktion dar anropet gors.En funktionsdeklaration innebar inte att nagon ny funktion skapas. Deklarationen hanvisar bara till dendefinition som skall finnas nagon annanstans.

Antag t.ex. att vi har tva olika kallkodsfiler f i och f 2 och att det nagonstans i filen f ifinns ett anrop av en funktion q som ar definierad i filen f2.For att det skall fungera paratt satt maste vi lagga in en deklaration av q i filen f i nagonstans fore anropet. (Dettaar viktigt. Kompilatom behover inte ge nagon vaming om man glommer det.)

100 © Studentlitteratur

Page 104: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6.2 Fmktionsdeklarationer

/* filen f1 */

float q(float X, float y); /* deklaration av q */

z = q(a, b); /* anrop av q */

/* filen f2 */

float q(float X, float y) /* definition av q */

Det skall papekas att funktionen q kan anropas franfunktioner somligger efterq i filenf 2 utan att nagon ytterligare deklaration av q behovs.

Det far som sagt bara finnas en enda definition av vaije funktion men det kan finnashurmanga deklarationer somheist. Eftersom deklarationema ochdefinitionen normaltligger i olika filer kan inte kompilatom kontrollera att deklarationema stammer over-ens med definitionen. Det ar programmerarens ansvaratt se till detta.Kopplingen mel-lan en extem fimktion och anropen till den gors av lankningsprogrammet forst sedanalia de olika programdelama har kompilerats var for sig.

En funktionsdeklaration ser likadan ut som funktionshuvudet i en fiinktionsdefinition,

men det ar tillatet att utelamna parametramas namn. Man behover ju egentligen intekanna till parametramas namn utanfor funktionskroppen. (Om parametemamnen arinstmktiva kan det anda vara lampligt att ha dem med i funktionsdeklarationen. De kanda gora programmet lattare att forsta.)

Funktionsdeklaration

Innan en funktion anropas bor den vara deklarerad.Detta kan goras antingen i en fmskiionsdefinition eller en fm^iiomdeklaration.En funktionsdeklaration innehMler bara hmktionshuvudet (kallas prototyp):

resultattyp funktionsnamn (typl paraml, typ2 param2, etc.);

Parametemamnen far utelamnas.

Om funktionen saknar parametrar skriver man:

resultattyp funktionsnamn (void);

Exempel pa flmktionsdeklarationer ar

double sin(double x);

int langd(char s[]);

)Studentlitteratur 101

Page 105: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6. Funktioner och programstruktur

Ofta brukar man skriva ordet extern framfor (aven omdetta inte ar nodvandigt) forattunderstryka att funktionens definition finns i en annan kallkodsfil.

extern double sin(double x);

extern int langd(char s[]);

Nagra ytterligare exempel pa funktionsdeklarationer ar

double pow(double x, double y);

double pow(double, double);

int abs(int j);

int rand(void);

int strcmp(const char *sl, const char *s2);

int strcmp(const char const char *);

(I det sista exemplen betyder char * att parametrama ar pekare till objekt av typenchar. Se kapitel 7.)

Med hjalp av ellips-notation kan man precis som i en funktionsdefinition ange att enfunktion skall ha ett variabelt antal parametrar. Deklarationema av printf och scanthar t.ex. utseendet

int printf(const char *format, ...);

int scant (const char ^format, ...);

Skulle man raka utelamna resultattypen i en funktionsdeklaration antas att resultat-Itypen ar int. Detta ar emellertid inte tillatet i C99.

Nagot som ar riskabelt i C ar att anropa en funktion innan man har deklarerat eller defi-nieratden.Dettaar inte tillateti C99menom kompilatom inte foljerC99 och en okandidentifierareforekommeri ett uttryck och det finns en vansterparentesefter identifiera-ren sa antar kompilatom att det ar fraga om en funktion och gor en implicit deklara-tion. Om vi t.ex. gor anropet

f (x, y)

och identifieraren t ar okand i den aktuella kallkodsfilenantar kompilatom att man hargjort deklarationen

extern int t();

Har anvands egentligen en aldre syntax for funktionsdeklarationer. (Se avsnitt 6.4). Detomma parentesema anger att antalet parametrar och deras typer ar okanda. Man faralltsa inte nodvandigtvis nagon vaming om man anropar en funktion som inte deklare-rats tidigare. Regeln om implicit deklaration kan ocksa fa foljden att om man lagger ettanrop av en funktion t fore deklarationen av t, sa kan man fa en felutskrift i vilken det

sags att t ar dubbeldeklarerad.

Det innebar ocksa att man inte sakert far nagon felutskrift eller vaming om man harglomt att deklarera en funktion. Detta ar farligt. Om man t.ex. har satsen

102 © Studentlitteratur

Page 106: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

63 Funktionsanrop

z = sqrt (x) ,

(dar Xhar typen double) och har glomt att deklarera sqrt antas att sqrt ger ett resultatav typen int. I sjalva verket ger sqrt ett resultat av typen double. Detta innebar att zkommer att fa ett belt felaktigt varde aven om z har typen double.

Ett praktiskt sM att alltid se till att alia standardfunktioner man anvander ar deklare-rade ar att inkludera filema stdio. h, math. h etc. i sin kallkodsfil. Dessa inkluderings-

filer innehMler just deklarationer av de olika standardfunktionema.

#include <math.h> /* har deklareras bl.a. sqrt */

z = sqrt (x) ;

6.3 Funktionsanrop

En funktion exekveras varje gang den anropas. Man anropar enklast en funktiongenom att skriva dess namn fbljt av en lista av argument inom parentes. Parentesemamaste finnas med, aven om funktionen inte har nagra argument. Nagra exempel:

printf("Vardet blev %d\n", k * n);

z = max (X, y - x) ;

c = getchar () ;

z = max(sin(x), 2 * cos(y))

Ett funktionsanrop betraktas som ett uttryck dar man applicerar postfix-operatom (....)pa en operand som betecknar en ftinktion. (Egentligen skall operanden i ett funktionsanrop vara en pekare till en funktion men kompilatom gor automatiskt omvandlingenfran funktionsnamn till pekare.) Ett ftinktionsanrop far darfor forekomma pa alia stal-len i ett program dar det kan sta ett uttryck. Resultatet av ett funktionsanrop blir detreturvarde som funktionen lamnar.

De argument, som ges i ett funktionsanrop kallas ibland ocksa aktuella parametrar tillskillnad fran de parametrar som anges i funktionsdefinitionen. Argumenten skall varauttryck (de behover alltsa inte vara enkla variabler) med kommatecken mellan.

Exekveringen av ett funktionsanrop innebar forstas att parametrama far de vardenargumenten har och att satsema i funktionens kropp utfors. Om vi antar att den funktion som anropasfinns deklarerad eller definierad tidigare i den aktuella kallkodsfilenoch att funktionens deklaration inte anvander ellips-notation sa utfors automatiskt fol-jande steg vid ett funktionsanrop:

1. Argumenten berdknas. I vilken ordning de argumenten beraknas ar odefinierat. Detbehover alltsa inte alls vara sa att den forsta parametem beraknas forst. I de fiestafall spelar detta ingen roll men om argumenten ger sidoeffekter kan det vara

© Studentlitteratur 103

Page 107: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6. Funktioner och programstruktur

vasentligt. Antag t.ex. att vi vill lasa in tva tal och berakna vardet av det forsta taletupphojt till det andra. Man skulle kunna skriva

z = pow(las_tal(), las_tal()); /* FEL */

dar lastai ar en flmktion som laser in ett tal fran tangentbordet och ger det inlas-ta talet som resultat. Detta ar emellertid en felaktig konstruktion, eftersom vi intevet vilket av det bada anropen av las tai som kommer att utforas forst. I vissaimplementeringar kan det bli ratt och i andra fel. For att klara ett fall som dettamaste man anvanda sig av en tilldelning.

X = las_tal();

z = pow(x, las_tal());

2. Argumenten anpassas och overfors till funktionen. Nar argumentens vardenberaknats sker en anpassning av deras typer. Varje argument omvandlas sa att denkan tas emot av den anropade funktionen. Forst och framst omvandlas aliaargument som ar fait eller funktioner till pekare till fait respektive funktioner.Darefter kommer vaije argument att omvandlas till den typ som motsvarandeparameter har. Om en parameter t.ex. har typen float och motsvarande argumenthar typen int sa kommer argumentetatt omvandlastill typen float. Kompilatomkontrollerar att antalet argument overensstammer med antalet parametrar och attargumenten har sadana typer att de onskade omvandlingama ar mojliga.

3. Argumenten tas emot av den anropade funktionen och tilldelas till parametrarna.Nar en funktion anropas reserveras ett tillfalligt minnesutrymme, en s.k.aktiveringspost,for funktionen. I aktiveringspostenfinns bl.a. plats for funktionensparametrar och for de variabler som definieras inne i funktionen.Aktiveringspostenexisterar bara sa lange funktionsanropet pagar.

4. Satsema ifunktionens kropp exekveras. Har sker exekveringen pa normalt vis ochparametrarna anvands som vanliga intema variabler deklarerade i flmktionen.

5. Retur sker Nar en return-sats utfors i funktionskroppen eller da exekveringen narsista hogerklammem i funktionskroppen sker retur till det stalle dar funktionenanropades och exekveringen fortsatter dar. Funktionens aktiveringspost forsvinner.

For att studera effektema av ett anrop konstruerar vi ett program som beraknar denstorsta gemensamma divisom till tva heltal m och n. Berakningen kan goras medEuclides algoritm som lyder:

1. Dividera m med n och beteckna resten vid divisionen med r.

2. Om r = 0 sa ar berakningen klar och resultatet finns i n.

3. Satt annars m till n och n till r och ga tillbaka till steg 1.

Vi utfor sjalva berakningen i en separat funktion storsta div och later huvudpro-grammet skota lasning och skrivning.

104 © Studentlitteratur

Page 108: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6.3 Funktionsanrop

/* Berakning av storsta gemensamma divisorn */

int storsta_div (int m, int n)

{

int r;

while ((r = m % n) != 0) {

m = n ;

n = r;

}

return n;

J

#include <stdio.h>

int main()

{

int i, j, d;

printf("Ange tva heltal: ");

scanf("%d%d", &i, &j);

d = storsta_div ( i, j);

printf("Storsta gemensamma divisorn till ");

printf("%d och %d ar %d", i, j, d);

En koming av programmet kan se ut pa foljande satt:

Ange tva heltal: 8 12

Storsta gemensamma divisorn till 8 och 12 ar 4

Observera att vardena av variablema i och j inte paverkas av anropet av storstadiv.Vid anropet kopieras de varden som finns i i och j till parametrama moch n. De till-delningar som gors till moch n inne i storsta div paverkar darfor bara moch n ochinte i och j. Ett argument kan aldrig andras av ett funktionsanrop.

Men anropet av funktionen scant forandrar ju i och j? Detta beror pa att vi som argument till scant inte skickar i och j utanpekare till dem. Sjalva pekama andras inte avfunktionsanropet. Daremot kan det de pekar pa andras.

Lat OSS studera ett annat exempel:

void ta_bort_tab (char text[])

{

int i = 0;

while (text[i] != ' \0') {

if (text [i] == '\t' ) {

text [ i] = ' ' ;

}

i + + ;

) Studentlitteratur 105

Page 109: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6. Funktioner ochprogramstruktur

Denna funktion ersatter alia tabulatortecken i en textstrang medblanka tecken. Omvibar deklarationen

char s [ 100] ;

och gor anropet

ta_bort_tab(s);

sa kommer alia tabulatortecken i den textstrang som finns i s att andras till blankatecken. Man kan da tycka att argumentet s andras. Argumentet andras emellertid inte.Det ar namligen inte teckenfaltet s som kopieras till parametem text, utan en pekaretill s. Ettargument somar ett faitomvandlades ju alltidautomatiskt i steg2 ovantill enpekare till faltet. Via pekaren till s kan sa fiinktionen komma at att andra inne i s.

Sammanfattningsvis galleralltsa att argument aldrigkan andras av ett funktionsanrop.Villman astadkomma dennaeffektfar man i stalletsomparameter ge en pekaretill detobjekt som skall kunna andras.Detta diskuteras i kapitel 7.

En funktion far forstas anropaen funktion som i sin tur innehaller ytterligare ett anropo.s.v. Man far en kedja av anrop. Teoretiskt finns det ingen grans for hur manga anropsom kan paga samtidigt. Gransensatts av det tillgangligaminnesutrymmet i datom.

En funktion far faktiskt aven anropa sig sjalv (antingen direkt eller indirekt). Dettakallas rekursion, Ett klassiskt exempel ar foljande rekursiva funktion som beraknar n!(Ix2x3x ... x^).

int nfak(int n) /* beraknar n! */

{

if (n<=0) {

return 1;

}

else {

return n * nfak(n - 1);

}

}

Vid berakningen anvander funktionen formeln

f 1 omn = 0n\ = \

[«(«-l)! om « > 0

Observera att vid rekursion ar flera upplagor av en funktion igang samtidigt. Man baren kedja med flera anrop av samma funktion. Vid varje anrop skapas en ny aktive-ringspost sa som beskrivits ovan. Varje upplaga av funktionen kommer darfor att basin egen upplaga av sina parametrar ocb sina intema variabler. Nar funktionen nfak

106 © Studentlitteratur

Page 110: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6.3 Funktionsanrop

anropas meden parameter somar storre an 0 kommer det darforatt existera fleraupp-lagor av parametem n och alia dessa kommer att ha olika varden.

Ett annat exempel ar en funktion som skriver ut en textstrang baklanges.

void skrivbak(char text[], int i)

/* skriver ut en text baklanges */

{

if (text [i] != ' \0' ) {

skrivbak(text, i + 1);

putchar (text [i] ) ;

}

Den forsta parametem ar det teckenfalt som innehaller textstrangen. Den andra para-metema anger var i teckenfaltet textstrangen borjar. Det antas att en textstrang somvanligt avslutas med ett nolltecken \o. Om t.ex. teckenfaltet s innehaller textstrangengurka och texten borjar i position o i s kommer foljande anrop att ge utskriften akrug.

skrivbak(s, 0);

Mangaberakningar kan utforas antingenmed repetitioneller med rekursion. En rekur-siv losningblir ofta nagot mindreeffektivmen i vissa typer av problemblir losningar-na mycketenkla och elegantamed rekursion. Det ar t.o.m. sa att en del problem losestrivialt med rekursion medan en annan losning kan vara mycket svar att finna. Vi skallsenare se att rekursion ar oumbarlig da vi skall behandla s.k. listor och trad.

Allt vi diskuterat hittills i detta avsnitt har fomtsatt att den funktion som anropats varit

deklarerad tidigare, vilket innebar att antalet parametrar och deras typer varit kanda.Detta ar det normala. C89 tillater emellertid (tyvarr) att man anropar en funktion sominte deklarerats alls eller deklarerats pa det aldre sattet (se avsnitt 6.4.2). Da ar inteparametrama kanda och det hela blir det lite mer osakert. Detta galler ocksa om enfunktion anvander sig av ellips-notation.

Anropet av en odeklarerad funktion gar till pa samma satt som vi beskrev i de olikastegen tidigare, men i steg 2 maste kompilatom gissa, eftersom den inte kanner tillparametramas antal och typer. Detta steg utfors darfor istallet pa foljande satt:

2. Argumenten anpassas och overfors tillfunktionen. Alia argument som ar fait ellerflinktioner omvandlas till pekare till fait respektive fimktioner. Darefter tillampasnagot som vi kan kalla standardanpassningar. Dessa gar ut pa att alia argumentsom ar av typen float omvandlas till typen doiibie och att alia argument som ar avheltalstyp utsatts for s.k. heltalsvidgning (se Appendix C). Denna innebar for detmesta att alia argument som ar av en typ kortare an int omvandlas till int.

) Studentlitteratur 107

Page 111: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6. Funktioner ochprogramstruktur

6.4 Aldre syntaxI den ursprungliga versionen av C, fore C89, anvandes ett annat skxivsatt for att defi-niera och deklarera funktioner. IC89 infordes det nya skrivsatt, med prototyper, som vianvant hittills ochkommer att fortsatta att anvanda. Anledningen till att detnya skriv-sattet infordes varattdetaldre skrivsattet ur spraklig synpunkt inte ar speciellt bra. Detgernamligen inteen kompilator mojlighet att kontrollera att en funktion anropas medratt slag avparametrar. NarC standardiserades kunde manemellertid inteforbjuda detaldre skrivsattet eftersom dettaskulle lett till att de mangder av C-program somredanfanns plotsligt skulle strida motstandarden. Darfor tillater standarden bade detnyaochdetaldre skrivsattet. Vi rekommenderar inteattmananvander sigav detaldre skrivsattet men beskriver det anda har kortfattat for att lasaren skall kunna forsta redan existe-

rande C-program som ar skrivna pa det aldre sattet.

6.4.1 Aldre funktionsdefinitioner

En funktionsdefinition skriven pa det aldre sattet har den allmanna formen

resultattyp funktionsnamn {parameternamnslista)

deklarationer av parametrar

{

deklarationer och satser

}

Det ar tillatet att utelamna resultattypen. I sa fall antas att funktionen somresultat gerett varde av typen int. I parametemamnslistan skall namnen pa flmktionens aliaparametrar angesmed kommatecken mellan. Daremotanges inte deras typer. Om funktionen inte har nagra parametrar skriver man en tom parentes.

Parametrama deklareras efter parametemamnslistan, utanfor fiinktionskroppen.Deklarationenahar samma form som vanliga variabeldeklarationer. Utelamnas dekla-rationen av nagoneller nagraav parametrama antasatt de har typen int.

Som exempel visas funktionema las teck, langd och skriv rubrik ffan avsnitt 6.1.

int las_teck()

{

}

laser forsta icke blanka */

int langd(s) ger langden av s */

char s[];

{

}

108 © Studentlitteratur

Page 112: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

void skriv_rubrik(r, nr)

char r [ ];

unsigned int nr;

6.5 inline-funktioner

6.4.2 Aldre funktionsdeklarationer

En funktionsdeklaration skriven pi det aldre sittet bar formen

resultattyp namn ();

Denna form ger informationen att namn ar namnetpa en funktion som ger ett resultatav typen resultattyp. Utelamnas resultattypen antas den ar int. Den aldre formenger ingen som heist information om funktionens parametrar. Det sags inget om hurmanga de ar och om vilka typer de bar. Man forlitarsig pa att programmeraren kannertill parametramaoch utformaranropetpa ratt satt. Kompilatombar inte nagon mojlig-bet att kontrollera att ett anrop ar riktigt.

Nagra exempel pa aldre fUnktiondeklarationer ar

void skriv_flera();

int langd();

int scanf () ;

Observera att man inte skriver nagot innanfor parentesema, men att detta inte far tol-kas som att funktionen saknar parametrar. For att ange att en funktion saknar parametrar maste man anvanda det nya skrivsMet och skriva void innanfor parentesema.

6.5 inline-funktioner

Att utfora ett flinktionsanrop innebar att en viss kod alltid maste utforas. Detta galler |oberoende av bur den anropade funktionen ser ut. Man maste t.ex. bereda plats forfunktionens aktiveringspost och boppa till och atervanda fran funktionen. Om denfunktion man anropar ar mycket enkel kan den kod som finns i funktionskroppen varakortare an den kod som fordras for ett funktionsanrop. I sadana fall skulle det lona sigfor kompilatom att lagga ut funktionskroppens kod direkt vid anropet istallet for attlagga ut kod for funktionsanrop. Man kan rekommenderar kompilatom att gora padetta satt genom att markera en funktion med ordet inline. Har kommer ett exempel.

inline int max( int 11, int 12 )

{

return 11 < 12 ? 12 : 11;

}

© Studentlitteratur 109

Page 113: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6. Funktioner ochprogramstmktur

Det star kompilatom fritt att folja rekommendationen eller inte. Det vasentliga ar attnarmananropar en funktion sa skall resultatet bli detasamma, antingen denimplemen-terats som inline eller inte.

Altemativet till iniine-funktioner bar tidigarevarit att anvanda makron med parame-trar, en mycket samre metod. Se vidare i kapitel 9.

6.6 DeklarationsomrMe och synlighetDeklarationeroch definitionerfar placeraspa tva stallen: pa yttersta nivan, utanfor aliafunktioner, eller inne i en sammansatt sats. Dessutom kan man definiera parametrar iett funktionshuvud. Ett undantag galler definitioner av funktioner. Dessa far bara placeras pa yttersta niva. Man far inte ha nastlade funktioner. Daremot kan man ha dekla

rationer av funktioner aven inne i en sammansatt sats. Nagra exempel ar

int a;

double f(int);

void g (float b)

{

char c;

int h(double);

{

double d;

}

float p(void)

{

float x;

}

Det ar tillatet att ha sammansatta satser, eller block, inne i andra satser och man far lag-

ga deklarationer och definitioner inne i block. (I C89 maste de ligga forst i blocket.) Ivanliga fall brukar man bara gora detta i sadana block som utgor kroppen till en funktion men det ar tillatet att lagga deklarationer och definitioner aven i andra block.

En deklaration eller definition specificerar en viss identifierare, ett namn. En identifie-rares deklarationsomrdde ar den del av programmet dar deklarationen eller defmitio-nen av identifieraren galler. En identifierare ar synlig inom hela sitt deklarationsomra-de utom pa sadana stallen dar den eventuellt doljs av andra definitioner (se nedan).

110 © Studentlitteratur

Page 114: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6.6 Deklarationsomrdde och synlighet

For alia identifierare (utom for namn pa lagen) galler att deklarationsomradet boijar padet stalle i programmet dar deklarationen eller definitionen av identifieraren finns. Hurlangt deklarationsomrMet stracker sig beror pa var deklarationen eller definitionenfinns. Om en identifierare deklareras eller definieras pa yttersta niva stracker sig dekla-rationsomrMet till slutet av den aktuella kallkodsfllen. Deklarationsomradet for en

identifierare som deklareras eller definieras inne i ett block stracker sig till slutet avdetta block. En parameter bar ett deklarationsomrade som stracker sig till slutet av detblock som utgor funktionens kropp. En identifierare ar alltsa inte synlig utanfor detblock den deklarerats eller definierats i. Man kan t.ex. inte komma kt en variabel som

ar defmierad inne i en funktion utanfor funktionen.

I exemplet ovan ar identifierama a, f och g synliga bade i funktionen g och funktionenp, medan identifierama b, c och h bara ar synliga i g och x bara ar synlig i p. Identifieraren d ar bara synlig i det innersta blocket i g.

I ett block far man ha endast en definition av en identifierare. En variabel i en funktion

far inte heller ha samma namn som en parameter. Daremot far man i ett block ha defi-nitioner av identifierare som ocksa ar definierade i andra block eller pa yttersta niva.Definierar man variabler med samma namn i olika block kommer de olika definitio-

nema att definiera olika variabler som upptar olika minnesutrymme och inte har nagotannat gemensamt an att de rakar ha samma namn.

Om en identifierare I har ett deklarationsomrMe som ett visst block B ingar i och mangor en ny definition av / inne i B, sa ddljer den nya definitionen den gamla deklarationen eller definitionen av 7. Utanfor B galler den gamla deklarationen eller definitionenav /. Nar man anvander en identifierare soker kompilatom den deklaration eller definition som ligger narmast och om derma firms i det block man befinner sig i sa ar det densom galler. Det hela framgar bast av ett exempel. I kommentarema anges vilka vardensom kommer att skrivas ut nar programmet kors.

int i = 1 ;

int j = 5 ;

void f (void)

{

int k = 7 ;

printf("%d %d %d\n", i,/

j / k) ; / * U 5, 7 */

\

int j = 0;

printf("%d %d %d\n", j, k); / * 0, 7 */

i++; j++; k++;1;

printf("%d %d %d\n", i. j f k) ; /* 2, 5,8*/

) Studentlitteratur 111

Page 115: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6. Funktioner ochprogramstruktur

int main ()

{

int j = 10;

int k = 20;

printf("%d %d %d\n", i, j, k); /* 1, 10, 20 */

f 0 ;

printf("%d %d %d\n", i, j, k); /* 2, 10, 20 */

}

Att man far anvanda sig av samma identifierare i olika block ar naturligtvis bekvamtnar man konstruerar funktioner. Nar man valjer variabelnamn inne i en funktion beho-ver man inte bry sig om vilkavariabelnamn som anvands inne i andra funktioner i pro-grammet. Rakar man valja samma namn pa en variabel spelar detta ingen roll.

6.7 LagringsklasserNar man gor en deklaration kan man ange en s.k. lagringsklass for den identifierareman deklarerar. De olika lagringsklasser som kan forekomma ar

auto register extern static

Lagringsklassen bestammer hur minnesutrymme skapas for ett objekt och om fleradeklarationer av samma identifierare skall referera till samma funktion eller variabel.

Lagringsklassen skrivs normalt forst i en deklaration

extern double f(int);

static int m = 1;

register int i, j;

Man far ange hogst en lagringsklass i varje deklaration. Lagringsklassema auto ochregister far inte anvandas i deklarationer pa yttersta niva. De far bara forekommainne i funktioner.

Det ar tillatet att inte ange lagringsklass i en deklaration. I sa fall gors foljande stan-dardantaganden:• For en funktion antas att lagringsklassen ar extern.• For en parameter till en funktion antas att lagringsklassen ar auto.• For en variabel som deklareras inne i ett block antas att lagringsklassen ar auto.

• For en variabel som deklareras pa yttersta niva antas att lagringsklassen ar extern.

Vi skall behandla de olika lagringsklassema var for sig nedan. Vi kommer ocksa att

diskutera nyckelordet volatile som kan inga i en deklaration. Detta betecknar visser-ligen inte en lagringsklass men bar anda att gora med hur en variabel lagras.

112 © Studentlitteratur

Page 116: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6.7 Lagringsklasser

6.7.1 Lagringsklassen auto

Lagringsklassen auto ar den vanligaste. Alia variabler som deklareras inne i en funk-tion far i de fiesta fall denna lagringsklass. Man brukar oftast inte ange ordet auto isina deklarationer, utan utnyttjar standardantagandet. Deklarationen

{

auto int k;

}

ar t.ex. ekvivalent med

int k;

Minnesutrymme for ett objekt med lagringsklassen auto reserveras automatiskt narexekveringenav det block dar deklarationen ar gjord startar och om man bar en initie-ring kommer denna att utforas varje gang. En objektdeklaration med lagringsklassenauto tjanar ocksa som en definition av objektet. Minnesutrymmet ar reserverat medanexekveringen pagar i det aktuella blocket. Nar man lamnar blocket ar utrymmet intelangre reserverat och man kan darfor inte langre komma at objektets varde.

6.7.2 Lagringsklassen register

Man anv^der lagringsklassen register for att ange for kompilatom att man onskaratt ett visst objekt skall placeras i ett av datoms snabba maskinregister. I ovrigt bar ettobjekt med lagringsklassen register samma egenskaper som ett objekt med lagringsklassen auto. (Man far dock inte anvanda &-operatom). Det ar inte alldeles sakert attett objekt bamnar i ett register for att man angivit lagringsklassen register. Varjedator bar ett mycket begransat antal maskinregister ocb flera av dem ar vanligtvisredan upptagna for andra andamal. Kompilatom kanske darfor bara klarar av att pla-cera nagra fa objekt i register. I en dator ar det dessutom sa att endast objekt av vissatyper kan finnas i register. Kan inte kompilatom placera objektet i ett register beband-las det som om det bade baft lagringsklassen auto.

Avsikten med att anvanda sig av lagringsklassen register ar forstas att forsoka okaexekveringsbastigbeten for en programdel som anvands mycket ocb dar bastigbeten arviktig. Ofta placerar man i ett register nagon parameter eller variabel som anvands fli-tigt, t.ex. som raknare i en repetitionssats. Som exempel visas en funktion som far ettteckenfalt som parameter. I teckenfaltet finns en textstrang ocb funktionen andrardenna pa alia stallen dar det finns mer an ett blankt tecken i foljd. Dessa tecken ersattsmed endast ett blankt tecken. Den nya textstrangen blir kortare an den gamla ocb placeras i samma teckenfalt. Variabeln i bailer reda pa var man ar i den gamla textstrangen ocb j bailer reda pa aktuell plats i den nya.

© Studentlitteratur 113

Page 117: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6. Funktioner och programstruktur

void komprimera (register char s[])

/* tar bort flera blanka i foljd */

{

register int i, j;

i = j = 0;

while ((s[j++] = s[i]) != '\0') {

if (s [ i + + ] == ' ' ) {

while (s [i] == ' ') {

i + +;

}

}

}

6.73 Lagringsklassen extern

Man kan ha lagringsklassen extern bade vid deklaration av funktioner och variabler.Man anvander i forsta hand denna lagringsklass nar man vill komma at funktioner ellervariabler som ar definierade i andra kallkodsfiler. I avsnitt 6.2 sag vi hur detta gjordesnar det gallde extema funktioner. Har skall vi behandla externa variabler.

Varje extern variabel maste definieras pa exakt ett stalle i programmet och definitionenmaste ligga pa yttersta niva, inte inne i nagon funktion. En deklaration som innehdlleren initiering av den extema variabeln raknas som en definition. For extema variabler

galler att initieringsvardet maste vara konstant. Nagra exempel ar

/* externa variabeldefinitioner */

int antal = 5;

const int max_antal = 100;

const double pi = 3.1415926536;

Det antas automatiskt att lagringsklassen ar extern. Det gar aven att skriva nyckel-ordet extern framfor om man vill, men det rekommenderas att man inte har med ordet

extern i en definition. Man kan da lattare skilja mellan defmitioner och deklarationer.

Liksom nar det gallde extema funktioner far man ha flera deklarationer av en extemvariabel. En deklaration innebar inte att nagot minnesutrymme skapas for variabeln.Deklarationen utgor bara en hanvisning till den definition som skall finnas. Om t.ex.definitionen av en extem variabel ligger i en kallkodsfil f i och man vill anvanda variabeln i filen f 2, maste man i f 2 ha en deklaration av variabeln. Exempel pa deklarationer av extema variabler ar:

/* externa variabeldeklarationer */

extern int antal;

extern const double pi;

extern int n;

extern const int maxantal;

114 © Studentlitteratur

Page 118: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6.7 Lagringsklasser

En deklaration av en extern variabel far (till skillnad fran en definition) ligga antingenpa yttersta niva eller inne i en funktion. Det rekommenderas att man alltid bar mednyckelordet extern i en deklaration av en extern variabel. Det kan da inte bli nagramissforstand cm huruvida en konstruktion ar en deklaration eller en definition.

I standarden flxngerar en konstruktion som varken innehaller ordet extern eller nagoninitiering som en prelimindr definition. Om ingen riktig definition patraffas senare isamma kallkodsfil betraktas den forsta preliminara definitionen som en definition darvariabeln initierats till vardet 0. Om daremot en riktig definition patraffas senare i filenblir den preliminara definitionen endast en deklaration som refererar till den senaredefinitionen.

int il; /* preliminar definition. blir deklaration */

int i2; /* preliminar definition. blir definition V

int il = 2; /* riktig defi.nition

int 12; /* preliminar definition. blir deklaration

Minnesutrymmet for en extern variabel finns kvar sa lange som programmet exekve-rar. Det forsvinner inte nar olika funktioner anropas och avslutas. De kan anvandassom ett sM att kommunicera mellan funktioner som finns i olika kallkodsfiler. Nor-

malt betraktas det inte som god programmeringsstilatt gora pa detta satt, eftersom detleder till att fiinktionema far svaroverblickbara sidoeffekter. Kommunikation mellan

funktioner bor ske med hjalp av funktionsparametrar. I nagra fall kan man emellertidtanka sig att det ar acceptabelt att anvanda extema variabler.

Ett fall ar nar de extema variablema innehaller en samling konstanta varden som skallvara tillgangliga for en gmpp av funktioner. Detta fall kan naturligtvis ocksa losas medhjalp av makron men man kan ocksa tanka sig att vardena inte behover vara kanda vidkompileringstillfallet och da maste extema variabler anvandas. En av fiinktionemakanske bar till uppgift att lasa in data och initiera de extema variablema vid exekve-ringens borjan. Det kan t.ex. rora sig om data som anger bur utskrifler fran ett programskall ske, vilken utskriftsfil som skall anvandas, sid- och radstorlek i utskriften etc.

Ett annat fall ar nar en gmpp av funktioner alia arbetar pa en gemensam datastmkturoch det ar belt klart av sammanhanget att det ar sa. I ett program kan t.ex. indataraderlasas en rad i taget och analyseras av olika funktioner i programmet. I stallet for att daskicka indataraden och aktuellt index i indataraden som parametrar till alia dessa funktioner, kan indataraden och dess index vara extema variabler. En typ av program dardetta kan vara aktuellt ar t.ex. kompilatorer. For att undvika att behova anvandaextema variabler i detta fall bor man om mojligt samla alia funktioner som arbetar paden gemensamma datastmkturen till en enda kallkodsfil och i stallet anvanda sig avvariabler av lagringsklassen static (se nasta avsnitt) som bara ar kanda i denna fil.

) Studentlitteratur 115

Page 119: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6. Funktioner ochprogramstruktur

Vi skall visa ett lite storre exempel med kompilatoranknytning darbade extema funktioner och variabler anvands. Exemplet demonstrerar ocksa hur man genom att brytaner ett relativt komplicerat problem i mindre delaroch lata varje delproblem losas aven funktion kanfa problemet hanterligt. Exemplet garut pa att skriva ett program somlaser in en indatarad i taget och som kontrollerar att den inlasta raden innehaller ettaritmetiskt uttryck.Ett exempelpa hur det kan se ut nar programmet kors ar

a + bc-24 * 11/antal

OK

abc

OK

a + _a

I

Felaktig operand

a+b = c

I

Felaktig operator

a+b*

I

Felaktig operand

Innan vi specificerar fler detaljer kan vi konstruera huvudprogrammet som placeras ien egen kallkodsfil f i. c.

/* I kallkodsfilen fl.c */

#include <stdio.h>

extern char rad[];

extern int lasrad(void);

extern int uttryck(void);

int main()

{

while (las_rad()) {

if (rad[0] != ' \0') {

if (uttryck() ) {

printf("OK\n");

}

}

}

Huvudprogrammet forutsatter att det nagon annanstans i programmet finns en funktionlas rad som laser in en indatarad och placerar den i en extern variabel rad. Denextema funktionen uttryck antas kontrollera den inlasta indataraden och ge vardet 1eller 0 beroende pa om raden innehaller ett godkant aritmetiskt uttryck eller inte.

L^t OSS sa konstmera funktionen las rad. Vi placerar den i en annan kallkodsfil f 2. c.I f 2. c placerar vi ocksa en funktion fei som anvands for att producera felutskrifter.

116 © Studentlitteratur

Page 120: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6.7 Lagringsklasser

/* I kallkodsfilen f2.c */

#include <stdio.h>

char rad[100] =

int nasta = 0;

int las_rad(void) /* laser en rad, ger 0 vid EOF */

{

int c, i;

if ((c = getcharO) == EOF) {

return 0;

}

else {

i = 0;

while (c != '\n' && c != EOF) {

rad[i++] = c;

c = getchar();

}

rad[i] = '\0';

nasta = 0;

return 1;

}

}

void fel(char text[ ] ) /* skriver ut felmeddelande */

{

int i;

for (i = 0; i < nasta ; i++) { /* skriv blanka */

putchar{' ');

}

printf ( "!\n%s\n", text); /* markera felet */

}

I f 2. c finns definitionen av den extema variabeln rad. Den initieras med ett uttryck

som betecknar en torn strang. (Ett nolltecken placeras i det forsta elementet i rad.) Vibar ocksa bar en definition av en extern variabel nasta som anger vilket tecken i radsom star i tur att kontrolleras.

Funktionen las rad laser ett tecken i taget tills indataraden tar slut eller tills end offileintraffar. I det forra fallet ger las rad vardet 1 som resultat ocb i det senare vardet 0.De inlasta tecknen placeras i rad som avslutas med ett nolltecken. Efter varje inlast radnollstalls variabeln nasta.

Funktionen fei anropas da nagon flinktion bar upptackt ett fel i den inlasta raden. Somparameter far funktionen en text som skall vara felutskrift. Innan felutskriften skrivs ut

skrivs en rad med ett utropstecken i den position som inneballer det forsta felaktigatecknet pa raden.

) Studentlitteratur 117

Page 121: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6. Funktioner ochprogramstruktur

I en tredje kallkodsfil f 3. c placeras de funktioner som undersoker cm en rad innehal-ler ett korrekt aritmetiskt uttryckViantarbar att ett aritmetiskt uttryckskallha formen

operand op operand op op operand

Operandema skall vara identifierare eller heltal. En identifierare far besta av sma bok-

staver 'a' till 'z' och siffror. Det forsta tecknet i en identifierare skall vara en bokstav.

Ett heltal far bara besta av siffror. Operatorema ar +, * och /. For enkelhets skullantas att inga parenteser far forekomma.

I f 3. c finns flera kortare funktioner som var och en bar till uppgift att kontrollera omdet som foljer i rad ar en viss konstruktion. En av funktionema kontrollerar t.ex. om defbljande tecknen i rad utgor en identifierare. I funktionen uttryck utnyttjas det faktumatt formen for ett aritmetiskt uttryck kan skrivas om pd foljande satt:

operand op uttryck

Filen f 3. c bar utseendet

/* I kallkodsfilen f3.c */

extern char rad[];

extern int nasta;

extern void fel(char text[]);

int bokstav(void)

{

if ('a' <= rad[nasta] && rad[nasta] <= ' z') {

n a s t a + + ;

return 1 ;

}

else {

return 0;

}

}

int siffra(void)

{

if ('0' <= rad[nasta] && rad[nasta] <= '9') {

n a s t a + + ;

return 1;

}

else {

return 0;

}

118 © Studentlitteratur

Page 122: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

int identifierare(void)

{

if (bokstav()) {

while (bokstavO | | siffraO)

return 1;

}

else {

return 0;

}

}

int konstant(void)

{

if (siffraO) {

while (s i f f ra())

return 1;

}

else {

return 0;

}

}

int operand(void)

{

if (identifierare() 11 konstant()) {

return 1;

}

else {

fel("Felaktig operand");

return 0 ;

}

}

int operator(void)

{

switch (rad[nasta]) {

case case case case

n a s t a + + ;

return 1;

default:

fel("Felaktig operator");

return 0;

6.7 Lagringsklasser

) Studentlitteratur 119

Page 123: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6. Funktioner ochprogramstruktur

void ta_bort_blanka(void)

{

while (rad[nasta] == ' ' I I rad[nasta] == '\t')n a s t a + + ;

int uttryck(void)

{

ta_bort_blanka();

if(operand()) {

ta_bort_blanka();

if (rad[nasta] == '\0') {

return 1;

}

else {

ta_bort_blanka ();

return operator () && uttryck()

}

}

else {

return 0;

6.7.4 Lagringsklassen static

Lagringsklassen static kan anvandas vid deklarationer bade av variabler och funktioner. En variabel som ges lagringsklassen static kommer att fa ett minnesutrymmesom existerar under hela programmets exekvering. Variabelns minnesutrymme blirstatiskt. En static-variabel initieras bara en enda gang under programmets gang.Detta sker nar exekveringen startar.

Det fmns tva anvandningsomraden for lagringsklassenstatic. Det ena ar att deklarerastatiskavariabler innei funktioner. En sManvariabel kommer, till skillnad franvanligaautomatiska variabler, att finnas kvar nar fimktionen avslutas. Variabeln bar kvar sitt

gamla varde om fimktionen anropas pa nytt. Variabeln ar dock inte tillganglig utanforfimktionen. Som exempel studerar vi en ny version av funktionen skriv rubrik fransidan 100.

void skriv_rubrik(char r[]){

static unsigned int nr = 1;

printf("\n%u.\t%s\n", nr, r);

n r + + ;

120 © Studentlitteratur

Page 124: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6,7 Lagringsklasser

Denna version har intenagon parameter somanger kapitlets nummer. I stallet bar denen statisk variabel nr. Nar programmet startar initieras nr till vardet 1 och i slutet avvarjeanrop av skriv rubrik okasdenmed 1.Dettainnebar att forsta gangen funktio-nen anropas skrivs kapitelnumret 1 ut, andra gangen kapitelnumret 2 o.s.v.

Det andra anvandningsomrMet for lagringsklassen static ar vid deklarationer payttersta niva. Vi har tidigare sett att en variabel med lagringsklassen extern, precissomvariabler medlagringsklassen static, har ett statiskt minnesutrymme somexiste-rar under hela programmets exekvering. Det som skiljer mellan lagringsklassemaextern och static pa yttersta niva ar alltsa inte lagringssattet. Skillnaden ar att enidentifierare med lagringsklassen static inte kan goraskand utanforden aktuellakall-kodsfilen. Man kan alltsa anvanda static i stallet for extern nar man vill "gomma"

funktioner eller variabler inne i en kallkodsfil. (Ordet static ar lite olyckligt valt ochkan vara missvisande, intern hade kanske varit battre.)

Som exempel skall vi studera en s.k. slumptalsgenerator, en ftinktion som vid anropger ett till synesslumpmassigt tal somresultat. Vi skallvisahur fimktionema rand ochsrand somvi anvandc i exempleti avsnitt3.7 kan se ut. Somvi tidigarenamntar de talman far av en slumptalsgenerator egentligen inte slumpmassiga pa riktigt. Vaije gangman anropar flinktionen rand far man ett tal som ar genererat utgaende fran det forratalet som gavs. Detta gors vanligen med formeln

= Ku^ + L mod M

dar konstantema K, L och M skall valjas pa lampligt satt for att de genererade talenskall verka slumpmassiga. Har betyder mod modulo-operatom, d.v.s. den operator sombetecknasmed %i C. Foljande definitionerav funktionema rand och srand ar hamtadeur beskrivningen av standarden och ar skrivna pa sadant satt att de skall vara flyttbaramellan olika C-system.

static unsigned long int next = 1;

int rand(void) /* ger ett slumptal i intervallet 0 .. 32767 */

{

next = next * 1103515245 + 12345;

return (unsigned int) (next/65536) % 32768;

}

void srand(unsigned int seed) /* initierar */{

next = seed;

Man anropar funktionen srand for att ge slumptalsgeneratom ett startvarde, ett s.k. fro.Att diskutera hur konstantema i rand ar valda ligger utanfor ramama for denna bok.Har skall vi koncentrera oss pa den statiska variabeln next. Denna ar deklarerad pa

© Studentlitteratur 121

Page 125: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6. Funktioner ochprogramstruktur

yttersta niva och ar alltsa synlig bade i rand och srand. next maste ha ett statiskt min-nesutrymme eftersom den skall "komma ihag" sittvarde mellan anropen av rand. LatOSS anta att vi placerar slumptalsgeneratom i en egen kallkodsfil. Fordelen med attdeklarera next som static och inte som extern ar att den blir oatkomlig utanfordenna fil. Detta betyder att den inte kan andras pa nagot okontrollerat satt, t.ex. avmisstag. For att slumptalsgeneratom skall producera slumptal med goda statistiskaegenskaper kravs att inte next andras utifran sedan man initierat den.

I en kallkodsfil far det bara finnas en enda definition av en statiskvariabel. En deklara-tion dar den statiska variabeln initieras raknas som en definition. Initieringsvardetmaste vara konstant.

Man far ocksaha funktioner som defmieras med lagringsklassen static,

static int dold(float x)

En sMan funktion blir bara kand inne i den aktuella kallkodsfilen.

Man far faktiskt i en kallkodsfil ha flera deklarationer av en statisk identifierare. Detta

masteman anvandaom man har statiskafunktioner som ar omsesidigt rekursiva, d.v.s.anropar varandra. Foljande satt att avgora om ett positivt tal ar udda eller jamntdemonstrerar detta. (Problemet loses naturligtvis enklare medhjalp av %-operatom.)

static int jamnt(int); /* deklaration av jamnt */

static int udda(int tal) /* definition av udda

{

return tal == 0 ? 0 : jamnt (tal - 1);

}

static int jamnt(int tal) /* definition av jamnt */

{

return tal == 0 ? 1 : udda(tal - 1);

}

En variabeldeklaration med ordet static men utan initiering raknas som enprelimindrdefinition. Patraffas en riktig definition (en med initiering) senare i filen raknas denpreliminara definitionen som en deklaration, annars raknas den som en definition med

initieringsvardet 0.

6.7.5 Flyktiga objekt - volatile

I en dator finns ofta nagra speciella minnesceller, s.k. FO-register, som anvands for atthantera kommunikationen med yttre enheter som kommandofonster, skivminnen, skri-

122 © Studentlitteratur

Page 126: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6.8 Modularprogramutveckling

vare etc. Genom att manipulera bitama i en sadan minnescell kan man fran ett programstarta dataoverforingar mellan datoms centralenhet och de anslutna yttre enhetema. Deyttre enhetema kan ocksa ge ett program signaler om vad som bar bant genom att and-ra i ett I/O-register. De kan t.ex. signalera att en dataoverforing ar klar eller att nagotfel bar uppstatt. Ett annat exempel ar en realtidsklocka, en enbet som med jamna tids-mellanrum andrar en bit i en viss minnescell.

I ett program kan man representera I/O-register som objekt som ar placerade pa nagotvisst stalle i minnet. Det som ur programmets synpunkt ar speciellt med dessa objekt aratt de inte bara kan andras ocb avlasas av programmet. De kan ocksa andras av de yttreenhetema, "bakom ryggen" pa programmet. Vi sager att ett sadant objekt ar flyktigt.Dess varde ar inte bestandigt. Om man i en sats i programmet bar avlast objektet kanman i nasta sats inte vara saker pa att objektet fortfarande bar samma varde. Man angeratt en variabel ar flyktig genom att anvanda nyckelordet volatile vid deklarationen.

volatile int tangentbords_reg;

extern volatile const int realtidsklocka;

Ett annat tillfalle nar man kan ba flyktiga variabler ar nar programmet anvander sig avflera s.k. tradar, dvs. programdelar som exekverar parallellt. Da kan en trad andra envariabel utan att det syns i en annan trad.

Anledningen till att man bor ange att en variabel ar flyktig ar att kompilatorer ofta for-soker optimera den genererade koden for att gora programmet sa effektivt som mojligt.En kompilator kan t.ex. komma ibag att vardet av en viss variabel finns i ett maskin-register. Kompilatom lagger kanske darfbr inte ut kod for att bamta variabelns vardefran minnet nasta gang variabeln skall anvandas. Sadana optimeringar fungerar inteom en variabel ar flyktig. Genom att deklarera en variabel som volatile talar man omfor kompilatom att variabeln inte far inga i nagra optimeringar av detta slag.

6.8 Modular programutveckUng

Nar man skall konstmera ett storre program ar det lampligt att dela upp programmet iflera separata delar, dar varje del placeras i en egen kallkodsfil. Detta ar nodvandigt foratt programmet skall blir banterbart. Programmet blir lattare att forsta ocb vaije delkan skrivas ocb kompileras separat. Det bmkar ocksa vara mojligt (atminstone delvis)att testkora de olika programdelama var for sig. I ett storre programprojekt kan flerapersoner vara inblandade i programmeringsarbetet ocb olika personer kan utvecklaolika delar av det totala programmet.

Lat OSS som exempel anta att vi skall utveckla ett storre berakningsprogram. Program-met skall vara interaktivt ocb kommunicera med en anvandare som kor programmet.Programmet skall ocksa kunna presentera berakningsresultat pa grafisk form, t.ex. som

© Studentlitteratur 123

Page 127: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6. Funktioner ochpwgramstruktur

staplar och diagram, i kommandofonstret eller pa nagon annan utskriftsenhet. Ettsadant program kanman valja att dela in i fyra separata delar: ett huvudprogram somskoter om den overgripande administrationen, en berakningsdel som utfor de berak-ningar som onskas, en in- och utmatningsdel som har hand om kommunikationen medanvandaren och en grafikdel som tillhandahaller hjalpfunktioner for att kunnapresen-tera data pa grafisk form. Vi antar att de fyra delama placeras i fyra separatakallkods-filer huvud. c, ber. c, inut. c respektive graf. c.

Eftersom de olika delama i ett program skall kunna kommunicera med varandra m§stedet finnas extema funktioner (och ev. variabler). Som vi har sett maste det i vaije fildar en extem funktionanropas finnas en deklarationav den extema funktionen. I varjefil kan man forstas halla reda pa vilka extema funktioner som anropas fran filen ochlagga in deklarationer av dessa funktioner men dettablir mycketsnart ohanterligt. Detar latt att man glommer att ta med nagon deklaration som horde finnas och man kanocksa raka fa med deklarationer som inte behovs. Glommer man en deklaration kan

detta vara allvarligteftersomkompilatom da gor en implicit (ofta felaktig) deklarationav den aktuella funktionen. Har man med onodiga deklarationer paverkas inte pro-grammets funktion men programmet kan bli rorigare och svarare att forsta. En annanrisk med att lagga in deklarationer av extema funktioner i en kallkodsfil ar att denextema funktionen kan andras under programutvecklingens gang. (Resultattj^en kan-ske andras.) Det ar mycket latt hant att man da glommer att andra alia deklarationersom fmns av funktionen i de olika kallkodsfilema.

Losningen pa problemet ar att anvanda sig av inkluderingsfiler som innehaller deklarationer av de extema funktionema. En inkluderingsfil kan ocksa innehalla deklarationerav extema variabler, definitioner av makron samt typdeklarationer (upprakningar, poster och unioner). En inkluderingsfil skall daremot aldrig innehalla nagon definition.

I stallet for att i de olika kallkodsfilema lagga in deklarationer av extema identifierareskall man nu inkludera en eller flera inkluderingsfiler genom att skriva pa nagot av fol-jande satt. (En inkluderingsfil bmkar i de fiesta system ha ett namn som slutar p& .h.)

#include "dek.h" /* finns i eget bibliotek */

#include <stdio.h> /* finns i standardbibliotek */

Den forsta formen anvander man for inkluderingsfiler man skrivit sjalv och den andrafor inkluderingsfiler som beskriver C:s standardfimktioner.

En satt att anvanda inkluderingsfiler ar att konstmera moduler (paket). Detta anslutertill synsattet i modemare sprak. En modul ar en separat programdel som har till uppgiftatt hantera sadant som logiskt hor ihop. Man kan t.ex. ha en modul som skoter om grafisk presentation av utdata eller en modul med matematiska standardfunktioner. Varje

modul bestar av tva delar, modulens specifikation och modulens kropp. Specifikatio-nen talar om for en anvandare av modulen hur modulen skall anvandas. Man kan saga

124 © Studentlitteratur

Page 128: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

# include <math.h

) Studentlitteratur

nnath

math.h

math.c

huvud.c

# include "ber.h

#/r7c/t;cfe"inut.h"

inut.h

inut.c

# include graf.h# include <stdio.h>

6.8 Modularprogramutveckling

# include <stdio.h>

stdio.h

stdio.c

Figur 6.1 Anvdndning av moduler

125

Page 129: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6. Funktioner ochprogramstmktur

att denarmodulens "skyltfdnster". Specifikationen innehaller deklarationer (inte defi-nitioner) av de extema identifierare som definieras i modulen. Det kan t.ex. vara deklarationer av modulens extema funktioner. Specifikationen kan naturligtvis ocksa inne-halla fortydligande kommentarer. Modulkroppen innehMler definitionema av deextemafunktioner och variabler somdeklarerats i specifikationen. Hur modulkroppenserut behover inteanvandaren av modulen bry sig om.Det ar baraen angelagenhet forden som implementerar modulen.

I C finns inga "riktiga" moduler inbyggda i spraket men vi kan simulera en sMangenom att gora pa foljande satt. Modulkroppen med sina olika funktionsdefinitionerplaceras i en kallkodsfil. Modulspecifikationen skrivs i en annan separat fil som vilater vara en inkluderingsfil. (Normaltlater man filen med kroppenbeta modulnamn.coch filen med specifikationen modulnamn.h. Iden ar nu att i vaije programdel barainkludera de moduler som verkligen behovs. Vart berakningsprogram skulle t.ex.kunna stmktureras som i figur 6.1.

Har bar vi betraktat filema stdio. h och math. h som specifikationerav standardmodu-lema stdio och math. Vi ser att en modul kan utnyttja en eller flera andra moduler.

Fordelen med anvandning av moduler ar att ett program kan byggas upp pa ett snyggtoch valstmkturerat satt. Dessutom behover man i varje programdel bara kompilera deextema deklarationer denna del behover. Det framgar tydligt hur de olika program-delama ar beroendeav varandra. Detta ar en fordel underprogramutvecklingen. Om enmodul andras kan man latt se vilka programdelar som ar beroende av denna och kan-ske ocksa maste justeras och kompileras om.

Den slumptalsgenerator vi studerade i foregaende avsnitt kan betraktas som en modul.Modulkroppen har vi redan visat. Vi kan tanka oss att den placeras i en kallkodsfilslump. c Specifikationen av modulen slump lagger vi da i en annan fil slump. h.

/* Specifikation av modulen slump */

extern int rand(void);

/* ger ett slumptal i intervallet 0 .. 32767 */

extern void srand(unsigned int seed);

/* initierar slumptalsgeneratorn */

Som avslutning skall vi visa en modul som hanterar en stack. En stack ar en datastmk-tur som ofta forkommer i program. I en stack kan man lagga in och ta ut enskilda element. En stack paminner om en ko men arbetar enligt principen "sist in - forst ut".Operationen push lagger ett nytt element overst pa stacken och operationen popplockar bort det oversta elementet fran stacken. Operationen empty anvands for attundersoka om stacken ar tom.

126 © Studentlitteratur

Page 130: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6.8 Modular programutveckling

Vi skall har visa en modul som hanterar en stack dar de enskilda elementen ar tecken.

Specifikationen placeras i filen stack.h och har foljande utseende:

/* Specifikation av modulen stack */

extern void push(char);

extern char pop(void);

extern int empty(void);

I filen stack. c laggs modulens kropp.

/* Kropp till modulen stack */

#lnclude <stdlo.h>

#lnclude "stack.h" /* Inkludera egen .h-fll for kontroll! */

#deflne MAX 200

static char s[MAX];

static int top = -1;

void push(char c)

{

if (top < MAX - 1) {

s[++top] = c;

}

else {

prIntf("Stacken full!\n");

}

}

char pop(void)

{

if (top >= 0) {

return s[top--];

}

else {

printf("Stacken tom!\n");

}

}

int empty(void)

{

return top < 0;

}

Sjalva stacken lagras i faltet s och variabeln top haller reda pa stackens topp, d.v.s.index for det element som lagts in sist. Bade s och top har deklarerats som static. Dear alltsa intema i modulen och kan inte andras utifran.

Vi kan nu med anvandning av modulen enkelt konstruera ett program som laser in entext fran tangentbordet och skriver ut den inlasta texten baklanges.

© Studentlitteratur 127

Page 131: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6. Funktioner ochprogramstruktur

/* laser in en text och skriver ut den baklanges */#include <stdio.h>

#include "stack.h"

int main ()

{

int c;

while ((c = getchar()) != EOF) {

push ( c) ;

while (!empty()) {

putchar (pop ()) ;

6.9 Ovningsuppgifter1. Skriven funktionsom skriverut ditt namn vaije gang den anropas.

2. Skriv en funktion skriv fiera med tva parametrar c och n som skriver ut dettecknen som finns i c n gangerpa tangentbordet. Skriv sedanatt anropav flinktio-nen vilket astadkommer att en hel rad med understrykningstecken skrivs ut. (For-utsatt att en rad bestar av 80 tecken.)

3. Konstrueraen funktion uppho j som beraknar dar bade jc ochy ar reella tal. Tips.Med hjalp av vanliga matematiska formler for logaritmrakning finner vi att

/ = /'"W

4. De s.k. binomialkoefficienterna kan definieras for icke negativa heltal n och k pafoljande satt:

k\ X{n-k)\

Skriv med hjalp av funktionen nfak i avsnitt 6.3 en funktion som beraknar binomi-alkoefficienten for tva tal.

5. For att berakna kvadratroten till ett tal x kan man anvanda Newtons metod som

fungerar pa foljande satt:

Man borjar med att gissa ett tal g >= 0. Nar man bar gissat g vet man att det mastefinnas ett annat tal h sadant att g * /? = x. Om man har riktig tur och gissat bra sagaller att g och h ar ungefar lika och da har man funnit losningen. I normala fallgor man nog inte en sa god gissning. En ny battre gissning kan man da gora genomatt bilda medelvardet av g och h:

128 © Studentlitteratur

Page 132: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

nygissnmg = ^

Mankan nu ersattag med den nya gissningen och beraknaett nytt tal h. Genomattbilda medelvardet av de nya vardena g och h kan man en annu battre gissningo.s.v.

Anvand denna metod for att konstruera en funktion som beraknar roten ur x.

Anvand som forsta gissning vardetx/2 och lat gissningama fortsatta tills skillna-den mellan tva gissningar armindre 10' .

Ett satt att berakna den storsta gemensamma divisom till tva positiva heltal ar attutnyttja definitionen:

, X

6.9 Ovningsuppgifter

sgd{m, n) =

m om m = n

sgd{m - n, n) om m>n

sgd(m, n-m) annars

Skriv en rekursiv funktion storsta div som beraknar den storsta gemensammadivisom till tva positiva heltal utgaende fran denna definition.

7. Konstmera ett program som undersoker om en rad innehaller en korrekt tilldel-ning. En tilldelning skall ha foljande form:

identi f ierare = uttryck;

dar identifierare och uttryckar definierade som i exemplet i avsnitt 6.7.3. Utnyttjaatt kallkodsfilema f 2. c och f 3. c i avsnitt 6.7.3 redan finns.

8. Utoka modulen slump i avsnitt 6.8 (och 6.7.4.) med en ny funktion krona kiavesom slumpvis och med lika sannolikhet ger vardet 0 eller 1 nar den anropas.Anvand funktionen rand.

9. Konstmera en modul som hanterar en ko dar de enskilda elementen ar tecken. Det

skall kunna ga att satta in element sist i kon, att ta ut kons forsta element och attundersoka om kon ar tom.

10. Skriv en funktion sif f sum som far ett heltal av typen unsigned int som parameter. Funktionen skall summerar siffrorna i talet och ge denna summa som retur-varde. Tips. Dividera upprepade ganger med 10 och summera restema vid divisio-nema.

11. Tva positiva heltal i och j kallas for relativa primtal om det inte finns nagot heltalstorre an 1 som de bada ar delbara med. (Talen 16 och 21 ar t.ex. relativa primtal,men daremot inte talen 18 och 21 eftersom de bada ar delbara med 3.) Skriv enfunktion som far tva positiva heltal som parametrar och som undersoker om de tvatalen ar relativa primtal. Funktionen skall som resultat ge ett sanningsvarde.

) Studentlitteratur 129

Page 133: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

6. Funktioner ochprogramstruktur

12. Standardfunktionen printf bar inget format for utskrift i binar form. Konstrueradarfor en egen funktion printb som skriver ut ett heltal som ar storre an eller likamed noil i binar form. Funktionen skall ha tva parametrar: ett tal k av typenunsigned long int som skall skrivas ut samt ett heltaltal p som anger bur mangapositioner somminstskall finnas i utskriften. Om talet k bebover farre an p positioner skall utfyllnad skemednollor till vanster. Skulle taletinterymmas i p positionerskallflerpositioner anvandas sa att taletprecis kan skrivas ut. Tips. Anvandrekursion.

13. En irriterande sak med standardfunktionen fgets ar att den lagger med radsluts-tecknet sist i den inlasta raden. (Se sidan 52.) Det ar ocksa lite klumpigt att manmSste ba med argumentet stdin nar man laser fran tangentbordet. Skriv en egenversion av denna funktion, mygets, som inte bar dessa tva nackdelar. (Du kan for-stas anvanda dig av funktionen fgets inne i din funktion.)

130 © Studentlitteratur

Page 134: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Pekare och fait 7

Pekarvarden ar minnesadresser for objekt (variabler eller annat minnesutrymme). Deanvands ofta i C i ett antal olika sammanhang, till exempel vid parameteroverforing,hoplankning av datastrukturer eller for aft hjalpa kompilatom att generera effektivastmojliga kod.

Vi kommer i detta kapitel att se bur pekarvarden och pekarvariableranvandsoch upp-tacka att de ger oss en mycket stor flexibilitet. Detta leder dock till att man maste vetaprecis vad man gor nar man anvander dem. I manga andra sprak ar pekamas anvand-ning begransad for att minskamojligheten for felanvandning. Sa ar inte fallet i C, darhela ansvaret faller pa programmeraren.

Fait anvands for att skapa utrymme for ett antal varden av samma typ. Man kankomma at de enskilda vardena via indexering med heltal, precis som i de fiesta andraprogrammeringssprak. I C bar falten en stark koppling till pekarvarden — fait repre-senteras av ett pekarvarde till forsta elementet i faltet. Darfor behandlas bade pekareoch fait i detta kapitel.

7.1 Pekare och adresser

Alia variabler (och fimktioner) bar en minnesadress, det vill saga de firms pi enbestamd plats i minnet. Derma adress brukar ocksa kallas referens eller pekarvarde.(Observera att man dock noggrant bor undvika att kalla dem "referenser" i C eftersomC++ infort "referens" som ett eget begrepp som skiljer sig fran pekare.) I de fiesta programmeringssprak bar man ingen mojlighet att ta reda pa adressen till en variabel, meni C kan man gora det med adressoperatom &. For att till exempel fa adressen till varia-beln u skriver vi:

Vi kommer att illustrera pekarvarden med schematiska bilder sa som visas i figur 7.1dar u ar namnet pa variabeln och &u ar pekarvardet som pekar till variabeln u. Dettavarde kan vi tilldela en pekarvariabel, det vill saga en variabel som ar deklarerad att

© Studentlitteratur

Page 135: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

7. Pekare ochfait

Figur 7.1

just innehalla pekarvarden, precis som flyttalsvariabler ar avsedda att iimehalla justflyttal. C skiljer dessutom pa pekare till olika typer av varden, en pekare till heltal aralltsa nagot annat an en pekare till tecken eller flyttal.

Pekarvariabler deklareras pa ett lite underligt satt:

int *ip;

float *flytref;

char *tpek;

Denna deklaration ger oss en heltals-, en flyttals- och en teckenpekarvariabel. Asteris-ken "hor till" det deklareradevariabelnaninet. Skall man deklarera flera pekarvariablerskall det vara en asterisk pa vaije namn

int *ip, *ip2, i3, 14;

Denna deklaration ger oss foljaktligen tva heltalspekarvariabler och tva heltalsvariab-ler. Asterisken skall dock inte vara med nar vi anvander pekarvariabeln:

13

ip

78;

&13;

Dessa satser kommer att tilldela heltalsvariabeln vardet 78 och satta pekarvariabeln ipatt peka pa variabeln 13. Efter tilldelningen kommer det att se ut som i figur 7.2.

&i3

ip:

Figur 7.2

Notera att ip bar plats for en pil till ett heltal, medan 13 har plats for ett heltal och attdet aldrig ar pa nagot annat satt.

132 ) Studentlitteratur

Page 136: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

7.1 Pekare och adresser

Pekarvarden kan naturligtvis komma fran en annan pekare likaval som fran en vanligvariabel med adressoperatom &. Satsen

ip2 ip;

satter ip2 att peka pa sammasak som ip. Det ar pilen som tilldelas, inte det den pekarpa. Detta illustreras i figur 7.3.

ip2:

Figur 7.3

Hittills bar vi inte brytt oss om det som ligger dar pekaren pekar. For att fa tag i detanvander vi oss ater av asterisken:

14 = *ip;

Vi tar vardet av ip (adressen till 13 sedan tidigare), applicerar ^-operatom som da gervardet 78 vilket tilldelas 14. Vi far da situationen i figur 7.4. Notera att inga pilar and-ras vid tilldelningen.

ip:

ip2:

78

i4: 78

Figur 7.4

Det ar viktigt att forsta att asterisken anvands pa tvd olika sdtt ihop med pekare: delssom markor for pekarvariabel i en deklaration, dels som operator i uttryck for att fa detsom ligger dar dit pekaren pekar.

Precis som vanliga variabler blir inte pekare automatiskt initierade nar de deklareras.Man vet inte vart de pekar. Innan man anvander en pekarvariabels varde eller anvander

) Studentlitteratur 133

Page 137: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

7. Pekare ochfait

vardet som ligger dar denpekar maste pekarvariabeln saledes ges ett varde. Om manuttryckligen villmarkera attenpekare inte skall peka panagonting alls finns detforde-finierade pekarvardet null somkantilldelas vilken pekare somheist:

ip = NULL;

Vikommer att visa NULL-pekarvardet somett kryss sa somvisasi figur7.5.

Figur 7.5

Om man trots allt forsoker gora operationer pa det som ligger dar en NULL-pekarepekar, ar resultatet odefinierat. Forsoker manandra vardetfarmani en operativsystem-miljo ofta ett minnesfelavbrott. Det ar dock inte specificerat i C vad som skall handa.

7.2 Fait och pekarvarden

Fait ger oss ett enkelt satt att lagra ett antal varden av samma typ under samma namn.Man kommer at de enskilda vardena genom att de ar numrerade med heltal. Dessa talbrukarkallas index och operationen att valja ut ett visst elementindexering. Fait fung-erar i stort sett som i andra programmeringssprakmen har en del ytterligare egenheteri C. Vi borjar med den del som ar "vanlig".

Fait deklareras genom att vi anger deras storlek i antal element:

int iflt[10], iflt2[10];

char buffert [ 1000] ;

Deklarationema ger oss tva heltalsfalt och ett teckenfalt. Storleksangivelsen maste fin-nas pa varje faltvariabelnamn.

7.2.1 Faltstorlek

Storleken pa ett fait maste vara angiven som en konstant heltal, dvs. kand vid kompile-ringen. Det ar till och med sa petigt att det inte ens duger med en const int:

#define SIZE 100 /* oftast i include-fil */

const int size = 100;

float fltl[size]; /* FEL, inte "tillrackligt" konstant */

float flt2[SIZE]; /* OK */

134 © Studentlitteratur

Page 138: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

7.2 Fait ochpekarvdrden

Skalet till denna egendomlighet ar att const int-variabeln kunde ha varit extern-deklarerad och initierad i en annan fil och i sa fall osynlig for kompilatom.

Det finns ett trick man kan anvanda cm man inte vill anvanda ett makro. enum-varden

ar "tillrackligt konstanta" for att anvandas som faltstorlek:

eniom { esize = 100 }; oftast i include-fil */

float fits [esize]; OK */

Notera att enum-typen inte ens fatt nagot namn, vi har ju inte tankt att defmiera nagravariablerav dennatyp,utan endastanvandavardetav esize. Fordelenmeddetta fram-for makron ar att det skots belt av kompilatom som har koll pa syntaxen, inte av pre-processom som bara blint byter ut text mot text.

C99 lyfter bort begransningen om konstant storlek pa fait men bara for sadana som ar |deklarerade i funktioner. Nar faltet val ar skapat kan man dock inte andra dess storlek.

void ftest ( int storlek )

{

float data[storlek]; // tillatet i C99

7.2.2 Faltindexering

Indexeringen av element startar alltid mQd index o. Det innebar att det sista elementethar ett indextal som ar ett mindre an storleken pa faltet. Faltet ifit ovan har alltsa tioelement: ifit [o] t.o.m. ifit [9]. Man kan inte ha fait med annan undre indexgrans an

0. Indexering gar till pa vanligt satt med hakparenteser:

iflt[8] = 732;

buffert[iS + 1] = 'q';

Man kan naturligtvis ha godtyckliga uttryck som indexvarde, bara de vid berakningenger ett heltal som ligger inom faltets storlek.

Att arbeta med fait innebar ofta att man vill gora nagot med vaije element i faltet ochda anvands vanligen for-loopen for att lopa igenom hela faltet.

/* satt alia element i faltet till 0 */

for(int i = 0; i<10; i++) {

iflt[i] = 0;

}

Vad bander nu om man skulle raka indexera med ett varde som ar storre an faltstorle-

ken? Far vi en felutskrift? Nej, i C overlats detta ansvar pa programmeraren. Vill manha en indexkontroll far man belt enkelt lagga in en test av indexuttrycket innan indexeringen av faltet och vidta lamplig atgard om indexet ar felaktigt. Att C inte lagger in

© Studentlitteratur 135

Page 139: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

7. Pekare ochfait

kontroller avindexgranser beror paattdet i detgenerella fallet ar omojligt attkontrol-leragransema vid indexeringsoperationen. Vikommer att forsta dettanedan, avenomdet i fallet ovan ser ut som om det vore enkelt. Skalet ar ocksa att man i C bar som

princip att inte behova betala for sadant man inte bar bett om ocb eftersom indexkon-troll kostar lite tid, ar den inte inford som standard.

Menvad hdnderda om man indexerar fel? Enligtstandarden ar resultatet odejinierat,d.v.s. man vet inte generellt vad som bander. Vad som kan banda ar till exempel attmanfardetvarde somskulle ba legat darom faltet badevaritsastort. Enannan mojlig-bet ar att man bar indexerat sa mycket fel att det inte ens finns minnespositioner ditmanrefererar ocb da far mannormalt felavbrott i en skyddad operativsystemmiljo ocbnagot annat konstigt bander i en naken processormiljo.

Det som ar speciellt med fait i C ar anvandningen av faltnamnet ensamt. I praktiskttaget alia fall ar det inte ett namn pa bela faltet som man skulle kunna tro utan ettpekarvarde som pekar till det forsta elementet. Detta ar mycket viktigt att forsta ocbillustreras i figur 7.6.

Figur 7.6

Faltnamnen ar alltsa namn pa pilama, pekarvardena. Det innebar vidare att faltnamneninte ar namn pa variabler ocb man kan alltsa inte tilldela fait till varandra. Det vore lika

befangt som att forsoka tilldela ett beltalsvarde till ett annat:

iflt = iflt2;

34 = 71;

/* FEL */

/* FEL */

(Egentligen ar det sa att faltnamnet faktiskt dr namn pa bela faltet men att det i de allrafiesta fall, ett viktigt undantag ar som operand till sizeof-operatom, omvandlas till ettpekarvarde som pekar till forsta elementet.)

Vi kan daremot tilldela ett faltnamns varde till en pekarvariabel

ip = iflt;

vilket innebar att vi far situationen i figur 7.7.

136 © Studentlitteratur

Page 140: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

73 Aritmetikpa pekarvdrden

ifit

ip:

Figur 7.7

13 Aritmetik pa pekarvardenVi ska nu se vad indexering egentligen ar for en operation. Vi utgar fran figur 7.7. Hargaller vardelikhetema

ip == iflt

ip == &ifIt [ 0]

*ip == iflt [0]

d.v.s. vardet av ip ar samma sak som ett pekarvarde till borjan av faltet (vilket ifltar). Detta ar naturligtvis samma sak som adressen till element nummer o i faltet. Densista likheten uttrycker att det som ip pekar pa ar samma sak som innehallet i elementnummer o i faltet. Vi kan faktiskt anvanda ip och ifit pa precis samma satt, anvandaderas varden pa samma satt med enda undantaget att ip skulle kunna sattas att peka panagot annat medan ifit alltid pekar till samma sak. Foljande likheter galler salundaocksa alltid:

ip[0] == iflt[0]

ip[l] == iflt[l]

Indexering utfors alltsa alltid pa ett pekarvarde. Om det kommer fran ett faltnamn elleren pekarvariabel eller eventuellt nagon annanstans ifran spelar ingen roll. Naturligtvismaste pekarvariabeln ha satts att peka pa objekt av den typ den ar deklarerad att pekapa innan man kan indexera pa den.

Nu infor vi nagot nytt. Vi kan gora aritmetik med pekare, addera och subtrahera heltaltill/fran dem, till exempel:

ip + 2

For en assemblerprogrammerare skulle detta innebara adressen till en minnescell tvasteg langre fram i minnet. I C innebar det adressen tva heltalspositioner langre fram iminnet, eftersom ip ar en heltalspekare. Man behover inte som i assembler halla redapa hur stort ett heltal ar.

) Studentlitteratur 137

Page 141: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

7. Pekare ochfdlt

Nukan vi sevadindexeringsoperationen egentligen ar. Standarden sager namligen att

ip[2] == *(ip + 2)

iflt [3] == * (iflt + 3)

eller generellt

uttryckl[uttryck2] == * (uttryckl + uttryck2)

d.v.s. indexering innebaratt man laggertill heltalsindexet till pekarenoch sedantar detsom ligger dar med *-operatom. Nu ser vi varfor det ar viktigtatt man bara pekar paheltal med en heltalspekare.

7.4 Pekarstegning i stallet for indexeringEn vanligt forekommande anvandningav pekare ar att stega igenom ett fait med den.Lat OSS ta ett exempel dar vi vill summera flyttalen i ett flyttalsfalt pa tva satt, forstmed vanlig indexering

/* summera talen i ett fait */

int i;

float summa, fflt[1000];

/* har finns kod som ger fflt varden */

summa = 0.0;

for (i = 0; KIOOO; i + +) {

summa += ffIt [ i] ;

}

och sedan med pekarstegning

int i;

float summa, fflt[1000], *fp;

/* har finns kod som ger fflt varden */

summa = 0.0;

fp = fflt; /* satt fp att peka pa 0:e elementet i fflt */

for (1 = 0; KIOOO; i + +) {

summa += *fp;

fp++; /* flytta fp att peka pa nasta element i fflt */

}

Vinsten med att stega en pekare ar att vi slipper en multiplikation i varje varv. For attrakna ut minnespositionen maste processom multiplicera index med elementstorlek,aven om det inte syns i programkoden. I fallet med pekarstegning adderas bara en elementstorlek till pekaren i varje varv.

De tva radema i f or-satsen kan skrivas samman pa ett mycket vanligt satt

138 © Studentlitteratur

Page 142: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

7.5 Pekarvdrden som parametrar

summa += *fp++;

Detta kraver en liten utredning. Forst okas fp med i men eftersom ++ star efter varia-beln ar vardet av operationendet gamla vardet av fp. Pa detta gamla varde applicerarvi *-operatom och far det utpekade vardet. I nasta vary pekar nu fp pa nasta elementoch procedurenupprepas.Efter sista okningen kommer fp att peka strax utanfor faltet.Det gor ingentingeftersomvi da bar gatt ur for-satsen och inte forsoker ta det som lig-ger dar.

Vi skulle ocksa ha kunnat avvara stegvariabelni. I stallet kunde vi testat pa pekarvari-abeln och stegat fram den i for-satsen. Da hade det sett ut sa bar:

for (fp=fflt; fp<&ff1t[1000]; fp++) {

summa += *fp;

}

Det ar mycket vanligt att uttrycka sig sa bar i C. Vi later pekaren stegas tills den naradressen till det forsta elementet utanfor faltet (men anvander inte det som finns dar).

7.5 Pekarvarden som parametrar

I C sker som tidigare papekats parameteroverforing alltid som vardeanrop. Den anro-pande fimktionen skickar alltid med ett vdrde som i den anropade fiinktionen laggs iparametem. Det blir da en kopia av det medskickade vardet. Om vi nu vill lata en flmk-tion andra pa en parameter sa att det marks bos den anropande ar alltsa detta omojligt.Vi vill anda pa nagot satt kunna gora detta. Lat oss skriva en funktion som okar en hel-talsvariabel med 3. Om vi anropar den for att oka vardet pa heltalsvariabeln i med 3kan vi alltsa inte skriva

plus3(i); /* FEL */

Hur funktionen pius3 an ar defmierad kan den inte andra pa i eftersom det somskickas som parameter till plus3 bara ar vardet av i. Vad vi skall gora i stallet ar attskicka ett pekarvarde som pekar ut i.

p 1 u s 3 ( &i ) ;

Funktionen ar definierad pa foljande satt:

/* funktion som adderar 3 till en intvariabel */

void plus3(int *iref)

{

•^iref = *iref + 3;

Som parameter till plus3 skickar vi alltsa ett pekarvarde (vi skickar alltid ett vdrde)som pekar till variabeln i i den anropande funktionen. Funktionen plus3 lagger en

© Studentlitteratur 139

Page 143: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

7. Pekare ochfait

kopiaav dettapekarvarde i den lokala pekarvariabeln iref. Genom att addera 3 till detvarde som finns dit iref pekar och sedan tilldela summan dit iref pekarbar vi okatvariabeln i i den anropande funktionen med 3. Funktionen plus3 bar ingen aning cmvart iref pekar cob bar naturligtvis ingen mojligbet att ta reda pa att det var just i. Detar den anropandes uppgifl att se till att det finns ett beltal dar parametem pekar. Sefigur 7.8.

den anropande funktionen funktionen plus3

iref:

Figur 7.8

Detta ar givetvis forklaringen till att &-operatomanvands i anropet till scanf for inlas-ning av beltal:

scanf ( "%d", &i) ;

For att scanf skall kunna ge i det inlasta vardet maste vi skicka en pekare som pekartill i ocb inte vardet av i.

Om vi anropar en flinktion med ett ensamt faltnamn som parameter ar det foljaktligenett pekarvarde som skickas. Vi tar ett exempel med sortering av ett beltalsfalt med"bubbel-sortering" (eng. bubble sort). Denna metod gar ut pa att vi gar igenom belafaltet gang pa gang. Vi jamfor bela tiden par av intilliggande varden ocb byter plats padem om de ligger i fel ordning. Pa detta satt kommer talen att "bubbla upp" till sin rattaplats. Nar inget byte skett under en genomgang ar det fardigt:

140 ) Studentlitteratur

Page 144: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

7.5 Pekarvdrden som parametrar

#include <stdio.h>

/* las in, sorters och skriv ut 20 heltal */

void sortera(int f[20])

int i, bytt, temp;

do {

bytt = 0;

for (i=0; i<19; i++) {

if (f [i] > f[i + 1] ) {

temp = f [ i] ;

f[i] = f[i + 1] ;

f[i+1] = temp;

bytt = 1;

while (bytt);

}

int main ()

{

int iflt[20];

/* las in varden till iflt */

sorters(ifIt); /*inte &iflt, iflt ar redan ett pekarvarde */

/* skriv ut iflt */

Som parametertill sorters ges ett pekarvarde.Trots att f tydligt ar deklareradsom ettfait blir det inte nagot utrymme for faltet deklarerat utan f dr endast enpekare. Efter-som C inte kontrollerar indexgranserar informationenom storlekendessutomoverflo-dig. Man skulle lika gama ha skrivit

void sorters(int f[])

eller

void sorters(int *f)

Detta ar dock speciellt for fait som parametrar. I vanliga variabeldeklarationer masteman alltid ange storleken i ett fait och en pekarvariabel maste deklareras som en sadan.

Inget av de tre skrivsatten talar om for sorters hur manga tal det finns i faltet och detfinns inget satt for sorters att ta reda pa det. Som funktionen ser ut kan den bara sor-tera fait med 20 heltal. For att funktionen skall kunna sortera ett godtyckligt stort faitmaste vi skicka med storleken som en separat parameter:

) Studentlitteratur 141

Page 145: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

7. Pekare ochfdlt

/* sortera angivet antal heltal i ett fait */

void sortera(int f[], int n)

{

int i, bytt, temp;

do {

bytt = 0;

for (i=0; i<n-l; i++) {

if (f[i] > f [i +1]) {

temp = f [ i] ;

f[i] = f[i + 1] ;

f[i+l] = temp;

bytt = 1;

} while (bytt);

}

Anropet av funktionen skall nu naturligtvis se ut sa har:

sortera (iflt,20);

11 C99 firms fdltuttryck, ett satt att skrivafdlt-konstanter direkt. Sadana konstanter kanskickas till fimktioner somforvantar sig ett fait somparameter. I stallet for att anropamed ett faltnamn eller en pekare till faltet kan vi alltsa ange ett fait direkt.

/* skriv ut ett fait av n heltal */

void printfIt (int fit [ ], int n) {

for (int i = 0; i<n; i + + ) {

printf("%d fit [i]);

}

}

int main ()

{

/* skicka med fait som direkt faltuttryck */

print fit ( (int[4] ){3,32,8,12}, 4);

7.6 Pekare och const

Det reserverade ordet const betyder "nagot vi inte far andra pa" och det far en lite spe-ciell betydelse i samband med pekare. Vi kan ju dels vilja forhindra att en pekare and-ras, dels det den pekar ut. Lat oss borja med det sistnanmda:

142 © Studentlitteratur

Page 146: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

7.6 Pekareoch const

int i = 56; /* vanligt heltal */

const int ci = 98; /* oandringsbart heltal */

int *ip; /* vanlig heltalspekare */

const int *cip; /* pekare till oandringsbart heltal*

ip = &i; /* OK, sjalvklart */

cip = &ci; /* OK, eftersom *cip inte far andras */ip = &ci; /* FEL, eftersom *ip far andras */

cip = &i; /* OK, men man kan inte andra i via cip */

Egentligenar det sista raden som ar mest intressant: vi kan peka ut ett objekt som i ochfor sig gar att andra pa (har variabeln i), men vi far inte andra pa det via pekaren.Pekare till const int betyderalltsa inte att vi bara far peka ut int-konstanter, utan vil-ken int som heist, fast vi "lovar" att inte andra pa den. Detta ar mycket anvandnings-bart nar vi skickar pekare som funktionsparametrar:

/* funktion som summerar ett heltalsfalt med n heltal */

int sumflt ( const int *flt, int n )

{

int i, sum = 0;

for (i = 0; i < n; i++) {

sum += f11 [ i] ;

}

return sum;

Nar vi skickar ett heltalsfalt som forsta parameter till funktionen sumflt, skickas baraen pekare till funktionen, precis som i foregaende avsnitt med sorteringsfunktionen.Dar skulle vi sortera faltet och behovde kunna andra pa de utpekade heltalen i faltet.Funktionen sumflt skall inte andra pa talen utan bara lasa av dem. Genom att const-

deklarera pekaren, "lovar" sumflt att inte andra pa heltalen i faltet.

Att anvanda const pa detta satt har tre fordelar:• lasbarheten okas genom att man inte behdver lasa igenom koden i sumflt for att

kontrollera om nagon andring gors, det racker med att titta pa flmktionshuvudet.• sakerheten okas genom att kompilatom kontrollerar att vi faktiskt inte gdr nagon

andring av det som fit pekar pa och endast skickar fit vidare till sadana funktio-ner som ocksa har lovat att inte andra.

• kompilatom kan i manga fall generera effektivare kod nar den kan rakna med attnagot inte andras.

Man kan aven deklarera pekare som inte kan andras, fast dessa ar inte sa vanliga.

int ifIt [10] ;

int *const icp = iflt; /* konstantpekare maste initieras */

*icp = 42; /* OK */

icp++; /* FEL, pekaren ar oandringsbar */

© Studentlitteratur 143

Page 147: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

7. Pekare ochfait

7.7 Initiering av faitMan kan initiera ett helt fait pa samma gang. Vardena som man initierar med maste davara konstanta uttryck.

Vi anger initieringsvardet som en lista av varden med klamrar runt:

float tabell[7] = { 20.3, -3.0, 5.2, 67.1, 12.1, 1.1, -34.4 };

Vi behover inte skriva faltets storlek men gor vi det far det inte ange farre an initie-ringsuttryckets antal element:

const int mdagar[] = { 31, 28, 31, 30, 31, 30,

31, 31, 30, 31, 30, 31 };

/* januari har nummer 0 har */

int buf [ 1000] ={3,4,6};

/* 997 oinitierade heltalselement */

IMan kan bara utelamna initieringsvarden fran slutet. IC99 kan man dock anvSnda s.k.bendmnda initierare fbr att ange precis vilka platser i ett fait som skall initieras.

int fl[100] = { [16] = 42 }; // plats 16 far vardet 42

int f2[20] = { 56, 81, [7] = 88, 76 };

Det gar att blanda benamnda initierare med det gamla sattet att initiera, sa i faltet f 2initieras platsema 0, 1, 7 och 8 med angivna varden

7.8 Teckenfalt och textstrangarFait av tecken flmgerar precis som andra fait, men nar de anvands for att lagra textstrangar, d.v.s. en foljd av tecken fmns det en del extra saker att tanka pa. Eftersom ettfait har fast langd maste de textstrangar vi forvarar i faltet alltid vara kortare an faltet.Vi maste dessutom ha ett satt att markera langden pa strangen. En textstrang represen-teras i C av en pekare till det forsta tecknet i strangen och dess slut markeras med teck-net \o efter sista tecknet i strangen. Det finns alltsa inte nagon uttrycklig langdangivel-se nagonstans. Att ett nolltecken utgor slutmarkering innebar naturligtvis att dettatecken inte kan inga i textstrangar, vilket i allmanhet inte utgor nagon begransning.

Att initiera att teckenfalt med en strang gors enligt:

char namnflt[100] = { 'B', 'e', 'r', 't' , ', '1' , '\0' };

/* otympligt!! */

Det finns ett forkortat skrivsatt for detta:

char namnflt[100] = "Bertil";

Bada ser ut precis som i figur 7.9.

144 © Studentlitteratur

Page 148: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

namnfit

7.8 Teckenfdlt och textstrangar

B e r t i 1 \0

0 1 2 3 4 5 6 7 98 99

F/gwr 7.9

Notera att slutmarkeringen, nolltecknet, inte behover anges vid skrivsattet med cita-tionstecken. Faltet maste dock givetvis ha plats for det. Om vi hade latit bli att angestorlek pa faltet hade detblivit precis lagom stort for strang inklusive slutmarkering:

char namnfit| = "Bertil";

Vid initiering av teckenfalt lagger kompilatom direkt in tecknen vi angivit i faltet.Tecknen i "Bertii" finns ingen annanstans.

Om en textstrangskonstant inte star som initieringsvarde till ett teckenfalt bar den enannaninnebord. Da lagger kompilatom tecknen i minnetpa lampligt stalle och vdrdetav textstrangen ar enpekare till forsta tecknet. Dettapekarvarde kannaturligtvis tillde-las en pekarvariabel eller skickassom parameter. Har kommer ett exempel:

#include <stdio.h>

/* skriv ut strangar */

void skrivstr(const char *tp) /* parametern ar charpekare */

{

while ( *tp != '\0'){ /* sluta vid nolltecknet */

putchar (*tp ++) ; /* stega pekaren, tag tecknet (gamla */} /* pekarvardet), ge till putchar */

}

int main()

char *tpek;

tpek = "Bertil";

skrivstr ( "Hej ");

skrivstr(tpek);

/* lat tpek peka strangen */

/* parametern ar en pekare */

/* parametern ar en pekare */

Observera att det inte gdr nagot att vi stegar ivag med pekaren tp. Den ar en lokalkopia av vardet som skickades vid anropet och kan inte alls paverka tpek i main.

Eftersom det ar vanligt att skriva ut strangar finns det en standardflinktion puts, somfungerar pa samma satt som var funktion skrivstr.

) Studentlitteratur 145

Page 149: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

7. Pekare ochfdlt

Detar viktigt att skilja pa en pekare till en torn strang ochen NULL-pekare. Att skickaen NULL-pekare till en fiinktion somforvantar sigen strang ledertill fel eftersom fimk-tionen forvantar sigattkunna ta varden darpekaren pekar. Vi serpaettexempel:

char *nullpekare = NULL, *tomstr =

Det ser ut som i figur 7.10.

nuilpekare:

tomstr:

Figur 7.10

Vi tar ett exempel till for att belysaskillnaden mellan teckenfalt och teckenpekare. Vihar deklarerat foljande p& global niva:

char *namnpek = "David";

char namnflt[] = "Erik";

Se figur 7.11.

namnpek:

namnfit

D a V i d \0

E r i k \0

Figur 7.11

Pekaren namnpek ar en pekar-som skulle kunna peka pa vilket tecken somheist. Om vi tilldelade namnpek ett nytt varde skulle textstrangen med David i varaomojlig att fa tag pa igen, den har inget eget namn. I det andra fallet ar namnfit ett falt-namn och kan inte sattas att peka pa nagot annat eftersom det ar ett konstant pekarvar-de. Trots detta kan vi anvanda namnpek och namnfit likadant i uttryck: indexera paeller skicka som parameter eftersom bada ger ett pekarvarde vid evaluering.

146 ) Studentlitteratur

Page 150: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

7.8 Teckenfdltoch textstrdngar

Vi skriver en funktion for att mata ut ett hexadecimalt siffertecken givet ett hexadeci-malt ental som parameter:

/* skriv ut en hexadecimal siffra */

void hexsif(int i) /* forutsatt att 0 <= i <= 15 */

{

static const char hsiftab[] = "0123456789ABCDEF";

putchar(hsiftab [i] ) ;

}

Lika bra vore:

void hexsif(int i) /* forutsatt att 0 <= i <= 15 */

{

putchar("0123456789ABCDEF" [i] ) ;

}

Strangens varde ar en pekare till borjan pa sig sjalv och pekare gar bra att indexerapa.

Vi tar ocksa ett exempel med kopiering av textstrangar:

char utrymme [ 100] ;

char ^original = "text som skall kopieras";

char *kopia;

Den forsta falla man skulle kunna ga i ar att tilldela pekaren:

kopia = original; /* sprakligt korrekt men INTE vad vi ville */

Denna tilldelning gdr att kopia nu pekar dit original pekar, d.v.s. till samma strang.Vi far ingen kopia av strangen. Nasta falla: vi tar det som pekarenpekar pa med *-ope-ratom:

*kopia = ^original; /* EEL, och dessutom inte vad vi menade */

Pekama pekar inte pa hela strangamautan bara pa forsta tecknet. Dessutompekar intekopia nagonstans an (vi antar att vi inte gjort forra exemplet) och vi kan inte lagganagot alls dit. Vi skulle troligtvis fa avbrott for minnesfel. Losningen blir att vi masteanropa en funktion som utfor sjalva kopieringen och innan dess maste vi se till attkopia pekar pa en ledig plats

kopia = utrymme;

strkopia ( kopia, original);

Funktionen strkopia maste kunna lita pa att den forsta parametem pekar pa tillrack-ligt med utrymme for att fa plats med den strang den andra parametem pekar pa. Detfinns inget satt for funktionen att ta reda pa om det verkligen finns plats. Lat oss nuskriva strkopia:

) Studentlitteratur 147

Page 151: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

7. Pekare ochfait

/* kopiera strangen ori till kop */void strkopia (char *kop, const char *ori)

{

while( *kop++ = *ori++ )

I varje varvstegas badapekama fram, *-operatom appliceras pa degamla vardena, till-delningen gors och testen sker pa det tilldelade vardet. Vardet blirnoil narstrangen tarslut. Nolltecknet maste ocksa kopieras mendethinner goras innan testen i sistavarvet.

Inlasningsfunktionen scant kansom vi vetanvandas forinlasning av strangar:

char instr[100];

scanf ( "%s", instr) ;

Har finns inget &framfor instr eftersom det redan ar ett pekarvarde. scant bar ingenmojlighet att kontrollera att det verkligen finns utrymme dar parametem pekar. Detmaste anroparen se till.

printt kan skriva ut strangar och anvander da samma formatkod:

printt("Den inlasta strangen var: %s\n", instr);

Nar printt ser formatkoden %s forvantar den sig en pekaretill en strangsomar avslu-tad med ett nolltecken.

Vi har ovan bara studerat teckenstrangar av en-bytes-tecken char. For strangar avbredatecken wchar t gallerprecissamma regler, mende representeras av enpekare avtyp wchar t *, teckenkonstanter skrivs som L"bred strang" och de avslutas med ett

noll-tecken l' \ o'. Se mer i avsnitt 3.4.

Det finns en mangd standardfunktioner for att hantera textstrangar, av respektive typchar * och wchar t *. Vi aterkommer till dem i kapitel 10.

7.9 Flerdimensionella fait

Fait med flera dimensioner ar i C definierade som fait av fait vilket visar sig i deklara-tionen av dem. Lat oss se pa en tvadimensionell matris:

int matr[4] [ 3] ;

C ser detta som ett fait med 4 element som vardera ar ett fait med 3 element som ar

heltal. (Man far inte skriva int matr [4,3])

Initiering av faltet visar ocksa detta:

148 © Studentlitteratur

Page 152: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

7.9 Flerdimensionellafdlt

int matr[4] [3 ] = { { 0, 1, 2 },

{ 3, 4, 5 },

{ 6, 7, 8 },

{ 9,10,11 }

Nar man arbetar med flerdimensionella fait anvands ofta nastlade for-loopar:

/* skriv ut faltet, en rad per 3-ints-falt */

for (int rad = 0; rad < 4; rad++) {

for (int col = 0; col < 3; col++) {

printf("%3d ", inatr[rad][col]);

}

p r i n t f ( " \ n " ) ;

C kanner inte till nagot om rader och kolumner, sa man maste vara konsekvent sjalv.

Elementen hamnar definitionsmassigt i radvis ordning i minnet. Detta betyder ocksaatt man skulle kunna indexera fram ett belt fait (en pekare naturligtvis) genom att baraange ett index, och precis sa flmgerar det:

matr[2] /* ar faltet (pekare till l:a element) */

/* som innehaller 6,7,8 */

En bild av matrisen skulle se ut som figur 7.12.

matrmatr [2]

0 1 2 3 4 5 6 7 8 9 10 11

Figur 7.12

Nar en flerdimensionell matris ar en parameter till en funktion maste man ange storle-ken av dimensionema sa att funktionen som skall anvanda faltet kan indexera korrekt.

Den fbrsta dimensionen storlek behover dock inte anges:

void matrfunk(int m[] [3] [8] )

/•*

Funktionen matrfunk ser msom pekare till ett godtyckligt stort fait av treelements faitav attaelements heltalsfalt.

) Studentlitteratur 149

Page 153: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

7. Pekare ochfait

7.10 Fait av pekare samt pekare tillpekareKanvi ha faitav fait sa kanvi naturligtvis avenha faitmedpekare. Videklarerar:

char *rader[200];

Detta ger oss ett fait med 200 platser for pekare. Lat oss direkt se en anvandning avdem. Vi vill lasa in en text med 200 olika langarader och vill sedankunna kommaatradema var for sig via indexering. En metod vore att deklareraett tvadimensionellt faitoch lasa in en rad till varje delfalt. Lat oss gora detta:

#include <stdio.h>

/* las in rader till en tvadimensionellt fait */

int main ()

{

char buffert[200] [80];

int i;

for (i=G; i<200; i++) {

gets (buffert[i]) ; /* gets laser in en rad */

} /* dit parametern pekar *//* testa */

puts (buffert [ 30] ) ; /* skriv en rad */

putchar(buffert[50][2]); /* skriv ett tecken */

}

Metoden har tva negativa egenskaper, bMa harrorande fr^n det tvadimensionella fal-tet: dels kan ingen rad vara langre an 80 tecken (79 plus sluttecken), dels orsakar aliarader kortare an 80 tecken ett sloseri med utrymme. Lat oss gora en ny variant dar vibattre tar vara pa utrymmet:

#include <stdio.h>

/* las in rader till ett endimensionellt fait */

int main()

{

char buffert[8000];

char *rader[200];

char *aktpos = buffert;

int i;

for (i=0; i<200; i++) {

gets(aktpos);

rader[i] = aktpos;

aktpos += strlen (aktpos) + 1;

}

puts ( rader[30] ) ; /* testa */

putchar(rader [ 50] [2]);

150 © Studentlitteratur

Page 154: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

7.10 Fait avpekare samtpekare tillpekare

I for-snurran satter vi det i:te elementet i rader till aktuell position. Denna positionflyttas sedan fram sa manga steg som strangen ar lang plus ett. Standardfunktionenstrlen beraknarstranglangden (exklusive nolltecknet). En bild av situationen nar pro-grammet befinnersig i slutetpa snurran efler tredjevarvetvisas i figur 7.13.

aktpos:

buffert

Figur 7.13

Vi bar vunnit tre saker: langre rader tillatna, inget sloseri vid korta rader (vi flyttarfram aktpos till att peka pa nasta lediga tecken i buffert direkt efter foregaende rad)samt en snabbare indexering (kompilatom behover inte multiplicera ihop faltlangdmed index, utan bara folja pekare). Notera att vi kan gora precis samma sorts indexering pa ett fait av pekaresompa ett tvadimensionellt fait. Den forsta indexeringen val-jer nu ut ett pekarvarde som det andra indexetappliceras pa. Indexgranser ar dock fort-farande vart eget bekymmer, och vi maste lita pa att det totalautrymmeti buffert intetar slut. I ett riktigt program skulle vi naturligtvis kontrollera bMa.

Lat OSS skriva en version som ar sakrare. Forst maste vi ersatta gets. Denna funktion

ar farlig, trots att den ar med i standarden. Eftersom den bara tar en pekare som parameter, finns det inget satt for gets att veta hur mycket minne som finns att laggatecken i. Vi maste alltsa hoppas att den inlasta raden inte ar for lang. Standardbibliote-ket erbjuder altemativet fgets, som bar tva extra parametrar vilka anger var vi skalllasa iffan samt det maximala antalet tecken som far lasas.

#include <stdio.h>

#define BSIZE 8000

/* las in rader till ett endimensionellt fait och kolla allt */

int main ()

{

char buffert [BSIZE] ;

char *rader[200];

char *aktpos = buffert;

char *slutpos = buffert+BSIZE;

) Studentlitteratur 151

Page 155: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

7. Pekare ochfait

int i;

for (i=0; i<200 && aktpos<slutpos; i++) {

if (fgets(aktpos, slutpos-aktpos, stdin) == NULL) {

break; /* om lasningen misslyckas eller EOF */

}

rader[i] = aktpos;

aktpos += strlen (aktpos) + 1;

}

if (i>50) {

puts(rader [ 50] ) ;

putchar(rader [ 50] [2]);

Har ger vi fgets antalet resterande lediga platser i buffert med uttrycket slutpos-aktpos och kollar dessutom att det verkligen gick att lasa med fgets. Vi avbiyterocksa for-satsen iiman200 rader lasts in om utrymmet i buffert tagit slut.

Initiering av pekarfalt ar vanligt vid tabeller med strangar i. Vi tar ett exempel somansluter till slumpmelodiprogrammet i avsnitt 3.7 dar vi nu indexerar direkt med upp-rakningsvardet i stallet for att testa igenom alia fall:

const char *notnamn[] = { "c "c# "d "d#

"e "f "f# "g

"g# "a "b "h "};

Kompilatom lagger strangama pa "lampligt stalle" och initierar pekarfaltet medpekama till borjan pa vaije strang. Nu kan vi slippa den nastlade if-satsen och skriva:

puts(notnamn[ny_ton]);

Antag att vi gor foljande anrop:

skrivton(diss, notnamn);

Funktionen skrivton maste da som forsta parameter ha ett upprakningsvarde och somandra parameter en pekare till en pekare till en char. Sa har ser fiinktionen ut.

/* skriv ut ton ur tabell */

void skrivton(enum ton t, const char **tontabell)

/* eller: const char *tontabell[] */

{

if (t >= c && t <= h) {

puts(tontabell [t] ) ;

}

else {

puts("felaktig ton ");

152 © Studentlitteratur

Page 156: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

7.11 Pekare till dynamiskt minne

7.11 Pekare till dynamiskt minneEn pekare "vet" aldrig vad denpekarpa. Om en pekare satts att pekapa heltalsvaria-beln i eller om den pekarmitt in i ett heltalsfalt kan inte skillnaden avgoras via peka-ren. Detta innebar att om vi kunde fa minne fran nagonstans, utan att ha deklarerat envariabel, och satt en pekare att pekadit, kan vi anvanda dettaminne precis somvilketannat minne som heist som vi har en pekare till. Minne som "skapats", allokerats, padetta satt brukarkallas dynamiskt minne eftersom man bMe kan allokera minne ochfrigora det efter behov.

C har i sjalva spraket ingen mekanism for att hantera dynamiskt minne, utan det farmangoravia funktioner: standardfunktioner ellersadana manskriver sjalv. Vi studerarett exempel pa hur standardfunktionema anvands.

Sag att vi vill skapaplats for antai stycken flyttal av typendouble. Da anropar vi enfunktion som allokerar minne:

calloc (antal, sizeof(double)) ;

Denna kommer att pa ledigt stalle i minnet bereda plats for flyttalen och retumera enpekare. Men vilken typ av pekare retumeras? caiioc skall kunna retumera pekare tillvilken typ av objekt som heist och vi har inget satt att tala om for funktionen att det arflyttalvi allokerar. Just for detta andamalfinnsen speciellpekartypvoid * (pekaretill"vadsomhelst"), som ar avsedd att betyda en pekartyp som kan konverteras till ochfran vilken pekartyp som heist, caiioc ar alltsa definierad att retumera denna typ. Vigor en explicit typomvandling innan vi tilldelar pekarvardet till en pekarvariabel(elleranvander det pa annat satt):

/* allokera plats for 100 double och lat dp peka dit */

double ^dp;

dp = (double *) caiioc (100, sizeof(double));

En bild av detta ser ut som i figur 7.14. Observera att minnet som pekaren pekar ut intehar nagot namn. Skulle vi satta pekaren att peka pa nagot annat blir detta minne oat-komligt. Notera att i alia tidigare fall har utpekat minne haft ett variabelnamn. Dynamiskt allokerat minne ar anonymt.

99

Figur 7.14

) Studentlitteratur 153

Page 157: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

7. Pekare ochfait

Notera att dp nu kan anvandas precis som ett fait av 100 stycken double. Eftersomcaiioc anropas nar programmet kors, behover inte storleken vara en konstant. Talet

100 kundelika gtoa ha varit ett vardefran nagonvariabel med vilketvardesomheist.

Omvi efterett tag inte langre behover dettaminne bor det aterlamnas. Dettagorsmedstandardfunktionen free!

free (dp);

Parametem dpandras inteav dettaanrop och trotsatt dpalltsafortfarande pekartill dettidigare anvanda minnet far vi nu inte anvanda det langre. Minnet kommer troligtvisatt anvandas till andra saker och om vi skulle anvanda det ar det odefinierat vad som

hander. Att glomma att lamna tillbaka allokerat minne och att av misstag fortsMa attanvanda aterlamnat minne ar vanliga fel som dessutom ar mycket svaraatt upptacka.Felens konsekvenser visarsig oftastintedirektutankanske en langtsenare ochkan davara nastan omojliga att knyta till orsaken.

En lika vanlig biblioteksfunktion for allokering av minne ar maiioc, som bara tar enparameter: antalet bytes som erfordras. Anvandaren far multiplicera sjalv:

dp = (double *) malloc(100 * sizeof(double));

Vi kommeratt ge fler exempel pa anvandning av dynamiskt minne i kapitel 8, dar vivisar vilka kraftfullaoch flexibla strukturersom kan byggas upp med hjalp av pekareoch dynamiskt allokerade poster som aven kan peka till varandra.

7.12 Pekare till funktioner

Precis pa samma satt som faltnamn utan indexering ar ett pekarvarde till faltet galleratt ett funktionsnamn utan parenteser effer ar en pekare till funktionen. Detta kan t.ex.anvandas till att skicka pekare till fimktioner som parametrar, ha fait av funktionspe-kare och att anropa funktionen i fraga.

Deklarationen av en funktionspekare ar inte helt trivial. Man maste ange exakt vilkensorts funktioner pekaren skall f^ peka pa: parametrar och returtyp m^ste alltid anges.Vi visar som exempel en funktionpekarvariabel som kan peka pa funktioner som baren double-parameter och som retumerar double:

double (*funkptr) (double);

Nar variabeln ar nydeklarerad pekar den inte pa nagon funktion. Har ar en fimktionsom funkptr skulle kunna peka pa:

double trippel (double x) {

return 3 * x;

}

154 © Studentlitteratur

Page 158: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

7.12 Pekare tillfunktioner

For att lata funkptr peka pa denna funktion tilldelar vi som vanligt. Notera att funk-tionsnamnet trippei utan parenteser betecknar en pekare till funktionen.

funkptr = trippei;

Nu kan vi anropa den funktion som funkptr for tillfallet rakar pekapa, just nu pekarden pa funktionen trippei.

double resultat;

resultat = funkptr (7.3);

Vi kan satta funkptr att peka pa en annan funktion, t.ex. sinusfunktionen fran stan-dardbiblioteket, som vi far tag i genom att inkludera math. h.

#include <math.h>

/* ... */

funkptr = sin;

I fortsMningen kommer nu anropav funkptr att anropa sin. Att det fungerar berorpaatt sin bar sammasignatur som trippei och deklarationen av funkptr. Noteraatt alltmaste stamma: parameterantal, parametertyper och returtyp.

Att skicka funktionpekare som argument till funktioner ar intressant, for da kan vipaverkaen funktionmed beteendefran en annan. I avsnitt 8.2.2ser vi ocksahur vi kanlagra beteende, i form av funktionspekare, tillsammans med data.

Lat OSS ta ett mer anvandbart exempel: Vi vill ha en fimktion som approximerar enintegral av en matematisk funktion f i intervallet a till b med hjalp av trapetsformelnmed n trapetser. (Integralen av en funktion ar arean mellan kurvan och x-axeln,begransad av a och b i sidled). Trapetsformeln beraknar en approximation till integralen genom att dela in ytan under kurvan i trapetser ("avskuma rektanglar") och sum-mera deras areor, se figur 7.15.

Vi skriver en funktion trapets som beraknar integralen:

/* approximera integralen av funktionen f i intervallet a,b

i n steg med trapetsmetoden */

double trapets(double a, double b,

double (*f) (double), int n)

{

int i;

double sum, h = (b-a)/n;

sum = f(a)/2 + f(b)/2; /* se nedan */

for (1=1; i<n; i++) {

sum += f(a + i *h) ;

}

return h*sum;

) Studentlitteratur 155

Page 159: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

7. Pekare ochfait

(X, f(x))

jI

Figur 7.15

Foljande anrop beraknar integralen av sin(x) fran 0 tillpi med 4 trapetser:

♦include <math.h>

/* ... */

resultat = trapets(0.0, 3.1415926535, sin, 4);

I deklarationen av parametem f inne i fiinktionen trapets maste *f sattas inom paren-tes, annars blir tolkningen att det ar en fiinktion som retumerar pekare till double. Vidanropen av f behdver man inte skriva (*f), men man far om man vill (i gammal C varman tvungen):

sum = (*f) (a)/2 + (*f) (b)/2; /* exakt samma som ovan */

Vilken variant man anvander spelar ingen roll: man kan tycka att det blir tydligare omman visar att funktionen anropas via pekare genom att anvanda det aldre skrivsattet.

7.13 Komplicerade deklarationer och typedefSyntaxen vid deklarationer dar pekar-, fait- och funktionstyper blandas ar inte latt attbena ut om man gor mer komplicerade saker. Lat oss se pa nagra exempel aven omman inte lar stota pa dem speciellt ofta:

© Studentlitteratur

Page 160: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

7.13 Komplicerade deklarationer och typedef

/'^ ovanliga kombinationer av fait, pekare och funktioner */char cff[5][5]; /* fait av fait av tecken */

char *cpf[5]; /* fait av pekare till tecken */char **cpp; /* pekare till pekare till tecken */char (*cfp) [5]; /* pekare till fait av tecken */char *cpF(void); /* funktion som returnerar

pekare till tecken */

char (*CFp)(void); /* pekare till en funktionsom returnerar tecken */

char (*cfpF(void))[5]; /* funktion som returnerar

pekare till fait av tecken */

char (*cpFf[5])(void); /* fait av pekare till funktionersom returnerar tecken */

Somen snabbhjalp kan man sagaatt () och [] har hogreprioritet an * ochatt det somstarnarmast namnet "gar forst". Andring av denna ordning gorsmedparenteser.

Oftast kan man skriva deklarationemabegripligare om man anvander typedef for attsattabeskrivande namnpa ett eller fleradelsteg av deklarationen. Lat oss ta den sista ilistan ovan. Borja med en typedef som satter namn pa typen "pekare till funktion somreturnerar char":

typedef char (*ptrToCharFunc) (void);

och sedan gor ett fait av sadana

ptrToCharFunc cpFf[5];

Nu ar det betydligt lattare att se att cpFf ar ett fait. Samt att detta fait innehaller funk-tionspekare.

Vi tar det nasta sista exemplet ocksa. Borja med att deklarera ett namn pa typen "faitav fem tecken":

typedef char fiveCharArray[5];

Sedan namn pa typen "pekare till ovanstaende":

typedef fiveCharArray *ptrToFiveCharArray;

Och till sista sjalva funktionsdeklarationen, funktionen som returnerar ovanstaende:

ptrToFiveCharArray cfpF(void);

7.13.1 restrict pa pekare

I C99 kan man lagga till det reserverade ordet restrict vid deklaration av pekare. Det |innebar att den som ger pekaren ett varde lovar att detta ar den enda pekaren som pekartill objektet. Ett sadant lofte ger kompilatom chans att optimera koden battre. Om lof-tet inte stammer kan dock odefinierade saker handa. For den vanlige programmeraren

© Studentlitteratur 157

Page 161: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

7. Pekare ochfait

ardet nog bast att undvika restrict, da det kan leda till valdigt egendomliga fel cmman inte ar alldeles sakerpa vad man gor. En del standardfimktioner anvander restrict och da maste man tanka efter. Ett exempel ar standardfimktionen memcpy somkopierar n bytes fran from till to.

void *memcpy(void * restrict to,

const void * restrict from, size_t n) ;

Vid anrop avmemcpy blir det alltsa anroparens ansvar att se till attde tva pekama intepekar pasamma minne, d.v.s. attomradet vikopierar fran inte alls overlappar omradetvi kopierar till. Detkan vara lurigt att levadetta ochmanga tycker att detta Srettkon-troversiellt tillagg till standarden.

7.14 Ovningsuppgifter1. Skriv enfunktion som vander pa tecknen i entextstrang med hjalp av tva pekare.

Denendaparametem till funktionen skall varaen pekare till strangen.

2. Tag redapa (och/eller prova) vadsombander om du i ditt system forsoker ta var-det som liggerdar en NULL-pekare pekar.

3. Skriv en egen funktion som kopierar strangar och som kontrollerar att inte formycket kopieras genom att storleken pa kopiefaltet skickas med som parameter.Hur bor man lampligentala om att det misslyckades?

4. Leta reda pa det minsta heltalet i en stor tredimensionell matris. Gor det med badeindexering ochpekarstegning ochse omdetblirnagon skillnad i tidsatgang.

5. Gor en funktion som givet ett dagnummer skriver ut motsvarande veckodags-namn. Anvand ett initierat fait av strangar.

6. Skriv en funktion som laser in en rad till ett tillrackligt stort fait, sedan allokerarett for radenlagom stortfait, kopierar radendit ochretumerar en pekaretill den.

7. Skriv en egen minnesallokeringsfunktion som delar ut minne fran ett stort tecken-falt. Testa ihop med uppgift 6.

8. Det finns en battre metod an trapetsformeln som heter Simpsonsformel. Tagredapa hur den fungerar, implementera och testa den.

9. Biblioteksfunktionen qsort sorterar fait. Den vill bland annat ha en funktionspe-kare som argument. Funktionen skalljamfora tva elementav sadantyp som finns ifaltet sa att qsort far veta i vilken ordning elementen skall sorteras. Las manualoch testa!

158 © Studentlitteratur

Page 162: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Sammansatta typer 8

Detta kapitel kommer att handla om typer dar man kombinerat ihop varden av olikatyp till en ny typ. De fiesta modema programmeringssprak barnagon mekanism forattgora detta oeh vitsen med det ar man far en battre overblick over sina program ochdatastrukturer. C bar utover det som brukar forekomma i programmeringssprak moj-ligbet att specifieera strukturer anda ned pa bitniva. Detta ar vasentligt om man villbeskriva data som man vill ba lagradepa ett visst satt eller kommunicera med bardvaradar enskilda bitar i ett register bar sin speeiella mening.

8.1 Poster (struct)

Nar vi diskuterade Mt sag vi att de ger ett satt att under ett namn bantera flera vardenav sammatyp. Vikundearbetamed de olikavardena genomatt indexera demmed bel-tal. Att vi slog ibop alia vardena till ett fait bade sin orsak i att det var naturligt ocbpraktiskt att kunna betrakta dem som en enbet.

Nu skall vi gora samma sak, men vi vill nu kunna sla ibop ett antal varden av olika typtill en enbet. En sadan sammanslagen typ kallas vanligtvis en post (eng. record) ocbmarkeras i C med det reserverade ordet struct. Vi satter lampliga namn pa delama. Vitar ett exempel:

struct bok {

char titel[50], forfattare[50];

int antal_sidor, pris;

float vikt;

struct bok bl, b2;

Vad vi nu gjort ar att forsta deklarera vad som ingar i en struct bok ocb sedan dekla-rera tva variabler bi ocb b2 av denna typ. Bada variablema ar av typen struct bok{bMa orden utgor typnamnet). De bar vardera fem delar: tva teckenfalt, tva beltal ocbett flyttal. For att komma at delama 1postvariablema anvands punktoperatorrr.

© Studentlitteratur 159

Page 163: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8. Sammansatta typer

bl.antal_sidor = 276;

printf("Boken heter %s och kostar %d kronor\n",

b2.titel, b2.pris);

Man kan inte gora alia tankbara operationer pa postvariabelvarden, det ar t.ex. intemeningsfullt av addera tva poster. De operationer som C tillater ar selektering (medpunktoperatom som ovan), tagande av adress till posten (&-operatom) och tilldelningav postvarden. Man far ocksa skicka poster somparametervarden och ge poster somreturvarde fran funktioner.

Nukommer vi tillenviktig skillnad gentemot fait: postvariabelnamn arriktiga variab-ler och inte bara en pekare till det forsta objektet som ar fallet for fait. Det garalltsabra att skriva:

bl = b2;

b2 = bearbeta (bl) ;

Detta innebar atthelapostvardet med aliaingaende delar kopieras, saval vidtilldelningsom vid parameteroverfbring, samt nar en hel post lamnas som returvarde.

Som vi settkanposter innehalla vilka andra typer som heist, aven faitochandra post-typer. Falten har kvar sina vanliga egenskaper. Betrakta t.ex. faltet titei:

bl.titel

Detta ar (som vanligt nar det galler fait) bara en pekare till det forsta tecknet i faltet. Enbild av posten ovan skulle kunna ritas som i figur 8.1.

titel

forfattare

bl:

antaLsidor pris: vikt:

Figur 8.1

Det kan mycket val finnas "oanvanf' utrymme i posten eftersom manga processorerkraver att vissa datatyper bara far finnas pa speciella minnesgranser. (Det lar i alia fallintebli sa myckettomrumsomdet ser ut i figuren.) Det finnsnaturligtvis bara plats fordata i posten och inte nagon information om vad postens delar heter.

160 ) Studentlitteratur

Page 164: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8.1 Poster (struct)

Vi ser att faltnamnen endast ar pekarpilar, medan de delama som bar andra vardenfungerar som "vanliga"variabler. En inlasning med scanf visar dettapa ett bra satt:

scanf("%s %s %d %d %f", bl.titel, bl.forfattare,

&bl.antal_sidor, &bl.pris, &bl.vikt);

Parametrama bi. titei och bi. forfattare skall inte ha nagot &eftersom de redan arpekare. Precis somnar det gMler upprakningar behover manbara ange beskrivningenav delamai postenen endagang. Vikan alltsadeklarera flera variabler genom attbaraange typnamnet. Denfulladeklarationen av posten maste dockfinnas nagonstans tidi-gare i texten:

struct bok b3, b4, bf[100];

Har ser vi att vi ocksa kan deklarera fait av poster, bf blir ett fait med hundra bokpos-ter. Lat oss ta ett komplett exempel som laser in data till tio person-poster och sokerratt pa och skriver ut posten med den aldsta personen. Lat oss borja med inledandedeklarationer:

#include <stdio.h>

struct person {

char namn[100];

int alder;

} ;

#define NPERSON 10

Varje person-post innehaller ett teckenfalt for namn och ett heltal for att betecknaalder. Huvudprogrammet ser ut sa bar:

/* las in personposter och skriv ut den aldsta */

int main()

{

struct person inperson(void);

void utperson(struct person p);

struct person p [NPERSON];

int i, aldst;

for (i=0; i<NPERSON; i++) {

p[i] = inperson();

}

aldst = 0;

for (i = 0; KNPERSON; i + + ) {

if (p[i].alder > p[aidst].alder) {

aldst = i;

utperson(p[aldst]

}

) Studentlitteratur 161

Page 165: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8. Sammansatta typer

Vi gar igenom faltet tv^ ganger med indexering. I forsta genomgangen tilldelar vivaijefaltelement, som ar en post, det postvarde som inperson retumerar. Innan andragenomlopningen borjar vi med att anta att person nummer o ar aldst. Vi jamfor sedanaldem med samtliga posteroch sparar indexet till den aldstapersonen vilken vi sedanskriver ut medfiinktionen utperson. Lat ossse pa in-ochutmatningsflmktionema:

/* las in en personpost och returnera den */

struct person inperson(void)

{

struct person p;

scanf("%s %d", p.namn, &p.aider);

return p;

}

Funktionen inperson retumerar varden av typ struct person, d.v.s. hela postvardet.Det ar intenagonpekaretill p som retumeras utan en kopia av helapostvardet.

/* skriv ut en personpost som aidst */

void utperson(struct person p)

{

printf ( "Personen %s, %d ar, ar aidst\n",p.namn, p.aider);

}

Denna funktion far som parameter hela postvardet som laggs i p och sedan skrivsdelama ut.

Om vi funderar lite upptacker vi att inlasningen i inperson sker till en lokal variabel,vars varde retumeras och kopieras in i faltet p i huvudprogrammet. Pa liknande sattkopieras vardet av posten till utperson for utskrift. Bada dessa kopieringarkanns liteonodiga eflersom vi har inte har nagon nytta av en kopia. Kopieringama kunde tamycket tid om de varit stdrre och om vi behandlade manga poster. Vi skall strax se hurvi kan undvika kopieringama med hjalp av pekare.

IC99 definierar ett satt att direkt uttrycka struct-varden sa att vi t.ex. kan skicka enstruct person som argument till fimktionen utperson utan att behova anvanda en

struct-variabel.

utperson((struct person){ "Bertli", 42});

8.1.1 Organisation av struct-deklarationer

Eflersom en struct-deklaration normalt skall anvandas pa fler stallen i olika filer ardet absolut vanligast att deklarationema placeras i #inciude-filer, tillsammans medfunktionsdeklarationer och sadant som alia behover kanna till. Denna inkluderas sedan

av alia filer som behover veta vad en person ar och vad som gar att gora.

162 © Studentlitteratur

Page 166: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

/* I filen person.h */

struct person {

char namn[100];

int alder;

};

#define NPERSON 10

void utperson(struct person p);

struct person inperson(void);

8.1 Poster (struct)

Om en struct-deklaration bara skall anvandas en enda gang, kan man deklarera vari-abler direkt ihop med struct-deklarationen. Da behover man inte heller satta nagotnamn pa sjalva struct-typen.

struct { /*inget namn pa struct-typen har */

char namn[ICQ];

int alder;

} unik; /* variabeldeklaration */

Detta skrivsatt skall forstas inte anvandas i #inciude-filer i vilka man inte bor ha nagra

defmitioner av variabler. Skrivsattet ar ganska ovanligt, men anvands ibland nar enstruct eller union ar nastlad i en annan struct, eller nar typedef anvands ihop medstruct. Se exempel nedan i avsnittet om union respektive typedef.

8.1.2 Initiermg av poster

Precis som fait kan poster initieras nar de definieras. Man anvander ocksa samma syntax, en lista med klamrar. Vi anvander bokexemplet ovan:

struct bok bokl = { "C struct secrets", "Joe Smith",

885, 799, 2.5 };

Precis som for fait kan man utelamna initieringsvarden bara fran slutet. Man kan nastlaklamrama om man har fait av poster, poster med fait eller poster i poster.

C99 har ett satt att initiera valda delar av en post via namn, s.k. bendmnda initierare. \/* initera bara med pris och vikt */

struct bok bok2 = { .pris = 799, .vikt = 2.5 };

8.1.3 Pekare till poster

Vi kan naturligtvis aven ha pekare till poster. De deklareras precis som vantat. Enpekare till var bok-post deklareras som:

struct bok *bokp;

bokp = &bokl; /* peka pa bok-posten bokl deklarerad ovan */

) Studentlitteratur 163

Page 167: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8. Sammansatta typer

Denna pekare kan nu endast peka pa hela poster av typen struct bok, inget annat.Pekarenpekar till hela bok-posten.

Att ha pekare till poster ar anvandbart av framfor allt tva skal. Det ena ar att vi kan faeffektivare kod om vi, som tidigare visats i avsnitt 7.4, stegar igenom fait av postermed hjalp avpekare i stallet foratt indexera. Vikanocksd undvika kopieringar genomatt skicka post-pekare somparametrar i stallet foratt skicka helapostema.

Det andra skalet ar att vi kan anvanda pekare till poster som en del i postema sjalvaoch lata postema peka tillvarandra och darigenom kunna bygga upp godtyckligt ihop-lankade avancerade datastmkturer.

Vi ska se ett exempel pa effektivitetsskalet genom att gora en ny version av person-Mderprogrammet ovan, medfullt utnyttjande av pekare. Innan vi gordet skall vi docktittapaennyoperator. Om viharenpekare tillenpostochvillvalja ut endelavpostenskullevi med det vi hittillsvet skriva(medbokpostexemplet ovan):

(*bokp).pris = 95;

Vad vi gorar att forst applicera ^-operatom pa pekaren sa att vardet blir detdenpekarpa, d.v.s. sjalva posten. Pa detta varde applicerar vi sedan punktoperatom sa att viselekterar delen pris. Det finns ett fdrkortat skrivsatt for detta som ar helt ekvivalent:

bokp->pris = 95;

Denna piloperator (sammansatt av minus- och "storre an"-tecknen) kan endast anvan-das pa ^os,i-pekare och har precis samma betydelse som kombinationen ovan.

Nu gar vi vidare med en ny version av programmet pa sidan 161.

#include <stdio.h>

#define NPERSON 10

struct person {

char namn[ICQ];

int alder;

};

void inperson(struct person *pref);

void utperson(const struct person *pref);

/* las in personer, skriv ut den aldsta

men anvand pekare som parametrar */

int main ()

{

struct person p[NPERSON], *pp, *paldst;

for (pp=p; pp < &p[NPERSON]; pp++) {

inperson (pp) ;

}

164 © Studentlitteratur

Page 168: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

paldst = p;

for (pp=p; pp < &p[NPERSON]; pp++) {

if (pp->alder > paldst->alder) {

paldst = pp;

}

}

utperson(paldst);

8.1 Poster (struct)

Vi ser att vi nu stegar pekaren pp genom faltet i stallet for att indexera med i. pp stegasfram anda tills vi nar adressen for det forsta objektet utanfor faltet. Till inpersonskickar vi endast en pekare till elementen i faltet p, ett i taget. Aven till utpersonskickar vi endast pekaren till posten som bar den hogsta aldem. Vi bar i paldst sparatpekaren till den post i faltet som bade den bogsta Mdem. Figur 8.2 visar bur det ser utefter det att balften av postema i faltet genomlopts i den andra av for-snurroma.

m 1 1 m n m n m Ti m n m 11LL1

8

11

95

_u IX.1

3

-LJ Ll_1 ...

49

u_1

33

11 LI 1

25

.1.1 1 1 1 ..LI

0LX1

67 98 32

paldst:

Figur 8.2

Parametrama for in- ocb utmatningsfunktionema maste andras eftersom de nu arpekarvarden. Avenanvandningen av parametrama mastejusteras:

void inperson(struct person *pref)

{

scanf("%s %d", pref->namn, &pref->alder);

void utperson(const struct person *pref)

{

printfC'Personen %s, %d ar, ar aldstXn",

pref->namn, pref->alder)

I Stallet for att retumera ett postvarde sa far nu inperson en pekare som parameter ocbkan via den direkt fylla i vardena i posten. For utperson bar punktoperatom bytts utmot piloperatom eftersom den nu far en pekare till posten i stallet for posten sjalv. Vi

) Studentlitteratur 165

Page 169: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8. Sammansatta typer

hardessutom anvant const for att ange att utperson inte tanker andra paperson-pos-ten som pekaren pekar pa.

Nagra ord om vilken variant som "bor" anvandas: Pekare med sin stora generalitettycks ha en formaga att trassla till sakernar man gjort fel, ofta pa ett svarhittat satt.Erfarenheten sager attdetar lattare hantattpeka fel an att indexera utanfor faltgranser.A andra sidan upptacker man snart att mycket av pekarhanteringen ar "idiom",invanda monster. Pekaranvandning kan dessutom spara exekveringstid och minne ikritiska delar av ett program. I manga fall, som vi skall se i nasta avsnitt, kan vi dessutom struktureravara data pa ett naturligt satt med pekare.

Valet blir alltsa en awagning mellan dessa faktorer. Anvandningen av pekare i existe-rande C-kod ar mycket vanlig och skall man underhMla programsystem skrivna i Cklararman sig definitivtinte utan att beharskapekare till fullo.

8.2 Lankade datastrukturer

Nu skall vi upptackavad vi kan uppna genomatt lata posterkunnapeka till varandra.Det gor vi genom att i en post ha med ett eller flera pekarvarden som pekar ut andraposter, av samma typ som posten sjalv eller av andra posttyper. Pa detta satt kan vibygga upp godtyckliga datastrukturer som kan modellera verkligheten pa ett mycketuttrycksfullt satt.

En helt generell sadan struktur brukar kallas en graf. Den ar uppbyggd av noder ochrelationer mellan noder, kallade bdgar. Grafer kan t.ex. representera personer ochderas slaktrelationer, stader och kommunikation mellan dem etc. En bild av en grafbrukar ritas som objekt och pilar mellan dem som i figur 8.3.

Figur 8.3

166 © Studentlitteratur

Page 170: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8.2 Lankade datastrukturer

I programmeringssprak ar det oftast lampligt att representera noder med poster ochbagar med pekare:

struct person {

char namn[100];

int alder;

struct person *far, *mor, *yngre_syskon;

};

Har bar vi utokat var tidigare deklarerade person-posttyp med pekare som kan pekavidare till andra person-poster. I poster kan bara finnas ett fixt antal delar sa vi latersyskonpekarenpeka ut foraldrar samt nasta yngre syskon. Nu skulle vi kunna skapa enlankad struktur genom att deklarera variabler och initiera dem:

/* initiera fyra person-poster */

struct person

pi = {"Johan Bengtsson", 45, NULL, NULL, NULL},

p2 = {"Karin Bengtsson", 38, NULL, NULL, NULL},

p3 = {"Pelle Bengtsson", 15, &pl, &p2, NULL},

p4 = {"Maria Bengtsson", 17, &pl, &p2, &p3 };

Figur 8.4 visar en bild pa detta.

Johan Bengtsson

45 X X X

Pelle Bengtsson

15 X

Karin Bengtsson

38 X X X

Maria Bengtsson

17

Figur 8.4

Vardet null for foraldramas far och mor later vi representera att vi inte kanner tilldem. Pelle har daremot inget yngre syskon.

Vi kan gora vilka slag av ihoplankade strukturer som heist men vi skall nu titta pa nag-ra speciella konstruktioner som ofta anvands i program.

) Studentlitteratur 167

Page 171: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8. Sammansatta typer

8.2.1 Lankade listor

Denenklaste formen av lankad struktur ar denlankade listan. Vaije nodbardarbaraenpekare sompekarvidare till nasta nod ellerar null om det inte finns nagon mernod ilistan. Denna linjto struktur liknar ett fait till sin natur men en lankad lista bar en delandra egenskaper ocb mojligbeter.

Vi skall fbrst studera ett exempel med en teckenlista dar varje nod innebMler etttecken. Detta skulle kunna vara ett altemativt satt att representera strangar. I vartexempel skall vi konstruera ett program som laser in ocb skriver ut sadana teckenlistor.Posttypen vi deklarerar blir:

struct tlist {

char t;

struct tlist *rest;

} ;

Vi kallar pekaren till nasta objekt i listan rest eftersom den pekar ut den forsta av res-ten av teckennodema i listan. En teckenlista skulle da kunna se ut som i figur 8.5.

Figur 8.5

Foljande funktion skriver ut alia tecknen i listan.

/* skriv ut tecknen i en lankad teckenlista */

void skrivtlist( const struct tlist *tp)

while (tp != NULL)

putchar (tp->t) ;

tp = tp->rest;

Vi ger som parameter till funktionen en pekare till det forsta objektet. Funktionen tes-tar forst att pekaren inte ar null (representerar en tom lista). Om sa inte ar fallet skriverden ut tecknet ocb later tp fa pekarvardet till nasta nod. Detta varde blir null nar visnurrat fardigt ocb kommit till sista noden.

Man kan tanka sig att skriva en rekursiv variant av denna funktion dar vi anropar funktionen sjalv for att skriva ut resten av listan nar vi skrivit ut tecknet i den forsta noden.

168 ) Studentlitteratur

Page 172: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8.2 Ldnkade datastrukturer

/* skriv ut tecknen i en lankad teckenlista */

void rskrivtlist(const struct tlist *tp)

{

if (tp != NULL) {

putchar (tp->t) ;

rskrivtlist(tp->rest);

}

Det blir en mindre del av listan som ar rest i varje anrop och till sist ar det NULL-peka-ren i sista noden som skickas och da behover vi inte gdra nagot alls.

Nu skall vi lasa in tecken till en teckenlista och da maste vi naturligtvis antingen hadenna listafardiglankad ellerocksa skapa nodema och lanka ihop demallteftersom vilaser in tecken. Vi valjerdet senare eftersom vi pa forhand inte vet hur manga teckensom det behovs noder for. Vi borjar med att skriva en funktion som skapar utrymmefor en struct tiist-nod. Funktionen retumerar en pekare till den skapade posten:

/* skapa ett dynamiskt allokerat teckenlistobjekt */

struct tlist ^skapatlist(void)

{

struct tlist *tp;

tp = (struct tlist *) calloc(l, sizeof (struct tlist));

if (tp == NULL) {

fprintf(stderr, "Minnet slut\n");

exit(99);

}

else {

return tp;

Minnet fas ffan det omgivande systemet med hjalp av standardfunktionen caiioc.Parametrama till denna funktion ar antalet objekt samt storleken pa objekten. caiiocretumerar en pekare till det allokerade minnet, men denna pekare ar av den generellapekartypen void * och darfor omvandlar vi den till ratt typ med en typomvandling,innan vi tilldelar den till tp. Om allt gick bra retumerar vi vardet av tp som ar pekarentill den nyskapade posten. caiioc retumerar null om skapandet av minne misslycka-des av nagon anledning. Om sa ar fallet gor vi en felutskrift och avslutar hela program-komingen med standardfunktionen exit. Parametem till exit ar en returkod till detomgivande systemet. Allt som inte ar noil betyder misslyckande pa nagot satt.

Nu ar vi mogna att skriva en funktion som laser in tecken for tecken, skapar en tecken-listnod for varje tecken, lankar ihop nodema och retumerar en pekare till den forsta

) Studentlitteratur 169

Page 173: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8. Sammansatta typer

noden. Vi later ett nyradstecken avsluta inlasningen (detta tecken later vi inte fiimasmed i den fardiga teckenlistan):

/* las en rad och lagra i en lankad lista */

struct tlist *laestlist(void)

{

struct tlist *ny;

char c;

c = getchar();

if (c != '\n') {

ny = skapatlist0;

ny->t = c;

ny->rest = laestlist () ;

return ny;

}

else {

return NULL;

}

}

Viutnyttjar rekursion igen. Narvi lastin tecknet testar vi omdet innebar slutet (nyrad),i sa fall ar det bara att retumera null som representerar den tomma teckenlistan.Annarsskaparvi en teckennod och stopparin teckneti den. For inlasning av resten avtecknen anropar vi funktionen rekursivt. Det vi far tillbakaar en pekaretill en tecken-listamedde resterande tecknen. Dennapekare tilldelar vi foljaktligen till rest.

Till sist skrivervi ett huvudprogram somtestar det hela genomatt lasa in en rad till enteckenlista och sedan skriva ut den igen med bada variantema av utskriftsfunktionen.

/* las in en rad och skriv ut den */

#include <stdio.h>

#include <stdlib.h>

struct tlist {

char t;

struct tlist *rest;

} ;

struct tlist *laestlist(void);

void rskrivtlist(const struct tlist *tp);

void skrivtlist(const struct tlist *tp);

int main ()

{

struct tlist *rad;

rad = laestlist();

rskrivtlist(rad) ;

skrivtlist(rad) ;

}

170 © Studentlitteratur

Page 174: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8.2 Lankade datastrukturer

Vihar inte glomtatt i borjanpa filen ta med ratt inkluderingsfiler for de standardfunk-tioner vi anvant. Inkluderingsfilen stdiib.h ar den som enligt standarden skall inne-halla deklarationer for calloc och exit.

Man kan ffaga sig vad vi har vunnitpa att hanteratextstrangar pa det har sattet i stalletfor pa det vanliga. Varje nod lar ta mer an dubbelt sa mycket plats som ett enkelttecken (kanske sex ganger mer om minnet har krav pa hur en pekare far vara placerad).Vad vi vinner jamfort med teckenfalt ar framst att vi inte behover veta i forvag hurmanga tecken vi behover bereda plats for maximalt. Vi kan ocksa pa ett enkelt sattandra i strangenutan att behova flytta pa langa sekvenserav tecken.Att satta in en nodmitt i en lista innebar bara att man behover flytta om pekama. Om postema ar storre,

som t.ex. bok-posten tidigare, innebar en extra pekare relativt sett mycket lite.

Vi skall studera nagra exempel pa hur man kan satta in och ta ut objekt i lankade listor.Lat OSS i exemplen anta att vi har en pekarvariabel ti till det forsta objektet i listan ochen pekarvariabel tob j till ett objekt som vi skall satta in i listan:

struct tlist *tl, *tobj;

Listan ser ut som i figur 8.6.

tobj:

t1:

n X

a

Fig^r 8.6

Vi boijar med att satta in objektet forst i listan:

/* satt in objektet som tobj pekar till fbrst i listan */

tobj->rest = tl;

t1 = tobj;

Vi maste gora tilldelningama i denna ordning annars tappar vi bort boijan till listan.

Att ta bort det forsta objektet i listan innebar belt enkelt att flytta ti till andra objekteti listan (fast vi bor kontrollera att listan inte redan ar tom):

/* ta bort forsta objektet i listan */

if (tl != NULL) {

tl = tl->rest;

}

) Studentlitteratur 171

Page 175: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8. Sammansatta typer

Noteraattdettaar ett sattatt implementera en stack. Jamformedexemplet i avsnitt6.8.

Nu skall vi anta att listan ar sorterad i teckenkodsordning och att objektet som skallsMas in skall hamna pa ratt plats. Det blir lite svarare: det maste fungera aven om listan ar torn innan insattningen eller om objektet skall hamna forst eller sist i listan.

Det blir enklast att skriva en separat funktion som skoter jobbet. Vi vill skicka medpekama ti och tobj som parametrar. Eftersom ti eventuellt skall andras (om listan artom innan eller om objektet skall in forst) kan vi inte skicka med ti:s varde utan vimaste skicka en pekare som pekar till ti. Anropet blir da:

tinlist(&tl, tobj);

Funktionen definieras som:

/* satt in tobj-posten pa ratt plats i listan */

void tinlist(struct tlist **tlref, const struct tlist *tobj)

{

while(*tlref != NULL && tobj->t > (*tlref)->t)

tlref = &(*tlref)->rest;

tobj->rest = *tlref;

*tlref = tobj;

Eftersom tiref ar en pekare till en pekare (ti i detta anrop) deklareras parametemmed tva asterisker.

I whiie-snurran stegar vi fram tiref genom listan och later den peka pa utrymmenafor pekama i listan. Vi slutar snurran antingen nar listan ar slut eller nar vi pekar pa enpekare som pekar till ett objekt som har en storre teckenkod an den vi skall satta in.Direkt efter snurran pekar tiref pa den pekare som skall andras till att peka pa tobj.Det kan vara antingen ti-pekaren eller en av rest-pekama. Da ar det bara att lanka intobj pa denna plats.

Vi visar i figur 8.7 hur det ser ut alldeles efter snurran men innan inlankningen skett.

Figur 8.7

172 ) Studentlitteratur

Page 176: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8.2 Lankade datastrukturer

Att vi gor pa detta satt med en pekar-pekare beror pa att vi vill undvika de specialfallsom annars uppstar nar objektet skall in forst respektive sist eller om listan ar torn.

8.2.2 Sokning i tabeller

Vi ska bar visa bur vi med bjalp av lankade strukturer bygger upp en tabell som det garfort ocb enkelt att bitta information i. Det skall ocksa vara enkelt att satta in nya objekt.

Var uppgift ar att skriva ett enkelt lagerbanteringsprogram. Varje vara i lagret bar ettvarunummer. Vi vill nu ba ett program som kan balla reda pa lagersituationen ocb ban-terar foljande operationer: insattning av ett nytt varunummer, uppdatering av antalet aven viss vara (in ocb ut), fraga om en viss vara samt utskrift av bela lagersituationen.

Vart forsta problem ar att bitta en lamplig datastruktur att representera alia varunumrenmed atfbljande varuantal. En forsta tanke vore ett fait med varunummer som index ocbvardena i faltet som antalet av motsvarande vara (ett negativt tal kunde fa betyda attvarunumret inte alls existerade).

int antal[MAXVARUNR];

Denna losning faller dock pa att varunumren ar beltal med anda upp till nio siffrorlanga varav manga ar oanvanda. Faltet skulle bli for stort.

Nasta forsok: Vi anvander ett fait av poster dar varje post bestar av varunumret ocbantalet:

struct lagerpost {

long int varunr;

int antal;

} ;

struct lagerpost lager[MAXVAROR];

For att bitta posten med ett visst varunummer soker vi i faltet tills vi bittar ratt post.Nackdelen med detta ar att vi i snitt maste soka igenom balva faltet for att bitta varpost. Vi skulle kunna ba postema i varunummerordning ocb "saxa" oss fram till densokta posten (binarsokning). Da forlorar vi emellertid den enkla insattningen av nyavarunummer, eftersom vi maste flytta undan alia med storre varunummer ett steg. Tillsist bar vi ocksa nackdelen med att antalet varunummer bar en fast ovre begransning.

Vi valjer en lankad struktur. En enda lankad lista vore inte sa bra. Det vore visserligenenkelt att satta in nya objekt i den ocb den skulle inte bli begransad, men vi skulle fort-farande fa soka igenom bela listan for att bitta vad vi sokte. Vi loser detta genom attanvanda flera listor. Anvander vi N stycken listor blir de i snitt N ganger kortare.

Vi deklarerarett fait av pekare till varulagersposterocb later varje pekarelement i faltetpeka till borjan av en lankad lista av poster:

© Studentlitteratur 173

Page 177: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8. Sammansatta typer

struct vpost {

long varunr;

int antal;

struct vpost *lnk;

} ;

#define NHASH 100

static struct vpost *vhash[NHASH];

Att vi kallar faltet vhash och antalet element i den nhash beror pa att metoden att for-dela ut tabellposter pa detta satt kallas hashing, ett engelskt uttryck som inte brukaroversattas. (Vi bar bar for enkelbets skull valt att ba en global basbtabell vhash somanvands direkt i alia rutinema nedan. I ett generellt fall borde vi i stallet skicka medtabellen som parameter sa att rutinema kan anvandas till olika basbtabeller. Hasbtabel-lema deklareras da lokalt eller allokeras i d3mamiskt minne.) En bild av det bela kom-mer att se ut som flgur 8.8, med en del poster insatta i tabellen.

vhash

800 13 99900 10 10000

56597 11197 150

6699 45

Figur 8.8

Nu skall vi bara bitta ett enkelt satt att avgora i vilken lista en viss nod skall finnas. Ettsatt ar att lata de tva sista siffroma i vamnumret avgora i vilken lista noden skall sitta.Det fungerar bra om inte de siffroma ar snedfordelade (da skulle vi kunna valja nagot

annat, t.ex. summan av de enskilda siffroma i vamnumret modulus antalet listor).

Vi ger OSS i kast med att skriva de funktioner som bebovs for att implementera de kom-mandon som skall finnas. Vi borjar med en fiinktion som soker en post med ett givetvamnummer ocb retumerar en pekare till den (null retumeras om posten inte finns).

174 ) Studentlitteratur

Page 178: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8.2 Lankade datastrukturer

/* hitta en post med givet varunummer */

struct vpost *hittapost(long vnr)

{

struct vpost *vp;

vp = vhash[hashfunk(vnr)];

while (vp != NULL && vp->varunr != vnr)

vp = vp->lnk;

}

return(vp);

Vi tar forst reda pa den pekare i vhash som utgor borjan pa den listan dar posten medvnr skall finnas. Funktionen som beraknar vilken lista det ar bar vi defmierat separat:

/* berakna listnummer for en viss post */

int hashfunk(long vnr)

{

return(vnr % NHASH);

Den plockar belt enkelt ut de tva sista siffroma i varunumret genom att ta resten viddivision med lOO (det fungerar ocksa med vilken annan storlek som belst pa nhash).

Nar vi fatt pekaren till det forsta objektet testar vi i en whiie-snurra pa tva villkor, delsatt vi inte kommit till slutet pa listan (det skulle innebara att posten inte finns), dels omvi bar bittat posten med ratt varunummer. (Notera att vi bar utnyttjar &&-operatomsegenskap att ga strikt fran vanster till boger ocb att inte berakna bogerledet om vanster-ledet ar falskt.). I bMa fallen som avslutar snurran inneballer vp ratt pekarvarde: enpekare till den fiinna posten eller null.

Nu kan vi skriva tva funktioner vi bebover, dels en som skriver ut lagerinneball for enviss vara, dels en som uppdaterar lagerinneballet.

/* skriv ut lagerstallning for viss vara */

void finns(long vnr)

{

struct vpost *vp;

if ((vp = hittapost(vnr)) == NULL) {

printf("Varunummer %ld finns ej\n", vnr);

}

else {

printfC'Det finns %d stycken\n", vp->antal);

}

}

Funktionen anropar belt enkelt hittapost ocb skriver ut lagerinneballet eller ett med-delande om posten inte finns.

© Studentlitteratur 175

Page 179: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8. Sammansatta typer

/* uppdatera given lagerpost */void uppdatera(long vnr, int ant)

{

struct vpost *vp;

vp = hittapost(vnr);

if (vp == NULL) {

printf("Varunummer %ld existerar ej\n", vnr),

}

else {

vp->antal += ant;

if (vp->antal < 0) {

printfC'Det finns endast %d st att ta\n",

vp->antal - ant);

vp->antal = 0;

}

printfC'Nu finns %d stycken\n", vp->antal) ;

Funktionen for att uppdatera tar som parameter ocksa lagerforandringen.Taletar nega-tivt cm det innebar att lagret skall minskas. Vi ser naturligtvis till att posten finns ochatt vi inte kan ta ut fler varor an vad som finns i lager.

For att skapa en ny varupost skriver vi forst en funktion som allokerar minne till den.

/* skapa en dynamiskt allokerad lagerpost */

struct vpost *skapavpost(void)

{

struct vpost *vp;

vp = (struct vpost *) calloc(l, sizeof (struct vpost));

if (vp == NULL) {

fprintf(stderr, "Minnet slut\n");

exit (99) ;

}

else {

return vp;

Denna funktion ar belt analog med den vi skrev i forra avsnittet. (I ett reellt fall skullevi nog gora nagot annat an att avbryta komingen av programmet om det inte fannsmera minne men det ar en hel del annat vi ocksa skulle gjort annorlunda i ett realistiskt

lagerhMlningsprogram. Bland annat diskuterar vi i ovningsuppgiftema till kapitel 10olika mojligheter att lagra varupostema permanent pa en fil.)

Nu kan vi skriva funktionen som satter in den nya varuposten i tabellen:

176 © Studentlitteratur

Page 180: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8.2 Ldnkade datastrukturer

/* satt in en ny lagerpost i datastrukturen */

void nyvara(long vnr)

{

struct vpost *ny;

if (hittapost(vnr) == NULL) {

ny = skapavpost();

ny->varunr = vnr;

ny->antal = 0;

ny->lnk = vhash[hashfunk(vnr)];

vhash[hashfunk(vnr)] = ny;

}

else {

printf("Varunummer %ld finns redanXn", vnr);

}

J

Om posten inte redan finns skapasen ny post. Vifyller i den medvarunummer och sat-ter antal varor till noil. Vi lankar sedan in den som forsta post i ratt lista genom att forstsMa in pekaren till den som forut var fbrst (eller null om det var tomt) i var nya postslankpekare. Sedan satter vi pekaren i pekarfaltet att peka pa var nya post.

Nu bar vi bara kvar flinktionen som skriver ut alia lagerposter:

/* skriv ut alia lagerposter */

void skrivallt(void)

{

int i;

struct vpost *vp;

printf("Varunummer Antal\n");

for (i=0; i<NHASH; i++) {

for(vp=vhash[i]; vp != NULL; vp=vp->lnk) {

printf("%101d: %8d\n", vp->varunr, vp->antal);

}

}

Vi bar tva nastlade for-snurror. Den yttre gar igenom alia elementen i pekarfaltet. Forvaije varv finns en inre snurra i vilken vi later en pekare stega sig fram i motsvarandelankade lista ocb for varje nod i denna skriver vi ut postens inneb^l.

Nu skall vi skriva ett program med en kommandotolkare, d.v.s. ett programavsnitt somlaser in text, ser vilket kommando vi vill ba utfort, laser in lampligt antal parametrarocb anropar ratt fimktion. Vi skall gora det pa ett strukturerat satt med flexibla tabeller.Vi borjar med att gora ett fait av poster som vardera inneballer tre ting: en strang somskall vara det kommando som skrivs pa tangentbordet, en pekare till motsvarade fimktion samt ett beltal som talar om bur manga parametrar flinktionen bebover.

) Studentlitteratur 177

Page 181: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8. Sammansatta typer

/'^ tabell med kommadon, motsvarande

funktion och parameterantal

struct komtyp {

char *kstr;

void (* funk) () ;

int nparam;

} kom[] =

uppd", uppdatera, 2},

f inns", f inns, 1},

invent",r skrivallt, 0},

>1

nyvara, 1},

sluta", sluta, 0}

Att gora pa detta satt har fordelen att tillagg av nya kommandon endast iimebar attskrivafunktionen och laggain den i tabellen tillsammans medett lampligt kommando-namn. Vi har lagt in ett nytt kommando sluta for att avslutakomingen:

void sluta(void)

{

printf("Tack for denna gang\n");

exit ( 0) ;

Vi skriver en funktion som jamfor en textstrang med de kommandon som fmns i tabellen och retumeraren pekaretill den funnapostenellernull om strangen inte matchadenagot kommando i tabellen.

/* finn en kommando-post i tabellen givet en kommandostrang */struct komtyp *hittakom(const char *str)

{

struct komtyp *kp;

for (kp=kom; kp<kom+(sizeof(kom)/sizeof(kom[0]));kp++) {

if (strncmp(kp->kstr,str,2) == 0) {

return(kp);

return NULL;

}

Det komplicerade uttrycket i for-villkoret ar ett flexibelt satt att ange slutet pa faltetkom. Vi dividerar storleken pa hela faltet med storleken pa den o:te posten och far sale-des antalet poster i faltet. Nar vi lagger detta heltal till faltnamnet kom, som ar ettpekarvarde, blir resultatet en pekare till slutet pa faltet. Fordelen med detta ar att faltetsstorlek inte behover anges nagonstans utan endast ar direkt beroende av det antal poster vi initierat faltet med.

178 © Studentlitteratur

Page 182: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8.2 Lankade datastrukturer

For att jamfora strangen str med de strangar som finns i tabellen anvander vi stan-dardfunktionen strncmp som retumerar o om strangama ar lika. Man kan dessutomtala om hur manga positioner som hogst skall jamforas. Vi bar valt antalet tva, vilketbetyder att man vid inmatning inte behover ge mer ar tva tecken av ett kommando.

Da skriver vi huvudprogrammet som skall anvanda allt detta:

/* huvudprogrammet for vart lagerprogram */

#include <stdio.h>

/* Har laggs funktionsdefinitioner (eller -deklarationer) ,

kommandotabellen kom samt globala variabler t ex vhash */

int main ()

{

long argl;

int i, arg2;

char inkomm[100];

struct komtyp *kp;

for (1 = 0; KNHASH; i + +)

vhash[i] = NULL ;

while (1) {

printf ( "\n\nKommando:") ;

scanf("%s", inkomm);

if ( (kp = hittakom(inkomm) ) == NULL)

printf("Felaktigt kommmando");

else {

switch(kp->nparam) {

case 0:

(*kp->funk) 0;

breaks-

case 1 :

if (scanf ("%ld"s &argl) != 1) {

printf ( "Felaktig parameter");

break;

}

(*kp->funk) (argl) ;

breaks-

case 2 :

if (scanf("%ld %d"s &argls&arg2) != 2) {

printf ("Felaktig parameter");

break;

}

(*kp->funk)(arglsarg2);

break;

}

) Studentlitteratur 179

Page 183: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8. Sammansatta typer

Vi borjar med att NULL-stalla hela pekarfaltet. Vi gar sedan in i en oandlig snurra (viavslutar med ett uttryckligt exit), dar vi forst laser in ett kommando med scant ochanropar hittakom for att soka i var kommandotabell. Fanns kommandot gor vi olikasaker beroende pa hur manga parametrar funktionen skall ha. For de funktioner somskall ha parametrar, laser vi in dessa med scant och kollar att returvardet ar m\X (scantretumerar antalet lyckade lasningar). Sedan anropar vi funktionen och avslutar varvet.

Vi avslutar detta avsnitt med att namna en variant pa lankade listor, dar vi inte barapekar ut nasta objekt utan aven foregangaren:

struct dnod {

char data;

struct dnod *tore, *etter;

};

Listor ihoplankade dubbelt pa detta satt kallas dubbelldnkade listor och ar anvandbaranar man behover kunna stega at bada hallen. De gor det t.ex. lattare att ta bort en nodur en lista givet en pekare till noden. I en enkellankad lista bar man inget enkelt sM attga tillbaka till den nod som pekar till den givna noden och det ar pekaren i denna fore-gaende nod som skall andras.

8.2.3 Trad

I foregaende avsnitt har vi endast sett exempel pa ihoplankade linjdra strukturer. Dehar inte forgrenat sig. Vi skall nu titta pa en struktur som kallas bindrt trdd. Ett sadanttvM bestar av en rot som har noil, ett, eller tva deltrad. Dessa deltrad ar ocksa binara

trad med sina eventuella deltrad osv. Vi skulle t.ex. kunna modellera ett slakttrad padetta satt, dar hogerdeltradet representerar fadem och hans vidare forfader och vanster-deltradet representerar modem och hennes slakt. Man inser att varje deltrad ar till sinstmktur precis likt hela tradet (figur 8.9).

Man kan anvanda binara trad till andra saker. Vi skall bygga vidare pa vart lagerhall-ningsexempel och i stallet for att ha postema i hashade listor skall vi lagga in dem i etthinart trad. Vi skall dessutom satta in dem i tradet pa ett sadant att vi latt kan hitta dem.Vi ser till att tradet ar ordnat vilket innebar att alia noder i vansterdeltradet ar mindre

an roten och alia noder i hogerdeltradet ar stdrre an roten, i vart fall med avseende pa

vamnumret. Nodema kommer att se ut sa har:

struct vpost {

long varunr;

int antal;

struct vpost *v,*h;

180 © Studentlitteratur

Page 184: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8.2 Ldnkade datastrukturer

Figur 8.9

Hela varulagret kan nu representeras av en pekare till roten pa tradet i stallet for enhashtabell. Se figur 8.10.

11197 150

6699 45 56597 3

10000 99900

Figur 8.10

Fran borjan ar tradet tomt, vilket vi representerar med att rot bar vardet null. Videklarerar rot som en lokal variabel i main:

struct vpost *rot = NULL;

Vi boijar med att skriva den nya versionen av funktionen som soker efler en post:

) Studentlitteratur 181

Page 185: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8. Sammansatta typer

/* hitta viss post i tradet */

struct vpost *hittapost(long vnr)

{

struct vpost *vp;

vp = rot;

while (vp != NULL && vp->varunr != vnr)

if (vnr < vp->varunr) {

vp = vp->v;

}

else {

vp = vp->h;

}

}

return(vp);

}

Viborjar att satta en pekaretill roten. Sa lange det finns ett trad (deltrad) att leta vidarei och vi inte bar hittat den ratta posten valjer vi att ga vidare till vanster eller hogerberoende pa om varunumret vi soker ar mindre resp. storre. Nar vi snurrat fardigt arpekarenantingen null om posteninte fanns eller sa pekar den pa den funnanoden.

Funktionema for uppdatering och utskrift av innehallet i en viss nod blir belt sammasom i forra avsnittet. De ar inte beroende av strukturensom postema lagras i.

Funktionen som skriver ut bela lagersituationen blir enkel. Genom att skriva ut tradet ien viss ordning kommer ocksa varunumren ut i nummerordning. Eftersom alia varu-numren i vansterdeltradet ar mindre an det i roten skriver vi ut dem forst, sedan skrivervi ut rotens varunummer ocb till sist alia numren i bogerdeltradet. Om vi tillamparsamma regel ocksa vid utskrift av deltraden kommer alia numren ut i ordning. Dennaordning brukar kallas inordning. Vi anvander rekursion:

/* skriv ut alia noder i tradet 1 ordning */

void skrivallt(void)

{

struct vpost *vp;

printf("Varunummer Antal\n");

inordning (rot) ;

}

void inordning(const struct vpost *rot)

{

if (rot != NULL) {

inordning(rot->v);

printf("%10 Id: %8d\n",rot->varunr, rot->antal);

inordning (rot->h) ;

182 © Studentlitteratur

Page 186: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8.2 Ldnkade datastrukturer

Funktionen skrivaiit skriver en rubrik och anropar inordning med en pekare tillroten som parameter. Om pekaren inte ar null skriver vi forst ut vansterdeltradet medett rekursivt anrop, sedan rotens information och sist hogerdeltradet. Att skriva dennafunktion utan att anvanda rekursion blir mycket trassligt.

Till sist bar vi kvar att satta in en ny nod i tradet:

/* satt in en ny nod i tradet */

void nyvara(long int vnr)

{

struct vpost *ny;

if (hittapost (vnr) == NULL) {

ny = skapavpost () ;

ny->varunr = vnr;

ny->antal = 0;

ini_traed(&rot,ny);

}

else {

printf("Varunummer %ld finns redan\n", vnr);

void ini_traed(struct vpost **rotref, const struct vpost *vp)

{

if (^rotref == NULL) {

*rotref = vp;

}

else if (vp->varunr < (*rotref)->varunr) {

ini_traed(&(*rotref)->v,vp);

}

else {

ini_traed(&(*rotref)->h,vp);

}

}

Funktionen nyvara kollar att varunumret inte redan finns och skapar en ny post medskapavpost. (Vi kan anvanda funktionen fran forra avsnittet direkt, eftersom funktionen inte bryr sig om hur posten ser ut inuti, ratt storlek for den nya posten far den medsizeof.) Sedan anropar vi funktionen ini traed som tar en pekare till pekaren tillroten som parameter, det maste vi gora eftersom vi eventuellt vill kunna andra pa rot-pekaren. Darfor far parametem typen struct tiist **rotref. Om roten ar null finnsdet en ledig plats och da kan vi direkt stoppa in vp dar. Annars ar det upptaget och vianropar da ini traed rekursivt, med en pekare till pekaren till vanster- eller hogerdeltradet, beroende pa varunumret. Jamfor detta med exemplet att satta in ett objekt pasorterad plats i en lankad lista i tidigare avsnitt.

) Studentlitteratur 183

Page 187: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8. Sammansatta typer

Deovriga funktionema inklusive main behover inte andras, defungerar precis som for-ut eftersom deinte alls anvander sigavdepekare som bygger upp datastrukturen.

Tillsisten kommentar ochenjamforelse av de olikalagringssatten vi anvant: I ett rea-listisktfall hadevi sjalvklart lagratdatastrukturen pa ett lampligt satt i en fil for att havarulagerdata permanent atkomliga. Nu forsvinner alia data nar vi avslutar program-komingen. Se ovningsuppgiftema till kapitel 10for olikaforslag till lagring pa filer.

Om vi jamfbrhash-listan och tradetkan vi konstatera att bada ger en snabbdtkomst avposter om de ar val fordelade. I fallet med hash-listan innebar detta att de olika ked-

joma ar ungefar lika langa. Hashfunktionen skall vara sa konstruerad att den inte sned-fbrdelar postema. Antalet varv i en sokning blir i snitt halvatotala antalet posterdivi-derat med hashfaltets storlek. I fallet med det binara trMet vill vi att det skall vara

balanserat, d.v.s. att de bada deltraden ar ungefar lika stora. Detta galler for alia del-trM. Hadevi till exempel satt in alianya posteri stigande nummerordning skulletrMetblivit fullstandigt obalanserat eftersom alia poster skulle ha hamnat i en langkedjapahogersidan. Detta skullekunna handa om vi matade in postema fran en bunt sorteradeblanketter eller en pa forhand sorterad datafil. En oordnad insattningsordning ger ettbalanserattrad dar antalet varv i sokningen i snitt blir tva-logaritmen av totala antaletposter. Det firms ett stort antal varianter pa soktrad dar man t.ex. balanserar tradet omsnedfordelningen blivit for stor.

8.3 Bitfalt

Denna datatyp ger oss mojlighet att komma at enskilda bitar och bitgmpper i minnet.Det ar mycket fa andra programmeringssprak som direkt ger programmeraren dennamojlighet. Anvandningsomradena ar huvudsakligen tva: Det ena ar att vi har ont omminne och vill packa ihop data som kan representeras med en eller ett fatal bitar i precis sa manga bitar som gar at. Ett logiskt varde behover t.ex. inte ta mer an en enda bit.Det andra skalet ar att vi vill komma at bitar i minnet som betyder speciella saker. Detar till exempelmycket vanligtatt styrregisterfor yttre enheter firms i en viss mirmespo-sition och att de olika bitama i registret betyder olika saker.

Termen bitfalt {bitfield) innebar att man kan deklarera bitgmpper av olika langd. Demaste dock alltid deklareras i en struct:

struct egensjkaper {

unsigned man :1;

unsigned gift:1;

unsigned barn:5;

unsigned

};

levande:1;

184 © Studentlitteratur

Page 188: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8.3 Bitfdlt

En variabel av en skdan typ kan sedan deklareras pa vanligt satt:

struct egenskaper pe;

En sadan variabel skulle t.ex. kunna inga i en personregisterpost dar vi pa ett litetutrymme (kanske en byte, men det ar implementeringsberoende) far plats med informationomkon, gift/ogift, antalbam (farrean 32)och humvidavederborande ar i livet.Ett bitfalt far endast ha typen int, signed int eller unsigned int. Man kommer atbitfalten med punkt- eller piloperatom precis som for vanliga poster:

/* anvand delar i ett bitfalt */

pe.gift = 1;

pe.man = 0;

pe.barn = 8;

if (pe.gift && Ipe.man) {

printf("Kvinnan ar gift, har %d barn", pe.barn);

}

Lat OSS ta ett exempel med ett styr- och dataregister for en terminalportsutgang. I ettsystem utan operativsystem maste man ofta sjalv kommunicera med hardvara ochdetta gdrs ofta via ett styrregister som finns tillgangligt som en minnesposition. Deenskilda bitama i detta register har ofta sin speciella funktion. Vi antar i vart exempelatt det finns en bit for att tillata enheten att ge avbrott, en bit for att se om enheten arredo for nasta tecken och atta bitar data for utmatning av tecknet. Resten av bitama aroanvanda. Registret ar i manualen ritat som i figur 8.11.

send_data send_ready

Figur 8.11

intr_enable

Da kan vi definiera en post med bitfalt som representerar detta register:

/* deklaration av bitfalt motsvarande bitar

i ett maskinvaruregister */

struct utregister {

unsigned intr_enable:

unsigned

unsigned send_ready

unsigned

unsigned send_data

) Studentlitteratur 185

Page 189: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8. Sammansatta typer

De tomma bitfalten anvands inte men markerar var de oanvanda bitama i registretfinns. I vilken ordning bitama numreras, bur bitama motsvarar denordning ochbety-delse de barfor en vissprocessor ocbyttreenbet, ar alltidimplementeringsberoende.

8.3.1 Bitmasker

I manga C program,specielltaldre, anvands i stallet for postermed bitfalt vanligabel-tal.Man"maskar" ut delama medbitmonster somuttrycks sombeltalsvarden, oftastpaoktal eller hexadecimal form eftersom det gor det latt att tolka talet som ett bitmonster.

Lat OSS ta exemplet ovan med personegenskapema:

/* deklarationer av bitmasker som talar cm

var respektive bitar befinner sig */

#define MAN 01 /* bitmonster 0000000000000001 */

#define GIFT 02 /* bitmonster 0000000000000010 */

#define BARN 0174 /* bitmonster 0000000001111100 */

#define BARNSKIFT 2 /* antal bitar som BARN befinner

sig till vanster om 0-position */

#define LEVANDE 0200 /* bitmonster 0000000010000000 */

int pe; /* plats for 16 bitar */

Makrona defmierar nu bitmonster i olikapositioner som motsvarar de platser i beltaletdar vi tanker oss att lagra de olika egenskapema. Det lilla kodavsnittet ovan blir nu:

/* anvand bitmasker for att valja ut delar av ett heltal */

pe 1= GIFT; /* samma som pe = pe | GIFT; */

pe &= -MAN;

pe &= -BARN; pe |= 8 << BARNSKIFT;

if ( (pe & GIFT) && ! (pe & MAN)) {

printf("Kvinnan ar gift och har %d barn",

(pe & BARN) >> BARNSKIFT);

}

For att satta dit GiFT-biten maste vi gora en bitvis eller-operation mellan pe ocb giftocb lagga resultatet i pe. En vanlig tilldelning skulle skriva over de andra bitama i pe.

For att nollstalla MAN-biten i pe inverterar vi MAN-bitmonstret med tilde-operatom - ocbgor sedan en bitvis OCH-operation med innebMlet i pe vilket gor att vi beballer aliabitar utom MAN-biten.

Nar vi vill sMa antalet bam till atta maste talet sattas dit med en ELLBR-operation darvi skiftar attan tva steg (bitfaltet finns tva steg in i beltalet) men dessforinnan maste vinollstalla BARN-bitama, annars kunde ett gammalt varde finnas kvar.

186 © Studentlitteratur

Page 190: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8.4 Variabla typer (uni on)

For att sedan titta pa vardena maskar vi ut bitama for de olika egenskapema med bit-monstren. BARN-bitama maste dessutom skiftas tillbaka tva steg (det behovs inte medde logiska vardena eftersom allt skilt fran noil betecknar sant).

8.4 Variabla typer (union)

Vi kommer nu att ga igenom en ny datatypkonstruktion som ar mera sallan anvandmen ar anvandbar(och svarersattlig) vid speciellatillfallen.Namnet union bar den fattfor att ett objekt av union-typ kan innehalla ett varde av varierande typ. Det bar ingen-ting att gora med variabler som kan inneballa mangder av element enligt traditionellmangdlara. (SMana stods direkt i en del andra programmeringssprak men inte i C).

Jamfbrt med en postvariabel som t.ex. kan inneballa ett beltal och ett flyttal och ettteckenfalt, kan en union-variabel inneballa ett beltal eller ett flyttal eller ett teckenfalt.

Lat OSS se pa motsvarande deklarationer. Forst en vanlig postvariabel:

struct s {

int i;

float f;

char c [ 10] ;

struct s pi;

Deklarationen av en union-variabel ser precis likadan ut:

union u {

int i;

float f;

char c [ 10] ;

union u p 2 ;

Skillnaden ligger i att variabeln p2 endast kan innebMla antingen beltalet i, flyttalet feller teckenfaltet c. En union-variabel ar tillrackligt stor for att innebMla den storsta av

de ingaende delama. Selektering med punktoperatom sker precis som for poster, mendet ar endast meningsflillt att ta ut ett varde av den typ som senast tilldelades variabeln:

p2.i = 289;

p2 . f = 156.67; /* p2.i ar nu odefinierat */

p2.c[0] = ' r' ; /* p2.f ar nu odefinierat */

Notera att man inte kan ta reda pa vilken typ vardet i variabeln bar vid ett visst tillfalle.Det maste man veta eller ba noterat pa annat satt.

© Studentlitteratur 187

Page 191: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8. Sammansatta typer

Detvanligaste sattet attanvanda enunionarattspara utrymme genom attlataden ing^i en post sa att union-delen i posten bara behdver innehalla relevant information. Vil-kenvariant av union-delen som^ aktuell brukar manda lata framga av nagon annandel av posten.

Lat OSS ta ett exempel med produkteri ett varuregister. En del bar fast pris och en delbarprisersommaste raknas framvidvarje tillfalle genom attmananropar en fimktion.

struct produkt {

int n r;

char namn [50] ;

char fastpris;

union {

long konstant;

long (*dagspris) (const produkt *);

} pris;

};

Nar en produktpost skapas ocb initieras maste den fdrses med antingen ett fast priseller en fimktionspekare till berakningsfimktionen. Vi maste dessutom komma ibagvilket av dessa vi valt ocb anger det med sant eller falskt i variabeln fastpris. Enfunktion for utskrift av ett produktobjekt:

/* funktion som skriver ut namn och pris pa en produkt */void printprodukt (const produkt * vp) {

printf("Produkt %s kostar: vp->namn);

if (vp->fastpris) {

printf("%ld", vp->pris.konstant);

}

else {

printf("%ld", vp->pris.dagspris(vp));

}

Ytterligare ett exempel pa anvandning av union ar att beskriva ett bardvaruregistersom bar tva olika betydelser ocb bitindelning vid lasning respektive skrivning. Da kanvi inte lasa ocb skriva med samma bitfaltspost utan vi deklarerar tva olika, en for lasning ocb en for skrivning. Eftersom de trots allt finns pa samma stalle i minnet satter vide tva olika bitfaltspostema i en union ocb sedan anvander vi den ena varianten vidskrivning ocb den andra vid lasning.

Det ar inte sa ofta man bar anledning att anvanda union-variabler men da de verkligenbebovs ar det val att de finns. Det skulle bli ostrukturerade omskrivningar annars.

188 © Studentlitteratur

Page 192: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8.5 struct/union och typedef

8.5 struct/union och typedef

Vi har tidigare gatt igenom hur typedef anvands och skall har bara visa hur det ser utihop med postdeklarationer. Precis som tidigare skapar typedef bara ett altemativtnamn pa en redan existerande typ. Vi tar ett exempel med den lankade list-noden somvi anvant ovan:

typedef struct nod {

int data;

struct nod *rest

} nodtyp;

Namnet nodtyp ar nu samma sak som struct nod, d.v.s. de tva deklarationema

struct nod nl;

nodtyp n2;

skapar tva variabler av exakt samma typ. Hade vi utelamnat struct-namnet nod itypedef-deklarationen

typedef struct {

int data;

struct nod *rest

} nodtyp;

hade vi bara kunnat deklarera enligt den senare formen med nodtyp n2. Vi har i sa falldolt att typen nodtyp i sjalva verket ar en struct-typ. Vi kan nu resonera pa tva satt:

a) Det ar bra att dolja att typen nodtyp ar en struct, eftersom vi da kan andratypens implementering i framtiden utan att paverka de som anvander nodtyp.

b) Eftersom den som anvander en struct-typ ar betjant av att veta att det ar enstruct, ar det lika bra att vi deklarerar med struct nod sa det syns overallt. Vikommer ju anda att vilja anvanda delama via punkt-operatom. Dessutom archansen valdigt liten att det inte kommer att vara en struct aven efterandringar, och andringar/tillagg i typen gors enkelt i hkda.variantema.

Vilket argument som haller bast, beror nog pa hur stor del av koden som anvander helastruct nod-variabler och hur stor del som vill komma at delama. Existerande kod

anvander bMa satten.

Man kan fora samma diskussion humvida det ar bra att gomma en pekartyp bakom etttypedef-namn, t.ex.

typedef nodtyp *nodptr;

I fortsattningen betyder nu typen nodptr samma sak som nodtyp *. Antingen tyckerman att det ar trevligt att pekartypen fatt ett eget tydligt namn, eller sa kan man argu-mentera att den som skall anvanda pekare anda maste veta att det ar en pekare och da

© Studentlitteratur 189

Page 193: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

8. Sammansatta typer

ar det likabra att det syns tydligtmed en det finns kanske ett bra skal att typedef-namnet slutar pa -ptr? Existerande kod anvander bada variantema.

8.6 Ovningsuppgifter1. Testahur mycket langre tid det tar att skicka ett stort antal stora poster som para-

metervarde jamfort med att endast skicka pekare till postema.

2. Gor en funktion som skriverut en persons slakttradsom fmns lagrat i en graf somi avsnitt 8.2.

3. Implementera en stack(se avsnitt6.8) men med vardenalagrade i en lankadlista.

4. Prova att skriva en funktion som satter in ett element i en lankad lista (som tin-list i avsnitt8.2.1)men utan att anvandaen pekare till pekaretill listansom parameter. Anvand en pekare direkt till listan och se vilka svarighetersom uppstar.

5. Lagg till ett kommando och motsvarande funktion i varulagersexemplet i avsnitt8.2.2 for att ta bort en varupost.

6. Implementera en modul (se avsnitt 6.8) som hanterar dubbellankade listorbeskrivna i slutet av avsnitt 8.2.2. Tips: Det ar lampligt att representera en tomdubbellankad lista med en pekare till ett listobjekt (inte en NULL-pekare). Dettaobjekt bar da sig sjalv bade fore och efter sig.

7. Tank ut (eller ta reda pa) en metod att ta bort ett element ur ett ordnat binart tradoch gor om uppgift 5 for tradvarianten. Prova ocksa att implementerautskrift avnodema i trMet i inordning utan att anvanda rekursion, det ar inte trivialt!

8. Undersok med hjalp av en union av ett heltal och en bitfaltspost hur bitama i ettheltal numreras i ditt system.

190 © Studentlitteratur

Page 194: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Preprocessorn 9

Innan sjalva kompileringen av ett C-program startar bearbetas texten av en preprocessor. Denna kan betraktas som ett separat program som gor rent textmassiga operationerpa programkoden. Preprocessom forstar myeket lite av spraket C och dess syntax menar anda ett myeket anvandbart verktyg. Vi kommer att beskriva de viktigaste mekanis-mema oeh deras anvandning.

Alia preprocessordirektiv skall sta pa en egen rad med tecknet # forst pa raden.

9.1 Makron

Ett makro ar en identifierare som defmierats att betyda en viss textstrang. Nar makrotsedan patraffas i kalltexten byts det ut mot denna textstrang. Makrodefmitioner bar viredan sett:

♦define STORLEK 100

Raden skall besta av #-tecknet, preprocessordirektivet, "vitt" mellanrum, makronam-net, "vitt" mellanrum samt resten av raden vilken blir den text som makrot represente-

rar. Denna text kan innehalla preeis vad som heist. Preprocessom bryr sig inte omhumvida det ar korrekt C-kod eller inte. Texten satts endast in tecken for tecken.

En vanlig anvandning av makron ar att man deklarerar enkla konstanter som i exem-plet ovan. Att anvanda makron i stallet for att sprida numeriska och andra konstanteroverallt ar en sjalvklarhet i ett strukturerat program. Anvandningen sker belt naturligt:

int 1,fit[STORLEK];

for (1 = 0; KSTORLEK; i + +)

f11[i] = 0 ;

Preprocessom kanner till tillrackligt om C for att t.ex. inte byta ut text i strangar:

printf("STORLEK = %d\n", STORLEK);

kommer att ge utskriften

STORLEK = 100

© Studentlitteratur 191

Page 195: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

p. Preprocessorn

Det gar naturligtvis bra att ha strangkonstanter somvarden pa makron. Da maste manha med "" runt strangen, annars saknas de vidmakroexpansionen.

#define NAMN "Chalmers Tekniska Hogskola"

printf ( "Organisation: %s", NAMN) ;

Det gar bra att anvanda tidigare definierade makroni strangen, de kommer att expan-deras i sin tur. Manmaste dockvaramedveten omden rent textmdssiga expansionen:

#define EXTRASTOR (STORLEK + 5)

int storflt[10 * EXTRASTOR];

Hade vi inte haft parenteser runt storlek + 5 hade resultatet av lo * extrastor blivit10 * STORLEK + 5 vilket nog inte ar vad vi tankte OSS. En god grundregel ar att alltidsatta parenteser kring uttryck i makrodefinitionen.

Vi kan naturligtvis ha makron som expanderar till C-kod:

#define PANIK { printf ( "Detta kunde inte ske!!\n");\

exit ( 9 9) ; }

if (alltgickfel) {

PANIK

}

Har ser vi ocksa hur man kan lata en makrotext fortsatta over flera rader: ett bakatsned-

streck satts precis innan radslutet.

9.2 Makron med parametrarI forra avsnittet expanderadesalltid ett makro till en bestamd textstrang.Man kan avendeflniera makron med parametrar. Med sadana kan man lagga in en godtycklig text iden expanderade strangen. Det liknar ett funktionsanrop eftersom parenteser anvandsfor att skicka parametrama. Vi tar ett exempel, ett makro for utskrift av ett heltal:

#define HELTALUT(i) printf ("%d", i)

int tal = 57;

HELTALUT(tal);

Makrot kommer bar att expanderas till

printf("%d", tal);

Parametem tai till heltalut kommer att placeras dar i:et star i makrotexten. Obser-vera att det inte far vara nagot "vitt" mellan heltalut och vansterparentesen. Da upp-fattas inte parameterlistan, vare sig i definitionen eller vid anvandningen.

Ett exempel med tva parametar:

192 © Studentlitteratur

Page 196: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

9.3 Filinkludering

#define max(i,j) ((i) > (j) ? (i) : (j))

m = max(34*a, 67-b);

Notera alia parentesema som skydd for eventuellt fel ordning pa operatorersprioritet.

Makron med parametrar ar alltid lite "farliga". De ser ut som vanliga funktionsanropmen beter sig inte likadant. Hade vi t.ex. anropat makrot ovan som max(k++, 15) ;hade vi riskerat dubbel upprakning av k. Makron med parametrar bor anvandas medstor forsiktighet. Ett vanligt satt att vama anvandaren ar att stava alia makron medstora bokstaver som t.ex. max .

For att skriva sa sakra makron som mojligt fmns tre regler att folja:

a) Satt parentes runt hela makro-expansionenb) Satt parentes runt varje parameter i expansionenc) Se till att vaije parameter forekommer exakt en gang i expansionen (exemplet

ovan bryter mot denna regel!)

Ett skal att anvanda makron med parametrar ar att fa det att se ut som ett funktionsanrop men att undvika kostnaden for anropet genom att satta in hela koden vid varjeanrop. IC99 kan man istallet anvanda s.k. inline-funktioner (se avsnitt 6.5). Dessa har |samma effekt, men de skots snyggt av kompilatom och de har exakt samma betydelsesom ett riktigt funktionsanrop.

inline int max(int il, int 12)

{

return 11 < 12 ? 12 : 11;

}

Standardenbeskriver aven tva operatorer # och ## att anvandas i makroexpansioner foratt kunna "klistra" ihop parameterstrangar och delar av makrotextstrangen utan blankaemellan. Se manual for detaljer.

9.3 FilinkluderingNar man har ett program uppdelat i flera filer finns det som vi diskuterade i avsnitt 6.8ofta information som man vill skall vara gemensam for filema. Detta kan t.ex. vara

defmitioner av makron, extemdeklarationer av variabler, deklarationer av posttyper

och deklarationer av funktioner (inte deras defmitioner med intern programtext, deskall bara finnas pa ett stalle i en fil).

Ett satt att skriva vore att ta med dessa deklarationer i borjan pa vaije programtextfil.Nackdelen med att gora sa ar att om nagon andring skall ske maste det andras overallt,vilket baddar for fel. Vad vi i stallet bor gora ar att lagga dessa deklarationer i en sepa-rat inkluderingsfil (header file). Vi kan sedan ge preprocessom direktiv att ta meddenna fil i programtextfilen. Ett exempel pa innehall i en sadan fil:

© Studentlitteratur 193

Page 197: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

p. Preprocessorn

#define NAMNSTORLEK 80

struct person {

char namn[NAMNSTORLEK];

float vikt;

} ;

struct person inperson(void);

Har har vi en makrodefinition, en postnamnsdeklaration och en extemdeklaration aven funktion som laser in en post och retumerar den. Vi lagger denna text i filen per-son.h. (Enligt en allmant vedertagen konvention brukar inkluderingsfilers namn slutapa .h).

I en programtextfil kan vi nu skriva:

#include "person.h"

Preprocessorn kommer att byta ut denna rad mot hela innehallet i filen person. h.Exakt var filen soks i filsystemet och hur filnamnen ser ut ar systemberoende.

Det bidrar mycket till ett strukturerat program om man noggrant delar upp sina dekla-rationer i logiska grupper och har dem i var sin fil. Da kan en programtextfil sedaninkludera de filer som ar relevanta for just den filen.

Det finns en uppsattning standardinkluderingsfiler som innehMler definitioner ochdeklarationer avsedda att anvandas ihop med biblioteken av standardfimktioner. Dessabehandlas i kapitel 10. Att man avser dessa standardfiler visas med en speciell syntax:

#include <stdio.h>

#include <math.h>

I avsnitt 6.8 visades hur inkluderingsfiler kunde anvandas for att strukturera program.

9.4 Villkorlig kompileringPreprocessom har en mekanism for att valja ut huruvida delar av programtexten skall"slappas fram" till kompileringsfasen eller inte. Detta kan vara anvandbart om mankonstruerar ett program for fler an ett system men vill ha samma programtext, utom ide fa fall dar det kravs att man gor olika. Det finns en uppsattning direktiv for att goradetta. Det aldsta och hittills mest anvanda ar ett test pa om ett visst makro ar definierat.Lat OSS ta ett exempel med olika operativsystem:

programtext. ..

#ifdef UNIX

p = f o r k () ;

#el se

p = spawn (/* ...*/);

#endi f

programtext...

194 © Studentlitteratur

Page 198: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

9.5 Andra direktiv

Mitt inne i programtexten vill vi ha ett anrop till fork for att skapa en ny process iUNIX, for andra system anvander man i stallet spawn. Nagonstans, lampligtvis i eninkluderingsfil, deflnierar vi makrot unix om det ar ett sadant system vi har:

#define UNIX

Vardet pa makrot ar inte intressant i detta fall. Det racker med att makrot finns overhu-vudtaget. Det omvanda villkoret finns ocksa: #ifndef som testar om makrot inte ardefinierat.

Standarden beskriver ocksa mer generella testvillkor. Ett exempel:

#if VERSION == 1

^include "vl.h"

#elif VERSION == 2

^include "v2.h"

#else

^include "vstandard.h"

#endi f

Vi kommer bar att fa med olika inkluderingsfiler beroende pa vad makrot version harfor varde. Har testar vi alltsa pa vardet hos makrot och vi kan aven ha godtyckligauttryck som tolkas som falska om vardet ar lika med noli och annars sanna. #eiif ar enkombination av #eise och ett nytt #if.

9.4.1 Skydd mot flergangsinkluderande

En inkluderingsfil loper risk att inkluderas mer an en gang eftersom en inkluderingsfilmycket val kan behova inkludera en annan och da kan det latt bli dubbel-inkluderingnagonstans. Eftersom t.ex. samma struct-deklaration inte far komma flera ganger i enkompilering maste detta undvikas. Det idiomatiska sattet ar en s.k. inkluderings-vakt(include guard) som skrivs in i alia inkluderingsfiler:

/* I inkluderingsfilen buffert.h: */

#ifndef BUFFERT_H /* i borjan pa filen */

#define BUFFERT_H

/* inkluderingsfilens normala innehall */

#endif /* i slutet */

Forsta gangen filen lases av preprocessom tas innehallet med och buffert h definie-ras. Darpa foljande ganger exkluderar villkoret #ifndef hela filen.

9.5 Andra direktiv

Det firms ett litet antal ovriga direktiv som ar mer sallan anvanda:

© Studentlitteratur 195

Page 199: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

p. Preprocessorn

#undef MAKRO

#line radnummer

terror meddelande

tpragma kommando

#

Dessa kommadon betyder foljande: gor makro odefinierad, fa kompilatom att tro attden ar pa rad radnummer, ge ett felmeddelande pa lampligt satt respektive ge ett imple-menteringsdefinierat styrkommando. En vanlig skdan som manga preprocessorerimplementerar ar tpragma once, som ger skydd mot flergangsinkluderande som vi dis-kuterade i avsnittet ovan. Den sista raden ar ett nolldirektiv som inte gor nagot.

9.6 Fordefinierade makron

Standarden foreskriver att foljande makron skall vara fordefinierade (de inleds ochavslutas med dubbla understrykningstecken):

LINE aktuellt radnummer i filen

FILE aktuellt filnamn

DATE aktuellt datum (strang)

TIME aktuell tid (strang)

STDC definierad om man fdljer standarden

STDC_VERS10N version av C standard

Dessa makron kan anvandas i tester for villkorlig kompilering eller inne i C-kodensom vanliga makron. Narvaron av makrot stdc visar att den aktuella implemente-ringen foljer standarden. Humvida kompilatom foljer C99 kan testas med villkoret

#if STDC VERSION >= 199901L

9.7 Ovningsuppgifter1. Undersdk om man i ditt system kan fa ut den av preprocessom bearbetade (men

inte kompilerade) kalltexten och testa med nagot exempel.

2. Skriv ett makro med parametrar som implementerar funktionen absolutvdrde.Glom inte parenteser.

3. Undersok om de enligt standarden fordefinierade makrona finns med i ditt system.

196 © Studentlitteratur

Page 200: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Standardfunktioner och

standardbibliotek

Standarden skiljer noggrant pa sjalva spraket C och standardfunktionema. De funktio-ner som standardiseras beskrivs utforligt, men de betraktas inte som en inbyggd del ispraket ocb de bebover inte finnas i alia C-programmeringsmiljoer.

Man skiljer namligen pa olika omgivningar, miljoer, for programmering ocb exekve-ring. Den ena omgivningen kallar vi naken omgivning (freestanding environment). IC99 anges att de enda inkluderingsfiler som man skall kunna anvanda i en sadanomgivning ar float.h, iso646.h, limits.h, stdarg.h, stdbool.h, stddef.h Oebstdint.h. Ett program som kors i en naken omgivning kan inte rakna med stod avnagot operativsystem for t.ex. filbantering eller minnesskydd. I en naken omgivningmaste programmeraren sjalv ta band om in- ocb utmatning ocb ovrig kommunikationmed omgivningen genom att direkt paverka bardvaran. Mycket av den kod som skrivsfor ett sadan omgivning blir naturligtvis belt maskinberoende ocb inte flyttbar. Viagnar ett speeiellt avsnitt i kapitel 11 at detta.

Den andra typen av omgivning kallas operativsystemmiljo (hosted environment). Idetta kapitel kommer vi att ta upp de viktigaste av de funktionsbibliotek som enligtstandarden maste inga i en operativsystemmiljo. Program som anvander sig av funk-tionema i dessa bibliotek for att interaktera med omgivningen far maximal flyttbarbet,tack vare att funktionema ar standardiserade.

Man bor definitivt inte se detta kapitel som en referensmanual for existerande C-pro-

grammeringsmiljoer, utan som en introduktion till de mest anvanda funktionsbibliote-ken. Vi tar inte med alia flinktioner ocb alia deras detaljer, utan banvisar till referens-manualen.

C99 gor en ganska stor utvidgning av standardbiblioteken, men for en nyborjare ar det BEinte sa mycket av det som ar nytt som ar av intresse. Utvidgningen bestar mest avytterligare matematiska funktioner samt flinktioner for bantering av Unieode-text. Vigor en oversikt av det nya i slutet av kapitlet.

) Studentlitteratur

Page 201: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10, Standardfunktioner och standardbibliotek

10.1 InkluderingsfQerFor att man skall slippa att sjalv skriva extemdeklarationer av standardfunktioner ochatfoljande makron (med all risk for fel och problemmed flyttbarhet som blir foljden)finns deklarationema av standardfimktionema samlade i ett antal inkluderingsfiler(header files). Dessa inkluderar man i sin kalltext genom att vanligtvis i borjan av sinfil anvanda preprocessordirektivet #inciude. Vi bar redan sett flera exempel. Vi hart.ex. inkluderat filen stdio. h som hor till standardbiblioteket for in- och utmatning avtextfiler. Observera att direktivet #include inte medfor att sjalva biblioteket medobjektkod for flmktionema soks igenom eller att fimktioner som skall anvandas auto-matiskt lankas med i det fardiga programmet. Det kravs oftast att man vid kompileringoch lankning pa kommandoraden anger vilka filer som skall lankas samman. (Se t.ex.sidan 8.) Inkluderingsfilen innehaller bara deklarationer av funktioner och makron. Seaven avsnitt 6.8 dar vi diskuterade modular programutveckling.

Ibland ar det som ser ut som en funktion implementerat som ett makro i inkluderingsfilen. Skalet till detta ar ofta effektivitet. Man undviker namligen ett fimktionsanropoch lagger i stallet in koden direkt i programtexten. Att det ar ett makro och inte enfimktion spelar for det mesta ingen roll, men det kan stalla till problem nar det somman anger som argument har sidoeffekter vid berakningen (t.ex. *ptr++). Det ar darisk att det kan komma att beraknas flera ganger i det expanderade makrot. (Se kap 9.)

De vanligaste inkluderingsfilema som standarden beskriver ar foljande:

<as sert.h> - programdiagnostik under korning

<ctype.h> - teckentyptester

<float.h> - flyttalsegenskaper och -granser

<errno.h> - felkoder fran standardfunktionerna

<limits.h>- numeriska egenskaper och granser

<locale.h> - anpassning till lokal miljo

<math.h> - matematiska funktioner

<setjmp.h>- icke-lokala hopp i program

<signal.h> - bantering av mjukvaruavbrott

<stdarg.h> - variabelt antal parametrar

<stddef.h> - grundlaggande typer och makron

<stdio.h> - standard in- och utmatning

<stdlib.h>-

generellt anvandbara funktioner

<string.h> - bantering av textstrangar

<time.h> - bantering av datum och tid

Ofta tillkommer ytterligare icke standardiserade inkluderingsfiler for systemspecifikabibliotek.

Vi kommer nu att ga igenom de viktigaste funktionema i bibliotek for bibliotek och ge

exempel pa deras anvandning. Fore upprakningama av funktionsdeklarationema i de

198 © Studentlitteratur

Page 202: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10.2 Gemensamma definitioner <stddef .h> och <errno.h>

foljande avsnitten skriver vi respektive #inciude-direktiv pa forsta raden for att visavilken inkluderingsfil som innehaller deklarationema. I sitt program skriver manendast #inciude-direktivet.

10.2 Gemensamma definitioner <stddef. h> och

<errno.h>

I filen stddef .h definieras bland annat pekarvardet null och typen size t. VardetNULL anvands for att markera att en pekare inte pekar nagonstans. Denna konstant-pekare har typen void * for att automatiskt kunna konverteras till alia pekartyper.(Definitionen av null finns ocksa i inkluderingsfilen stdio.h.) Typen size t ar enheltalstyp som anvands for att ange storleken i bytes av ett objekt. Operatom sizeofger ett varde av denna typ och ett antal standardfunktioner anvander sig dessutom avden. Oftast ar typen size t definierad som unsigned int.

I filen errno. h definieras en global heltalsvariabel errno. Nar det upptrader ett fel i enstandardfunktion tilldelar den aktuella funktionen errno en viss felkod och talar sedan

om att det blivit fel genom att ge ett speciellt returvarde. Man kan da i den anropandefunktionen undersoka vardet av errno for att fa ytterligare information om feletskaraktar. (Man kan t.ex. anropa standardfunktionen perror som skriver ut en lampligfelutskrift.) De olika felkodema finns som makron i inkluderingsfilen errno. h.

103 Teckentyper <ctype.h>

Man ar ofta betjant av att enkelt ta reda pa om ett tecken ar en bokstav, en siffra, "vitfetc. For detta andamal finns ett antal funktioner (oftast definierade som makron) somretumerar logiska varden:

#include <ctype., h>

int i salpha(int c) ; /•k ar c en bokstav? V

int isdigit(int c) ; /* ar c en siffra ? * /

int i salnum(int c) ; /* bokstav eller siffra? V

int isprint(int c) ; / * skrivbart tecken? /

int isspace(int c) ; /* "vitt" tecken? * /

int isupper(int c) ; /* stor bokstav? */

int i s1owe r(int c) ; /* liten bokstav? * /

int iscntrl(int c) ; /* styrtecken?

Nagra funktioner konverterar mellan tecken och retumerar det konverterade tecknet:

int tolower(int c) ; stor bokstav -> liten

int toupper(int c) ; liten bokstav -> stor * /

) Studentlitteratur 199

Page 203: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10. Standardfunktioner och standardbibliotek

Alia dessa funktioner ar sjalvklart mycket battre attanvanda antester direkt pa tecken-koder eftersom man lattblirberoende avenviss teckenkods egenskaper. Till exempel:

if ( isalpha (teck))

ar definitivt battre an

/* DALIGT! */

if (teck >='a' && teck <=' z' \ \

teck >='A' && teck <='Z)

eftersom den senare testen forutsatter att bokstavstecknen kommer i sekvens i koden

(som i ASCII men inte EBCDIC), medan den fbrra ar implementerad korrekt for denaktuella teckenuppsattningen oberoende av vilken kod som anvands. Den kan dess-utom ta hansyn till nationella bokstavstecken. Se nasta avsnitt.

10.4 Lokala konventioner <iocaie. h>

Beteendet hos funktionema i foregaende styeke ar normalt "amerikanskt". Detta inne-bar att tecknen tolkas direkt enligt teckenkodema och att man inte tar hansyn till att vit.ex. i Sverige anvander flera teckenkoder till bokstaver. Man bar i standarden lagt tillen mekanism for att losa detta genom att definiera en funktion avsedd att stalla omtolkningen av olika kategorier (teckentyper, tidsangivelser, bokstavsordningar, deci-malpunkt resp. decimalkomma etc.) till olika lokala omgivningars tolkning. Allt dettaar naturligtvis implementeringsberoende. Normalt far vi en amerikansk tolkning. Semanualen for ytterligare detaljer. Det finns en funktion som staller om tolkningama:

#include <locale.h>

char * setlocale (int category, const char ^locale);

De varden for category som enligt standarden skall finnas ar lc collate som styrregler for ordning mellan olika textstrangar, lc ctype som styr teckentestemas resultatenligt ovan, lc numeric som paverkar huruvida decimalkomma eller decimalpunktanvands av respektive formateringsfunktioner, lc time som reglerar hur tid ochdatumstrangar formateras och till sist ett samlande namn for dem alia: lc all.

Vilka varden som kan ges som locale ar beroende av vilket system man anvandermen det skall alltid finnas en minimal lokal omgivning som betecknas med strangen"C". Denna omgivning ar den som galler om inget annat anrop till setiocaie bargjorts.

10.5 Matematiska funktioner <math, h>

Vi bar redan i avsnitt 4.1 diskuterat matematiska funktioner. De ursprungliga versio-nema av dessa fimktioner bar resultat och parametrar som alltid ar av typen double.

200 © Studentlitteratur

Page 204: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10.6 Standard in- och utmatning <stdio. h>

acos asin atan cos sin tan cosh sinh

tanh exp log loglO sqrt ceil floor fabs

De trigonometriska funktionema tar och ger vinklar i radianer. cell och floor retume-rar narmaste heltal (men av typ double) storre resp. mindre an argumentet. fabs gerabsolut varde. Har ar nagra med tva parametrar:

double atan2(double x, double y); /* ger arctan y/x */

double pow(double x, double y); /* ger x upphojt till y */double fmod(double x, double y); /* ger rest vid x/y */

Ett vanligt fel ar att man glommer att ta med inkluderingsfilen och anvandert.ex. sin ialia fall.

/* FEL, inkluderingsfi1 fattas */

double f = sin (3.14159265 / 4);

Da kommer som vi diskuterade i kapitel 6 ett aldre system att anta normalfallet, d.v.s.att det ar en funktion som retumerar int. Att vardet tilldelas en flyttalsvariabel hjalperinte. I ett aldre system sker detta dessutom utan att man far nagon som heist vaming.

Ett narbeslaktat fel ar att man anropar med heltalvarden som parametrar och har glomtatt ta med inkluderingsfilen. Aldre system vamar inte och heltalsparametem kommerfelaktigt att tolkas som flyttal:

double f = pow(4.5, 3);

10.6 Standard in- och utmatning <stdio. h>In- och utmatning bygger pa tva centrala begrepp: strommar ochfiler. Strommar ar detmer generella begreppet och modellerar en sekvens av bytes eller tecken som kan lasaseller skrivas, oberoende av om de kommer ffan eller skickas till natanslutningar, termi-

naler, filer etc.

All in- och utmatning gar via strommar. Man skiljer pa tva olika slag av strommar:tecken-strommar och bindra strommar. Teckenstrommar ar indelade i rader med

nagon form av radbegransning som alia funktioner som hanterar rader kanner till.Binto strommar har ingen sadan indelning utan data lases och skrivs precis som de ar.

Filer anvands for att permanent lagra data. En fil kan lasas ffan och skrivas till. Detgors genom att man associerar en strom med filen, oppnar filen, och sedan laser ochskriver fran/till denna strom. Man kan stanga en strom varvid associationen med filenupphor. Funktionema haller reda pa en aktuell position i filen och denna position kanflyttas vid behov.

Strommar kan vara obuffrade eller buffrade. I en obuffrad strom transporteras datasom lases eller skrivs sa snart som mojligt dit de ska. Om strommen buffras samlas en

© Studentlitteratur 201

Page 205: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10. Standardfunktioner och standardbibliotek

storre mangd data fran ett flertal fiinktionsanrop ihop iiman den fysiska skrivningensker. Dettagors av effektivitetsskal sa att tidskravande in- och utmatningsoperationerbehover ske mer sallan.

Modellenmed strommarkommer fran Unix och ger ett mycket flexibelt satt att hanterain- och utmatning. Fran Unix kommer ocksa tre fordefinierade strommar: standardinput, standard output och standard error. Dessa ger inlasning/utmatning respektivediagnostikutmatning fran/till "normalt" stalle. Detta stalle ar oftast anvandarenstangentbord och kommandofonster och strommama ar d^ obuffrade.

Vi gar nu igenom olika gmpper av funktioner. For att de skall fungera skall man normalt alltid ha med

#include <stdio.h>

i boijan av sitt program. I denna finns ett antal typer och makron definierade somanvands av fimktionema, t.ex. strommama stdin, stdout, stderr och returvardet for

filslutsmarkering eof.

10.6.1 Filer

Stmktur och betydelse av filnamn och operationer pa filsystemet ar beroende av hurdet omgivande operativsystemet bar definierat dessa.

En fbrdefinierad typ file anvands for att representera en strom. Funktioner anvandersig alltid avpekare till denna typ som parameter eller returvarde. Om en flmktion somnormalt retumerar en sadan pekare misslyckas retumerar den i stallet en NULL-pekare.

Att associera en strom med en fil gors med funktionen f open:

FILE *fopen(const char ^filename, const char *mode);

Funktionen oppnar filen for lasning/skrivning. Hur filen skall anvandas anges medhjalp av strangen mode. Vi tar nagra exempel:

/* Exempel pa oppning av filer */

FILE *infil, *utfil, *trans, *mfil;

infil = fopen("kontoplan", "r");

/* oppna for endast lasning */

utfil = fopen ("rapport", "w");

/* for skrivning, skapa filen */

/* om den inte finns redan, annars */

/* tom filen pa tidigare innehall */

trans = fopen("transaktion", "a+");

/* for tillagg till slutet (append) */

/* men aven for lasning */

202 © Studentlitteratur

Page 206: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10.6 Standard in- och utmatning <stdio.h>

mfil = fopen("matdata", "r+b");

/* lasning/skrivning av en */

/* existerande binardatafil

I mode-strangen skall alltid finnas antingen r, w, eller a forst, for lasning, skrivning pany och torn fil respektive tillagg till slutet pa existerande fil (eller nyskapad fil om deninte redan fanns). Till detta kan fogas tecknen +,betecknande dven den motsatta opera-tionen och/eller b som betecknar binardatafil. En omgivning behover inte nodvandigt-vis gora skillnad pa text- och binarfiler.

Normalt horde vi lagt in tester om det retumerade vardet blev null, vilket betyder attoppningen av filen misslyckats. Ytterligare information om felet fmns i sa fall som enfelkod i errno.

Foljande fimktioner hanterar strommar associerade till filer:

int fclose (FILE ^stream);

int fflush (FILE ^stream);

FILE *tmpf ile (void) ;

fclose stanger en strom. Detta ar anvandbart da man vill anvanda manga strommarefter varandra och om omgivningen satter en begransning pa antalet samtidigt oppnadestrommar. Nar ett program avslutas stangs automatiskt alia oppna strommar.

fflush tommer eventuell buffert pa data sa att det som finns i den skrivs dit det skall.Detta gors automatiskt vid en fclose.

tmpfiie oppnar en ny temporar fil for lasning och skrivning. Filen tas automatiskt bortvid stangning.

Tva funktioner opererar pa filer i filsystemet:

int remove(const char *filname);

int rename(const char ^old^ const char *new) ;

Dessa tar bort resp. byter namn pa en existerande fil och retumerar o om det gick bra.

Det finns aven funktioner for att dirigera om redan existerande strommar till filer samtfunktioner for att andra pa buffring av strommar.

10.6.2 In- och utmatning av oformaterad text

Har tar vi upp funktioner som laser in och skriver ut tecken och strangar precis som deser ut. Forst kommer nagra teckenfunktioner:

) Studentlitteratur 203

Page 207: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10. Standardfunktioner och standardbibliotek

int fputc(int c, FILE ^stream);

int putc(int c, FILE ^stream);

int putchar(int c);

int fgetc(FILE ^stream);

int getc (FILE ^stream);

int getchar(void);

fputc och putc skriver bada ett tecken till den givna strommen. putc ar oftast imple-menterad som ett makro av effektivitetsskal. (fputc ar dock alltid en riktig funktion.)Man far da se upp med parametervarden som bar sidoeffekter vid berakningen (t.ex.nar vi tar tecken stegvis ur en strang med *str++) eflersom parametervardet kan berak-nas flera ganger i makrot. putchar ar ekvivalent med putc till standard-

utmatningsstrommen. Alia tre retumerar tecknet som skrevs eller vardet eof vid fel.

Funktionema for inlasning ar belt analoga ocb retumerar det inlasta tecknet. KodenEOF retumeras vid slut pa filen eller annat fel. Det ar darfor samtliga teckenfunktionerretumerar int i stallet for char eftersom eof naturligtvis inte kan vara en tillaten teck-enkod. Vore det sa kunde inte det tecknet forekomma i en fil. eof ar definierad som ett

makro i inkluderingsfilen (ocb skall vara ett negativt tal).

Ytterligare en teckenfunktion finns:

int ungetc (int c, FILE *stream);

Funktionen stoppar tillbaka ett tecken till strommen sa att det kan lasas vid nasta las-ning. Man kan inte sakert stoppa tillbaka mer en ett tecken ocb att kombinera funktionen med flyttning av aktuell filposition etc. ger odefmierat resultat. Funktionen paver-kar inte inneballet i filen som strommen ar kopplad till.

Det finns aven en samling funktioner for in- ocb utmatning av textstrangar:

int fputs(const char FILE ^stream);

int puts(const char *s);

char *fgets(char int n, FILE *stream);

char *gets(char *s);

fputs skriver en strang precis som den ar till den givna strdmmen. puts skriver alltidtill stdout ocb lagger dessutom till ett nyradstecken (av bistoriska skal). Bada retumerar vardet eof om skrivningen misslyckades.

fgets laser en rad fran den givna strommen. Den laser dock bogst n-i tecken for att faplats med noll-tecknet som skall markera slutet pa strangen. Nyradstecknet finns kvarocb blir det sista tecknet i strangen om det fanns plats, gets laser fran stdin. Manmaste garantera att det finns tillrackligt med plats ocb nyradstecknet finns inte med iden inlasta strangen (av samma bistoriska skal som ovan). Eflersom gets inte bar

204 © Studentlitteratur

Page 208: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10.6 Standard in- och utmatning <stdio. h>

nagon langdparameter, ar det enfarlig funktion som egentligen inte bor anvandas. Detfinns alltid en risk att det teckenfalt man skickar med inte racker till. BMa retumerar s

om lasningen gick bra annars null.

Vi tar ett exempel med ett program som laser en fil kiartext. txt som innehaller ettmeddelande som skall krypteras och skrivas ut pa filen hemiig.txt. Vi vill dessutomspara alia krypterade meddelanden i en loggfil. Kryptomekanismen avslojar vi inte bar.Funktionen koda lamnar vi saledes oskriven. (Den far dock endast arbeta med kodningpa ett sadant satt att radema fortfarande enbart innehaller text.)

#include <stdio.h>

/* las fil, kryptera och skriv pa tva utfiler */

int main ()

{

FILE *in, *ut, *logg;

char rad[500];

if ((in = fopen("kiartext.txt", "r")) == NULL) {

fputs("Kan ej oppna infilen\n", stderr);

exit(99) ;

}

if ( (ut = fopen("hemlig.txt" , "w")) == NULL) {

fputsC'Kan ej dppna utfilenXn", stderr);

exit(99);

}

if ((logg = fopen ("loggfil.log", "a")) == NULL) {

fputs("Kan ej oppna loggfilen\n", stderr);

exit(99) ;

}

while (fgets(rad, 500, in) != NULL) {

koda(rad) ;

if (fputs(rad, ut) == EOF) {

fputs("Skrivfel pa utfilen\n", stderr);

}

if (fputs(rad, logg) == EOF) {

fputs("Skrivfel pa loggfilen\n", stderr);

}

Vi anvander flmktioner som laser och skriver en rad i taget. Vi testar alia returvardensa att vi ser att allt gatt bra. Annars skriver vi ut felutskrifter pa strommen standarderror, stderr. exit ar en standardfunktion som direkt avbryter programkomingen.

) Studentlitteratur 205

Page 209: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10. Standardfunktioner och standardbibliotek

10.6.3 Formaterad in- och utmatning av text

Vi beskriver nu in- och utmatning av text som omvandlas till och fran andra intemadatatyper. Vi har redan sett exempel pa detta i kapitel 3 dar vi anvande printf ochscanf och gjorde en ordentlig genomgang av deras formatstrangar. Har diskuterar vibara nagra andra funktioner som ar beslaktade med printf och scanf. For utmatninghar vi fimktionema

int printf(const char *format, ...);

int fprintf (FILE *stream, const char ^format, ...);

int sprintf (char const char ^format, ...);

Alia tre gor samma typer av konverteringar. Till fprintf anger man till vilken stromutmatningen skall ske. sprintf ar egentligen ingen utmatningsfunktion. Den laggerden formaterade strangen dit s pekar i stallet. Detta ar anvandbart om man vill ha denformaterade strangen att arbeta vidare med intemt. Funktionema retumerar antaletutskrivna tecken (for sprintf raknas inte det avslutande noll-tecknet), eller ett nega-tivt tal om nagot gick fel.

For konvertering vid inmatning finns tre analoga funktioner

int scanf(const char ^format, ...);

int fscanf (FILE ^stream, const char ^format, ...);

int sscanf(const char ^s, const char ^format, ...);

Parameterlistan skall har fortsattas med pekare till utrymme motsvarande de omvand-lingsspecifikationer som fmns i formatstrangen. Funktionema retumerar antalet lyck-ade konverteringar eller eof om nagot gick fel eller indata redan var slut. Observera attdet forsta tecknet for vilken konvertering misslyckas stoppas tillbaka i indatastrom-men, sa upprepade misslyckade anrop forbmkar inte indata och programmet kanhamna i en oandlig snurra.

Det fmns ocksa versioner av alia printf- och scanf-funktioner som utnyttjar mekanis-men for variabelt antal parametrar. De har fatt samma namn men med prefixet v. Seavsnittet om standardfunktioner for variabelt antal parametrar senare i detta kapitel.

Exempel med printf och scanf finns gott om i kapitel 3. Vi tittar pa ett sprintf-exempel: Pa ett visst system har filnamnen tva delar med foljande syntax:

[TEMP]iMINFIL

Vi rakar nu ha dessa tva delar av filnamnet i tva separata strangar: kat (med vardet"TEMP") respektive fii (med "minfil"). For att kunna oppna filen med fopen behovervi en strang. Vi konstmerar den enkelt med sprintf:

206 © Studentlitteratur

Page 210: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10.6 Standard in- och utmatning <stdio. h>

#include <stdio.h>

/* anvand sprintf for att kombinera strangar */

FILE *in;

char filnamn[100];

/* ... V

sprintf(filnamn, "[%s]:%s", kat, fil);

in = fopen(filnamn, "r");

10.6.4 Direkt in- och utmatning

Dessa funktioner anvands nar in- och utdata inte ar text utan generella binara data. Harkan vi hela tiden tala cm att vi har en aktuell position i filen. Den hanvisar till borjanpa filen nar filen just oppnats (eller till slutet cm filen oppnats med "append mode")och sedan hanvisar aktuell position till nasta objekt som star i tur att lasas eller skrivas.

For all lasning och skrivning av binara data anvands foljande fiinktioner:

size_t fread(void *ptr, size_t size, size_t n, FILE ^stream);

size_t fwrite(const void *ptr, size_t size, size_t n,

FILE * stream) ;

De laser till respektive skriver fran det stalle i minnet dit pekaren ptr pekar. n styckenobjekt av vardera storlek size kommer att lasas/skrivas fran/till den givna strommen.Bada retumerar antalet lasta/skrivna objekt, vilket kan vara farre an n om filen tog slut

eller skrivfel uppstod.

Vi kan ocksa uttryckligen flytta den aktuella positionen. Det finns tva satt att represen-tera en position i en fil. Det forsta sattet antar att bytepositionema ar numrerbara medett langt heltal. Foljande funktioner anvander detta:

long ftell(FILE ^stream);

void rewind(FILE ^stream);

int fseek(FILE ^stream, long offset, int whence);

Funktionen fteii retumerar aktuell position i filen (eller -i vid fel) och rewind satterpositionen till filens boijan. Funktionen f seek kan anvandas for att satta positionen tilloffset, vilket anges relativt boijan pa filen, aktuell position eller slutet pa filen. Vadsom galler anges med seek set, seek cur respektive seek end som varde pa whence.

Lat OSS ta ett kort exempel med en registerfil av lagerposter (sadana som i exemplet ikapitel 8) dar av nagon anledning postema 58 och 59 behover byta plats. Vi skriver ettkort program som gor detta:

) Studentlitteratur 207

Page 211: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10. Standardfunktioner och standardbibliotek

#include <stdio.h>

/* byt plats pa tva poster i en fil */

struct vpost {

long varunr;

int antal;

} ;

int main ()

{

struct vpost vl,v2;

FILE *lfil;

Ifil = fopen("lagerf11" , "rb+");

fseek(lfil, 58 * sizeof(struct vpost), SEEK_SET)

fread(&vl, sizeof(struct vpost), 1, Ifil);

fread(&v2, sizeof(struct vpost), 1, Ifil);

fseekdfil, -2 * sizeof (struct vpost), SEEK_CUR)

fwrite(&v2, sizeof(struct vpost), 1, Ifil);

fwrite(&vl, sizeof(struct vpost), 1, Ifil);

Vi har for iMlaslighetens skull struntat i att kontrollera returvardena fran standard-funktionema. Det borde vi egentligen gjort.

Filen ar oppnad med "rb+" for att visa att vi bade vill lasa och skriva den redan existe-rande binarfilen. Vi flyttar filpositionen till post nummer 58 (den forsta ar nummer 0)genom att med seek set ange att det galler avstandet fran borjan pa filen. Notera att vimaste multiplicera med storleken av en post eftersom funktionema vill ha positioner iantal bytes. Vi laser in ratt antal bytes till variablema vi och v2. For att skriva tillbakapostema i omvand ordning flyttar vi filpositionen tva steg tillbaka genom att medSEEK CUR ange att vi vill positionera oss relativt aktuell position.

Det andra sMet att representera en position i en fil anvands om positionen inte kananges med heltal, t.ex. pa grand av filen storlek. Det firms tva funktioner:

int fgetpos (FILE ^stream, fpos_t *pos);

int fsetpos (FILE ^stream, const fpos_t *pos);

Bada retumerar o om inget fel uppstatt. Positionsvardena tas fran och laggs dit pekarenpos pekar. Typen fpos t ar definierad i stdio. h och exakt hur ett objekt av derma typser ut ar inte specificerat i standarden. De varden som kan anvandas ar endast de somfas fran fgetpos.

10.6.5 Felhantering

Nagra fa funktioner anvands for att hantera fel och filslutshandelser:

208 © Studentlitteratur

Page 212: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

int feof(FILE ^stream);

int ferror(FILE ^stream);

void clearerr (FILE *stream),

void perror(const char *s);

10.7 Generellafunktioner <stdlib. h>

Funktionen feof testar om filslutet bar natts for en strom, ferror cm nagot fel upp-statt, ciearerr nollstaller feltillstandct eftersom det inte gors automatiskt. Funktionenperror skriver pa strommen stderr ut strangen s, foljd av ett lampligt felmeddelandefor den felsituationen som uppstatt. Den utnyttjar da den felkod som finns i errno.

10.7 Generella funktioner <stdiib. h>

I stdiib.h bar man samlat de generella funktioner som inte passar in i nagot annatbibliotek. Inkluderingsfilen skall finnas med nar funktionema skall anvandas:

#include <stdlib.h>

10.7.1 Strangkonvertering

I biblioteket finns funktioner som givet en strang boppar over blanka i borjan, tolkar defoljande tecknen som ett tal ocb slutar nar ett tecken som inte kan inga i talet patrafFas.Det finns en gammal uppsattning funktioner:

double atof(const char *ptr);

int atoi(const char *ptr);

long int atoi(const char ^^Ptr) ;

Dessa funktioner konverterar tecknen i strangen till ett flyttal, ett beltal respektive ettlangt beltal. De ger normalt inga felmeddelanden ocb retumerar o (o. o) om ingen kon-vertering alls kunde ske. Nyare versioner av funktionema ger oss storre mojligbeter attkontrollera resultatet. For beltal kan dessutom basen anges:

double strtod(const char *ptr, char **endptr);

long int strtol(const char *ptr, char **endptr,int base);

unsigned long strtoul (const char *ptr,char **endptr, int base);

Dessa funktioner ger mojligbet att bantera eventuella fel. Dels satts ermo till lampligfelkod om talet blir for stort eller for litet for att kunna representeras, dels kan manskicka med en pekare till en teckenpekare endptr. Denna pekare satts av funktionen attpeka pa forsta tecken efter de som just konverterats i strangen. Det gor att man kan seom nagot alls blev konverterat ocb man ges ocksa mojligbet att fortsatta konverte-ringen med resten av tecknen i strangen.

Om vi t.ex. i strangen ostr bar en sekvens av oktala siffror kan vi tolka detta som ettoktalt beltal ocb fa vardet:

© Studentlitteratur 209

Page 213: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10. Standardfunktioner och standardbibliotek

talet = strtol(ostr, NULL, 8);

Att vi angernull sompekare till pekarengor att funktionen inte bryr sig om att flyttanagon pekare till slutet av den omvandlade strangdelen.

Sadana bar omvandlingar kan som vi tidigare sett ocksa goras med sscanf.

10.7.2 Slumptalsgeneratorer

Vid simuleringar eller spel vill man ofta kimna lata ett program "kasta taming" for attslumpmassigt avgora den fortsatta exekveringen av programmet. For detta andamalanvands slumptalsgeneratorer. Dessa genererar faktiskt inte nagra riktiga slumptalutan bara nagot som ser ut som slumptal. (Se diskussionen pa sidan 121.) For att kunnagenerera olika serier av slumptal initieras ofta slumptalsgeneratom med ett fro. Vidflera komingar med samma fro genereras samma slumptalsfoljd, vilket kan vara varde-fullt om man vill testa olika versioner av nagot med samma styrdata. Slumptalen somar genererade pa detta satt kallas ofta pseudo-slumptal for att markera att de inte repre-senterar "riktig" slump.

Det fmns tva standardfiinktioner som realiserar detta:

int rand(void)}

void srand(unsigned int seed);

rand retumerar nasta slumptal i serien vid varje anrop. Heltalen ar i intervallet o tillRAND MAx. Generatom ges ett fro med srand. I avsnitt 6.7.4 visade vi bur dessa ftmk-

tioner skulle kunna vara implementerade. Se aven slumpmelodiexemplet pa sidan 42.Ett vanligt satt att fa tag pa ett for vaije gang slumpmassigt fro till generatom ar att taaktuell tid med standardfunktionen time, sa som visades i slumpmelodiexemplet.

10.7.3 Minneshantering

Att dynamiskt kunna allokera objekt ger ett flexibelt satt att bantera data. Eftersomdynamiskt tillgangligt minnesutrymme inte deklarerats (inte givits nagot variabel-namn) kan det endast kommas at via pekare. Alia funktioner som allokerar minne geralltsa som returvarde en pekare till det tilldelade minnesutrymmet. Var funktionema

verkligen far sitt minne ifran varierar naturligtvis fran system till system ocb man fkrinte anta nagot om detta i sitt program.

void *calloc(size_t n, size_t size);

void *malloc(size_t size);

Bide caiioc ocb maiioc allokerar minne. Skillnaden att caiioc allokerar flera objekti rad ocb garanterar att det allokerade minnet inneballer idel nollor. maiioc ger endast

plats for ett oinitierat objekt. Bada garanterar att allokeringen av objekten sker pa ratt

210 © Studentlitteratur

Page 214: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10.7 Generellafunktioner <stdlib.h>

granser i miimet {alignment). Storleken size beraknas i alia praktiska fall med opera-torn sizeof for flyttbarhetens skull. Resultattypen anges som void en pekartyp somkan konverteras till vilken annan pekartyp som heist, se kapitel 7. Om funktionemamisslyckas med att skapa minne retumeras null.

Ytterligare tva funktioner bar med minnesallokering att gora:

void free(void *ptr);

void *realloc (void *ptr^ size_t size);

Dessa bMa funktioner far som parameter en pekare till ett minnesutrymme som tidi-gare allokerats av maiioc eller caiioc. (Skulle man ge nagon annan pekare som parameter eller anropa med samma pekare fler an en gang blir resultatet odefinierat.) Funk-tionen free ger tillbaka minne till systemet sa att det kan ateranvandas. Funktionen vetsjalv om hur mycket minne som skall ges tillbaka. Funktionen reaiioc ar avsedd attanvandas om storleken for ett redan allokerat objekt behover andras. Minnesutrymmefor ett nytt objekt allokeras. Om mar den minsta av antalet minnesceller (bytes) for detgamla resp. nya objektet, sa garanteras att de forsta mminnescellema i det nya objektetbar samma inneball som motsvarande minnesceller i det gamla objektet.

Se kapitel 7 ocb 8 for exempel pa bur minne allokeras.

10.7.4 Interaktion med omgivningen

Har ges en samling funktioner som kommunicerar med omgivningen, oftast operativ-systemet:

void abort(void) ;

Denna funktion avbryter exekveringen av programmet omedelbart ocb signalerar palampligt satt till omgivningen att komingen misslyckats. Vad som da bander ar imple-menteringsberoende. Unix ger t.ex. en dump av bela programminnet. Funktionenretumerar aldrig.

int atexit(void (*func)(void));

void exit(int status);

Funktionen atexit registrerar funktionen func att anropas vid programmets normalaavslutande. Funktionen func kan t.ex. anvandas for att ta bort temporarfiler eller goraliknande uppstadning. Man kan registrera flera funktioner med fler anrop. De kommerda att utforas i omvand ordning vid programslutet. Ett program anses sluta normaltantingen nar man nar slutet av funktionen main eller nar man gdr ett anrop till funktionen exit. Efter det att funktioner som registrerats med atexit utforts toms alia buffer-tar till strommar ocb strommama stangs sedan. Till sist atervander programmet till

© Studentlitteratur 211

Page 215: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10. Standardfunktioner och standardbibliotek

omgivningen och vardet av parametem status gors tillgangligt pa nagot satt foromgivningen. En statuskod lika med noil betyder att kdmingen lyckats belt. Andravarden ar nagon form av implementeringsberoendediagnostisk kod. Funktionen exitretumerar naturligtvis aldrig.

En omgivningbar mojligbet att ge en mangd attribut/vardepartill ett program, t.ex. foratt tala om vilken typ av terminal man bar, vad anvandaren som kor programmet betereller vilket textredigeringsprogram som anvandaren foredrar etc. Funktionen getenvger OSS mojligbet att ta reda pa denna typ av information.

char *getenv(const char *name);

Man ger som argument namnet pa det attribut man ar intresserad av ocb far da motsva-

rande varde tillbaka. Bade attribut ocb varde ar textstrangar. Vilka attribut som finns,vilka varden som ar meningsfulla ocb bur de ges till programmet ar implementeringsberoende. Finns inte det efterfragade attributet retumeras null. Ett exempel:

char *anvandar_namn;

if ((anvandar_namn = getenv("USER")) == NULL) {

anvandar_namn = "okand":

}

printf("Valkommen %s!\n", anvandar_namn);

Detta programavsnitt avlaser (om det finns) ett attribut user som bar anvandarensnamn som varde.

Det finns en funktion som kor ett godtyckligtkommandopa det system man anvander:

int system(const char ^string);

Som argument ges en kommandorad avsedd att tolkas av det omgivande systemetsnormala kommandotolkare. Kommandoradens betydelse ar belt implementeringsberoende liksom det retumerade beltalet.

10.7.5 Funktioner for sokning och sortering

void *bsearch(const void *key, const void *base,

size_t n, size_t size,

int (*compar) (const void const void *));

Denna funktion soker i ett fait, pekat till med base, efter ett objekt som ar likadant somdet som key pekar till. Man anger antalet objekt n i faltet, storleken size pa varjeobjekt samt en pekare till en funktion som jamfor objekt med varandra. Denna jamfo-relsefiinktion skriver man alltsa sjalv. Den skall ba tva pekarparametrar till objektensom skall jamforas ocb retumera ett beltal som ar mindre, storre eller lika med noli foratt representera att det forsta objektet ar mindre an, storre an eller lika med det andra

212 © Studentlitteratur

Page 216: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10.8 Strdnghantering <string.h>

objektet. Funktionen bsearch kraver dessutom att objekten i faltet ar sorterade iimansokning paboijas. (Funktiosnamnet antyder att den ar implementerad med en binarsok-ningsalgoritm). Returvardet fran funktionen ar antingen en pekare till det funna objektet eller null om det inte kunde hittas.

Sortering av objekt i ett fait kan ske med en liknande funktion:

void qsort(void *base, size_t n, size_t size,

int (*compar) (const void const void '^));

Parametrama betyder precis samma sak som i foregaende funktion. Har sorteras objekten enligt de kriterier man anger med jamforelsefunktionen compar.

10.8 Stranghantering <string.h>Det finns en samling fiinktioner for att hantera strangar. Vi sag i kapitel 7 att text-

strangar representeras av pekare till det forsta tecknet och att slutet markeras med ettnolltecken (som inte anses inga i strangen men som det maste finnas plats for). Detenda som ar inbyggt i sjalva spraket C om strangar ar strangkonstanter som skrivsinom I sadana konstanter genererar kompilatom ett nolltecken sist som slut-markering. Teckenpekare, teckenfalt och strangar har i ovrigt ingen speciell status i Coch darfoTbehovs standardfunktioner for stranghantering.

10.8.1 Kopiering och sammanfogning

char *strcpy (char *sl, const char *s2);

char *strncpy(char *sl, const char *s2, size_t n)

BMa dessa funktioner kopierar en strang fran s2 till si (tank pa riktningen vid en till-delningsoperator). strcpy forutsatter att det finns tillrackligt med plats dit si pekar,medan stmcpy kopierar hogst n tecken. I bada fallen laggs ett nolltecken efter detkopierade i si utom nar langden pa strangen s2 ar n eller langre.

char *strcat (char *sl, const char *s2);

char *Stmeat(char *sl, const char *s2, size_t n);

Dessa funktioner har samma betydelse pa parametrama som ovan men kopierar instrang s2 efter strang si sa att si blir en av si och s2 sammanfogad (eng. catenated)strang. Forsta tecknet fran s2 hamnar alltsa pa nolltecknets plats i si. I stmcat ar dethogst n tecken som kopieras. Har vi till exempel ett teckenfalt fnamn med strangen"program" lagrad i det, kan vi enkelt utoka strangen till program, c:

strcat(fnamn, ".c");

Vi maste dock vara sakra pa att det finns plats for de extra tva tecknen.

© Studentlitteratur 213

Page 217: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10. Standardfunktioner och standardbibliotek

10.8.2 JSmforelse

int strcmp (const char *sl, const char *s2);

int strncmp(const char const char *s2, size_t n) ;

Har jamfors strangama med varandra, i teckenkodsordning. Heltalet som retumeras arstorre an, mindre an eller lika med noil, representerande att si ar storre an, mindre aneller lika med s2. Observera att teckenkod for teckenkod jamfors, strangen "14" art.ex. mindre an strangen "2" och "b" ar mindre an "a" (for ASCII-koden). Funktionenstrncmp jamfdr hogst n tecken. Se t.ex. funktionen hittakom pa sidan 178.

For att kunna sortera strangar enligt lokala teckenordningar (definierade enligt funktionen setiocaie) finns en funktion strcoii som konverterar strangar till ett jamforbartformat. Se manual for detaljer.

10.8.3 Langdberakning

size t strlen(const char *s);

Funktionen retumerar antalet tecken i hela strangen (exklusive nolltecknet).Anropet

n = strlen("En strang\n");

ger n vardet lo.

Foljande funktioner ger langden av en inledande delstrang av strangen s:

size_t strspn (const char *s, const char *s2);

size_t strcspn(const char *s, const char *s2);

Alia tecknen i denna inledande del av s skall for strspn aterfinnas bland samlingentecken i s2. Rakningen av tecken i s avbryts alltsa sa fort ett tecken dyker upp som intefinns i strangen s2. Ett exempel:

/* rakna antalet hexadecimala siffror i borjan av str */

antalhexsif = strspn(str, "0123456789ABCDEFabdcef");

Har beraknas antalet hexadecimala siffror som strangen str inleds med.

For strcspn galler det motsatta. Den retumerar langden av en inledande sekvens avtecken som inte ingar i s2.

10.8.4 Sokning

char *strchr (const char *s, int c);

char *strrchr(const char *s, int c);

char *strpbrk(const char *s, const char *s2);

char *strstr (const char *s, const char *s2);

214 © Studentlitteratur

Page 218: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10.8 Strdnghantering <string. h>

Dessa funktioner soker alia i en strang s. Funktionen strchr soker after forsta fore-komsten av tecknet c, strrchr after sista. strpbrk letar after forsta forekomsten i s av

ndgot av tecknen i s2 och strstr after forsta forekomsten av hela strdngen s2. Aliafiinktionema retumerar en pekare till det flinna eller null om inget hittades. strchroch strrchar finns 1vissa implementeringar under namnen index och rindex.

Vill vi t.ex. byta ut alia ? mot ! i strangen str kan vi skriva:

/* byt alia ? mot ! i strangen str */

char *p = str;

while ( (p = strchr(p, '?')) != NULL) {

*p = ' \ ' ;

}

Pekaren p flyttas fram att peka pa nasta fragetecken i vaije varv. Vi byter det till ettutropstecken och i nasta varv hittar vi nasta fragetecken anda tills det inte finns fler ochNULL retumeras.

Vi kan gora nagot liknande med hela delstrangar. Lat oss byta ut alia "katt" mot"hund" i strangen str:

/* byt alia "katt" mot "hund" i strangen str */

char *p = str;

while ((p = strstr(p, "katt")) != NULL) {

strncpy(p, "hund", 4);

}

Har utnyttjar vi egenskapen hos strncpy att den inte kopierar fler an angivet antaltecken och inte lagger till ett nolltecken om det inte rymdes inom antalet. (Vi vill inteha en slutmarkering mitt inne i strangen). Om orden vi bytte ut inte var lika langa hadevi inte kunnat gora bytet sa bar enkelt. Vi hade nog fatt ta till en extra strang, atmin-stone om det vi skulle byta till var ett langre ord.

Foljande funktion anvands for att dela upp strangen s i delar med avgransare specifice-rade i strangen s2. Se manual for detaljer.

char *strtok(char *s, const char *s2);

10.8.5 Operationer i generellt minne

Det finns en samling funktioner som arbetar med minnesutrymme som inte ar strangar

utan fait av bytes (minnesceller) som inte avslutas med ett nolltecken. Dar anger man istallet uttryckligen antalet bytes som avses. Pekama till objekten anges som void * foratt de skall kunna vara av vilken pekartyp som heist. Funktionema kan anvandas forfait, poster eller godtyckliga datastrukturer som ligger i en foljd i minnet. De fimgerarpa samma satt som motsvarande strangfunktion:

© Studentlitteratur 215

Page 219: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10. Standardfunktioner och standardbibliotek

void *memcpy(void *sl, const void *s2, size_t n);int memcmp(void *sl, const void *s2, size_t n);void *memchr(void *s, int c, size_t n);void *memset(void *s, int c, size_t n);

Den sistnamnda anvands for att fylla n bytes med vardet c.

For att t.ex. nollstalla ett belt fait av heltal kan vi skriva:

int stort [100000];

/* nollstall faltet stort */

memset(stort, 0, sizeof(stort));

Vitsen med att gora sa bar i stallet for att gora en snurra ocb nolla element for elementar att memset eventuellt kan gora det snabbare genom att utnyttja speciella maskin-instruktioner.

Notera specialfallet av faltnamnet stort som operand till sizeof. Det ensamma falt-namnet betecknar dar inte ett pekarvarde (vilket det gor i den forsta parametem) utanhela faltet.

10.9 Datum och tid <time.h>

Tva olika sorters tider bebandlas bar, delsprocessortid, som ar ett matt pa den tid pro-cessom bittills forbrukat pa att kora programmet, dels kalendertid, som i olika formerger OSS klockslag, datum etc.

10.9.1 Processortid

clock_t clock(void);

Denna funktion retumerar processortid, uttryckt i antalet "klocktickningar", som programmet bittills forbrukat. Makrot clocks per sec anger bur manga tickningar detgar pa en sekund processortid. (I aldre implementeringar anvands makrot clk tck.) Vikan t.ex. skriva:

#include <time.h>

printfC'Vi har forbrukat %.2f s processortid\n,

clockO / (double) CLOCKS_PER_SEC) ;

10.9.2 Kalendertid

Tva typer deklareras ocb anvands bar, dels en aritmetisk typ for representation avkalendertid (exakt vad typen innebMlerar implementeringsberoende):

t ime_t

216 © Studentlitteratur

Page 220: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10.9 Datum och tid <time. h>

dels en posttyp for att representera en uppdelad time_t-tid:

struct tm

Denna struct skall innehalla atminstone foljande komponenter:

int tm_ sec ; 0-59 */

int tm_ min ; 0-59 */

int tm_ h o u r ; 0-23

int tm_ mday ; /* 1-31

int tm_ men ; / * 0-11 CBS!

int tm_ year ; /* sedan 1900

int tm wday; /* sondag = 0

int tm yday ; /* dag/ar 0-365 OBS! V

int tm i s d s t ; /* sommartid

Dessa element kan i en viss implementering komma i godtycklig ordning. tm isdst arett logiskt varde som anger cm sommartid tillampas eller inte.

time_t time(time_t *timer);

double difftime (time_t timely time_t timeO);

Funktionen time retumerar (och lagger dit timer pekar) klockslag vid anropet, -iretumeras om tid inte ar tillganglig. difftime retumerar i sekunder skillnaden mellanklockslagen tlmel - tlmeO.

For att konvertera ett klockslag till kalenderinformation finns ett antal funktioner.

struct tm *localtime(const time_t *timer);

struct tm *gmtime(const time_t *timer) ;

Dessa bada konverterar ett klockslag till sina uppdelade komponenter, till lokal tidresp. Greenwich Mean Time (GMT). Har foljer ett par exempel:

#include <time.h>

/* skriv ut lokal tid och skillnaden gentemot greenwichtid */

time_t tid;

struct tm *nu, *gmtnu;

time(&tid);

nu = localtime(&tid);

gmtnu = gmtime(&tid);

printf ("Klockan ar %02d:%02d,", nu->tm_hour, nu->tm_min);

printfC'och tidsdifferens till GMT ar %d timmar",

nu->tm_hour - gmtnu->tm_hour);

Notera att de pekare som locaitime och gmtime retumerar pekar till statiskt deklare-rade poster som kan andra varde vid ytterligare anrop till funktionema. Om vi villspara postvardet definitivt maste vi tilldela det till en egen postvariabel.

© Studentlitteratur 217

Page 221: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10, Standardfunktioner och standardbibliotek

Det firms ocksa en flmktion mktime somomvandlar at andra ballet, d.v.s. tar en pekaretill en post med uppdelad tid och retumerar ett time t-varde:

time_t mktime(struct tm *timeptr);

Funktionen bryr sig inte om vardenapa veckodag och dagnummer men dessaoch aliaandra varden fylls i ochjusteras till de intervall somdefinierats ovan. Ett exempel:

/* lagg till fem timmar till en uppdelad tid-post */time_t tid, tid5;

struct tm *nu;

time(&tid);

nu = localtime(&tid);

nu->tm_hour += 5; /* fem timmar senare */

tidS = mktime(nu);

Anropet till mktime retumerar en tid somar femtimmar senare och bar dessutom juste-rat alia ingaendedelar i nu-posten, vilket behovs om vi passeratmidnatt. (Funktionenfungerar t.ex. fint for att rakna ut pa vilkenveckodag nagon foddes.)

Funktionema

char *asctime(const struct tm *timeptr);

char *ctime(const time_t ^timer);

ger bMa en textstrang med datum och klockslag pa amerikansktvis:

"Tue Mar 15 11:19:10 2011\n"

ctime gor konvcrteiing enligt lokal tid. For att fa en strang med lokal tid pa lokaltskrivsatt (enligt setiocaie) anvands funktionen

size_t strftime(char *s, size_t maxsize, const char ^format,

const struct tm *timeptr) ;

som konverterar klockslagskomponentema enligt formatstrangen med samma forma-teringsprincip som printf. For att t.ex. fa datumoch tid pa foljande vis

"Lokal tid: Tisdag 2011-03-15 11:19:10"

anges detta format

strftime(buf, 100, "Lokal tid: %A %Y-%m-%d %H:%M:%S", nu);

Resultatet laggs i buf med maximalt lOO tecken. For fullstandig lista over omvand-lingsspecifikationer i formatstrangen, se manual.

218 © Studentlitteratur

Page 222: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10,10 Icke-lokala hopp <setjmp.h>

10.10 Icke-lokala hopp <set jmp. h>Ett hopp med goto kan endast hoppa till ett lage inom samma funktion. Det gar inte attmed goto hoppa in i en annan funktion, vilket ju normalt skulle forstora en god pro-gramstruktur. Man kan dock tanka sig ett fall da det kan vara motiverat att gora ettsadant hopp. Det ar om man i ett program vill ha en aterstartspunkt dit man skall kunnaatervanda fran vilken del av programmet som heist.

Ett anrop av fimktionen set jmp "kommer ihag" ett lage och en omgivning i en funktion och longjmp kan sedan anvandas for att hoppa dit. Det kommer da att se ut somom s e t j mp retumerar.

#include <setjmp.h>

int setjmp(jmp_buf env);

void longjmp(jmp_buf env^ int val);

Den typiska anvandningen ar att man vid en lamplig aterstartspunkt gor ett set jmp (darenv ar globalt deklarerad). Sedan kan ett anrop av long jmp med samma env fa programmet att hoppa till denna punkt. Det kommer att se ut som om set jmp retumeradeoch vardet ar da det medskickade val fran longjmp-anropet. Alia anrop som gjorts daremellan och alia lokala variabler ar bortstMade (oftast genom att anropsstacken barskalats av och att en latsasretur fran set jmp lagts pa stacken).

10.11 Mjukvaruavbrott <signal. h>I vissa fall kan det vara av intresse att fa ett program att reagera pa ovantade tillstandpa ett asynkront satt, det vill saga avbryta sin pagaende exekvering for att ta hand omden speciella handelse som intraffat. Handelsen kan till exempel vara att processomupptackt en felaktig rakneoperation (division med noil etc.), ett minnesfel eller enbegaran om att programmet skall avbrytas (normalt fran operativsystemet).

En sMan handelse meddelas ett program via nagot som Unix-traditionellt kallas ensignal. En process som far en signal kan antingen valja att ignorera den eller att lata sigavbrytas. I det senare fallet kan den anropa en funktion som hanterar avbrottet ochsedan atervanda dit dar programmet blev avbrutet. Om programmet inte sagt vad somskall handa nar signalen kommer bander det "normala", vilket ar implementeringsbe-roende (i Unix t.ex. avslutas programmet direkt).

Mekanismen bar genomgatt ett antal revisioner i sin betydelse, framfor allt vad galleraterhopp till systemanrop som avbrutits av en signal. Det ar stor risk att program somflyttas ar icke-flyttbara nar det galler signalhantering.

) Studentlitteratur 219

Page 223: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10. Standardfunktioner och standardbibliotek

Standarden beskriver bara den grundlaggande delen av det som olika Unix-systemhanterar. Den totala uppsattningen signaler ar implementeringsberoende men mandefmierarnagra standardsignaler. Lat oss forst se pa deklarationenav signal:

#include <signal.h>

void (*signal(int sig^ void (*func) (int) )) (int) ;

En liten utredning av deklarationen kan kanske behovas. Det ar funktionen signalsom deklareras. Den bar tva parametrar: sig som ar signalnumret och func som ar denfunktion som man vill skall anropas nar programmet drabbas av signal nummer sig.func ar en pekare till en funktion som bar en beltalsparameter ocb som inte retumerarnagot. signal retumerar en pekare till en funktion som bar ett beltal som parameterocb denna funktion retumerar inte nagot.

Ett exempel blir mycket enklare. Lat oss deklarera en ftinktion som vi vill skall anro

pas nar det omgivande systemet bett om uppmarksambet, t.ex. nar anvandaren trycktpa "break"-tangenten. (Det ar dock upp till omgivningen att generera denna signal.)

/'^ funktion som skall anropas vid signal */

void ta_hand_om(int signalnummer)

{

/* gor nagot lampligt, t.ex. en longjmp eller

avbryt hela programmet efter uppstadning */

}

Nu kan vi (normalt i borjan pa programmet) tala om att det ar denna funktion vi vill baanropad:

/* registrera funktion som skall anropas vid signal SIGINT */

signal (SIGINT, ta_hand_om) ;

SIGINT ar ett signalnummer definierat som ett makro i inkluderingsfilen. Funktionenta hand om kommer att anropas med signalnumret som parameter, sa att man vet vil-ken signal som orsakade anropet. (ta hand om kan ta band om flera olika signaler.) Iinkluderingsfilen finns aven specialvarden for fimktionspekarparametem om man t.ex.vill ignorera en viss signal:

/* strunta i signal SIGINT */

signal (SIGINT, SIG_IGN) ;

Man kan aven generera signalen "inifran" programmet sjalvt.

/* generera signal SIGINT fran programmet sjalvt */

raise (SIGINT) ;

Detaljer i signalbanteringen kommer att skilja sig fran system till system ocb manual-lasning ar nodvandig. Unix bar t.ex. mojligbet att skicka signaler till andra processer/program.

220 © Studentlitteratur

Page 224: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10,12 Variabeltparameterantal <stdarg.h>

10.12 Variabelt parameterantal <stdarg. h>I kapitel 6 sag vi att man i ett funktionshuvud kunde skriva en s.k. ellips (...) for attmarkera att en funktion kunde ha ett godtyckligt antal okanda parametrar. For att enfunktion skall kunna ta emot sadana parametrar maste den veta var dessa parametrarfinns. I sjalva spraket C finns ingen mekanism for detta. De okanda parametrama barju inga namn.

printf ar ett exempel pa en sadan fimktion. Vi kan skicka med godtyckligt mangaargument for utskrift. Vi talar om for printf bur manga argumenten ar genom att angeomvandlingsspecifikationer i formatstrangen. Gor vi fel bar kommer printf att lita paanroparen ocb ta varden dar de skulle ba legat om de fimnits. Att det fungerar byggerpa att printf vet bur parametrar placeras, t.ex. i rad pa anropsstacken. Koden tillprintf kan darigenom inte vara portabel, aven om koden som anropar den ar det.

Eftersom placering av parametrar ar implementeringsberoendeskulle vi kunna fa problem om vi sjalva ville definiera funktioner med variabelt antal parametrar. Det finnsemellertid en makro-uppsattning som gor det mojligt att losa detta pa ett portabelt sattgenom att dolja mekanismen for att bitta "nasta" parameter (som inte bar nagot dekla-rerat namn i parameterlistan).

En funktion som skall deklareras med ett variabelt antal parametrar skall markera dettagenom tre punkter {ellips) efter sista kommat i parameterlistan:

void egenvfunk(char *str, int n, ...)

{

/* satser */

}

For att fa tag pa de ytterligare parametrama som skickats anvands foljande makron:

#include <stdarg.h>

void va_start(va_list ap, sistaparm);

void va_arg(va_list ap, typ);

void va_end(va_list ap);

va start skall anropas innan nagra av de ouppraknade parametrama skall anvandas,sistaparm ar namuet pa den sist uppraknade parametem i funktionsdefinitionen (i vartexempel n). Sedan kan man med successiva anrop av va arg fa parametervarde forparametervarde. Man maste dock veta ocb ange vilken typ de bar (det kan inte va argveta) ocb det maste naturligtvis vara samma typer som anroparen skickade som argument. Vi maste ocksa sjalva balla reda pa bur manga parametervarden vi ska plocka ut.Det far vi inte beller veta fran va_arg. Innan funktionen retumerar maste va end anropas for att eventuellt stalla saker tillratta.

I princip skulle en funktion som anvander sig av detta se ut sa bar:

© Studentlitteratur 221

Page 225: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10. Standardfunktioner och standardbibliotek

/* funktion som kan ta emot variabelt antal parametervarden */#include <stdarg.h>

void varpfunk(char *argl, int argn, ...);

{

va_list ap;

va_start (ap,argn) ;

/* ... */

while {fl er varden) {

variabel = va_arg(ap, parametertyp);

/* ... */

}

/* ... * /

va_end(ap);

Vi maste pa nagot satt veta hur lange det finns fler parametervarden att fa (t.ex. medparametem argn). For varje anrop av va arg maste vi kunna tala cm vilken typ para-metervardetbar, ange det som parameter och ta emot vardet pa ratt satt, bar genom attvariabel bar ratt typ. Vilkentyp parametrama bar kan vi antingenveta pa forband ellerfa reda pa genomen parameter. Vikan t.ex. anvandaomvandlingsspecifikationer pa ettliknande satt som printf gor.

Man kan inte pasta att tillvagagangssattet ar speciellt vackert, men det ger anda enmojligbet att bantera variabelt antal parametrar pa ett portabelt satt. Har man bebov avett variabelt antal parametervarden av samma typ, t.ex. textstrangar, bor man i stalletanvanda ett fait ocb en antalsangivelse som parametrar.

110.13 Bibliotek i C99 och framtiden

Nar man tog fram den nya standarden var man mycket noga med att inte infora nagotsom skulle kunna komma i konflikt med existerande kod. I synnerbet ville man und-vika s.k. tysta andringar. En tyst andring innebar att ett gammalt korrekt program fort-farande ar korrekt enligt den nya standarden,men beter sig pa ett nytt satt.

Vi bar medvetet inte betonat C99 i denna bok da de fiesta delama i C99 inte paverkaren nyboijare. C99 bar inte beller slagit igenom da manga av de saker man lagt till skotsbattre ocb enklare med andra sprak. Som exempel kan namnas att bade Java ocb C#anvander Unicode som enda teckenuppsattning. De slipper darmed den dubbla bante-ringen som finns i C99. De fiesta tillaggen i C99 stoter inte ens avancerade anvandarepa vid vanlig anvandning av C.

Det pagar ett fortsatt arbete med ytterligare standardisering av C, med samma mal somtidigare. Vi beskriver nedan de viktigaste delama i C99:s utvidgning av biblioteket,utan att ga in pa detaljer.

222 © Studentlitteratur

Page 226: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10.13 Bibliotek i C99 ochframtiden

10.13.1 Matematiska funktioner

C99 utokar spraket med direkt bantering av komplexa tal i och med de nya reserveradeorden__compiex och^imaginary och till detta kommerda alia matematiska funktionersom behandlar dem. Man bar ocksa som namndes i avsnitt 4.1 kompletterat det mate

matiska bihlioteket med funktionsvarianter for alia kombinationer av typer, t.ex. sinus-funktionen medprecision for long double. Aldre C var lite forvagt i sin beskrivningav precisionen vid flyttalsberakningar. Detta bar man skarpt till sa att C skall fungera iberakningssammanbang dar man ar beroende av att veta exakt vilken precision man farsamt vad som bander vid fel i berakningar.

10.13.2 Unicode och wchar_t

I gammal C var ett tecken alltid en byte vilket gjorde det enkelt for programmeraren,men svart for anvandaren eftersom alia teckenvarianter inte fick plats och kundeanvandas. Med inforandet av Unicode far nu all varldens tecken plats i uppsattningenmen banteringenblir krangligare. I filer bmkar man lagra Unicode som UTF-8, dvs devanligaste tecknen lagras i en byte och ovanligare i tva eller flera byte, detta for att intevanlig text skall ta onodigt mycket plats. Nar vi vill arbeta med tecken i ett program ardet oftast enklare att alia tecken ar lika stora, vilket ar skalet till att typen wchar t

infordes. Se vidare i avsnitt 3.4.

C99 lagger till ett stort antal funktioner for att dubblera de char-baserade funktionemamed wchar t-varianter. Dessutom finns ett antal funktioner for att kunna konvertera

mellan de olika lagringsformaten och styra detta i detalj. Eftersom det finns mangavarianter av Unicode-lagring blir biblioteken stora med manga detaljer och ganskabokiga att anvanda.

Namnen pa de nya funktionema inneballer ofta ett extra wfor wide men de nya namnenar inte belt konsekventa: printf bar blivit wprintf, toupper bar blivit towupper ocb

fgetc bar blivit fgetwc.

10.13.3 Okadsakerhet

Manga funktioner som fomt saknade sakerbet, finns nu i sakrare varianter, t.ex.sprintf vars resultat bamnar i en teckenfalt, men det ar svart att veta bur manga

tecken som faktiskt skrivs ocb darmed finns risk att man skriver utanfor teckenfaltet.

C99 utokar med varianten snprintf som tar ytterligare en langdparameter sombegransar antalet skrivna tecken.

) Studentlitteratur 223

Page 227: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10. Standardfunktioner och standardbibliotek

10.13.4 Nasta C-standard

I skrivande stundhallermanpa att utveckla en ny C-standard, att ersattaC99. Dennyastandarden gar underarbetsnamnet CIX. Vad somkommer att finnas medar naturligt-vis inte klart, men allt tyder pa att man fortsatter med sammaprincipersom i C99: attgora C till ett sakrare sprak dar man kan vara mer saker pa vad som bander,dock utanatt ge avkall pa C:s totala flexibilitet och kontroll over detaljer. Nya biblioteksforslaginnefattar stod for program med flera tradar, indexkontroll pa fait och enklare stod forUnicode.

Ytterligare ett litet smakprov: Man tanker sig att ta bort gets, eftersom det inte finnsnagot satt att gora den saker och garantera att den inte skriver bortom slutet av detutrymme som skickas med via pekare.

10.14 Ovningsuppgifter1. Jamfbr standardbiblioteken med de bibliotek som finns i ditt system. Titta efter

vilka som finns med och vilka bibliotek som finns utdver standarden. Se t.ex. eftervilka mojligheter som finns att med setiocaie fa ett "nationellt" beteende forteckentyper, datumutskrift eller bokstavsordning i strangar. Testa!

2. Testa med hjalp av t.ex. de matematiska funktionema om ditt system anvanderprototyper i inkluderingsfilema. Ge heltalsargument till en fimktion som egentli-gen skall ha flyttal och se om automatisk omvandling sker.

3. Testa vilket varde eof bar pa ditt system. Undersok ocksa om man kan forvara deti en char. Det far man egentligen inte forutsatta men det kan vara bra att veta burdet fbrhaller sig om man skall flyttaprogramtill sitt systemsom felaktigt fomtsat-ter negativa char-varden.

4. Skriv om varulagerprogrammet i kapitel 8 sa att det laser in alia postema vid pro-grammets start och skriver ut dem vid avslutandet sa att vi bar en permanent lag-ring av varulagerdatabasen. Lagg till ett kommandofor att under komingen skrivaut allt pa lagerfilen. Det kan ju vara bra att gora da och da om datom skulle stannaoch alia uppdateringar sedan programstarten annars forlorades.

Se till att fbrse alia anrop till standardfunktioner med tester huruvida det gick bra.Gor annars en felutskrift, t.ex. med perror.

Fortsatt ytterligare pa sakerhetslinjen: Sk fort en andring gors loggas den aven i entransaktionsfil. Skulle programmet nu stanna ovantat, bar vi alltid mojligheten attga tillbaka och knappa in andringama igen. (Eller skriv ett extra kommando somgor det automatiskt. Se bara till att gora det idiotsakert!)

Som en sista utvecklingsmojlighet kan vi ocksa lata bli att lasa in alia poster till enintern datastruktur utan i stallet andra postema dar de ligger i filen. For att effek-

224 © Studentlitteratur

Page 228: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

10.14 Ovningsuppgifter

tivt kimna soka i filema, lagga till och ta bort poster, finns en mangd metoderutvecklade. Titta i nagon bok cm databases

5. Undersok bur interaktionen med omgivningen fungerar pa ditt system. Anvandsystem for att t.ex. kora det kommando som skriver ut datum. Kan man pa ditt system skicka varden pa "omgivningsvariabler" {environmentvariables) till program-met, som dar blir atkomliga med getenv? Hur far systemet reda pa den returkodsom programmet retumerar med exit?

6. Skriv ett program som med laser en text och med hjalp av stranghanteringsfunk-tionema delar upp rader i ord och orden i stavelser samt skriver ut texten igen mennu med bindestreck dar avstavning kan ske. Anvand de enkla reglema (eller utokamed fler sjalv): En konsonant fore vokal efter bindestreck, x och ng fore bindestreck.

7. Skriv ett program som givet ar och manad skriver ut ett manadskalenderblad palamplig form.

8. Undersok hur man pa ditt system fran tangentbordet kan generera en signal till dittprogram. Skriv en funktion som skall anropas nar signalen kommer och anropasignal for att tala om det for systemet.

Komplettera med att i boijan pa huvudprogrammet anropa set jmp for att defmieraen aterstartspunkt. Lat nu avbrottsfunktionen ovan anropa long jmp for att goradenna aterstart nar en signal bar givits till programmet.

) Studentlitteratur 225

Page 229: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

C i olika omgiviiingar 11

C anvandas i tva principiellt olika omgivningar: antingen i en operativsystemmiljo{hosted environment) eller i en "naken" omgivning {freestanding environment).

I en operativsystemmiljo har man ofta hjalpmedel for programutveckling, skydd motkonstigt beteende hos programmen som kors, filsystem for att forvara data samt allraviktigast: en hel uppsattning tjanster som gors tillgangliga for programmen av opera-tivsystemet. Dessa tjanster erbjuder in- och utmatning, kommunikation med omgiv-ningen, samtidig exekvering av flera program och styming av dessa etc. Tjanstemaerbjuds som systemanrop vilka i C far formen av vanliga funktionsanrop.

I en naken miljo saknas allt detta stod. In- och utmatning liksom all annan kommunikation med omgivningen far ske genom direkt manipulerande av de yttre enhetemasstyrregister. Den aktuella processoms egenheter och minnets organisation blir nagon-ting som "syns" jamfort med operativsystemomgivningendar vi var behagligt Qarma-de ffan sadana bekymmer. Att direkt i programmeringsspraketkunna gora huvuddelenav dessa maskinnara operationer ar en av fordelama med C. Standarden skiljer noga pade tva olika typema av omgivning och mycket av det som standardiseras (t.ex. stan-dardfunktioner) galler bara i en operativsystemomgivning.

Vi kommer i detta kapitel att se bur C-programmering ser ut i dessa olika omgivningar.Som exempel pa operativsystem kommer vi att se pa Unix, det system som C en ganggjordes for. Vi kommer att kort belysa vad det innebar att programmera i C pa en per-sondator och ge en oversikt over vilka tekniker som kan behovas vid programmering inakna system och vid kontakt med assemblerkod. Det sista avsnittet handlar om vadman maste tanka pa for att skriva program avsedda att kunna flyttas mellan olika mil-joer och vilka typer av problem man kan stota pa nar man skall flytta ett program.

© Studentlitteratur

Page 230: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

IL C i olika omgivningar

11.1 Parametrar till programmetVi har hittills alltid definierat funktionen main utan parametrar. Standarden sager attman i en operativsystemomgivning antingen kan gora sa eller ocksa anta att main hartva parametrar:

int main ( int argc, char *argv[])

{

/* ... * /

}

Parametem argv ar ett fait av pekare till textstrangar. argv har pa ett systemberoendesatt skickats till main av omgivningen vid starten av programmet. Antalet strangaranges av parametem argc. Dessa strangar kommer fran den rad som skrevs till syste-mets kommandotolkare nar programmet kordes igang. Vi tar ett exempelmed ett program (i filen rader.c) som raknar antalet rader i de filer vilkas filnamn givits somparametrar till programmet.

/* rakna rader i filer */

#include <stdio.h>

int filrad(FILE *f)

{

char c;

int r = 0;

while ((c = fgetc(f)) != EOF) {

if (c == '\n') {

r + +;

}

}

return r;

}

int main(int argc, char *argv[])

{

int i;

FILE *fil;

for (i=l; i<argc; i++) {

if (( fil = fopen (argv[i] , "r")) != NULL) {

printf("%s: %d rader\n", argv[i], filrad(fil));

}

else {

perror (argv [ i] ) ;

}

}

}

Vi kan nu kompilera och lanka detta program. Vi kallar det rader.exe!

228 © Studentlitteratur

Page 231: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

11.2 Operativsystemmiljd, Unix

gcc rader.c -o rader.exe

Vi kor programmet och ger som argument namnen pa de filer vilkas radantal vi villrakna:

rader.exe fill.c storfil.txt felfil.log slutfil.txt

Programmet kommer t.ex. att skriva ut:

fill.c: 20 rader

storfil.txt: 18982 rader

felfil.log: No such file or directory

slutfil.txt: 4 rader

Standarden sager att argv[0] skall irmehalla "programnamnet", i vart fall rader. Detar darfdr vi inte tar med det som ett filnamn.

Pekaren argv [argc] (pekaren efter sista strangpekaren) skall vara null.

Returvardet fran main skall vara ett heltal som tas om hand av omgivningen och tolkassom en statuskod som talar om hur programkomingen gick.Noll betyderatt allt ar OKoch andra varden indikerar olika typer av fel. Om inte omgivningen ar beroende av attfa veta hur komingen gick kan man oftast lata bli att ha nagon return-sats i main,avenom manga kompilatorer vamar for detta.

Ett altemativ till att retumera ett varde ffan main ar att anropa biblioteksfunktionenexit (se avsnitt 10.7.4) som direkt avslutar programmet. Parametem till exit harsamma betydelse som returvardet fran main.

11.2 Operativsystemmajo, UnixEftersom C en gang konstruerades just for att skriva hela Unix-systemet i, finnermangaegenskaper (och egenheter) i C sin historiadar. Standardiseringen har t.ex. tagitmycket stor hansyn till de standardbibliotek som under en lang tid utvecklats i Unix-systemen.

Nar Unix i borjan pa 80-talet borjade bli mer och mer populart borjade det dyka uppolika versioner och andra system som mer eller mindre liknade Unix, en forvirrandemen kanske oundviklig utveckling. Vad som hant med C i denna utveckling ar att dettrots allt inte har skapats manga versioner av C. Detta beror frtost pa att man inte harhaft anledning att utoka C med systemspecifika tillagg till sjalva spraket eftersom Credan har de mekanismer som behovs.

Vi skall nu titta pa nagra exempel pa hur C ser ut i Unix-miljon och da hMla oss tillegenskaper som ar gemensamma for praktiskt taget alia Unix-system. Detta ar intenagon ftillstandig genomgang av hur man programmerar i Unix, utan vi visar exempelpa hur C kommer in i bilden.

© Studentlitteratur 229

Page 232: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

11. C i olika omgivningar

11.2.1 Systemanrop

For den som programmerar med hjalp av Unix ar systemanropen det som dr sjalvaUnix. Det ar dessa som skoter all kommunikation med omgivningen och gor Unix'dvriga operativsystemtjanster (t.ex. fragor om klockslag och igangkoming av andraprogram) tillgangliga for programmet.

For C-programmeraren ser ett systemanrop alltid ut som ett vanligt funktionsanrop.Man kan alltsa inte direkt skilja pa nagot som dr ett systemanrop och nagot som baraser ut som ett. Detta ar synnerligen praktiskt om man pa ett annat system vill simuleraett Unix-systemanrop for att lattare kimna flytta ett program skrivet for Unix till dennamiljo. Det ar mycket vanligt att det finns sMana simulerade systemanrop i t.ex. PC-miljoer for C-programmering. Det finns sa manga program utvecklade for Unix somkan vara intressanta att flytta.

Men vad ar egentligen ett systemanrop? Det kan variera mellan olika maskiner mendet vanligaste ar att en speciell maskininstruktion som orsakarett avbrotti processomsnormala instmktionsexekveringutfors. Exekveringenhoppar da in till ett speciellt stal-le i operativsystemet dar man tar handom avbrottetoch gor de sakeranroparen bad omefter att noggrant ha kontrollerat att programmet bar rattighet att gora operationen.Den speciella maskininstruktionen som orsakar avbrottet maste for det mesta laggas ien rutin skriven i assemblerkod. I avsnitt 11.5.3 beskrivs bur man anropar assembler-kod fran C.

Oftast anvander unixprogrammeraren inte systemanropen direkt utan anvander stan-dardfunktioner som i sin tur anvander systemanropen. Skalet till detta ar att man villfora upp kontakten mellan programmet och omgivningen pa en "hogre niva" som arlattare att standardisera for fler system an bara Unix.

Denna hogre niva ar just de standardfunktioner som beskrivs i kaptitel 10, sa bar skallvi ta upp exempel pa n&gra direkta systemanrop som Unix bar.

11.2.2 In- och utmatning

Har horde man nastan direkt bara hanvisa till kapitel 10 och standardfunktionema forin- och utmatning. Vid praktiskt taget all unixprogrammering anvands dessa eftersomde har funnits lange och ar modellerade efter Unix satt att se pa in- och utmatning somstrommar av bytes. Standardfunktionema ar lagda som ett "skal" mnt de operativ-systemanrop som utfor sjalva lasningama och skrivningama till operativsystemet.

For att t.ex. oppna, skriva och lasa filer finns direkta systemanrop. Vi tar ett exempel:

/* oppna och las fil med direkta systemanrop */

#include <fcntl.h>

230 © Studentlitteratur

Page 233: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

11.2 Operativsystemmiljd, Unix

int fildes,n;

char buf[100];

fildes = open("minkatalog/fill", 0_RD0NLY);

n = read(fildes,buf,100);

write (1, buf, n) ;

Vi oppnar filen for lasning (o betyder "endast lasning") och far ett heltal som fil-deskriptor vilken i fortsattningen representerar den oppna filen. Vi laser hogst lOObytes till faltet buf och far reda pa hur manga som verkligen lastes. Vi skriver ut dessapa standardutmatningen (normalt terminalen, representerad av fildeskriptor i).

Eftersom Unix aven ser kommunikation med yttre enheter, andra processor och andrasystem som strommar, finns dessutom systemanrop for att t.ex. styra beteendet hos enterminal.

11.2.3 Styrning av processer

Unix ar ett multiprocessystem, d.v.s. man kan ha flera processer aktiva samtidigt. Pro-cessema kan exekvera samma eller olika program. Varje process har ett unikt nummersom identifierar den. Det enda sattet att skapa en ny process i Unix ar att lata en redanexisterande process klyva sig i tva. Det gors med systemanropet fork:

/* skapa en ny process med fork */

int main ()

{

if (fork 0 ) {

printf ( "Detta ar faderprocessen\n") ;

}

else {

printf ( "De11a ar barnprocessen\n") ;

Returvardet ar o for den nyskapade processen. Vad som hander ovan ar att nar forkanropas sker klyvningen och bada processema atervander fran anropet, bamet med 0och fadem med bamets processnummer. Eftersom oppnade strommar arvs, kommerbada utskriftema ut pa terminalen men vilken som kommer forst vet man inte. Utskrif-tema skulle aven kunna komma iblandade i varandra.

Nu ar det ju sallan intressant att bara klyva en redan existerande process och korasamma program i tva processer, aven om de kan ta olika vagar efter klyvningen. Vi villofta kora igang ett nytt program i bamprocessen genom att ladda in koden for detta nyaprogram fran en fll. Lat oss fran en process kora igang ett annat unixprogram som skriver ut datum:

) Studentlitteratur 231

Page 234: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

11. C i olika omgivningar

/* skapa en ny process och kor programmet date i den */

int main ()

int status, barnid;

printfC'Sa har skrivs datum i Unix: "),

if (! forkO) {

execl("/bin/date", date);

}

else {

barnid = wait (&status) ;

printfC'med programmet date\n");

Vi gor en utskrift, klyver med fork och later bamet gora systemanropet execi. Dettaersatter hela bamprocessens innehall med koden i filen /bin/date och kor igang dettaprogram fran borjan. (Bamprocessen atervander aldrig till koden ovan eftersom denskrivits over av koden for date.) Faderprocessen gor systemanropet wait som vantarpa att den avynglade bamprocessen skall do, vilket bamprocessen gor nar den kort far-digt date. Da far fadem retur fran wait och statuskoden fran bamets koming skrivs avwait via en pekare till variabeln status sa att fadem kan fa veta hur det gick. For attveta vilken bamprocess som dog retumerar wait bamets processnummer. Med fork,execl och wait kan vi alltsa skapa, starta och synkronisera processor. Detta ar exempelpa systemanrop som anvands direkt utan att man gar vagen via standardfunktioner.

Har vi tillgang till ett processnummer for en viss process kan vi skicka en signal tillden. Om vi inte sagt nagot annat dor processen vanligen av detta. Den mottagande pro-cessen kan dock ha specificerat att i stallet lata avbryta sig, tillfalligt exekvera en spe-cificerad hmktion och sedan atervanda dit dar processen blev avbmten. Denna meka-nism bmkar ofta kallas mjukvaruavbrott. En enkel form av signalmekanismen finnssom standardfunktioner beskrivna i kapitel 10.

11.3 OperatiysystemmUjo, persondatorerDet som sags ovan om Unix galler aven for Unix-liknande system, t.ex. Linux ochBSD Unix. Det senare utgor basen for MacOS X som kors i varianter pa Macintosh,iPhone och iPad. Operativsystemet for mobiltelefoner Android har Linux som has.Microsoft Windows i sina olika versioner ser dock annorlunda ut i detaljema, aven omman kan gora liknande operationer. Det alia dessa system har gemensamt ar att deabsolut grundlaggande operativsystemanropen ar beskrivna i C.

Numera skrivs de fiesta anvandarprogram som har grafiska anvandargranssnitt inte i Cutan i objektorienterade sprak som C++, C#, Objective-C, Java, Visual Basic, m.fl. Det

232 © Studentlitteratur

Page 235: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

11.4 Programmeringsverktyg

beror pa att den stora komplexiteten i de grafiska anvandargranssnitten och aven andradelar av biblioteken blir mycket enklare att hantera i dessa sprak. Men nar det skall pil-las med detaljer far man ta fram C. Det ar inte for inte som C++, C# och Objective-Cbar kvar C:s mojligheter att peta i minnet och pa enskilda bitar. Java bar ocksa en val-definierad standard for bur man anropar C-funktioner, for de grundlaggande delama iJava-biblioteken bar ingen mojligbet att direkt tala med operativsystemet, utan masteta till C. Manga program med grafiska granssnitt gor heller inte sjalva jobbet, utan star-tar andra program skrivna utan nagon grafik. En del av dessa kan fortfarande lampa sigatt skriva i C.

11.4 ProgrammeringsverktygEn styrka bos Unix ar alia de stodprogram for programmering som fmns tillgangliga.Manga av dem ar belt traditionella sasom textredigeringsprogram (editorer), avlus-ningsbjalpmedel (debuggers) och formateringsprogram. Vi ska bar kort namna nagramer avancerade verktyg som konstruerats for unixmiljon men som aven gjorts tillgangliga i en del andra programmeringsmiljoer.

11.4.1 Statisk avlusning, lint

Programmet lint kors pa samma satt som en kompilator men det genererar ingen kod.Energin laggs i stallet ner pa att forsoka upptacka sa manga fel och konstigbeter sommojligt i programmet. Dels upptacks rent felaktiga (men kanske atminstone i tidigareversioner formellt tillatna) konstruktioner som t.ex. fel parameterantal och -typer.Dessutom kan man fa vamingar for saker som ser "konstiga" ut, t.ex. kod som aldrigkan nas, variabler vars varde anvands innan de initierats med nagot, konstanta uttryck itestvillkor (om man t.ex. skrivit i = i nar man menade i == i) och bopp in i snurror.Om man vill kan man aven fa rapporterat saker som inte ser ut att vara flyttbara tillandra system, t.ex. kod som forutsatter viss langd pa beltalsvariabler eller negativavarden pa char.

Manga av dessa tester kan inte utforas med bundraprocentig sakerbet. Det ar en prak-tisk ocb i flera fall teoretisk omojligbet. Att kora iint pa sitt program kan dock gemanga goda tips om fel som man kunde agnat timmar at att bitta pa andra satt. Detta

galler i synnerbet vid flyttning av ett belt program till en annan miljo. Se mer om dettai slutet av kapitlet.

Det kan papekas att iint utvecklades under en tid nar kompilatorema annu var ganskaprimitiva. De mer avancerade kompilatorer som anvands idag utfor normalt en del avde tester som tidigare fick goras separat med bjalp av iint.

) Studentlitteratur 233

Page 236: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

11. C i olika omgivningar

11.4.2 Syntaxanalys, yacc och lex

Dessa tva program anvands for att generera C-program. Som indata till programmenger man en beskrivningav hur programmetskall fungera och ut kommer en C-funktionsom beter sig pa detta satt. Nu kan man inte generera vilka program som heist, yaccoch lex ar specialsydda for att generera program som analyserar text. Indata till yaccoch lex ar de grammatiskaregler som texten skalluppfylla och utdata ar en C-funktionsomlaser in tecken, slar ihopdemtill storregrammatiska objektoch ger programmera-renmojlighet att ange vad somskallhandanarett visstsadant objekthittats. Dengene-rerade C-fiinktionen kan anvandas ihop med annan C-kod for att till exempel utgdradelar av en kompilator for ett visst programmeringssprak. Da utgdr de grammatiskareglema programmeringssprakets grammatik.

Programmet yacc bmkar sMunda kallas en syntaxanalysgenerator (eng.parser generator). lex ar konstmerad att generera ett "forsteg" till yacc och sla ihop enskildatecken till "ord", lexikala enheter, som sedan anvands av yacc. lex kan aven anvandasfor sig.

Att skrivaprogramsomutfor syntaxanalys enligtgivnagrammatiska reglerar kompli-cerat och svart att gora korrekt. Speciellt arbetsamt blir det om man senare vill infbrastorre andringar i de grammatiskareglema. Med verktygen yacc och lex (eller andraliknande programgeneratorer) kan arbetet reduceras avsevM.

Programmenfmns i varianter for oppen kallkod och heter da bison respektive flex.

11.4.3 ProgramunderhM, ma ke

Om man har konstmerat ett stort programsystem som bestar av ett 30-tal filer varavhalften ar inkluderingsfiler kan det bli arbetsamt att halla reda pa hur delama ar bero-ende av varandra. Dettavisar sig tydligtnar man skall kompilera om systemetefter attha gjort nagra andringar i ett par filer: hur mycket maste jag kompileraom?

Ett system kallat make, utvecklat under Unix, hjalper till med detta. Vi har en gang foralia skrivit ned i en fil hur filema hanger ihop. T.ex. ar en kalltextfil beroende av aliasina inkluderingsfiler. Nar vi kor make kontrolleras vilka filer som har andrats ochvilka andra filer som var beroende av dem och darmed blivit "forMdrade". Dessa kom-

pileras och lankas ihop till ett nytt komplett program.

Nagot motsvarande verktyg finns i nastan alia programmeringsmiljoer och fungerar paliknande satt. I modema integrerade programutvecklingssystem, som t.ex. Visual Studio ar de funktioner som ingar i make normalt integrerade i systemet.

234 © Studentlitteratur

Page 237: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

11.4 Programmeringsverktyg

11.4.4 ProgramprofQering, effektivitet

Nagon har formulerat en tumregel som sager att ett program spenderar 80% av sin tid i20% av koden (en del sager t.o.m. 90%-10%). Vadregeln egentligen vill saga ar att detoftast inte lonar sig att alltid knepa och knapa for att skriva "effektiv" kod. Kod somhar optimerats for att kunna exekvera sa snabbt som mojligt blir ofta mindre begripligan kod som skrivits for att vara sa lattlast som mojligt. Extremfallet ar nar man skrivitom ett kodavsnitt i assemblerkod. Eftersom en stor del av kostnaden for programvara

ar underhall och vidareutveckling, ar det viktigt att koden ar begriplig aven for andraan forfattaren och aven nagra ar efter det att programmet skrivits.

Vill man optimera kod galler det alltsa att gora det pa ratt stalle. Ibland ar det latt attinse var nagonstans programmet tar mest tid. Det ar ofta i snurror dar manga varv lopsigenom. Da kan man t.ex. se till att saker som kan beraknas en enda gang beraknasutanfbr snurran. Styrvariabeln for snurran kan deklareras med register och man kankanske ersatta indexering med pekarstegning. Om det inte ar sjalvklart var program-met ar trogt kan ett profileringssystem komma till pass. Det kor programmet i en kon-trollerad miljo dar alia anrop till funktioner raknas. Genom att sedan vid tata klockav-brott titta efter var nagonstans programmet befinner sig kan man fa en uppfattning omvar den mesta tiden gar at. Ofta ger man ett speciellt kommando vid kompileringen foratt fa profilering utford.

11.4.5 Symbolisk avlusning

Avlusning (eng. debugging) har traditionellt skett genom att man sjalv har lagt in test-utskrifter i kallkoden for att kunna studera vardet av vissa variabler under program-

mets koming. Man har ocksa kunnat ga in i den fardiga maskinkoden med en maskin-kodsavlusare och lagga in brytpunkter som gor att programmets koming avbryts viddessa stallen. Da har man med hjalp av maskinkodskunskaper kunnat ga in och studerainnehallet i minnesceller pa specifika minnesadresser for att titta pa variabelvarden.

En symbolisk avlusare ar ett system som ar betydligt vanligare mot anvandaren. Mankor sitt program i en "skyddad miljo" dar anvandaren hela tiden kan referera till programmet i kallkoden utan att behova lagga in speciella utskrifter eller begripa maskinkoden. Systemet haller ofta reda kopplingen mellan kallkod och maskinkod sa att mant.ex. kan stanna programmet efter en viss rad i kallkoden. Man kan titta pa variabelvarden, eventuellt andra dem och kora vidare eller stega fram en kallkodsrad i taget. Pamodema persondatorer dar man arbetar med flera fonster samtidigt pa skarmen finnsofta avancerade avlusningssystem dar man kan ha kallkoden i ett fonster, in- ochutmatning till programmet i ett annat och stymingen av avlusaren i ett tredje.

Man kanske bor tillagga att en sund programkonstmktion dar man tankt ordentligtinnan man borjade skriva kod inte kan ersattas av en aldrig sa avancerad avlusare.

© Studentlitteratur 235

Page 238: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

11. Ci olika omgivningar

11.5 Naken omgivningVi kommer i detta avsnitt att studera nagra tekniker som man behover anvanda narman kor program utan nagon operativsystemomgivning, en naken omgivning (eng.freestanding environment). Detkanvaraaktuellt ommanprogrammerar en processor ii en mindreenhet, aven om det idag ofta anvands minimalaoperativsystem aven i smaenheter. Om vi programmerar sjalva operativsystemet, eller delar av ett, t.ex. drivruti-ner, ar vi dock i samma nakna situation. Vi kor belt oskyddat och maste skota aliadetaljer sjalva. De saker vi behover kunna gora ar t.ex. att skrivapa bestamda stallen iminnet, omtolka minnesinnehall fran en typ till en annan, anropa kod som ligger panagon fomtbestamd minnesplats, utfora speciella processorinstruktioner,m.m.

I stallet for att fdrsoka beskriva en speciell miljo eller processor, fokuserar vi pa dedelar som ar generella och gemensamma och som behovs for att kunna skriva samycket som mojligt av koden i C.

11.5.1 Att anvanda en bestamd minnesplats

Alia datorer bar ett primarminne, RAM, och pa alia modema datorer bestar dettaminne av en foljd av atta-bitars bytes. De ar numrerade byte-vis och numren brukarkallas adresser eller pekarvarden. Detta bar vi beskrivit ingaende i kap 7. Normalt barman inget behov av att placera en variabel pa en bestamd minnesadress. Men det arvanligt att adressema inte bara numrerar minne, utan att det pa vissa adresser firmsstyrregisterfor yttre enheter. Dessa adresser beter sig oftast som mirme, men paverkarocksa en yttre enhets beteende.

Som exempel, lat oss anta att var dator bar ett ljudkort som kan spela upp ljud. Dafirms det troligvis ett antal styrregisterfor detta och ett av dem ar ljudstyrka. Vi laser imanualenatt detta styrregisterfirms pa adress 0xffff0034 och ar 16bitar langt. 0 bety-der tyst och alia ettor betyder maximal styrka.

Vi kan inte placera en variabel pa en viss mirmescell, men vi kan peka dit med enpekare och sedan andra ljudstyrkan via pekaren.

/* deklarera en pekare till 1judstyrke-registret och

andra ljudstyrkan */

#define VOLUMEREG_ADDRESS 0xFFFF0034unsigned short int *volumeptr;

volumeptr = (unsigned short int *) VOLUMEREG_ADDRESS ;

^volumeptr = 28000 ;

Vi vet att en short int ar 16 bitar i vart system. Alia bitama anvands for positiva hel-talsvarden, sa vi anvander \insigned. Vi tilldelar adressen till pekaren, dar typkonver-teringen ar nodvandig for att vi skall kurma betrakta heltalet som en adress till just ett

236 © Studentlitteratur

Page 239: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

11.5 Naken omgivnitig

16 bitars heltal. For enkelhets och sakerhets skull vill vi skriva funktioner som skoter

operationema pa registret.

/* funktioner for att satta och lasa av ljudvolymen */

void setvolume (unsigned short int vol) {

*volumeptr = vol;

}

unsigned short int getvolume (void) (

return *volumeptr;

Vi kollade i manualen att registret ocksa gar att lasa av, d.v.s. beter sig precis somminne. Da behover vi inte tala om nagot mer for kompilatom, som tror att det vi adres-serar ar minne och beter sig som sadant.

Vi tar ett ytterligare exempel. Antag att vi bar en utenhet som kan visa en lysandehexadecimal sifffa. Styrregistret bestar av atta bitar varav fem ar intressanta: fyra somtalar om vilken hexadecimal sifffa som skall visas och en som tander/slacker siffer-

fonstret. De ovriga tre bitama ar oanvanda. Se figur 11.1

oanvanda bitar Ipl/avI hexadecimal siffra

Figur 11.1

Det blir lampligt att definiera en bitfaltspost.

/* deklaration for sifferfonstret */

/* vi har kollat att bitarna hamnar i denna ordning */

struct mcr {

unsigned int disp_digit:4;

unsigned int disp_on:l;

:3;

Vi laser i manualen att detta register har egenheten att det inte kommer ihag detskrivna vardet. Det fungerar alltsa inte som normalt minne. Detta behover vi tala omfor kompilatom med nyckelordet volatile (Jlyktig), sa att kompilatom inte gor sakersom bygger pa att adressen skulle vara en vanlig lasbar minnesplats.

/* deklarera pekare till sifferfonstrets register */

volatile struct mcr *mcrpek = (volatile struct mcr *) OxFEOOFl;

© Studentlitteratur

Page 240: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

11. a olika omgivningar

/* funktion som visar siffran numb i sifferfonstret */

void display(int numb)

{

mcrpek->disp_digit = numb & OxF;

mcrpek->disp_on = 1;

Vi stoppar in siffran pa sin plats och tander sifferfonstret. Vi maskar for sakerhets skullbort allt utom de sista fyra bitama ifall fiinktionen fick ett for stort tal som parameter.(En felutskriflkunde varit pa sin plats men en sadan vore bara meningsfullom vi hadenagonstans att skriva, vilket inte ar sakert att vi bar.)

Om vi behoverkomma ihag sifferfonstrets varden kan vi deklarera en vanlig struct-variabel som minns vardet at oss, vi kan kalla den en skuggvariabel. Varden hamnar iminnet far kompilatom bestamma at oss.

volatile struct mcr *mcrpek = (volatile struct mcr *) OxFEOOFl;

struct mcr skuggmcr;

void display(int numb) {

mcrpek->disp_digit = skuggmcr.disp_digit = numb & OxF;mcrpek->disp_on = skuggmcr.disp_on = 1;

}

int readdigit (void) {

return skuggmcr.disp_digit;

}

I avsnitt 4.6 beskrevs bur man kan anvanda bitmasker i stallet for bitfalt for att komma

at bitar ocb bitgrupper. Det fungerar aven bar, men man far se upp eftersom bitamainte gar att lasa ocb da maste man troligtvis anvanda en skuggvariabel.

11.5.2 Att omtolka data godtyckligt

I forra avsnittet placerade vi objekt pa bestamda minnesadresser genom att tolka ombeltal till pekarvarden. Pa samma satt kan vi genom att peka pa minne med "fel" sortspekare betrakta minnesutrymmet pa vilket satt vi vill. Detta ar beroende av att vi vetprecis vad vi gor. Lat oss betrakta en doiabie-variabel som ett fait av bytes ocb skrivaut vaije byte i hexadecimal form.

/* exempel pa generell omtolkning av data */

double pi = 3.1415926536;

unsigned char *cp;

cp = (unsigned char *) &pi;

for (int 1=0; i < sizeof pi; i++) {

printf("%2x cp[i]);

}

238 © Studentlitteratur

Page 241: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

11.5 Naken omgivning

Notera att vi bara kan omtolka data genom att betrakta det aktuella minnet via en nysorts pekare. Pekaren cp bar ingen aning cm att den pekar in i nagot som aven ardeklarerat som en double-variabel. Den litar pa programmeraren och forutsatter att detar bytes som ligger dar. Denna typ av omtolkning med hjalp av pekare fungerar medvilka typer som heist, men alia omtolkningar ar inte meningsflilla.

Med samma teknik ar det ocksa mojligt att tolka bort const, vilket gor det mojligt attandra pa nagot man lovat att inte andra pa.

void lurabortconst (const int constdata[]) {

int *data = (int *) constdata; /* bort med const! */

/* andra pa faltet fast vi lovat att inte andra */

data[4] = 666;

}

Om argumentet vid anropet av funktionen faktiskt pekar till andringbara data gar dettabra, annars ar resultatet odefinierat. Lardomen av detta ar att const faktiskt inte bety-der konstant, utan nagot som kompilatom tanker kolla att vi inte andrar av misstag.Ovanstaende kod skrevs inte av misstag.

Detta later i forstone som meningslost bedrageri, men det kan ha sin anvandning omman vill ange att data som normalt inte skall andras, anda skall kunna andras i spe-ciella fall. Ett exempel ar en struct-variabel som ar const-deklarerad och darmedinnehaller data som inte skall kunna andras. Antag emellertid att den ocksa innehalleren datum-stampel som anger senaste anvandning. Denna stampel vill vi sakert kunnaandra aven om resten av variabeln skall forbli konstant.

11.5.3 Anrop av assemblerkod

Behovet att anropa assemblerkod ar oftast litet i C eftersom det redan firms sa mangasatt att pilla pa detaljer direkt i C. Det ar inte heller troligt att man kan skriva specielltmycket snabbare kod i assembler an i C. Det kan dock vara sa att man vill kuima kdraen specialinstruktion for den specifika processom som kompilatom inte kanner till ochalltsa inte kan generera.

Det enklaste sattet ar med ett eget reserverat ord, asm, som standarden rekommenderarmen inte kraver.

/* exempel pa hur assemblerkod kan ges direkt 1 C-kallkoden */

void setprloO() {

asm (" setpr 0 ");

For att kunna gora detta maste vi kurma processom utan och innan och veta precis nardet ar mojligt att utfdra olika maskininstmktioner.

© Studentlitteratur 239

Page 242: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

11. C i olika omgivningar

Manga kompilatorer kan kompilera C till lasbar assemblerkod. Man skriver en ftink-tion i C, kompilerar den till assemblerkod och studerar bur argumenten overfors tillparametrama samt ser hur returvardet retumeras. Da kan man lattare forsta hur och var

man kan utfora specialinstmktioner.

Att anropa en funktion skriven i assemblerkod fran C gar bra, men kompilatom beho-ver ha sett en deklaration av funktionen for att kanna till parametrar och returtyp.

/* funktion deklarerad har men skriven i assembler */

extern int special(char *str);

/* anrop fran C */

int i ;

i = special ("xxxyyy") ;

Assemblerkoden dversatts for sig och C-koden kompileras for sig. Lankaren som sat-ter ihop delama till ett korbart program klarar av att matcha ihop adressen special(eventuellt kan namnet ha forandrats lite, t.ex. special) i assemblerkoden med fimk-tionsdeklarationen av special i C-koden sa att anropet fungerar.

Precis pa samma satt kan en C-fimktion anropas fran assemblerkod. I bada fallen mas-te programmeraren veta hur argument skickas och hur returvarde skall retumeras. Det

kan studeras med C-till-assembler-oversattkod enligt ovan.

11.5.4 Avbrottshantering

Yttre enheter har ofta en mojlighet att avbryta det program som kors for att akut kunnahantera att nagot har hant i enheten. Det kan t.ex. vara att nagon tryckt pa en tangenteller att en timer har raknat ned till 0. For att ange vad som skall handa brukar varjeyttre enhet vara forknippad med en avbrottsvektor, en funktionsadress till en avbrotts-mtin som hantererar respektive avbrott. Avbrottsvektorema for olika yttre enheter bm-kar ligga efter varandra i ett fait pa en fomtbestamd plats i minnet. Detta fait kan i Cbetraktas som ett fait av funktionspekare, och kan darmed tilldelas varden via C-kod.Tyvarr brukar man i en funktion som hanterar avbrott vara tvungen att anvanda en spe-ciell maskininstruktion for att retumera fran funktionen. Denna instmktion kan nor-

malt inte genereras av kompilatom. En del kompilatorer har ett inofficiellt nyckelordinterrupt (eller liknande) som kan anges for en C-funktion for att indikera att funktionen skall vara en avbrottsmtin. Finns inget sadant nyckelord kan man skriva en kortavbrottsmtin i assembler. Denna mtin kan anropa en C-funktion som far gdra sjalvaarbetet. Nar denna atervander till assemblermtinen, atervander denna i sin tur med

hjalp av den speciella aterhoppsinstmktionen.

240 © Studentlitteratur

Page 243: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

11.5 Naken omgivning

Ett exempel: vi laser i manualen for var processor att minnesplats 0 ar reserverad forett fait av 256 avbrottsvektorer. Var yttre enhet xyz har nummer 47. Vi skriver funktio-nen xyzinterrupt i C som skall ta hand om avbrotten.

/* avbrottshanteringsfunktion for enhet xyz */

void xyzinterrupt(void) {

// kod som skall koras vid avbrott

}

Var processor kraver en speciell instruktion for aterhopp fran avbrott, sa vi kan intestoppa in adressen xyzinterrupt direkt i avbrottsvektom. Vi skriver en liten avbrotts-rutin i assemblerkod med adressen xyzstub. Denna anropar xyzinterrupt och retur-nerar sedan med den speciella aterhoppsinstruktionen. Vi kan i C skriva en extem-deklaration for assemblerrutinen.

extern void xyz stub (void) ;

Till sist kan vi deklarera faltet av avbrottsvektorer och stoppa in adressen till xyzstubpa ratt plats. Vi gor deklarationen i tva steg, forst en typedef som deklarerar enavbrottsvektor sedan en pekare till faltet av sadana.

typedef void (*lvector) (void); // typen: en avbrottsvektor

1vector *lvectable; // pekare till sadana

Ivectable = (Ivector *) 0; // Inltlera med platsen for faltet

//. . .

Ivectable[47] = xyzstub; // Inltlera vektorn for enhet 47

Nar enheten nu orsakar ett avbrott, hamtar processom adressen pa plats 47 och anropardarmed assemblerrutinen xyzstub. Denna anropar i sin tur C-fimktionen xyzinterrupt som gor sjalva jobbet. xyzinterrupt atervander till assemblerkoden, som utforaterhoppet pa mtt satt.

11.5.5 Att gora belt vansinniga saker i C

C ar ett synnerligen tillatande sprak. Kompilatom litar (med fa undantag) pa att pro-grammeraren vill gora det som star i koden, oberoende av om det ar meningsfullt ellerinte. Med tiden och med nya C-versioner har fler och fler vamingar for egendomlighe-ter infbrts, men man har hela tiden varit noga med att inte begransa programmerarens

"frihet". Denna egenskap ar viktig da det behovs ett sprak for att kunna pilla fritt padetaljer. De fiesta modemare sprak kringgardar vad som far goras just for att undvikaatt konstigheter uppstar.

Vill du se exempel som excellerar i konstigheter rekommenderas tavlingen The International Obfuscated C Code Contest med webbsidan http: / /www .ioccc.org som lis-tar alia vinnande bidrag. Man har till och med en tavlingsklass om vem som pa bastasM lyckats bryta mot tavlingens regler!

© Studentlitteratur 241

Page 244: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

11, a olika omgivningar

11.6 Flyttbarhet av C-programAtt skrivaprogramsom kan kompileras och korasdirekt pa mangaolika systeminne-bar naturligtvis en storbesparing i programmeringsarbete. Ett av malenmedprogram-meringsspraken ar just att deras standardisering skall medverka till att flyttbarheten(portabiliteten) okar.

Det finns mycket sagt och skrivet om vad som gor ett program flyttbart. Ett av destorsta hindren for flyttbarhet ar att man vill utnyttja saker som inte ar flyttbara. Ettvisst system kan ha vissa positiva egenskaper som man helt inte har rad att avvara trotsatt man vet att programmet inte kan flyttas direkt om man utnyttjar dessa. Det kan varaett visst operativsystems tjanster, ett grafikpakets mojligheter eller en viss person-dators teckenuppsattning.

Flyttbarhetsproblemet vad galler det ovan namndahar ingenting med val av program-meringssprakatt gora. Det ar viktigt att inse det. Det enda man kan gora ar att se till attett program som utnyttjar specialegenskaper blir sa flyttbart som mojligt. Det gor mangenom att noggrant separera de delar av programmet som ar systemberoende fran deovriga och lika noggrant dokumentera vad som behover anpassas. Det basta ar attanvanda sig av modultankade i programkonstruktionen och lagga de systemberoendedelama i en valspecificerad modul for sig.

Unix-systemet ar ett mycketbelysande exempelpa detta. Att Unix' flyttbarhet byggerpa att Unix ar skrivet i C ar bara halva sanningen. Den andra halvan ar att man noggrant skilt pa de delar i operativsystemet som tar hand om minne, yttre enheter ochspeciellt processorbeteende fran de delar som ar oberoende av vilken maskinvara somsystemet kors pa. Det har ocksa lange pagatt ett intensivt arbete i standardiseringspro-jekt som t.ex. POSIX.

Det vi hittills sagt innebar att vi lagger huvuddelen av ansvaret for flyttbarheten paprogramkonstruktdren och programmerarenoch att detta galler oberoende av program-meringssprak.

Fordelen med C i detta sammanhang ar att man praktiskt taget alltid kan gora det manvill utan omskrivningar eller tillagg till spraket. Manga sprak samtida med C, t.ex. Pascal, fick en mangd olika dialekter och tillagg for att spraket sjalvt var begransat. Nagonmotsvarande utveckling med olika varianter av C har inte skett. Nar det galler moder-nare sprak som Java och C# har man varit synnerligen noga med att standardisera bib-liotek och att se till att nya versioner av spraken inte forandrar beteendet hos existe-rande kod, utan bara ar rena utvidgningar.

Vi skall ta upp nagra av de omraden som staller till problem. Manga av problemen harsin grund i att man i C vill behMla narheten till maskinen. I standarden har man darfor

deklarerat att vissa egenskap ar implementationsberoende. Det vi tar upp har ar inte

242 © Studentlitteratur

Page 245: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

11.6 Flyttbarhet av C-program

alia implementationsberoende detaljer, utan endast de som erfarenhetsmassigt visat sigstalla till problem.

11.6.1 Identifierare

Det som kan orsaka problem ar om man bar anvant langa identifierare och flyttar ettprogram fran ett system som accepterar fler signifikanta tecken till ett system som tarfarre. Eftersom det inte ar fel att ha langa identifierare sa kommer eventuellt flera identifierare att betyda samma sak. Vill det sig ilia upptacks inte felet av kompilatom, meni basta fall kan det orsaka foljdfel som upptacks. Speciellt galler detta extema identifierare, dar en del aldre system endast bar sex eller sju signifikanta tecken.

11.6.2 Heltalstyper

Typen int ar i en implementering den "naturliga" langden for en viss processor. Denar oftast 16 eller 32 bitar ocb man bor alltid anvanda long om man inte vet att det rack-

er med 16 bitar.

Omvandlingar mellan olika heltalstyper staller ofta till problem i dag, speciellt nar detgaller parametervarden, eftersom de omvandlas till int om inte annat sags. Flyttningarfran ett 32-bits till ett 16-bits-system ger nastan utan undantag problem om inte pro-grammeraren varit noggrann med att deklarera long overallt dar det skulle bebdvas (i32-bits-systemet fimgerar det bra anda eftersom int dar oftast ar identiskt med long).

Huruvida typen char kan innebMla negativa varden ar ett klassiskt problem dar stan-darden sager att det ar upp till vaije implementering. Typfallet ar att man lagrar eof(som skall vara ett negativt varde) i en char vilket endast gar bra i system dar char

betraktas som signed.

Skillnader i teckenkoder mellan olika system, t.ex. for persondatorema med sina egnateckenuppsattningar, ar ett besvarligt problem, men det ar inte C-specifikt. Medanvandning av wchar t ocb Unicode minskar problemet.

Som vi diskuterade i avsnitt 3.3.1 fmns det i C99 speciella namn for typer med exakt |storlek, t.ex. ints t ocb uint32_t, ocb detta bjalper den som vill skriva portabelt.

11.6.3 Poster

Hur delama i poster verkligen placeras beror pa vilka krav en speciell processor stallerpa bur olika typer skall placeras i minnet. Detta galler bade vanliga poster ocb postermed bitfalt. Problem uppstar om man forutsatter en viss placering. Operatom sizeoftar bansyn till om det bebover laggas till extra tomt utrymme for utfyllnad i poster.

) Studentlitteratur 243

Page 246: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

11. C i olika omgivningar

11.6.4 Funktioner

Manga av de problem som uppstatt kring funktioner, deras returvardenoch parametrarbar fbrsvunnitnar man inte langre anvander den gamla formen av fiinktionsdeklaratio-ner och -defmitioner, utan utnyttjar prototyper. Standarden sager att det gamla sattetbor vara pa vag bort. Men det fmns fortfarande kvar som ett altemativ eftersom detfinns sa mycket C-kod som anvanderdet. C99 kraver att kompilatom vamar.

Problemet ar dels att funktioner som inte deklarerats ansesha returvarde av typen int,dels att argumenten vid anropinte med sakerhetomvandlas till den typ parametembar.Se kapitel 6 for exempel pa bada fallen.

Om man far ett programutan anvandningav prototypersom skall flyttas till ett systemsom stoder prototyper kan det sakert vara vM arbetet att skriva om alia funktions-

deklarationer och -definitioner med prototyp.

11.7 Ovningsuppgifter1. Undersok bur programparametrama (till main) skickas till programmet i ditt sys

tem. Skriv nagot enkelt exempel.

2. Tag ett program och profilera det. Se dock forst efter om du kan se av programko-den direkt var det kommer att ta mest tid.

3. Tag reda pa bur parameteroverfbring och returvarden fran funktioner overfors iditt system. Om det ar mojligt, skriv en liten funktion i assembler (t.ex. en fiink-tion som lagger ihop tva heltal och retumerar summan) och lanka ihop den med etthuvudprogram for testning.

4. Om du bar tillgang till en naken processor och mojlighet att kompilera C-programfor den: Flytta nagot program du skrivit tidigare till denna miljo. Du kommer att falasa manga manualer och prova dig ffam en hel del. Det basta ar om du bar en"guru" att fraga.

5. Om du kan fa ut den assemblerkod som din kompilator genererar, undersok huru-vida nyckelordet volatile gor nagon skillnad. Om du kan valja att fa C-kompila-tom att optimera koden, gor det och se bur det paverkar saken.

6. Tag ett mindre program utvecklat i en annan miljo och flytta det till ditt C-system.Du tvingas troligvis noga ta reda pa bur ditt system ser pa identifierarlangd, storlekpa int etc. Det kan mycket val vara sa att du vid lankningen upptacker att programmet anvant funktioner som inte finns med i ditt system. Forsok att hitta ellerskriv en ersattning.

7. Ladda hem nagra program ffan http: / /www. loccc. org och se om du kan begripabur de faktiskt fungerar.

244 © Studentlitteratur

Page 247: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Appendix AReserverade ord

och operatorer

I tabell A.l visas de reserverade orden i C.

auto

break

case

char

const

continue

default

do

>Student! itteratur

double inline sizeof volatile

else int static while

enum long struct Bool

extern register switch Complex

float restrict typedef Imaginary

for return union

goto short unsigned

if signed void

Tabell A.l Reserverade ord

Page 248: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

AppendixA Reserverade ord och operatorer

I tabell A.2 ges en sammanstallning av de operatorer som finns i C. I de tva overstarutoma finns de operatorer som bara bar en operand, de s.k. undra operatorema, och ide ovriga rutoma de operatorer som bar tva operander. (Undantaget ar villkorsopera-tom som bar tre operander.) Operatorema ar uppraknade i prioritetsordning. Operatorema i den overstamtan bar bogst prioritetocb operatoremai den nedesta lagstpriori-tet. Sadana operatorer som star i samma mta bar samma prioritet

indexering ocb anropselektion i stmcter ocb unioner

postfix, okning ocb minskning

[] 0

->

++

prefix, okning ocb minskningunara + ocb -

adress ocb dereferencinglogiskt NOT ocb bitoperatom NOTtest av storlek

explicit typomvandling

++

+

& *

!

sizeof

(typnamn)

multiplikation, division, rest * / %

addition, subtraktion +

shift « »

mindre an ocb storre an

II

A

II

V

A

V

likbet, olikbet == !=

bitoperatom AND &

bitoperatom XOR

bitoperatom OR 1

logiskt AND &&

logiskt OR 1 1

villkorsoperatom 7 j

tilldelningsoperatorer += -= *= /= %=

<<= >>=

&= '^= 1 =

kommaoperatom .

Tabell A.2 Operatorer

246 ) Studentlitteratur

Page 249: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Appendix BLATIN Ikoder

\xOO nul \x20 space \x40 @ \x60

\x01 soh \x21 ! \x41 A \ X 61 a

\x02 six \x22M

\x42 B \x62 b

\x03 etx \x23 # \x43 C \ X 6 3 c

\x04 eot \x24 $ \x44 D \x64 d

\x05 enq \x25 % \x4 5 E \x65 e

\xO 6 ack \x26 & \x46 F \x66 f

\x07 bel \x27»

\x47 G \x67 g

\x08 bs \x28 ( \x48 H \x68 h

\x09 ht \x29 ) \x49 I \x69 i

\xOA If \x2A * \x4A J \x6A j\xOB vt \x2B + \x4B K \x6B k

\xOC ff \x2C J \x4C L \x6C 1

\xOD cr \x2D - \x4D M \x6D m

\xOE so \x2E \x4E N \x6E n

\xOF si \x2F / \x4F 0 \x6F 0

\xlO die \x30 0 \x50 P \x70 P

\xll del \x31 1 \x51 Q \x71 q

\xl2 dc2 \x32 2 \x52 R \x72 r

\xl3 dc3 \x33 3 \x53 S \x73 s

\xl4 dc4 \x34 4 \x54 T \x74 t

\xl5 nak \x35 5 \x5 5 U \x75 u

\xl6 syn \x36 6 \x56 V \x76 V

\xl7 etb \x37 7 \x57 W \x77 w

\xl8 can \x38 8 \x58 X \x78 X

\xl9 em \x39 9 \x59 Y \x79 y

\xlA sub \x3A \x5A Z \x7A z

\xlB esc \x3B \x5B [ \x7B {\xlC fs \x3C < \x5C \ \x7C 1\xlD gs \x3D = \x5D ] \x7D }\xlE rs \x3E > \x5E A \x7E ~

\xlF us \x3F ? \x5F \x7F del

tabellenfortsdtter pd ndsta sida

© Studentlitteratur

Page 250: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Appendix B LATIN ! koder

\x80 \xAO nbsp \xCO A \xEO a

\x81 \xAl i \xCl A \xEl d\x82 \xA2 0 \xC2 A \xE2 a

\x83 \xA3 £ \xC3 A \xE3 a

\x84 ind \xA4 • \xC4 A \xE4 a

\x85 nel \xA5 ¥ \xC5 A \xE5 k\x86 ssa \xA6 1

1\xC6 IE \xE 6 ae

\x87 esa \xA7 § \xC7 C \xE7 9\x88 hts \xA8 \xC8 E \xE8 e

\x89 htj \xA9 © \xC9 E \xE9 e

\x8A vts \xAAa

\xCA E \xEA e

\x8B pld \xAB « \xCB E \xEB e

\x8C plu \xAC - \xCC i \xEC i\x8D ri \xAD - \xCD 1 \xED 1

\x8E ss2 \xAE (D \xCE I \xEE i

\x8F ssS \xAF \xCF 1* \xEF i'

\x90 dcs \xBOo

\xDO D \xFO 6

\x91 pul \xBl ± \xDl N \xFl n

\x92 pu2 \xB22

\xD2 6 \xF2 6\x93 sts \xB3 3 \xD3 6 \xF3 6

\x94 cch \xB4 \xD4 6 \xF4 6

\x95 mw \xB5 \xD5 0 \xF5 6

\x96 spa \xB6 \xD6 0 \xF6 0

\x97 epa \xB7 \xD7 X \xF7

\x98 \xB8 \xD8 0 \xF8 0

\x99 \xB91

\xD9 u \xF9 u

\x9A \xBAo

\xDA u \xFA u

\x9B csi \xBB » \xDB u \xFB u

\x9C St \xBC % \xDC u \xFC ii\x9D osc \xBD ^2 \xDD Y \xFD y\x9E pm \xBE y4 \xDE I> \xFE

\x9F ape \xBF i \xDF B \xFF y

TabellB.l LATIN_!-koder

I Tabell B.l visas de tecken som ingar i LATrN_l. Dessa tecken overensstammer medde fbrsta 256 tecknen i Unicode. Vissa tecken, t.ex. esc och del, ar kontrolltecken ochsaknar grafiska symboler. I tabellen bar dessa teckens symboliska nanin skrivit medkursiv stil.

248 ) Studentlitteratur

Page 251: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Appendix CBinar lagringavtal

3

Binar lagring av heltal

Det for manniskor naturliga sattet att representera tal ar att anvanda det decimala tal-systemet. Tal som 4329 tolkar vi utan att kanske reflektera narmare over det som

4 X10^ + 3 X10^ + 2 xlo' + 9x 10°

Generellt kan sagas att ett decimalt tal

(dar a:na betecknar heltalssiffror) egentligen betyder

a^xlO" +a„-jxio" +... +ajXlo' +Oqx10°

Har bar basen 10 anvants och for varje siffra galler forstas0 < a,- < 10

Detta kan generaliseras till talsystem dar man anvander andra baser an basen 10. Om

vi betecknar basen med B far vi da att ett tal

uttryckt i basen B betydern" I r>"~ 1 I I r»l I+5^_jX5 +...+5jX5 +5qX5

Pa analogt satt galler bar for siffroma i talet att0<Sj<B

Valjer man B = 2 far man det bindra talsystemet dar siffroma alltsa bara tillats ba var-dena 0 ocb 1. For en dator ar det naturligt att anvanda detta talsystem eftersom varjesifffa da kan representeras av en bit i datoms minne. Det binara talet 10111 kan t.ex.tolkas som

Ix2'^ + 0x2° + lx2^+lx2' + lx2°

eller 23 om vi uttrycker det decimalt.

© Studentlitteratur

Page 252: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Appendix C Bindr lagring av tal

Tva andra talsystem som ofta anvands i datorsammanhang ar det oktala talsystemetsom har basen 8 och det hexadecimala talsystemet som bar basen 16. Eftersom det intefirms nagra sifFertecken med talvarden storre an 9 anvander man i det hexadecimalatalsystemet bokstavema A, B, C, D, E och F for att beteckna hexadecimala siffror med

vardena 10, 11, 12, 13, 14 respektive 15. Anledningen till att de oktala och hexadecimala talsystemen ar anvandbara ar att man mycket latt kan vaxla mellan det binara talsystemet och dessa system. Om man grupperar sifFroma i ett binart tal tre och tre farman talet uttryckt i oktal Form. Det binara talet 101GO 1111 blir t.ex. i oktal Form 517.

101 001 111

5 1 7

Grupperar man i stallet siffroma fyra och fyra i ett binart tal far man talet uttryckt ihexadecimal Form. Talet 0110101100001111 kan t.ex. skrivas som 6B0F.

0110 1011 0000 1111

6 B 0 F

En oversattningmellan binar och vanlig decimal Form ar betydligt mer komplicerad.

Vi har sett att ett objekt i C ar ett miimesutrymme som bestar av ett antal bytes, darvarje byte bestar av minst 8 bitar. Vi skall boija med att diskutera en lagringsForm darman belt enkelt later varje bit representera en binar siFfira. Lat oss kalla derma FormForteckenldsform. Om man har bytes som bestar av 8 bitar kan man i derma Form i varjebyte lagra ett tal med 8 binara sififror. Bitmonstren

01001110 10001001

kan tolkas som talet 78 (64+8+4+2) respektive 137 (128+8+1). Bitmonstren

00000000 11111111

kan tolkas som talen 0 respektive 255 och ar det minsta respektive storsta tal man kanlagra i en 8-bitars byte om man anvander teckenlos Form. Anvander man fiera bytes foratt lagra ett heltal kan man lagra tal som bestar av ett storre antal binara sifFror. Gene-rellt kan sagas att det storsta tal som kan lagras med N bitar i teckenlos Form ar

2^-1 .

Problemet med den teckenlosa lagringsFormen ar naturligtvis att man inte kan lagranegativa tal. Behoverman gora detta far man anvandaen arman lagringsForm. Den lagringsForm som ar vanligast och anvands i de fiesta datorer kallas tvdkomplementsform.

I tvakomplementsFormen lagras alia positiva tal med en inledande nolla Foljd av N-1bitar som representerar de binara siffroma. Nar man skall lagra ett negativt tal utgarman Fran motsvarande positiva tals bitmonster och ersatter alia nollor med ettor och

250 ) Studentlitteratur

Page 253: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

AppendixCBindrlagringavtal

viceversa.Darefteradderarmentalet1.Latosssomexempelsehurtalet-40lagrasitvakomplementsform(med8bitar).Forstskrivervinerhurtalet+40lagras.Darefterbyterviettormotnollorochadderartalet1.

0010100011010111

+1

11011000

Talet-40representerasalltsamediioiiooo.Mankannerlattigenalianegativatalpaattdebarenettaforst.DetarlikalattattgaatandrahMlet.Omvit.ex.barbitmonstret11011000ocbundrarvilkettaldettarepresenteraritvakomplementsformgorvipasammasa\t.Forstbytervinollormotettorocbsedanadderarvitalet1.

11011000=>00100111

+1

00101000

Resultatet00101000tolkarvilattsom+40vilketbetyderattdetlagradetaletvar-40.

OmmanbarNbitartillsittforfogandeocbanvandertvakomplementsformkanmanlagrataliintervallet-2^~^till2^~-1.Med8bitarkanmant.ex.lagrataliinterval-let-128till127,med16bitarkanmanlagratalen-32768till32767ocbmed32bitartalen-2147483648till2147483647.1faktarutanvisasbeltalsomlagratsi8bitar.

Principerforlagringavheltal

BitmonsterTeckenlostTvakomplement

0000000000

00000001+1+1

00000010+2+2

01111110+126+126

01111111+127+127

10000000+128-128

10000001+129-127

11111110+254-2

11111111+255-1

Binarlagringavflyttal

Idetdecimalatalsystemetgallerbasen10aventillbogeromdecimalpunkten.Detdecimalatalet0.625betydert.ex.egentligen

6x10"'+2X10"^+5X10"^

)Studentlitteratur251

Page 254: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Appendix C Bindr lagring av tal

Pa analogt satt kan man tolka ett binart tal

som

Z?jx2 +^72^2 ^+... + X2

Det binara talet 0.101 kan alltsa tolkas som

0.5 + 0 + 0.125 = 0.625

For att i vanlig skrift ange reella tal anvander man ofta s.k. exponentform. Detta ar spe-ciellt anvandbart for att ange tal som ar till beloppet mycket sma eller mycket stora.Talen 79000000 och 0.000000016 kan t.ex. skrivas som

0.79 X10^ 0.16 X10"^

Man kan saga att man later decimalpunkten "flyta omkring" inom talomradet sa attman alltid kan ange ett tal pa samma satt. Vid lagring av reella tal i datom anvanderman en motsvarande teknik med flytande punkt och man brukar darfor tala omflyttalnar man lagrar reella tal enligt denna teknik.

Principen beskrivs bast med ett exempel. Antag att vi bar talet 13.5. Binart kan dettatal skrivas som 1101.1.Anvander vi exponentform kan talet skrivas

+0.11011 X2"^

Den fbrsta delen 0.11101 brukar kallas for mantissa och den andra delen 100for exponent. Bada dessa anges som binara tal (exponenten 100 betyder 4 decimalt).

For att kunna lagra ett reellt tal som ett flyttal i en dator maste man alltsa lagra taletstecken, dess exponent och dess mantissa. For att lagra tecknet behovs bara en bit (0brukar betyda + och 1 betyda -). Hur manga bitar som anvands for mantissanrespek-tive exponenten varierar fran dator till dator. Om vi t.ex. antar att man anvander 1 bitfor tecknet, 8 bitar for exponenten och 23 bitar for mantissan, skulle talet 13.5 kunnarepresenteras pa foljande satt med 32 bitar:

00000010011011000000000000000000

(Har bar vi lagt teckenbiten forst, darefter exponenten och sist mantissan.) Det exaktasattet att lagra flyttal skiljer sig at fran dator till dator men den allmanna principen arden som bar beskrivits.

Hur stora och sma talen kan vara till beloppet bestams av antalet bitar som anvands foratt lagra exponenten (exponenten kan ocksa vara negativ). Antalet bitar som anvandsfor lagring av mantissan avgor hur manga siffrors noggrannhet man far. (Det kravs igenomsnitt 3.32 bitar per decimal sifffa.)

252 © Studentlitteratur

Page 255: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Appendix DAritmetiska

typomvandlingar

I delta appendix beskrivs omvandlingar mellan olika aritmetiska typer. Med aritmetiska typer menas heltalstyper och flyttalstyper. Vi graderar forst de aritmetiskatypema pa foljande salt, dar "hogre" typer anges forst och "lagre" typer anges sist:

long double

double

float

unsigned long int

long int

unsigned int

int

unsigned short int

short int

unsigned char

signed char

Bool

hogsta typen

lagsta typen

Vi sager t.ex. att typen float ar "hogre an" int och att short int ar "lagre an" unsigned short int. Typen char raknas antingen som signed char eller som unsigned

char, beroende pa den aktuella implementeringen.

De "vanliga" aritmetiska typomvandlingarna

Nar man bar operatorer med tva operander sker omvandlingen av operandema enligt"de vanliga aritmetiska typomvandlingarna" som gar till pa foljande salt: Kalla denoperand som har hogst typ for ol och den andra operanden for o2. Om bada operandema bar samma typ sa kalla den ena av dem for ol och den andra for o2. Kalla ol:styp for tl och o2:s typ for t2. Om tl ar en typ som ar hogre an unsigned int sa

omvandlas o2 till typen tl. Annars utfors s.k. heltalsvidgning (se nedan) pa bada operandema och om nagon av operandema efter heltalsvidgningen har typen unsignedint sa omvandlas ocksa den andra operanden till unsigned int.

Heltalsvidgning av en operand gar till pa foljande salt: Om operanden har nagon avtypema signed char, unsigned char, short int eller unsigned short int Sa omvand-

© Studentlitteratur 253

Page 256: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Appendix D Aritmetiska typomvandlingar

las den till typen int eller till typen unsigned int. (Typen char betraktas antingensom signed char eller som unsigned char.) Det normala ar att omvandling sker tilltypen int. Omvandling till unsigned int sker bara om operandens alia mojliga var-den inte kan representeras i en int. (Detta kan t.ex. intraffa om operanden bar typenunsigned short int och lika manga bitar anvands for att representera short int ochint.)

Ejfekter av aritmetiska typomvandlingar

I de fbljade avsnitten beskrivs effektema av typomvandlingar, automatiska eller expli-cita, mellan de olika aritmetiska typema. I den fortsatta beskrivningen betecknar tlden typ vi skall omvandla fran och t2 den typ vi skall omvandla till.

Omvandlingfrdn en Idgre till en hogre heltalstyp

Om tl ar unsigned och t2 en hogre typ som ocksa ar unsigned sa forandras inte var-det. Om tl ar unsigned och t2 en hogre typ som ar signed forandras inte heller vardetom det ar sa att t2 representeras med fler bitar an tl. Om tl och t2 i detta fall skullevara lika langa kan flera av de mojliga vardena inte representeras med typen t2. Vadsom bander med dessa varden ar implementeringsberoende.

Om tl ar signed och t2 en hogre typ som ocksa ar signed, sa forandras inte vardet.Om tl ar signed och t2 en hogre typ som ar unsigned, sa forandras inte vardet om detar storre an eller lika med noil. Om vardet ar mindre an noil sa omvandlas det forst till

entyp tx som ar signedoch som ar lika lang som t2. Darefter adderas talet 2^ tillvardet innan det omvandlas till typen t2. (N ar det antal bitar som t2 representeras med.)Denna sista omvandling fran tx till t2 resulterar inte i nagon andring av bitmonstret omtvakomplementform anvands. Bitmonstret behalls men tolkas bara pa ett annat satt.

Vi studerar nagra exempel. Antag att 8 bitar anvands for att representera typen shortint och 16 bitar for typen int. Antag vidare att variabeln s bar typen short int, ustypen unsigned short int, i typen int OCh ui typen unsigned int.

us = 30 0;

ui = us ; /* ui far vardet 300 * /

i = us; /* i far vardet 300 * /

s = 200 ;

i = s; /* i far vardet 200 * /

ui = s ; /* ui far vardet 200 V

s = -3;

i = s; /★ i far vardet -3 * /

ui = s ; /* ui far vardet 65533

254 © Studentlitteratur

Page 257: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Appendix D Aritmetiska typomvandlingar

Omvandlingfrdn en hogre till en Idgre heltalstyp

Om tl ar en heltalstyp och t2 en kortare heltalstyp som ar unsigned sa kommer detresulterande vardet att bli den positiva rest som uppstar da det urspmngliga vardetdivideras med 2^. (N ar antalet bitar som typen t2 representeras med). Denna regelinnebar belt enkelt att om tl ar unsigned eller om tl ar signed och representeras i tva-komplementform, sa kommer de bitar till vanster som "inte far plats" att slangas.

Om t2 ar signed och tl antingen ar langre an t2 eller unsigned och lika lang som /2, saar det implementeringsberoende vad som bander med sadana varden som inte kanrepresenteras av typen t2. Nagra exempel (vi gor samma antaganden som ovan):

u i = 5 0 0 ;

us = ui; /* us far vardet 244 */

i = 600;

us = i; /* us far vardet 88 */

i = -2;

us = i; /* us far vardet 254 */

s = us; /* implementeringsberoende */

Omvandlingar mellanflyttalstyper

Om tl ar en lagre typ an t2 forandras inte vardet. Om tl ar en hogre typ an t2 kan tvasaker intraffa. Antingen ligger vardet utanfor det talomrade som kan beskrivas medtypen t2 eller sa ligger vardet innanfor /2:s talomrade. I det forsta fallet ar resultatet avtypomvandlingen odefmierat. I det andra fallet kan det handa att vardet inte kanbeskrivas exakt med typen t2 eftersom t2 kan ha samre precision an /7. I sa fall blirresultatet ett av de tva varden som kan representeras med typen t2 och som ligger nar-mast det urspmngliga vardet.

Omvandlingar mellan heltal ochflyttal

Om tl ar en heltalstyp och t2 en flyttalstyp blir resultatet i princip oforandrat men om

t2 inte bar sa manga siffrors noggrannhet att vardet kan beskrivas exakt kan man fa enfbrlust av precision i det omvandlade vardet.

Om tl ar en flyttalstyp och t2 en heltalstyp sa tar man bara hansyn till heltalsdelen avdet urspmngliga vardet. Decimalema "slangs" alltsa och man far avhuggning, inteavmndning. Om det heltalsvarde man da far inte kan representeras av typen t2 (vardetar t.ex. for stort), sa ar resultatet odefinierat.

Omvandlingar till ochfrdn typen _Bool I

Vid omvandling ffan ett varde av en skalar typ (numerisk typ eller pekartyp) till typen__Booi blir resultatet 0 om vardet som skall omvandlas ar lika med 0 och 1 annars. Vid

© Studentlitteratur 255

Page 258: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Appendix D Aritmetiska typomvandlingar

omvandling fran typen _boo1 till ett numeriskt varde galler reglema som beskrivitsovan, vilket innebar att det resulterande vardet blir antingen 0 eller 1.

256 © Studentlitteratur

Page 259: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Appendix Eprintf

E

printf ("formatstrang", argumentl, argument2, etc);

Argumenten ar varden som skall skrivas ut. Formatstrangen kan innehalla vanlig textplus ett godtyckligt antal omvandlingsspeciftkationer med formen

%[flaggor][antal_positloner][.precision]typspecifikation

Det inom [] kan utelamnas. Lika manga argument som omvandlingsspecifikationer.

Flaggor i omvandlingsspecifikationer

0 Utfyllnad sker med inledade nollor istallet for blanka tecken

Utskriften vansterjusteras

+ Plus- eller minustecken skrivs alltid ut.

biankt Blankt tecken skrivs forst om talet ar positivt

# For formaten o och x: inledande o resp. ox skrivs alltid ut

For flyttalsformat: decimalpunkten skrivs alltid ut

Typspecifikationer vid utskrift av text

Vardet tolkas som en int innehallande en teckenkod.

Motsvarande tecken skrivs ut.

Vardet tolkas som en pekare till ett fait med komponenter av

typen char. Alia tecknen i faltet, ffam till nolltecknet, skrivs ut.

Vardet tolkas som en wchar t innehallande en teckenkod.

Tecknet omvandlas till en foljd av bytes (UTF-8) och skrivs ut.

Vardet tolkas som en pekare till ett fait med komponenter avtypen wciiar t. Alia komponentema i faltet, fram till nolltecknet,

omvandlas till en foljd av multibyte characters (UTF-8) och skrivs ut.

Page 260: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Appendix E prin tf

Typspecifikationer vid utskrift av heltal

d, i Vardet tolkas som int, decimal utskrift

u Vardet tolkas som unsigned int, decimal utskrift

0 Vardet tolkas som unsigned int, oktal utskrift

X, X Vardet tolkas som unsigned int, hexadecimal utskrift

med sma resp. stora bokstaver

1 Kan sta fore nagon av ovanstaende, t.ex. id och lu

Da tolkas vardet som long int, unsigned long int, etc

11 Kan sta fore nagon av ovanstaende, t.ex. iid och iiu.

Da tolkas vardet som long long int, unsigned long long int, etc.

Typspecifikationer vid utskrift av flyttal

f, If Vardet tolkas som double. Utskrift sker i "vanlig" form medminst en heltalssiffra. Antalet decimaler bestams av precisions-angivelsen. Om precision inte anges skrivs 6 decimaler ut.

e, le Vardet tolkas som double. Utskrift sker i exponentform meden heltalssiffra. Antalet decimaler bestams av precisions-angivelsen. Om precision inte anges skrivs 6 decimaler ut.

E, IE Samma som formen e men med den skillnaden att ett stort E

anvands i stallet for ett litet e i utskriften.

g, ig Vardet tolkas som double. Utskrift sker antingen i "vanlig"form eller pa exponentform. Om vardet ar "stort" (exponenten arstorre an precisionen) eller "litet" (exponenten ar mindre an -4)sker utskriften i exponentform, annars i "vanlig" form.

G, iG Samma som formen g men med den skillnaden att ett stort E an

vands i stallet for ett litet e om utskriften sker i exponentform.

L Kan sta fore nagon av ovanstaende enkla, t.ex. Lf och Le.

Da tolkas vardet istallet som long double.

258 ) Studentlitteratur

Page 261: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

scan ("formatstrang", argumentl, argument2, etc);

Argumenten ar adresser till variabler som man skall lasa in data till. Formatstrangen

kan innehalla vanlig text plus ett godtyckligt antal omvandlingsspecifikationer medformen

%[ *] [ antal_posi t ioner]typspeci f ikat ion

Det inom [] kan utelamnas. Lika manga argument som omvandlingsspecifikationer.Om * anges sker dock ingen tilldelning och motsvarande argument skall utlamnas.

c Argumentet skall vara en pekare till en variabel av

typen char. Ett tecken lases in och laggs dit pekaren pekar.Nolltecken laggs inte till.

s Argumentet skall vara en pekare till ett teckenfalt. Inledande vitatecken hoppas forst over. Efterfoljande tecken som inte ar vita lases

sedan in och laggs dit pekaren pekar. Ett nolltecken laggs till sist.

[text] Som specifikationen s, men matchar bara tecken som ingar i text.

Y^text] Som specifikationen s, men matchar bara tecken som inte ingar i text.

1 Kan sta fore de ovanstaende, t.ex. ic och is.

Argumentet skall vara en pekare till en variabel avtypen wchar t resp. till ett fait med komponenter av denna typ.

De inlasta tecknen tolkas da som multibyte characters och gors omtill typen wchar_t innan de laggs dit pekaren pekar.

) Studentlitteratur

Page 262: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Appendix F scanf

hh

11

Typspecifikationer vid lasning av heltal

De inlasta tecknen skall tolkas som ett decimalt heltal och

placeras i en variabel av typen signed int.

De inlasta tecknen skall tolkas som ett decimalt heltal och

placeras i en variabel av typen unsigned int.

De inlasta tecknen skall tolkas som ett oktalt heltal och

placeras i en variabel av typen unsigned int.

De inlasta tecknen skall tolkas som ett hexadecimalt heltal och

placeras i en variabel av typen unsigned int.

De inlasta tecknen tolkas pa samma satt som en heltalskonstant(se avsnitt 3.3.2) och placeras i en variabel av typen signed int.

Kan sta fore de ovanstaende, t.ex. hhd och hhi.

Talet placeras da i en char istallet for en int.

Kan sta fore de ovanstaende, t.ex. hd och hi.

Talet placeras da i en short int istallet for en int.

Kan sta fore de ovanstaende, t.ex. id och ii.

Talet placeras da i en long int istallet for en int.

Kan sta fore de ovanstaende, t.ex. iid och iii.

Talet placeras da i en long long int istallet for en int.

Typspecifikationer vid lasning av flyttal

e, f, g De inlasta tecknen skall tolkas som ett reellt tal

och placeras i en variabel av typen float.

1e, if, 1g De inlasta tecknen skall tolkas som ett reellt tal

och placeras i en variabel av typen double.

Le, Lf, Lg De inlasta tecknen skall tolkas som ett reellt tal

och placeras i en variabel av typen long double.

260 ) Studentlitteratur

Page 263: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Sakregister

- 58

— 61

68

71

DATE 196FILE 196LINE 196STDC 196TIME 196

_Bool 29, 255_Con^lex 37_Iniaginary 37

, 87

: 67

! 65

!= 63

? 67

. 159, 185, 187

... 96, 102, 221" 9, 213* 58, 132, 133, 156** 157

V 11

*= 71

*const 143

/ 58

/* 11

/= 71

\0 20, 34\a 34

\b 34

\f 34

\n 9, 34\r 34

\t 34

\v 34

&68, 131&& 65

&= 71

# 191, 193, 196

## 193

#define 13, 191, 192

#elif 195

#else 194

#endif 194

#error 196

#if 195

#ifdef 194

#ifndef 195

#include 9, 13, 103, 194, 198

#line 196

#pragma 196

#pragma once 196

#undef 196

% 58

%= 71

+ 58

++ 61

+= 71

< 63

« 68

«= 71

<= 63

-= 71

= 71

== 63

-> 164, 185

> 63

>= 63

» 68

»= 71

I 68

1= 71

I I 65

261

Page 264: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Index

A

abort 211

abs 60

absolutvarde 60

acos 60, 201acosf 60

acosl 60

Ada 3

addition 58

adress 131

adressera bestamd minnesplats 236adressoperator 131aktiveringspost 104aktuell filposition 207aktuell parameter 103alert 34

Algol 60 3allokering av minne 74, 153, 210anpassning av parametrar 104, 107anrop av assemblerkod 239anrop av funktion 103ANSI-C 4

argc 228

argument 103argv 228

aritmetik pa pekarvarden 137aritmetisk typ 253aritmetisk typomvandling 38aritmetiskt skift 70

aritmetiskt uttryck 58ASCII 32, 33, 247asctime 218

asin 60, 201asinf 60

asinl 60

asm 239

assemblerkod 239

atan 60, 201atan2 201

atanf 60

atanl 60

atexit 211

atof 209

atoi 209

atol 209

auto 112, 113automatisk typomvandling 38avbrott 219, 239avbrottshantering 240avbrottsrutin 240

avbrottsvektor 240

avbryta exekvering 211

262

avhuggning 255avlusning 235avrundning 45

B

backspace 34balanserat trad 184

Basic Multilingual Plane 32BCPL 3

Bell Laboratories 3

benamnda initierare 144, 163berakningsordning 74berakningsriktning 74binar fil 207

binar lagring av flyttal 251binar lagring av heltal 249binar strom 201

binara talsystemet 249binarsdkning 212binarttrad 180

bit 26, 68, 184bitfalt 184, 237bitmask 70, 186, 238bitmdnster 69

bit-operator 68bitvis ELLER 68

bitvis exklusiv ELLER 68

bitvis OCH 68

block 78

BMP 32

bokstaver 32

boolean 28

break-sats 90

brytpunkt 235bsearch 212

bubble sort 140

buffert 203

buffrad strom 201

byte 26bige 166

c

C# 242

C++ 5

CIX 224

C89 4

C99 4

calloc 153, 210carriage return 34cast 39

ceil 60, 201

Page 265: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

ceilf 60

ceill 60

char 28, 33CHAR_BIT 29CHAR_MAX 29CHAR_MIN 29charl6_t 35, 36char32_t 35, 36citationstecken 9, 213clearerr 209

CLK_TCK 216

clock 216

clock_t 216CLOCKS_PER_SEC 216code point 32const 27, 142, 239const ochpekare 142, 166continue-sats 91

cos 60, 201cosf 60

cosh 60, 201coshf 60

coshl 60

cosl 60

ctime 218

ctype.h 199

D

datum 216

DBL_DIG 37DBL_MAX 37DBL_MIN 37de vanliga aritmetiska typomvandlingama 253debugging 235decimal heltalskonstant 31

decimala talsystemet 249, 251decimalkomma 200

decimalpunkt 38, 200define 13, 191, 192definition 100

definition av funktion 18, 95definition av makro 13, 191, 192deklaration 100

deklaration av egen typ 53deklaration av funktion 100

deklaration av fait 134

deklaration av posttyp 159deklaration av union 187

deklaration av upprakning 40deklaration av variabel 26

deklarationsomrade 110

delstrang 89deltrad 180

deluttryck 58difftime 217

direkt in- och utmatning 207division 58

do-sats 85

double 37

dubbellankad lista 180

dynamiskt minne 153, 210

E

elif 195

ELLER 65, 69ellips 221ellips-notation 96, 102, 221else 194

empty 126

end of file 17, 51, 55, 202, 204endif 194

enkel tilldelning 71enum 40, 135EOF 17, 51, 55, 202, 204errno 199, 209errno.h 199

error 196

escape-sekvens 34, 35execl 232

exekvering 7exit 211

exklusiv ELLER 68

exp 60, 201expf 60

expl 60

explicit typomvandling 39exponent 252exponentform 38, 252extern 102, 112, 114extern fimktionsdeklaration 100

extern variabel 114

extemtnamn 25

F

fabs 60, 201fabsf 60

fabsl 60

falskt 28

fclose 203

felhantering 208feof 209

terror 209

Index

263

Page 266: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Index

llabs 60

locale.h 200

localtime 217

log 60, 201loglO 60, 201loglOf 60

loglOl 60

logaritm 60logf 60

logisk operator 65logiskt skift 70logisktvarde 28, 63, 65logl 60

lokala konventioner 200

long double 37

long int 28

long long int 28

LONG_MAX 29LONG_MIN 29longjmp 219

lage 92, 219lankad datastruktur 166

lankadlista 168

lasning av flyttal 49, 260lasning av heltal 48lasning av textstrang 19, 204, 259

M

Macintosh 232

Maclaurin-serie 85

main 8, 95, 228make 234

makro 13, 191, 192makro med parametrar 192makrodefinition 13, 191, 192malloc 74, 154, 210mantissa 252

mask 70, 186matematisk standardfunktion 59, 200, 223math.h 59, 200matris 148

memchr 216

memcmp 216

memcpy 216

memset 216

Microsoft Windows 232

miljo 197, 211minne, allokera 74, 153, 210minne, dterlamna 154

minskningsoperator 61mjukvaruavbrott 219

266

mktime 218

modul 124

modulo 30

modulo-operator 58modular programutveckling 123multibyte character 33multiplikation 58

N

naken omgivning 197, 227, 236namn 25

negation 68new line 9, 34nod 166

nolltecken 20, 34, 144null-character 20, 34, 144NULL-pekare 134, 146, 199nyckelord 11, 25nyradstecken 9, 34nastlad if-sats 79

nastlad repetitionssats 89

o

Objective-C 5objekt 26objektorientering 5obuffrad Strom 201

OCH 65, 69oktal heltalskonstant 31

oktala talsystemet 250omgivning 197, 211omtolka data 238

omvandlingsspecifikation 11, 43, 257, 258, 259operand 57operativsystemmiljd 197, 211, 227, 229, 232operativsystemtjanster 211operator 57, 246operator, unar 58, 246operatorprioritet 74, 246optimera kod 235ordnattrad 180

overflow 30

paket 124parameter 18, 96parameterlista 96parametrar till main 228parser generator 234

Pascal 3

Page 267: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

pekararitmetik 137

pekare 131pekare och const 142, 166pekare som parameter 143pekare till dynamiskt minne 153pekare till funktion 154, 177, 240pekare till pekare 150, 172, 183pekare till post 163pekare till struct 163pekare, konstant 143pekarfalt 150, 240pekarstegning 138perror 199, 209persondator 232piloperator 164, 185pop 126portabilitet 242post 159

post, pekare till 163postfixoperator 61pow 60, 201powf 60

powl 60

pragma 196

prefixoperator 61preliminar definition 115, 122preprocessor 13, 191primarminne 236primart uttryck 57printf 9, 43, 206, 257prioritet 58, 74process 231processortid 216programprofilering 235programtext 7

prototyp 96, 244punktoperator 159, 185, 187push 126putc 204

putchar 23, 204puts 204

Qqsort 213

R

raise 220

RAM 236

rand 42, 121, 210RAND_MAX 210realloc 211

record 159

reellt tal 36

referens 131

register 112, 113, 235rekursion 106, 182remove 203

rename 203

repetitionssats 12, 16, 83, 85, 86repetitionssats, nastlad 89reserverat ord 11, 25, 245restrict 157

resultattyp 97, 108, 109return-sats 9, 19, 90, 97, 229rewind 207

rindex 215

rot 180

sammansatt sats 78

sammansatt tilldelning 71sant 28

sats 9, 77scanf 11, 16, 48, 206, 259SCHAR_]yiAX 29SCHAR_MIN 29SEEK_CUR 207SEEK_END 207SEEK_SET 207setjmp 219

setjmp.h 219

setlocale 200

short int 28

SHRT_MAX 29SHRT_MIN 29sidoeffekt 77, 96signal 220

signal. h 219

signed 28

signed char 30

sin 60, 201sinf 60

sinh 60, 201sinhf 60

sinhl 60

sinl 60

size_t 74, 199, 207, 210sizeof 73, 153, 178, 199, 216skalartyp 39skift 70

skuggvariabel 238slumptal 42, 121, 210

Index

267

Page 268: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

Index

slumptalsgenerator 121, 210sortering 140, 212specifikation 124sprintf 206

sqrt 60, 201sqrtf 60

sqrtl 60

srand 43, 121, 210sscanf 206

stack 126

standard error 202

standard input 202standard output 202standardbibliotek 197

standardfunktion 9, 59, 103, 197static 112, 120statisk funktion 122

statisk variabel 120

stdarg.h 221

stddef .h 35, 199stderr 202

stdin 202

stdio.h 8, 201stdlib.h 60, 209stdout 202

strcat 213

strchr 214

strcmp 214

strcpy 213

strcspn 214

strftime 218

string.h 213

strlen 214

strncat 213

strncmp 214

strncpy 213

strpbrk 214

strrchr 214

strspn 214

strstr 214

strtod 209

strtol 209

strtoul 209

struct 159

struct tm 217

struct, pekare till 163stranghantering 213Strom 201

styming av process 231styrregister 185, 236stanga fil 203subtraktion 58

268

supplementary characters 32switch-sats 81

symbolisk konstant 13, 191synlighet 110syntaxanalysgenerator 234system 212

systemanrop 227, 230sakerhet 223

sokning 212

sokning i tabell 173sokning i textstrang 89, 214

tabell med textstrangar 152tabulator 34

tan 60, 201tanf 60

tanh 60, 201tanhf 60

tanhl 60

tanl 60

teckenfdlt 19, 36, 144, 213teckenkod 32, 200, 214, 247teckenkonstant 33

teckenliteral 33

teckenlos form 250

tecken-strom 201

texteditor 7

textstrang 9, 19, 35, 144, 213textstrang, lasningav 19, 204, 259textstrang, utskrift av 19, 47, 204, 257textstrSngskonstant 35, 145textstrangskonvertering 209textstrangsliteral 35, 145tid 216

tilde-operator 68, 186tilldelning 14, 71tilldelningsoperator 71time 43, 217time_t 216time.h 216

tmpfile 203

tolower 199

tom sats 77

tom strang 146

tomttrad 181

toupper 199

trad 180

tvMimensionellt fait 148

tvikomplementsform 250typ 27

Page 269: Hogkvalite - Vagen Till C, 4de - Jan Skansholm, Ulf Bilting

typdeklaration 53, 189typedef 53, 156, 157, 189typomvandling 38typspecifikation 43, 44, 46, 48, 49, 50, 257,

259, 260tyst andring 222

u

uchar.h 35

UINT_MAX 29ULONG_MAX 29undef 196

ungetc 204

Unicode 32, 222union 187

Unix 3, 229, 232unsigned 28

unsigned char 30

unar operator 58, 246upprakning 40upprakningstyp 39USHRT_MAX 29UTF-16 33

UTF-32 33

UTF-8 33, 223utskrift 19, 21, 201, 230utskrift av flyttal 45, 258utskrift av heltal 44, 258utskrift av textstrang 19, 47, 204, 257uttryck 57uttryckssats 77

V

va_arg 221va_end 221va_start 221vansinniga saker 241variabel 11, 26variabelt)^ 187variabel, extern 114

variabel, statisk 120

variabeldeklaration 26

variabelinitiering 27variabelt parameterantal 96, 102, 221vertikal tabulator 34

villkorlig kompilering 194villkorsoperator 67villkorsuttryck 67Visual Studio 8

void 43, 96, 97, 100, 101volatile 122, 237

vansterskift 68

vardeanrop 139

w

wait 232

wchar_t 35, 223while-sats 16, 83

Y

yacc 234

Aaterlamna minne 154

aldre funktionsdeflnition 108

aldre funktionsdeklaration 109

6dkningsoperator 61dppna fil 202

Index

269