Dagens tema Basistyper i C Typekonvertering Formater i printf Pekere i C En kort repetisjon om pekere Hva er egentlig en peker? Pekere til alt og ingenting Pekere som parametre Pekere og vektorer Ark 1 av 21 Forelesning 23.2.2000
Basistyper I C finnes følgende basistyper: Navn Alternativt navn Antall byte signed char char? 1 unsigned char char? 1 short int short 2 unsigned short int unsigned short 2 int 2 unsigned int unsigned 2 long int long 4 unsigned long int unsigned long 4 float double Forelesning 23.2.2000 Ark 2 av 21
Dette programmet forteller hva som gjelder: #include <stdio.h> int main(void) printf("sizeof(char): %d\n", sizeof(char)); printf("sizeof(short): %d\n", sizeof(short)); printf("sizeof(int): %d\n", sizeof(int)); printf("sizeof(long): %d\n", sizeof(long)); printf("sizeof(float): %d\n", sizeof(float)); printf("sizeof(double): %d\n", sizeof(double)); return 0; På Ifis SGI-maskiner finner vi følgende: sizeof(char): 1 sizeof(short): 2 sizeof(int): 4 sizeof(long): 4 sizeof(float): 4 sizeof(double): 8 Forelesning 23.2.2000 Ark 3 av 21
Typekonvertering C har (som de fleste andre språk) automatisk konvertering ved tilordning: int i; unsigned int u; float f; i=u; f=i; C benytter følgende konverteringsalgoritme: float int benytter en egen omregningsfunksjon. unsigned char unsigned short og unsigned short unsigned long setter inn 0-bit. signed char signed short og signed short signed long setter inn kopier av fortegns-bit. (un)signed short (un)signed char og (un)signed long (un)signed short fjerner overflødige bit. signed xxx unsigned xxx endrer ingenting. Forelesning 23.2.2000 Ark 4 av 21
Eksplisitt konvertering Vi kan også angi konvertering når vi trenger det: #include <stdio.h> int main(void) int a = 8, b = 3; float res; res = a/b; printf("heltallsdivisjon: 8/3 = %f\n", res); res = ((float)a)/((float)b); printf("flyttallsdivisjon: 8/3 = %f\n", res); res = a/(float)b; printf("flyttallsdivisjon: 8/3 = %f\n", res); return 0; Kjøring av dette programmet gir: Heltallsdivisjon: 8/3 = 2.000000 Flyttallsdivisjon: 8/3 = 2.666667 Flyttallsdivisjon: 8/3 = 2.666667 Forelesning 23.2.2000 Ark 5 av 21
Konvertering og overflyt Når vi konverterer til noe som har færre bit, må vi passe på at vi ikke får overflyt (dvs at vi ikke har nok bit til å lagre svaret). #include <stdio.h> int main(void) int x; signed char sc; unsigned char uc; x = 257; uc = x; printf("konvertering av %d (=0x%08x) til byte gir %d.\n", x, x, uc); uc = 254; sc = uc; printf("konvertering av %d (=0x%02x) til signed gir %d.\n", uc, uc, sc); return 0; gir følgende svar: Konvertering av 257 (=0x00000101) til byte gir 1. Konvertering av 254 (=0xfe) til signed gir -2. Forelesning 23.2.2000 Ark 6 av 21
Formater i printf Når vi skriver printf("x = %d.\n", x); skjer følgende: 1. Tegnene «x»,, «=» og skrives ut. 2. Tegnene «%d» skrives ikke ut; i stedet skrives verdien av x (tolket som heltall). 3. Tegnene «.» og«\n» (linjeskift) skrives ut. Format-«språket» til printf kan generelt inneholde Koder % - tall gir venstrejustering. type tall angir minimum antall tegn som brukes. Hvis tallet starter med 0, vil utskriften bli fyllt opp med nuller. Forelesning 23.2.2000 Ark 7 av 21
type angir hva slags type data som skal skrives ut: c tegn d heltall (dvs signed char og int) u heltall uten fortegn (dvs unsigned char og insigned int) ld stort heltall (dvs long) lu stort heltall uten fortegn (dvs unsigned long) x heltallpåhex-form lx stort heltall på hex-form s tekst (avsluttet med 0-byte) f flyttall (dvs float) lf store flyttall (dvs double) (Det finnes enda flere; se i C-boken.) Forelesning 23.2.2000 Ark 8 av 21
Kort repetisjon om pekere Forrige uke lærte vi at Pekervariable deklareres med en * før navnet: Java Apa; C struct a *pa; Objekter opprettes med kall på malloc: Java pa = new A(); C pa = malloc(sizeof(struct a)); sizeof gir antall byte som trengs. Man får tak i elementer i objektene ved å bruke ->: Java C pa.a = 3; pa->a = 3; Når objekter ikke trengs mer, må de frigis: Java C free(pa); Forelesning 23.2.2000 Ark 9 av 21
Hva er egentlig en peker Hurtiglageret (RAM eller MEM) i datamaskinen er en samling byte indeksert med en tallverdi: 0x00FFFFFF RAM. 0x0000000C 0x00000008 0x00000004 0x00000000 En peker er rett og slett en adresse i RAM! Forelesning 23.2.2000 Ark 10 av 21
Adressen til variable C-operatoren & gir oss en variabels adresse: #include <stdio.h> int v1 = 1; int main(void) int v2 = 2; printf("v1 har adresse 0x%08lx og innhold %d.\n", (long)&v1, v1); printf("v2 har adresse 0x%08lx og innhold %d.\n", (long)&v2, v2); return 0; Resultatet av kjøringen vil selvfølgelig variere fra én maskin til en annen; ett mulig resultat er: v1 har adresse 0x10001050 og innhold 1. v2 har adresse 0x7fff2edc og innhold 2. Forelesning 23.2.2000 Ark 11 av 21
Vi kan peke på alt I C kan vi peke på alt som ligger i RAM. #include <stdio.h> int v1 = 1, *p1; int main(void) int v2 = 2, *p2; p1=&v1; p2=&v2; printf("p1 har adresse 0x%08lx og innhold 0x%08lx.\n", (long)&p1, (long)p1); printf("v1 har adresse 0x%08lx og innhold %d.\n", (long)&v1, v1); printf("p2 har adresse 0x%08lx og innhold 0x08%lx.\n", (long)&p2, (long)p2); printf("v2 har adresse 0x%08lx og innhold %d.\n", (long)&v2, v2); return 0; Dette programmet gir litt mer informasjon enn det forrige: p1 har adresse 0x100010f0 og innhold 0x100010a0. v1 har adresse 0x100010a0 og innhold 1. p2 har adresse 0x7fff2ed8 og innhold 0x7fff2edc. v2 har adresse 0x7fff2edc og innhold 2. Forelesning 23.2.2000 Ark 12 av 21
Hvordan følge en peker? Operatoren * brukes til «følge en peker»; den gir oss det pekeren peker på. #include <stdio.h> int v1 = 1, *p1; int main(void) int v2 = 2, *p2; p1=&v1; p2=&v2; printf("pekeren p1 inneholder 0x%08lx.\n", (long)p1); printf("følger vi den, finner vi verdien %d.\n", *p1); printf("v1 har adresse 0x%08lx og innhold %d.\n\n", (long)&v1, v1); printf("pekeren p2 inneholder 0x%08lx.\n", (long)p2); printf("følger vi den, finner vi verdien %d.\n", *p2); printf("v2 har adresse 0x%08lx og innhold %d.\n", (long)&v2, v2); return 0; Dette programmet gir tilsvarende resultat: Pekeren p1 inneholder 0x100010e0. Følger vi den, finner vi verdien 1. v1 har adresse 0x100010e0 og innhold 1. Pekeren p2 inneholder 0x7fff2edc. Følger vi den, finner vi verdien 2. v2 har adresse 0x7fff2edc og innhold 2. Forelesning 23.2.2000 Ark 13 av 21
Aksess via pekere Det er det samme om vi aksesserer en variabel direkte eller via pekere: #include <stdio.h> int main(void) int v, *p = &v; v = 5; printf("v er %d.\n", v); ++v; printf("v er %d.\n", v); *p = 11; printf("v er %d.\n", v); ++*p; printf("v er %d.\n", v); return 0; En kjøring gir følgende: v er 5. v er 6. v er 11. v er 12. Forelesning 23.2.2000 Ark 14 av 21
Spesielle konvensjoner Pekere til «ingenting» En pekerverdi 0 brukes som en peker til «ingenting» i C. I stdio.h og andre steder er NULL definert som et navn for 0. Java null C NULL Operasjoner på pekere Vi har tre operasjoner på pekere i C: &v Gi adressen til en variabel v (dvs lag en peker til v) *p Følg en peker p (dvs gi det som pekeren p peker på) p->c Det samme som (*p).c (dvs hent element c fra struct pekt på av p Den siste varianten er egentlig ikke nødvendig, men den er innført fordi vi så ofte har pekere til struct-er. Forelesning 23.2.2000 Ark 15 av 21
Pekere som parametre Noen programmeringsspråk har ulike typer parametre (verdi-, referanse- og navneparametre). I C (som i Java) finnes kun verdiparametre. Dette innebærer Ved kallet oversendes en kopi av parameteren. Lokale endringer av parameteren vil ikke synes utenfor funksjonen. Forelesning 23.2.2000 Ark 16 av 21
Eksempel #include <stdio.h> void gi_igjen(int belop, int kr20, int kr10, int kr5, int kr1, int ore50) kr20 = belop / 2000; kr10 = belop%2000 / 1000; kr5 = belop%1000 / 500; kr1 = belop%500 / 100; ore50 = belop%100 / 50; int main(void) int k20, k10, k5, k1, o50; gi_igjen(14750, k20, k10, k5, k1, o50); printf("kr 147,50 blir %d 20kr, %d 10kr,\n", k20, k10); printf(" %d 5kr, %d 1kr og %d 50ører.\n", k5, k1, o50); return 0; Kjøring gir følgende gale svar: Kr 147,50 blir 263616052 20kr, 0 10kr, 262893064 5kr, 2147430332 1kr og 0 50ører. Forelesning 23.2.2000 Ark 17 av 21
Løsningen er å overføre pekere til de aktuelle variablene: #include <stdio.h> void gi_igjen(int belop, int *kr20, int *kr10, int *kr5, int *kr1, int *ore50) *kr20 = belop / 2000; *kr10 = belop%2000 / 1000; *kr5 = belop%1000 / 500; *kr1 = belop%500 / 100; *ore50 = belop%100 / 50; int main(void) int k20, k10, k5, k1, o50; gi_igjen(14750, &k20, &k10, &k5, &k1, &o50); printf("kr 147,50 blir %d 20kr, %d 10kr,\n", k20, k10); printf(" %d 5kr, %d 1kr og %d 50ører.\n", k5, k1, o50); return 0; Nå blir svaret riktig: Kr 147,50 blir 7 20kr, 0 10kr, 1 5kr, 2 1kr og 1 50ører. De mystiske &-ene i scanf Dette er grunnen til at parametrene til scanf må ha en & foran seg. Siden variablene skal oppdateres, må vi sende pekere til dem. Forelesning 23.2.2000 Ark 18 av 21
0x12340020 0x1234001C 0x12340018 0x12340014 0x12340010 0x1234000C 0x10040014 0x10040010 0x1004000C 0x10040008 0x10040004 14750 ore50 kr1 kr5 kr10 kr20 belop 0x10040014 0x10040010 0x1004000C 0x10040008 0x10040004 1 2 1 0 7 o50 k1 k5 k10 k20 Forelesning 23.2.2000 Ark 19 av 21
Pekere og vektorer I C gjelder to litt uventede konvensjoner: Bruk av et vektornavn gir en peker til element nr. 0: a &a[0] Når en vektor overføres som parameter, er det altså en peker til starten som overføres. Følgelig trenger vi ikke noen & når vi skal lese inn et ord med scanf: char str[2000]; scanf("%s", str); Aksess av vektorelementer kan også uttrykkes med pekere: a[i] *(a+i) Det er altså det samme om vi skriver a[3] eller *(a+3). Forelesning 23.2.2000 Ark 20 av 21
Anta unsigned char a[6]; a[0] a[1] a[2] a[3] a[4] a[5] a +3 0x12340009 0x12340008 0x12340007 0x12340006 0x12340005 0x12340004 0x12340003 0x12340002 0x12340001 0x12340000 Forelesning 23.2.2000 Ark 21 av 21