.c och.h filer När programmen blir större är det nödvändigt att dela upp det i mindre enheter. Varje enhet definieras oftast som en del i programmet som hanterar en väl avgränsad uppgift. Denna enhet bildar oftast en samling typdeklarationer (typedef), ev. Globala variabler och en samling funktioner. Då andra delar ev. programmet vill kunna använda innehållet samlas alla deklarationer (typdeklarationer och funktionsprototyper) i en s.k. header-fil som slutar på.h. Denna headerfil kan sedan inkluderas på samma sätt som t.ex. stdio.h i andra filer och funktionerna kan användas. Funktionsdefinitionerna däremot återfinns i en.c fil. Varje.c fil kan sedan separatkompileras. Programmeringsteknik 113
Assertions Header-filen assert.h innehåller makrot assert(). Det är bra programmeringsvana att använda sig av assert() för att kontrollera att argumenten till en funktion är korrekta. #include <assert.h> double div (double a, double b) { assert(b!= 0); return a/b; int main(void) { div(2, 0); /* assertion will fail */ Programmeringsteknik 114
Assertions Om ett argument till assert() är falskt, kommer programmet att skriva ut ett felmeddelande, t.ex. Assertion failed on line 23 och programmet kommer att avslutas. Assertions är enkla att skriva och gör källkoden robustare. Andra läsare av källkoden kan dessutom lättare förstå innehållet. Använd assert()!!! Programmeringsteknik 115
Räckvidd (scope) Grundregeln är att identifierare är bara tillgängliga inne i det block där de deklarerades. De finns inte utanför blocket. Detta låter som en enkel regel, men programmerare väljer ofta att använda samma identifierare på olika variabler. Frågan uppstår då vilken objekt identifieraren refererar till... Programmeringsteknik 116
{ Exempel int a = 2; printf("a = %d\n", a); { int a = 5; printf("a = %d\n", a); printf("a = %d\n", a); Vad skrivs ut? Programmeringsteknik 117
Räckvidd int a = 1; int b = 2; int c = 3; void fkn (int b) { int a = 4; return (a + b); int main (void) { int a = 5; fkn(c); Programmeringsteknik 118
Exempel { int a = 1, b = 2, c = 3; printf("a = %d b = %d c = %d\n", a, b, c); { int b = 4; float c = 5.0F; printf("a = %d b = %d c = %f\n", a, b, c); a = b; { int c = b; printf("a = %d b = %d c = %d\n", a, b, c); printf("a = %d b = %d c = %f\n", a, b, c); printf("a = %d b = %d c = %d\n", a, b, c); Programmeringsteknik 119
{ Partiella och nästlade block int a, b; { float a;... /* int b is known but not int a */ { float b;... /* int a is known but not int b */ Huvudsyftet med att använda block är att spara minne. Minne reserveras bara när variabeln behövs. Vid andra tillfällen kan minnet användas till annat. Programmeringsteknik 120
Lagringsklasser Varje variabel och funktion i C har två attribut: datatyp och lagringsklass. De fyra lagringsklasserna är auto, extern, register och static. Den vanligaste lagringklassen är auto, men den vi ska beröra är static. Programmeringsteknik 121
Lagringsklassen static Statiska deklarationer låter en lokal variabel behålla sitt gamla värde. void f (void) { static int cnt = 0; ++cnt; if (cnt % 0) /* do something */ else /* do something else */ Varannan gång funktionen ovan anropas kommer den att göra en sak och varannan gång något annat. Programmeringsteknik 122
Statiska funktioner En funktion kan också deklareras som static och blir därmed privat för den filen. static int g(void) /* funktionsprototyp */ /*g() är tillgänglig här men inte i andra filer*/ void f (int a) {... static void g(void) {... Programmeringsteknik 123
Initiering Variabler innehåller normalt skräpvärden om de inte har initialiserats. Däremot så initialiseras statiska variabler till noll om inte programmeraren anger ett värde. Vissa C-kompilerare sätter dock värdet på autovariabler till noll om programmeraren inte anger något annat. Detta kan dock skilja sig åt och som programmerare bör man inte lita på detta utan istället explicit sätta värden. Programmeringsteknik 124
Rekursion En funktion är rekursiv om den anropar sig själv, direkt eller indirekt. Alla funktioner kan anropas rekursivt. I sin enklaste form är rekursion enkel att förstå. #include <stdio.h> int main(void) { printf("the universe is never ending\n"); main(); return 0; /* Kommer aldrig att nås */ Programmeringsteknik 125
Exempel För att summera de fem första heltalen kan man skriva int sum(int n) { if (n >= 1) return n; else return n + sum(n-1) sum(1) 1 sum(2) 2 + sum(1) eller 2 + 1 sum(3) 3 + sum(2) eller 3 + 2 + 1 sum(4) 4 + sum(3) eller 4 + 3 + 2 + 1 Programmeringsteknik 126
fakultet.c Matematiskt så lyder definitionen av fakultet som följer 0!=1, n!=n (n-1) 2 1 n>0 eller ekvivalent 0!=1, n!=n (n-1)! n>0 I C kan detta skrivas rekursivt som int factorial (int n) { assert (n >= 0); return (n <= 1? 1 : n * factorial(n-1) ); Programmeringsteknik 127
Iterativ motsvarighet Funktionen kan också skrivas iterativt int factorial (int n) { int product = 1; assert(n >= 0); for ( ; n > 1; n-- ) product *= n; return product; Programmeringsteknik 128
Effektivitet Många algoritmer har både rekursiva och iterativa lösningar. Normalt så är den rekursiva formuleringen vackrare och använder färre variabler för att göra samma beräkning. Vissa datorspråk använder bara rekursion för att åstadkomma upprepning (dvs. de har inte t.ex. while eller for). Rekursion orsakar att datorn lägger gamla värden av variabler i en stack för att komma ihåg dem. Ofta kan rekursion orsaka många funktionsanrop. Programmeringsteknik 129
fibonacci.c Fibonaccisekvensen definieras som följer ƒ 0 =0, ƒ 1 =1, ƒ i+1 =ƒ i +ƒ i-1, i=1.2, int fibonacci (int n) { return (n <= 1? n : fibonacci(n-1) + fibonacci(n-2); N fibonacci(n) Antalet funktionsanrop 0 0 1 1 1 1 2 1 3 42 267.914.296 86.698.873 43 433.494.437 1.402.817.465 Programmeringsteknik 130
Rekursion vs. iteration Vissa programmerare anser att rekursion inte ska användas p.g.a. de många funktionsanropen ineffektivitet. Rekursion har dock många fördelar de är ofta enklare att skriva lättare att förstå lättare att underhålla Därför kan det vara motiverat att använda rekursion trots dess nackdelar. Programmeringsteknik 131
Endimensionella vektorer Har man relaterat data kan man lagra detta i vektorer istället för i enkla variabler. Vektorer deklareras och indexeras med hjälp av hakparenteser ([ och ]) direkt efter variabelnamnet. Exempel: int x0; int x[3]; int x1; int x2; x[0] = 2; x[1] = 1; x0 = 2; x[2] = x[0]; x1 = 1; x2 = x0; Vektorer har en fix längd, n, och indexeras från 0, dvs 0,1,2,,n-1. Programmeringsteknik 132
Endimensionella vektorer Vektorer kan liksom enkla variabler initieras med ett värde vid deklarationen. int x0 = 2; int x[3] = {2, 1, 4; int x1 = 1; int x2 = 4; Om listan med initieringsvärden är kortare än vektorn, initieras resterande element till 0. int x[100] = {1,0; /* första elementet i x blir 1 resten 0 */ int y[100] = {0; /* nollställer alla element i y */ Programmeringsteknik 133
Endimensionella vektorer Om en vektor är deklarerad utan längd, men initierad med en lista av initierare, är dess storlek implicit satt till antalet initierare. Följande två deklarationer är därmed ekvivalenta int x[] = {5, 1, -3, 4; int x[4] = {5, 1, -3, 4; För vektorer av tecken finns en alternativ notation. Följande två deklarationer är ekvivalenta. char s[] = { a, b, c, \0 char s[] = "abc"; Programmeringsteknik 134
Endimensionella vektorer #include <stdio.h> #define N 5 /* Läs in heltal till en vektor. Beräkna och skriv ut dess summa */ int main(void) { int values[n]; int i; int sum = 0; for (i=0; i<n; i++) { printf("enter value %d: ",i); scanf("%d",&values[i]); for (i=0; i<n; i++) { sum += values[i]; printf("\nsum: %d\n",sum); Programmeringsteknik 135
Flerdimensionella vektorer I C kan man deklarera vektorer av alla typer, inklusive vektorer av vektorer. På så vis kan man skapa vektorer i flera dimensioner. int a[100]; /* en endimensionell vektor */ int b[2][7]; /* en två-dimensionell vektor */ int c[6][1][5]; /* en tredimensionell vektor */ Initiering sker på liknande sätt som med endimensionella vektorer. int a[2][2] = {{1, 2, {4, 0; int b[2][3][1] = {{{4,{6,{1, {{7,{2,{1; int b[][][] = { {{4, 2, {6, 1, {{1, 0, {2, 0 ; Programmeringsteknik 136
Två-dimensionella vektorer Det är lättast att tänka på två-dimensionella vektorer som en matris, med rader och kolumner. Deklarationen int a[3][5] kan ses på följande vis: I minnet lagras dock flerdimensionella vektorer kontinuerligt. Programmeringsteknik 137
Två-dimensionella vektorer #include <stdio.h> #define N 5 #define M 3 int main(void) { double A[M][N]; int i, j; for (i=0; i<n; i++) { for (j=0; j<i; j++) { A[i][j] = 0; printf("%4.0lf", A[i][j]); for (j=i; j<m; j++) { A[i][j] = pow(j-i+1, 2); printf("%4.0lf", A[i][j]); printf("\n"); /*Ny rad för varje rad i A */ Programmeringsteknik 138
Pekare Varje variabel i ett program har en unik minnesadress. Med pekare kan man referera och manipulera minnesadresser. Värdet i en pekarvariabel är minnesadressen till en variabel. Om vi har en variabel v så anger &v minnesadressen till v. Deklarationer av pekare görs med en * före variabelnamnet. int *ip; char *cp; double *dp; Programmeringsteknik 139
Pekare Bland de tillåtna adresserna att referera finns alltid adressen 0. Adressen 0 har i C ett speciellt namn, nämligen NULL. Adressen NULL används som defaultvärde hos pekare då inga variabler kan ha den adressen, d.v.s. &v kan aldrig vara 0 (NULL). Några exempel på tilldelning av pekare kan vara: int i; int *p; p = 0; p = NULL; p = &i; Programmeringsteknik 140
Pekare int a = 3; int *p; int a = 3; int *p = NULL; int a = 3; int *p = &a; Programmeringsteknik 141
Pekare Man kan referera värdet på variabler med hjälp av pekare. Om p är en pekare som pekar på b:s minnesadress, ger *p värdet av b, en s.k. indirekt adressering. Exempel: int b = 3; int *p = &b; /* Initiering till adressen av b */ printf(" b: %d\n p: %p\n*p: %d\n", b, p, *p); Ger utskriften b: 3 p: effffbac /*Adressen skriven i hexadecimal form */ *p: 3 Programmeringsteknik 142
Pekare int a = 1; int *p; p = &a; printf(" a = %d *p = %d\n\n", a, *p); *p = 2; /* ekvivalent med a = 2 */ printf(" a = %d *p = %d\n\n", a, *p); a = 3; /* ekvivalent med *p = 3 */ printf(" a = %d *p = %d\n", a, *p); Ger utskriften: a = 1 *p = 1 a = 2 *p = 2 a = 3 *p = 3 Programmeringsteknik 143
Pekare I C används void * som generell pekartyp. Jämför med void.... int a = 3; char c = c ; void *p; p = &a; p = &c;... I ANCI C kan man inte göra konverteringar mellan olika pekartyper om inte den ena är av typen void *. Programmeringsteknik 144
Call-by-Reference När en variabel skickas som argument till en funktion kopieras dess värde till funktionens parameter och värdet på variabeln är oför ändrat. Detta kallas Call-by-value. Om i stället en referens till variabeln skickas som argument kan variabelns värde ändras i den anropade funktionen. Detta kallas Call-by- Reference. C använder alltid Call-by-value, men pekare kan användas for att åstadkomma samma effekt som i Call-by-reference. Programmeringsteknik 145
Programmen Call-by-Reference #include <stdio.h> #include <stdio.h> void set(int a, int value){ void set(int *a, int value){ a = value; *a = value; int main(void) { int main(void) { int a = 5; int a = 5; set(a, 2); set(&a, 2); printf("a = %d\n",a); printf("a = %d\n",a); ger utskrifterna: a = 5 a = 2 Programmeringsteknik 146
Pekare vs. vektorer En vektorvariabel, ex. float x[20], är i sig själv en adress (ett pekarvärde). Medan pekare kan anta nya pekarvärden så är vektorvariabler fixa. Både pekare och vektorer kan indexeras. Om a är en vektor, är a[3] ekvivalent med *(a + 3). a + 3 är ett pekaruttryck som anger tredje elementpositionen efter a. Ekvivalent, om p är en pekare, är p[3] ekvivalent med *(p + 3). Programmeringsteknik 147
Pekare vs. vektorer #define N 5 int main(void) { int a[n] = {0, 1, 2, 3, 4; int i; for (i=0; i<n; i++) { printf("a[%d]=%d *(a + %d)=%d &a[%d]=%p (a + %d)=%p\n, i, a[i], i, *(a + i), i, &a[i], i, (a + i)); Ger utskriften: a[0]=0 *(a + 0)=0 &a[0]=effffb98 (a + 0)=effffb98 a[1]=1 *(a + 1)=1 &a[1]=effffb9c (a + 1)=effffb9c a[2]=2 *(a + 2)=2 &a[2]=effffba0 (a + 2)=effffba0 a[3]=3 *(a + 3)=3 &a[3]=effffba4 (a + 3)=effffba4 a[4]=4 *(a + 4)=4 &a[4]=effffba8 (a + 4)=effffba8 Programmeringsteknik 148
Pekare vs. vektorer Vi kan alltså se det som att det ser ut så här i minnet. Observera C tillåter oss att referera och ändra hur vi vill i minnet. Om vi försöker ändra minne som programmet inte äger avslutar vanligtvis operativsystemet programmet. Däremot kan vi ändra i det minne som programmet äger. Om vi antar att i har adressen effffb94 kan vi alltså ändra värdet på i genom att skriva *(a - 1) = 3;. Programmeringsteknik 149
Pekare vs. vektorer Vi kan alltså använda pekare för att referera positioner i vektorer. Exempel: #define N 5 int a[n] = {0, 1, 2, 3, 4; int *p; int sum = 0; p = &a[1]; /* *p == 1 */ p = (a + 2) /* *p == 2 */ *p = 7; /* a[2] == 7 */ for (p=a; p<&a[n]; p++) { sum += *p; Värdet på sum är efter detta 15. Programmeringsteknik 150
Vektorer som argument Man kan även ge vektorer som argument till funktioner. När vektorer skickas som argument kopieras vektorns adress med Call-by-value, medan vektorelementen inte kopieras. Exempel: void scale(double v[], int n, double scale) { int i; for (i=0; i<n; i++) { v[i] *= scale; Observera att följande deklaration är ekvivalent. void scale(double *v, int n, double scale) {... Programmeringsteknik 151
Bubble sort void swap(int *a, int *b) { int tmp; tmp = *a; *a = *b; *b = tmp; void bubble(int v[], int n) { int i, j; for (i=0; i<n-1; i++) for (j=0; j<n-i-1; j++) if (v[j] > v[j+1]) swap(&v[j], &v[j+1]); Programmeringsteknik 152
Dynamisk minnesallokering Allokera minne för de variabler man behöver och frigör minne när man är klar. För att allokera minne finns i C, de inbyggda funktionerna malloc och calloc. #include <stdlib.h> void *malloc(size_t size); void *calloc(size_t nelem, size_t size); Returnerar: En pekare till allokerat minne om OK, annars NULL Typen size_t är i ANSI C definierad till unsigned int. Vanligtvis är typedef använd i stdlib.h. Programmeringsteknik 153
Dynamisk minnesallokering malloc allokerar size bytes med minne. För att allokera minne som räcker för en viss mängd data och datatyp invänds oftast sizeof. int *v = (int*)malloc(10*sizeof(int)); Här allokeras minne för en heltalsvektor med längden 10. Observera att vi också gör en typecast från void* till int*. Detta är inte nödvändigt, men är bra programmeringsstil. v[i] kan användas p.s.s. som om man gjort deklarationen v[10]. Programmeringsteknik 154
Dynamisk minnesallokering calloc allokerar nelem element av storleken size. calloc initierar det allokerade minnets alla bitar till 0; Motsvarande exempel för calloc blir: int *v = (int*)calloc(10, sizeof(int)); Programmeringsteknik 155
Dynamisk minnesallokering Minne som är dynamiskt allokerat lämnas inte automatiskt tillbaka till systemet. #include <stdlib.h> void free(void* ptr); Funktionen free används för att explicit lämna tillbaka minne. int *p = (int*)malloc(10*sizeof(int));... free(p); Allokerat minne är inte knutet till en specifik pekarvariabel. Om man förlorar referensen till allokerat minne kan man inte sedan referera det eller frigöra det. Programmeringsteknik 156
Dynamisk minnesallokering int* new_int_array(int n) { int *v; v = (int*)malloc(n*sizeof(int)); return v; int main(void) { void *p; p = new_int_array(10); p = new_int_array(10); /* memory lost!!! */ free(p); Programmeringsteknik 157
Strängar Strängar är endimensionella vektorer av typen char. En sträng avslutas med ett \0 tecken, dvs ett tecken med alla bitar satta till 0. En strängs längd kan ses på två sätt: En fix maximal längd som avgörs av vektorns allokerade längd. En variabel längd som bestäms av första \0 tecknet. \0 tecknet måste rymmas inom det allokerade utrymmet. Programmeringsteknik 158
Strängar char s[] = "abc"; char s[] ={ a, b, c, \0 char *p = "abc"; Programmeringsteknik 159
strcat & strncat #include <string.h> char *strcat(char *s1, const char *s2); char *strncat(char *s1,const char *s2, size t n); Returnerar: Pekaren s1. Konkatenerar (slår ihop) två strängar och lägger resultatet i s1. s1 måste vara stor nog för att rymma resultatet. strncat lägger till som mest n tecken. Programmeringsteknik 160
strcmp & strncmp #include <string.h> int strcmp(char *s1, const char *s2); int strncmp(char *s1, const char *s2, size_t n); Returnerar: Ett heltal < 0, lika med 0 eller > 0, beroende på om s1 är lexikografiskt mindre, lika eller större än s2. Jämför två strängar. strncmp jämför som mest n tecken. Programmeringsteknik 161
Strängfunktioner #include <string.h> char *strcpy(char *s1, const char *s2); char *strncpy(char *s1,const char *s2, size_t n); Returnerar: Pekaren s1. Innehållet i s2 kopieras till s1 tills \0 flyttas. s1 måste ha plats för resultatet. strncpy kopierar som mest n tecken. #include <string.h> size_t strlen(const char *s); Returnerar: Antalet tecken före \0. Undersöker längden av eller antalet tecken före\0 hos en sträng. Programmeringsteknik 162
Strängexempel #include <stdlib.h> #define MAX_STR_LEN 20 int main(void) { char s[] = "I love pointers"; char *p; p = (char*)malloc(max_str_len*sizeof(char)); strncpy(p, s, MAX_STR_LEN); free(p); Bild just innan free anropas. Programmeringsteknik 163
Argument till main() Två argument, vanligtvis kallade argc och argv kan användas med main() för kommunikation med operativsystemet. argc anger antalet argument på kommandoraden. argv är en vektor med pekare till strängar med respektive argument. Kommandot, eller programnamnet, är alltid första argumentet. void main(int argc, char *argv[]) {... Programmeringsteknik 164
Echo exempel #include <stdio.h> int main(int argc, char *argv[]) { int i; for (i=1; i<argc; i++) { printf("%s", argv[i]); if (i < argc-1) printf(" "); else printf("\n"); return 0; Programmeringsteknik 165
Echo exempel Om det tidigare exemplet döpts till my_echo och kompilerats på C: kan ett exempel vid kommandopromten bli: C:> my_echo I love C I love C C:> I minnet kommer det att se ut så här: Programmeringsteknik 166
Sammansatta datatyper Vektorer finns för homogent data. Vad gör man med relaterat data av olika datatyper? char *name1 = "Adam Gudmundsson"; int age1 = 23; double weight1 = 74.7; char *name2 = "Eva Gudmundsdotter"; int age2 = 27; double weight2 = 56.2; Programmeringsteknik 167
Strukturer Strukturer definieras med nyckelordet struct. struct post { /* definition */ char *name; int age; double weight; ; struct post p1, p2; /* deklaration */ Definitionen skapar en mall, med vilken man sedan kan deklarera variabler. Strukturer kan användas som andra datatyper i bl.a. vektorer. Programmeringsteknik 168
Strukturer För att göra accesser till medlemmar av en struktur används punktoperatorn. ( genetiv s) struct post p1, persons[2]; p1.name = "Adam Gudmundsson"; p1.age = 23; p1.weight = 74.7; persons[0] = p1; /* hela posten kopieras */ persons[1].name = "Eva Gudmundsdotter"; persons[1].age = 27; persons[1].weight = 56.2; Programmeringsteknik 169
Strukturer Utelämnar man namntaggen till en struktur, kan strukturen inte användas till ytterligare deklarationer. struct { int r; int g; int b; red, blue, green; Detta till skillnad mot om den tas med struct colour { int r; int g; int b; ; struct colour red; struct colour green; struct colour blue; Programmeringsteknik 170
Strukturer Vanligtvis använder man typedef tillsammans med strukturer. När man gör typdeklarationer är namntaggen vanligtvis onödig. typedef struct { double re; double im; complex; /* ny datatyp */ complex a, b, c[n]; Typdeklarationer ökar flexibiliteten och portabiliteten hos kod. Programmeringsteknik 171
Strukturer och dynamiskt minne Vid hantering av pekare till strukturer kommer nyttan av sizeof till sin fulla rätt. sizeof returnerar, för strukturer, summan av storlekarna hos dess medlemstyper. Om vi har strukturen complex. typedef struct { double re; double im; complex; ger sizeof(complex) summan av storleken på två double, d.v.s. vanligtvis 16. Detta är mycket praktiskt vid allokering av nytt minne. complex *a = (complex*)malloc(sizeof(complex)); Programmeringsteknik 172
Strukturer och dynamiskt minne Om vi har en struktur som en pekare kan syntaxen fort bli komplex. complex *a = (complex*)malloc(sizeof(complex)); complex *v[n]; (*a).im = 3.0; (*a).re = 0.2; v[0] = (complex*)malloc(sizeof(complex)); (*(v[0])).im = 5.1; (*(v[0])).re = 2.8; I C finns operatorn -> för att göra accesser till medlemmar i en struktur via en pekare. a->im = 3.0; a->re = 0.2; v[0]->im = 5.1; v[0]->re = 2.8; Programmeringsteknik 173
Strukturer och funktioner Precis som andra datatyper kan strukturer användas med funktioner. typedef struct { int x; int y; coordinate; Med Call-by-value : coordinate move(coordinate p, int dx, int dy) { p.x += dx; p.y += dy; return p; och som pekare: void move(coordinate *p, int dx, int dy) { p->x += dx; p->y += dy; Programmeringsteknik 174
Initiering av strukturer Även strukturer kan initieras vid deklarationen. complex a = {1.0, 2.45; complex v[2] = {{1.4, 0.23, {-1.4, 5.1; person p = {"Adam Gudmundsson", 27, 74.7; Man kan även initiera en hel struktur med värde noll (alla bitar satta till noll) på vanligt sätt. complex c[4] = {0; Programmeringsteknik 175
Unioner Unioner är en datatyp som definierar set med alternativa typer. Definitionen sker på liknande sätt som hos strukturer och ger en mall. union int_or_float { int i; float f; ; Mallen kan sedan användas för att deklarera variabler. union int_or_float a; union int_or_float v[n]; union int_or_float *p; Programmeringsteknik 176
Unioner Det ligger på programmerarens ansvar att tolka det lagrade värdet rätt. Följande exempel går genom kompilatorn och kan exekveras utan fel, men kan ge mycket märkliga effekter. float f; union int_or_float a; a.i = 3; f = a.f; /* f == 4.203895e-45 och ej 3.0!!! */ Storleken på en union blir storleken av den största ingående datatypen. typedef union { int v[6]; double lf; union1; sizeof(union1) /*== 6*sizeof(int) == 6*4 == 24 */ Programmeringsteknik 177
Exempel på sammansatta typer typedef struct { enum {t_integer, t_float type; union { int i; float f; value; compound;... compound c; c.type = t_integer; c.value.i = 4; switch (c.type) { case t_integer: print("c: %d\n", c.value.i); break; case t_float: print("c: %f\n", c.value.f); break; Programmeringsteknik 178
Utskrifter till skärm Utskrifter sker med printf funktionen. #include <stdio.h> int printf(const char* format,...); Returnerar: Antalet utskrivna tecken om OK, annars negativt värde. Första argumentet, format, anger vad som skall skrivas ut och hur det skall skrivas ut. I format-strängen kan konverteringsspecifikationer anges som beskriver hur följande argument skall tolkas och konverteras. int a = 3; printf("%s\n","hello world!"); printf("a har värdet %d\n",a); Programmeringsteknik 179
Flaggor till printf [] flaggan behöver inte anges. eller x bredd på fältet y antalet decimaler som skall skrivas ut z antalet siffror i exponenten som skall skrivas ut. - vänsterjusterat space mellanslag om tecken saknas + skriver ut + eller - före nummer 0 fyller ut med 0:or. # alternativ formatering. Programmeringsteknik 180
Flaggor till printf %[-][x]c chars %[-][x]s strings %[ +][-][0][x]d decimal %[ +][-][0][x]u unsigned decimal %[ +][-][0][x]o octal %[ +][-][0][x]x hexadecimal (with a,b,...) %[ +][-][0][x]X hexadecimal (with A,B,...) %[ +][-][0][x][.y]f float %[ +][-][0][x][.y]lf double %[ +][-][#][0][x][.y][+z]e scientific notation (with e) %[ +][-][#][0][x][.y][+z]E scientific notation (with E) %[ +][-][#][0][x][.y][+z]g same as f or e, depending on the value. %[ +][-][#][0][x][.y][+z]G same as f or E, depending on the value. Programmeringsteknik 181
Exempel på printf #include <stdio.h> int main(void) { int i1 = -3; int i2 = 5; char c1 = c ; double f1 = -2.0; double f2 = 3.3; printf("%-10s%10s\n", "Variabel", "Värde"); printf("--------------------\n"); printf("%-10s%+10d\n", "i1", i1); printf("%-10s% 10d\n", "i2", i2); printf("%-10s%10c\n", "c1", c1); printf("%-10s%+10.2f\n", "f1", f1); printf("%-10s%+10.2+2e\n","f2", f2); return 0; Programmeringsteknik 182
Exempel på printf Ger utskriften: Variabel Värde -------------------- i1-3 i2 5 c1 c f1-2.00 f2 +3.30e+00 Programmeringsteknik 183
Inläsning från tangentbord Inläsning från tangentbordet sker vanligtvis med scanf. #include <stdio.h> int scanf(const char* format,...); Returnerar: Antalet lyckosamt matchade tecken. Vid fel EOF. Första argumentet, format, anger hur det som läses in skall formateras. Efterföljande argument anger adresser (pekare) till minnesutrymme för det inlästa datat. int i; char c; double d; scanf("%d%c%lf", &i, &c, &d); Programmeringsteknik 184
Exempel på scanf int i; char c; char s[15]; scanf("%d,%*s %% %c %5s %s", &i, &c, s, &s[5]); Med insträngen 45, ignore_this % C read_in_this** lagras 45 i i, C i c och read lagras i s[0] till s[4] och \0 i s[5]. Därefter lagras in this** i s[5] till s[13] och \0 i s[14]. Programmeringsteknik 185
Fler strängfunktioner #include <stdio.h> int sprintf(char *s, const char* format,...); Returnerar: Antalet utskrivna tecken om OK, annars negativt värde. sprintf fungerar på samma sätt som printf med den skillnaden att resultatet skrivs till en sträng, s, istället för till skärmen. #include <stdio.h> int sscanf(char* s, const char* format,...); Returnerar: Antalet lyckosamma matchningar. Vid fel EOF. sprintf läser text från en sträng och inte från tangentbordet. Programmeringsteknik 186
Filhantering Filer hanteras med en speciell struktur, FILE, eller snarare pekare till en sådan struktur FILE * Denna definieras i stdio.h, men hur den exakt ser ut är oviktigt. Även utskrifter till skärm och inläsning från tangentbort sköts med filpekare. Dessa benämns stdout och stdin. Dessutom finns en filpekare för utskrifter av felmeddelanden, stderr. Programmeringsteknik 187
Öppna och stänga filer För att kunna läsa eller skriva till en fil måste den först öppnas. #include <stdio.h> FILE *fopen(const char* filename, const char *mode); Returnerar: Filpekare vid OK, annars NULL. Vi öppnar filer genom att ange bl. a. dess namn. #include <stdio.h> int main(void) { FILE* ifp; FILE* ofp; /* open for reading */ ifp = fopen("any_file", "r"); /* open for writing */ ofp = fopen("another_file", "w");... Programmeringsteknik 188
Öppna och stänga filer Vid öppnandet anger man också vad man vill kunna göra med filen. Detta för att undvika misstag. Dessa kan vara r Öppnar textfil för läsning. w Öppnar textfil för skrivning. a Öppnar textfil för att lägga till. rb Öppnar binär fil för läsning. wb Öppnar binär fil för skrivning. ab Öppnar binär fil för att lägga till. r+ Öppnar fil för läsning och skrivning. w+ Öppnar fil för skrivning och läsning.... o.s.v. Programmeringsteknik 189
Öppna och stänga filer När man är klar med en fil bör den stängas. #include <stdio.h> int fclose(file *stream); Returnerar: 0 vid OK, annars EOF. #include <stdio.h> int main(void) { FILE* fp; /* open for reading */ fp = fopen("any_file", "r"); fclose(fp); Programmeringsteknik 190
Läsa och skriva på filer Motsvarigheten till printf och scanf för filer heter fprintf och fscanf. #include <stdio.h> int fprintf(file *stream,const char* format,...); Returnerar: Antalet utskrivna tecken om OK, annars negativt värde. fprintf fungerar på samma sätt som printf med den skillnaden att resultatet skrivs till en filpekare, stream, i stället för till skärmen. #include <stdio.h> int fscanf(file* stream,const char* format,...); Returnerar: Antalet lyckosamma matchningar. Vid fel EOF. fprintf läser text från en filpekare och inte från tangentbordet. Programmeringsteknik 191
Exempel med filer FILE* fp; int a = 5; fp = fopen("anyfile", "w"); fprintf(fp, "Skriver a: %d\n", a); fclose(fp); fp = fopen("anyfile", "r"); fscanf(fp, "%*s%*s %d\n", &a); fclose(fp); Programmeringsteknik 192
Skriva eller läsa enskilda tecken Motsvarigheten till getchar och putchar heter för filer getc och putc. #include <stdio.h> int putc(int c, FILE *stream); Returnerar: värdet av c. EOF vid filslut eller fel. #include <stdio.h> int getc(file *stream); Returnerar: Nästa tecken från strömmen. EOF vid filslut eller fel. Dessa anrop sker genom makron. Det finns också litet långsammare funktionsvarianter av dessa som heter fputc och fgetc. Programmeringsteknik 193
Exempel Observera att dessa två satser är ekvivalenta och att man inte kan öppna eller stänga stdout. fprintf(stdout, "Detta skrivs till skärm\n"): printf("detta skrivs till skärm\n"): Liksom att dessa tre satser är ekvivalenta. putc( a, stdout); fputc( a, stdout); putchar( a ); Programmeringsteknik 194