Endimensionella vektorer 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 x1; int x2; x0 = 2; x1 = 1; x2 = x0; • 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; int x[3]; • Om listan med initieringsvärden är kortare än vektorn, initieras resterande element till 0. x[0] = 2; x[1] = 1; x[2] = x[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 */ • Vektorer har en fix längd, n, och indexeras från 0, dvs 0,1,2,…,n-1. 126 127 Endimensionella vektorer 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"; 128 #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); } Pekare 129 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. • 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 int p = p = p = int *ip; char *cp; double *dp; 130 i; *p; 0; NULL; &i; 131 1 Pekare Pekare int a = 3; int *p; int a = 3; int *p = NULL; • 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 a = 3; int *p = &a; 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 132 133 Pekare Pekare int a = 1; int *p; • I C används void * som generell pekartyp. • Jämför med void. p = &a; printf(" a = %d *p = %d\n\n", a, *p); ... int a = 3; char c = ’c’; void *p; p = &a; p = &c; ... *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); • I ANCI C kan man inte göra konverteringar mellan olika pekartyper om inte den ena är av typen void *. Ger utskriften: a = 1 *p = 1 a = 2 *p = 2 a = 3 *p = 3 134 135 Call-by-Reference 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-byReference“. • C använder alltid “Call-by-value“, men pekare kan användas ¨ for att åstadkomma samma effekt som i “Call-by-reference“. 136 • Programmen #include <stdio.h> void set(int a, int value){ a = value; } #include <stdio.h> void set(int *a, int value){ *a = value; } int main(void) { int a = 5; set(a, 2); printf("a = %d\n",a); } int main(void) { int a = 5; set(&a, 2); printf("a = %d\n",a); } ger utskrifterna: a = 5 a = 2 137 2 Pekare vs. vektorer 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). #define N 3 int main(void) { int a[N] = {0, 1, 2}; int i; for (i=0; i<N; i++) { printf("a[%d]=%d *(a + %d)=%d", i, a[i],i,*(a+ i); printf("&a[%d]=%p (a + %d) =%p\n", 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 138 Pekare vs. vektorer 139 Pekare vs. vektorer • Vi kan alltså se det som att det ser ut så här i minnet. • Vi kan alltså använda pekare för att referera positioner i vektorer. • Exempel: • 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;. #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. 140 141 Vektorer som argument Strängar • 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: • 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. void scale(double v[], int n, double scale) { int i; for (i=0; i<n; i++) { v[i] *= scale; } } • \0 tecknet måste rymmas inom det allokerade utrymmet. H • Observera att följande deklaration är ekvivalent. void scale(double *v, int n, double scale) { ... 142 e j ! \0 ? ? ? 143 3 Strängar strcat & strncat char s[] = "abc"; a b c \0 char s[] ={’a’,’b’,’c’,’\0’} a b c \0 char *p = "abc"; a b c \0 #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. 144 strcmp & strncmp 145 Strängfunktioner #include <string.h> int strcmp(char *s1, const char *s2); int strncmp(char *s1, const char *s2, size_t n); #include <string.h> char *strcpy(char *s1, const char *s2); char *strncpy(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. • 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. 146 Flerdimensionella vektorer 147 Två-dimensionella 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][2][5];/*en tredimensionell vektor*/ • 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: • Initiering sker på liknande sätt som med endimensionella vektorer. int a[2][2] = {{1, 2}, {4, 0}}; • I minnet lagras dock flerdimensionella vektorer kontinuerligt. 148 149 4 Två-dimensionella vektorer Bubble sort #include <math.h> #include <stdio.h> #define N 5 #define M 3 int main(void) { double A[M][N]; int i, j; for (i=0; i<M; i++) { for (j=0; j<N; j++) { A[i][j] = i*N+j; printf("%4.0f", A[i][j]); } printf("\n"); /*Ny rad för varje rad i A */ } } 150 void swap(int *a, int *b) { int tmp; tmp = *a; *a = *b; *b = tmp; } void bubble(int v[], int n) { int i=0, j, swaped=1; while(swaped) { swaped=0; for (j=0; j<n-i-1; j++) if (v[j] > v[j+1]) { swap(&v[j], &v[j+1]); swaped=1; } i++; } } 151 5