PowerPoint-presentation

Download Report

Transcript PowerPoint-presentation

Maskinnära
programmering i C
Föreläsning 5
Mats Brorsson
IS1200 Datorteknik
F1
F2
Ö1
F3
Ö2
F4
Ö3
lab
nios2time
F5
Ö4
hemlab C
Ö6
lab nios2io
F7
Ö7
lab nios2int
F8
Ö8
F9
Ö9
F10
Ö10
F6
Ö5
hemlab
cache
hemlab
trådar
Assemblerprogram
C
In- och utmatning
Avbrott och "trap"
Cacheminnen
Trådar, synkronisering
tentamen
2
Hemlaboration 1:
Maskinnära programmering i C
• Pekare, array, struct, malloc med mera
• Lab-PM + C-kod på webben,
kompilera och kör med valfri C-kompilator
• Svara på alla frågor, tänk efter: "Varför?"
• Muntlig redovisning, 2 studenter med 1 lärare
• Läs
http://www.ict.kth.se/courses/IS1200/bokade.txt
• Mejla till [email protected] och boka
Ge alltid flera alternativa tider
3
C jämfört med andra språk
• Java
• objekt, referenser, garbage-collection, byte-code, starkt
typsystem
•C
• inga objekt, pekare, malloc/free, maskinnära, stöd för
enkla datatyper
• Assembler
• inga typer alls, adresser, ingen inbyggd felkontroll
4
Den här föreläsningen
• Enkla datatyper och datarepresentation: Heltal, flyttal,
• Mer komplicerade datatyper (struct, array)
• Satser och block
• Pekare, pekar-aritmetik
• Lagring av globala och lokala variabler
• Stack
• Heap
• Alla exempel med handöversättning till assemblerkod använder
Nios II assemblersyntax
5
Rekommenderad litteratur
• Häftet "C för den som kan Java"
• Kan hämtas som PDF, http://www.nada.kth.se/datorer/haften/java2c/
• Tips för den som vill läsa ännu mer:
• Bilting, Skansholm: Vägen till C
• Kernighan, Ritchie: The C Programming Language
• Jonas Skeppstedt, Christian Söderberg, Writing efficient C code : a thorough
introduction for Java programmers, http://writing-efficient-c-code.com/
6
C för dig som kan Java
Ditt första C-program
main(int argc, char *argv[]) {
printf(“hello, world\n”);
}
Samma program i Java:
public class Hello {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
7
Datatyper i C
Mats Brorsson
KTH
8
Den här föreläsningen
• Enkla datatyper och datarepresentation: Heltal,
flyttal,
• Mer komplicerade datatyper
• Uttryck, satser, block
• Pekare, pekar-aritmetik
• Lagring av globala och lokala variabler
• Stack
• Heap
9
Hexadecimala talsystemet
• Lär dig denna tabell utantill!
Binärt
0000
0001
0010
0011
0100
0101
0110
0111
Hex
0
1
2
3
4
5
6
7
Binärt
1000
1001
1010
1011
1100
1101
1110
1111
Hex
8
9
A
B
C
D
E
F
• Ex: 0110 1000 1010 0001 = 0x68A1
• Ett tal som börjar med 0x är i hexadecimal notation
10
C – inledning
• Syntaxen liknar Java
• Körningen börjar med funktionen main
• Inga funktioner körs om man inte anropar dem
• "Metoderna" kallas funktioner i C
• en Java-metod tillhör en klass och modifierar objekt men
C har inga klasser och inga objekt
11
C – inledning
• Inga referenser, men pekare
• en pekare är en minnesadress med viss typkontroll
• kraftfullt, men lätt att göra fel
• Inga objekt, men poster: struct
• som objekt utan metoder, innehåller bara data
• Ingen "new", ingen garbage-collection
• biblioteksfunktionen malloc reserverar minne
• reserverat minne måste återlämnas med free
12
Deklaration av variabeltyp
Definition av variabelvärde
• Typ-deklaration, så att kompilatorn kan
typkontrollera programmet
• Definition, en deklaration som medför att
kompilatorn reserverar minnesutrymme
• Initiering, en definition där programmeraren anger
variabelns startvärde
• int foo = 17;
13
Deklaration och definition
int a;
int b = 17;
int b;
int b = 85;
/* Deklaration och definition,
kompilatorn reserverar plats
i minnet för variabeln a */
/* Definition med initiering,
kompilatorn reserverar plats
och lägger in startvärdet */
/* Deklaration igen, anger
samma typ, tillåtet! */
/* Ny definition, förbjudet! */
14
Datatyper
Grundtyper:
• heltal
• flyttal
Kombinationstyper:
• array
• struct
• union (ej behandlat
här)
15
Datatyper: heltal
signed char
short
int
long
long long
minst 8 bit
minst 16 bit
minst 16 bit (oftast
32 bit)
minst 32 bit
minst 64 bit
• Filen limits.h ger värden för aktuell kompilator
unsigned char
unsigned short
unsigned int
unsigned long
unsigned long long
#include <limits.h>
• Exempel: INT_MAX kan ha värdet 2147483647
16
Exempel: gcc 3.4.6 för Nios II
(Nios II har 32-bits adresser)
• char
• short
• int
• long
• long long
• 8 bit (1 byte)
• 16 bit (2 byte)
• 32 bit (4 byte)
• 32 bit (4 byte)
• 64 bit (8 byte)
• minst 8 bit
• minst 16 bit
• minst 16 bit
• minst 32 bit
• minst 64 bit
• float
• double
• 32 bit (4 byte)
• 64 bit (8 byte)
• minst 32 bit
• minst 32 bit
17
Exempel: gcc 4.4.3 för amd64
(AMD64 har 64-bits adresser)
• char
• short
• int
• long
• long long
• 8 bit (1 byte)
• 16 bit (2 byte)
• 32 bit (4 byte)
• 64 bit (8 byte)
• 64 bit (8 byte)
• minst 8 bit
• minst 16 bit
• minst 16 bit
• minst 32 bit
• minst 64 bit
• float
• double
• 32 bit (4 byte)
• 64 bit (8 byte)
• minst 32 bit
• minst 32 bit
18
sizeof()
• Operatorn sizeof( någonting )
ersätts vid kompileringen med en konstant
som är lika med storleken i bytes
för den typ som någonting har
• Exempel
long long fsize = sizeof( float );
• Annat exempel
double r;
long long dsize = sizeof( r );
19
Heltal: unsigned
• 1001 1111 0101 0000 1111 0111 0111 0000
• Hexadecimalt: 9f 50 f7 70
• i C: 0x9f50f770
• Tolkas som positivt heltal (2 672 883 568)
20
Heltal: signed
• 1001 1111 0101 0000 1111 0111 0111 0000
• Hexadecimalt: 9f 50 f7 70
• i C: 0x9f50f770
• Teckenbiten (längst till vänster) är ettställd
• Tolkas som negativt heltal (-1 622 083 728)
21
Binärkod för värdet minus ett
Koden för + 1
0000 0000 .... 0001
Bitvis invertering
Addition av ett
1111 1111 .... 1110
+ 0000 0000 .... 0001
Och vi får kod för -1
1111 1111 .... 1111
(Koden för -1 är alltid ”bara ettor” för alla ordlängder)
22
Värdet av kod för negativt tal
Koden för negativt tal 1111 .... 1010 0101
Bitvis invertering
Addition av ett
0000 .... 0101 1010
+ 0000 .... 0000 0001
Och vi får kod
0000 .... 0101 1011
Som tolkas till värdet 64+16+8+2+1 = 91
Alltså -91
23
Snabbfrågor
• Omvandla följande decimala tal till 32-bitars tal i
tvåkomplementrepresentation:
• 512
• -1023
• -4000000
• Skriv talen i hexadecimalt format
24
Byte-ordning i minnet:
little-endian, skrivning
little-endian byte-order
0101
0000
1111
0111
0111
0000
...
1001
1111
adress 0
0111 0000
1111 0111
0101 0000
1001 1111
...
(lilla änden först i minnet)
adress 2n-1
25
Byte-ordning i minnet:
big-endian, läsning
adress 0
big-endian byte-order
...
0111
0000
1111
0111
0101
0000
1001
1111
0111 0000
1111 0111
0101 0000
1001 1111
...
(stora änden först i minnet)
adress 2n-1
26
Datatyper: flyttal: approximation av reella tal
• float
• double
• long double
• Filen float.h ger värden för aktuell kompilator
#include <float.h>
• Exempel: FLT_MAX kan ha värdet
• Oftast 32 bitar (IEEE
754 )
• Oftast 64 bitar (IEEE
754 )
• ”Mer än double” 80 bit
eller (IEEE 754 quad
precision)
340282346638528859811704183484516925440.000000
27
Decimalt flyttal
• -047,120
• -047,120 * 100
• -04,7120 * 101
mantissa
decimalt fixtal
decimalt flyttal
samma flyttal
exponent
tecken
28
Binärt flyttal
• -0101.110
• -0101,110 * 20
• -01,01110 * 22
binärt fixtal
binärt flyttal
normaliserat flyttal
exponent
mantissa
tecken
29
Binärt flyttal: float (32 bit)
• -0101,110
• -0101,110 * 20
• -01,01110 * 22
binärt fixtal
binärt flyttal
normaliserat flyttal
exponenten lagras med 8 bit i excess-127-kod
bara bråksiffrorna av mantissan lagras, men inte "1,"
tecken: 1:a för negativa tal
30
Binärt flyttal: double (64 bit)
• -0101,110
• -0101,110 * 20
• -01,01110 * 22
binärt fixtal
binärt flyttal
normaliserat flyttal
exponenten lagras med 11 bit i excess-1023-kod
bara bråksiffrorna av mantissan lagras, men inte "1,"
tecken: 1:a för negativa tal
31
Flyttal IEEE 754, 32 bitar
FLOAT: 1 10000011 00010000000000000000000
s exp
mantissabitar
värde = (–1)s * (1 + mantissabitar2 ) * 2 (exp–127)
–17 (dec) = –1 0001 (bin) = – 1,0001 * 24
1 1000 0011 000 1000 0000 0000 0000 0000
1100 0001 1000 1000 0000 0000 0000 0000
0x C
1
8
8
0
0
0
0
32
Algoritm för omvandling till flyttal
• Antag ett decimalt flyttal:
• -4,7120 * 1023 = -0,47120 * 1024
• Omvandla till -1t * 1.M2 * 2(e-127)
• Bestäm t, M och e så att följande villkor är
uppfyllda:
•
•
•
•
1.M2* 2(e-127) = 4,712010 * 1023
M10 < 1,0
M2 < 1.0
e är ett heltal
1. Antag M = 0 och bestäm exponent, x = e-127
•
•
•
•
•
2.
Lös ut M från ekvationen:
1.M*278 = 4,712*1023
•
•
3.
4,7120 *1023 = 2x
log(4,7120 * 1023) = log(2x)
log(4,7120) + 23 = x*log(2)
x = (23 + log 4,712 ) /log(2) = 78
e = 78+127 = 205 = 0xcd = 0b1100 1101
1.M = 4,712*1023/278 =
1,5590700185399465615976311028135
M = 1000 1111 0001 1111 0011 011
IEEE-talet är:
1 1100 1101 1000 1111 0001 1111 0011 011
33
Varför
• Excess-127/1023?
• För då kan man använda
heltalsinstruktioner för att avgöra
vilket tal som är störst
34
Information, 32 bitar
adress / värde
Little endian byte ordering
FLOAT:
0xC1
0x88
0x00
0x00
0x00
0x00
0x88
0xC1
+
-17 i float-format: 0x C1 88 00 00
35
Flyttal IEEE 754, 64 bitar
DOUBLE:
1 10000000011 000100 ... 000000000
s exponent
mantissabitar
(-1)s * (1 + mantissabitar2 ) * 2 (exponent-1023)
-17 (dec) = - 1 0001 (bin) = - 1.0001 * 24
1 100 0000 0011 000 1000 0000 ... 0000
1100 0000 0011 0001 0000 0000 ... 0000
0x C
0
3
1
000000000000
36
Information, 64 bitar
adress / värde
Little endian byte ordering
DOUBLE:
0xC0
0x31
0x00
0x00
0x00
0x00
0x00
0x00
0x00
0x00
0x00
0x00
0x00
0x00
0x31
0xC0
-
+
-17 i double-format: 0x C0 31 00 00 00 00 00 00
37
Snabbfrågor
• Omvandla följande till 32-bitars binärkod i
hexadecimalt format:
• Instruktionen add r12,r1,r3
• Heltalet
147425338
• Det reella talet 1,3092239146306080983655304890341
* 10-33
38
Addition/subtraktion av flyttal
Algoritm:
1. Jämför de båda talens exponenter. Skifta det minsta talets signifikand (inkl.
implicit etta) till höger så att de båda talens exponenter blir lika stora
2. Addera/subtrahera signifikanderna
3. Normalisera summan
4. Om det blir exponent overflow är det aritmetiskt fel
5. Avrunda signifikanden
6. Om avrundningen medför att talet blir onormaliserat, gå till steg 3
39
Multiplikation av flyttal
Algoritm:
1. Addera exponenterna och dra ifrån en excess
2. Multiplicera signifikanderna
3. Normalisera produkten
4. Om det blir exponent overflow är det aritmetiskt fel
5. Avrunda signifikanden
6. Om avrundningen medför att talet blir onormaliserat, gå till steg 3
7. Beräkna produktens tecken
40
Den här föreläsningen
• Enkla datatyper och datarepresentation: Heltal, flyttal,
• Mer komplicerade datatyper
• Uttryck, satser, block
• Pekare, pekar-aritmetik
• Lagring av globala och lokala variabler
• Stack
• Heap
41
Mer komplicerade datatyper
iC
Mats Brorsson
KTH
Den här föreläsningen
• Enkla datatyper och datarepresentation: Heltal, flyttal,
• Mer komplicerade datatyper
• Uttryck, satser, block
• Pekare, pekar-aritmetik
• Lagring av globala och lokala variabler
• Stack
• Heap
43
Arrayer
• En array i C har alltid fix storlek
• Deklarationen
int vektor[27];
reserverar plats för 27 heltal i en array
• Numreringen börjar på noll
vektor[0]
vektor[1]
...
vektor[26] /* Nu är det slut! */
44
Array i minnet
adress 0 1 byte
• Heltals-vektor
• int v[27];
v[0] = 1234;
v[1] = 4711;
• Resultat i minnet
(little-endian)
adress för v[0]: 0x800b70 0xd2
1234 (decimalt) =
0x000004d2 (hex)
0x04
0x00
0x00
och här kommer v[1]: 0x800b74 0x67
4711 (decimalt) =
0x00001267 (hex)
0x12
0x00
0x00
adress för v[2]: 0x800b78
...
adress
0xffffffff
45
Matriser
• Matriser finns inte som egen typ, utan görs som arrayer av arrayer
• Deklarationen
int matris[17][42];
ger plats för 17 rader med 42 heltal i varje rad
• Numreringen börjar på noll
matris[0][0]
matris[0][1]
...
matris[16][41] /* Nu är det slut! */
• För dynamiskt allokerade matriser är det lite mer krångligt.
46
Matriser, mera detaljer
• Lagring i minnet
matris[0][0]
matris[0][1]
...
matris[0][41]
matris[1][0]
matris[1][1]
...
matris[1][41]
matris[2][0]
... och så vidare till
matris[16][41]
• Först hela rad 0
• Sedan hela rad 1
• Och sen hela rad 2
• ...
• Kallas radvis lagring
• Eng. Row major ordering
• Fortran har Column major
ordering
47
Bokstäver och strängar
• Tecknet 'A' är ett litet heltal (65)
• En char använder 1 byte i minnet, och
har plats för ett litet heltal (exempelvis 65)
• En array of char har plats för en sträng:
char s[20]="Now you C a string.";
• Strängar slutar med en nollställd char (’\0’)
som slutmarkör
• Exempel:
"hej" == { 'h', 'e', 'j', '\0' }
48
Handöversättning till assembler
• C:
• char s[20]="Now you C a string.";
• Assembler:
• .data
.global s
s: .asciz "Now you C a string."
49
I minnet (exempel)
• Exempel i C:
adress 0
char c = 'Q';
int x = 4711;
• Assembler:
.data
.global c
c: .byte 'Q'
.align 2
.global x
x: .word 4711
1 byte
adress för c: 0x800b62 0x51
Obs! Exempel på adresser.
adress för x: 0x800b64
0x800b65
0x800b66
0x800b67
4711 (decimalt) =
0x00001267 (hex)
adress
0xffffffff
0x67
0x12
0x00
0x00
50
Snabbfrågor
• Följande sträng finns på adress 0x80740 i minnet:
char s = ”Nu ska vi C!”;
• Vilken adress har nästa lediga plats?
• 0x8074D
• 0x8074E
• 0x80750
• Om strängen omedelbart följs av nedanstående definition, vilken adress
läggs den på?
double d;
• 0x8074D
• 0x8074E
• 0x80750
I stället för objekt: struct
• En struct liknar ett objekt, fast utan metoder
• Objektet innehåller del-variabler (oftast flera)
• Del-variablerna är ofta av olika typer
• Steg 1: deklarera en struct-typ
• Steg 2: deklarera en variabel av den typen
• Steg 3: tilldela en del-variabel ett värde
52
struct-exempel
struct threadinfo {
int sp;
int id
};
• deklarerar en ny typ, struct threadinfo
• denna struct-typ innehåller två heltal
struct threadinfo thisthread;
• deklarerar en variabel av typ struct threadinfo
• användning:
thisthread.id = 4711;
• tilldelar ena heltalet i variabeln thisthread
53
Handöversättning till assembler
• C:
•
struct threadinfo thisthread;
• Assembler:
•
.data
.align 2
.global thisthread
thisthread:
.word 0
.word 0
54
Handöversättning till assembler
• C:
•
thisthread.id = 4711;
• Assembler:
•
.text
.align 2
movia r8,thisthread
movi r9,4711
stw
r9,4(r8) # id finns efter sp som tar 4 byte
55
I minnet (exempel)
1 byte
• De två
del-variablerna i en
struct ligger efter
varandra i minnet
• Ordningen är alltid samma
som i C-programmet
adress
0
thisthread: 0x800b90 0x00
0x800b91 0x00
0x800b92 0x00
0x800b93 0x00
0x800b94
0x800b95
0x800b96
0x800b97
Obs! Exempel
på adresser.
adress
0xffffffff
sp
0x67
0x12
0x00
id
0x00
56
Kopiering av hela structen
• struct threadinfo { int sp; int id };
• struct threadinfo thisthread;
struct threadinfo otherthread;
• användning:
• otherthread = thisthread;
• Hela variabeln thisthread (alla delar)
kopieras till variabeln otherthread
• Fungerar bara med struct, inte med array!
57
Kopiering av hela structen
• struct threadinfo { int sp; int id };
• struct threadinfo thisthread;
struct threadinfo otherthread;
• användning:
• otherthread = thisthread;
• Hela variabeln thisthread (alla delar)
kopieras till variabeln otherthread
• Fungerar bara med struct, inte med array!
Hur många load- och storeinstruktioner
behövs för att genomföra kopieringen till
vänster?
• 1 load och 1 store
• 2 load och 2 store
• 4 load och 4 store
58
Men... finns det inga Boolean?
• Heltal i stället för Boolean
• Heltalet 0 tolkas som "falskt"
• Alla heltal ≠ 0 tolkas som "sant"
• if( heltalsuttryck )
statement1;
else
statement2;
59
Den här föreläsningen
• Enkla datatyper och datarepresentation: Heltal, flyttal,
• Mer komplicerade datatyper
• Uttryck, satser, block
• Pekare, pekar-aritmetik
• Lagring av globala och lokala variabler
• Stack
• Heap
60
Uttryck, Satser och Block i C
Mats Brorsson
KTH
61
Den här föreläsningen
• Enkla datatyper och datarepresentation: Heltal, flyttal,
• Mer komplicerade datatyper
• Uttryck, satser, block
• Pekare, pekar-aritmetik
• Lagring av globala och lokala variabler
• Stack
• Heap
62
If-satser
C
Kommentar
if (i == j)
{
f = g + h;
}
else
{
f = g – h;
}
Om uttrycket
i==j är sant
görs detta,
annars detta
63
Relationsoperatorer
Relation
C
Likhet
==
Olikhet
!=
Övriga
<, <=, >, >=
64
Repetitionsslingor
C
Betydelse
while (vek[i] == k)
{
i = i + j;
}
for (i = 0; i < n; i = i+1)
{
...
}
do {
...
} while (i < 20);
Upprepa så länge villkoret vek[i] == k är
uppfyllt. Kontrollera villkoret före varje
iteration.
Upprepa så länge villkoret i<n är uppfyllt, låt
i=0 före slingan och uppdatera i varje iteration.
Upprepa slingan tills villkoret i<20 inte är
uppfyllt. Villkoret kontrolleras efter varje
iteration.
65
For-loopen
• Notera, for-loopen med följande struktur:
for (i=0; i < N; i = i + 1) {
…
}
• Är detsamma som:
i = 0;
while (i < N) {
…
i = i + 1;
}
66
Heltal i stället för Boolean
• if( heltalsuttryck ) statement1;
else statement2;
• Exempel på heltalsuttryck: a < b
• Om a < b
så är uttryckets värde 1,
annars 0
• Kan tilldelas variabler:
• int a_is_smaller_than_b = a < b;
• Motsvarande för while, for, ...
• while( heltalsuttryck ) statement2;
• for( init; heltalsuttryck; update ) statement3;
67
Ett komplett program
#include <stdio.h>
int n;
/* Huvudprogram */
#define PARAMETER 10
main(){
n = f(10);
p(n);
}
p(int k) {
printf(“n
= include
%d\n”, k)
#define är ett sätt att definiera
Genom
direktivet
}
konstanter.
kan man göra sina program
Namnet main
mer lättlästa.
int f(int a) {
betyder huvudprogram
int b;
b = a + 2;
Du
har möjlighet
att deklarera
return
används
för att
return b;
variabler
i en och för att
avsluta inne
en funktion
}
funktion/subrutin
Parametrar
till funktioner
och
tala om
vad som är
subrutiner
deklareras på samma sätt
returvärdet
som variabler
68
Struktur
C-programfil:
• #include
• #define
• deklaration av
globala variabler
• funktioner
Varje funktion:
• deklaration av namn,
parametrar och returvärde
• en sats (statement) eller ett
block
• vanligtvis ett block, förstås...
69
Stil: korthuggen programkod
• Utskrift av sträng
• int i; char s[20];
• for( i = 0; s[i]; i =
putchar( s[i] );
i + 1 )
• Uttrycket s[i] är ett heltalsuttryck
• Alla heltal utom 0 räknas som "sant"
• Testet s[i] är sant när heltalet s[i] ≠ 0
men falskt när s[i] == 0
eftersom strängens slutmarkör är heltalet noll
• Svårläst och det är bättre att skriva villkoret som s[i] != NULL där NULL
definierats till ’\0’.
70
Satser
• Satser kan vara
if-satser etc, förstås
• En sats kan också bestå av ett
uttryck
• En tilldelning är ett uttryck som
ofta finns ensamt i en sats
• Exempel:
x = 4711
är ett uttryck
• Sätt semikolon
;
efter uttrycket så får vi en sats
x = 4711;
71
Varning för oväntad tilldelning
• = betyder tilldelning
• == betyder test för likhet
• Satsen
if( x == 4711 ) y = 32767;
ändrar y, om x är lika med 4711
• Satsen
if( x = 4711 ) y = 32767;
medför alltid att y ändrar värde. Varför?
72
Snabbfrågor
• Hur många repetitioner kommer följande slinga exekvera?
char teckenstr = ”aaaaaaabbbbbcc”;
i = 0;
tecken = teckenstr[0];
• 1 gång
while (tecken = ’a’) {
• 7 gånger
// behandla tecknet a
• Oändligt antal
…
tecken = teckenstr[i++];
}
73
Block
• En sats (statement) kan alltid
bytas mot ett block
• Uttryck kan inte bytas mot block
Block:
{
• deklarationer först i varje block
• satser efter alla deklarationer
}
• C99 och senare standarder kan
blanda deklarationer och satser
74
Exempel: utskrift av sträng
• En kort funktion
void printstring( char s[] )
{
/* nytt block */
int i;
/* deklarationer först i blocket */
for( i = 0; s[i]; i = i + 1 )
{
putchar( s[i] );
}
}
• nytt block omkring putchar, onödigt men mer lättläst
75
Bitvisa operationer i C
• Ettställ valda bitar med OR-operation
tmp = tmp | 0x30 ;
• Nollställ valda bitar med AND-operation
(maska fram utvalda bitar)
tmp = status & 0x0F ;
• Invertera valda bitar med XOR-operation
tmp = val ^ 0x80;
76
Bit Operations
Assembly program (Nios-II)
• Skifta höger n steg (heltalsdivision med 2n)
SRLI r2, r7, n # det finns även SRAI r2, r7, n
• Skifta vänster m steg (multiplikation med 2m)
SLLI r9, r4, m # det finns ingen SLAI r9, r4, m
Olika hantering av teckenbiten vid högerskift
SRLI skiftar in 0
SRAI skiftar in teckenbiten
77
Bit Operations
C-program
• Skifta höger n steg (heltalsdivision med 2n)
tmp = tmp >> n ;
• Skifta vänster m steg (multiplikation med 2m)
tmp = tmp << m ;
Oftast implementeras högerskift som:
unsigned int tmp => skifta in noll vid högerskift
int tmp => skifta in teckenbiten vid högerskift
78
Bitwise operations
Logical operations
•
•
•
•
A&B
A|B
A^B
~A
• A && B
• A || B
• !A
bitvis AND
bitvis OR
bitvis XOR
bitvis NOT (invertering)
logisk AND
logisk OR
logisk NOT
• Notera INGEN operator ^^ i C (INGEN logisk XOR)
79
Snabbfrågor
• Du har läst in ett tecken från en I/O-enhet genom att läsa in till en
variabel med följande definition:
int tecken;
• Hur ska du förbehandla det för att kunna göra följande?
if (tecken == 0x41)
• Välj mellan
•
•
•
•
tecken = tecken & 0xff;
tecken = tecken && 0xff;
tecken = tecken | 0xff;
tecken = tecken || 0xff;
80
Den här föreläsningen
• Enkla datatyper och datarepresentation: Heltal, flyttal,
• Mer komplicerade datatyper
• Uttryck, satser, block
• Pekare, pekar-aritmetik
• Lagring av globala och lokala variabler
• Stack
• Heap
81
Pekare och pekararitmetik i C
Mats Brorsson
KTH
82
Den här föreläsningen
• Enkla datatyper och datarepresentation: Heltal, flyttal,
• Mer komplicerade datatyper
• Uttryck, satser, block
• Pekare, pekar-aritmetik
• Lagring av globala och lokala variabler
• Stack
• Heap
83
Pekare
• En pekare är en sorts variabel
• Pekaren innehåller en adress
• Exempel
int valle;
int * pelle;
adress 0
pelles adress
0x800b60
4 byte
0x800b98
Obs! Bara exempel på adresser.
pelle = &valle;
*pelle = 17;
valles adress
0x800b98
84
adress
0xffffffff
17
Pekar-operatorer
• Tilldela pekaren en adress
pelle = &valle;
• Operatorn & (med en variabel som argument)
betyder "minnesadressen till" variabeln
• Varning: genom att ta adressen till en variabel förindrar man att kompilatorn
kan allokera den i ett register
• Följ pekaren och skriv
*pelle = 17;
• Operatorn * (med en pekare som argument)
betyder "pekarens innehåll är adressen till en plats i minnet där
läsning/skrivning ska göras"
85
Exempel på handöversättning
• int valle;
int * pelle;
• Assembler
.data
.align 2
.global valle
valle: .word 0
• .data
.align 2
.global pelle
pelle: .word 0
adress 0
4 byte
pelles adress
0x800b60
valles adress
0x800b98
adress
0xffffffff
86
Handöversättning till assembler
•C
adress 0
pelle = &valle;
• Assembler
pelles adress
0x800b60
.text
.align 2
movia r8,valle
movia r9,pelle
stw r8,0(r9)
valles adress
0x800b98
adress
0xffffffff
87
4 byte
Steg för steg: pelle = &valle;
• Assembler
.text
.align 2
• movia r8,valle
adress 0
pelles adress
0x800b60
valles adress
0x800b98
0x800b98
r8
r9
88
adress
0xffffffff
4 byte
Steg för steg: pelle = &valle;
• Assembler
.text
.align 2
• movia r8,valle
• movia r9,pelle
adress 0
pelles adress
0x800b60
valles adress
0x800b98
0x800b98
0x800b60
r8
r9
89
adress
0xffffffff
4 byte
Steg för steg: pelle = &valle;
• Assembler
.text
.align 2
• movia r8,valle
• movia r9,pelle
• stw r8,0(r9)
adress 0
pelles adress
0x800b60
valles adress
0x800b98
0x800b98
0x800b60
r8
r9
90
adress
0xffffffff
4 byte
0x800b98
Handöversättning till assembler
•C
*pelle = 17;
adress 0
• Assembler
.text
.align 2
movia r8,17
movia r9,pelle
ldw r10,0(r9)
stw r8,0(r10)
pelles adress
0x800b60
valles adress
0x800b98
91
adress
0xffffffff
4 byte
0x800b98
Steg för steg: *pelle = 17;
• Assembler:
.text
.align 2
adress 0
pelles adress
0x800b60
valles adress
0x800b98
r8
r9
r10
92
adress
0xffffffff
4 byte
0x800b98
Steg för steg: *pelle = 17;
• Assembler:
.text
.align 2
• movia r8,17
adress 0
pelles adress
0x800b60
valles adress
0x800b98
17
r8
r9
r10
93
adress
0xffffffff
4 byte
0x800b98
Steg för steg: *pelle = 17;
• Assembler:
.text
.align 2
• movia r8,17
• movia r9,pelle
adress 0
pelles adress
0x800b60
valles adress
0x800b98
17
0x800b60
r8
r9
r10
94
adress
0xffffffff
4 byte
0x800b98
Steg för steg: *pelle = 17;
• Assembler:
.text
.align 2
• movia r8,17
• movia r9,pelle
• ldw r10,0(r9)
adress 0
pelles adress
0x800b60
valles adress
0x800b98
17
0x800b60
0x800b98
r8
r9
r10
95
adress
0xffffffff
4 byte
0x800b98
Steg för steg: *pelle = 17;
• Assembler:
•
•
•
•
.text
.align 2
movia r8,17
movia r9,pelle
ldw r10,0(r9)
stw r8,0(r10)
adress 0
pelles adress
0x800b60
0x800b98
valles adress
0x800b98
17
17
0x800b60
0x800b98
r8
r9
r10
96
4 byte
adress
0xffffffff
Pekartyper
• Exempel:
• pointer to char
• pointer to int
• pointer to double
• pointer to struct h
• char * cp;
• int * ip;
• double * dp;
• struct h * hp;
• men struct h
ska förstås vara definierad tidigare
97
Storlekar
• Hur stor är pekarvariabeln?
• struct h { int v1; int v2; char s[20]; };
struct h * hp;
printf( "hp: %ld \n", sizeof( hp ) );
• En adress är alltid lika stor (som andra adresser)
• Hur stor är den typ
som pekarvariabeln är deklarerad att peka på?
• printf( "*hp: %ld \n", sizeof( *hp ) );
• Väldigt olika för olika typer
98
Test: sizeof( hp ) och sizeof( *hp )
• C-program
#include <stdio.h>
int main()
{
struct h { int v1; int v2; char s[20]; };
struct h * hp;
printf( "hp: %ld \n", sizeof( hp ) );
printf( "*hp: %ld \n", sizeof( *hp ) );
return( 0 );
}
• Utmatning
hp: 8
*hp: 28
99
Void
• Typen void är speciell, betyder "inget värde"
• En funktion utan returvärde har returtypen void
void puttime( int mytime )
• En funktion utan parametrar kan skrivas med void (eller utlämnas)
int getchar( void )
• En voidpekare (pointer to void)
får peka på objekt av vilken typ som helst
• En voidpekare får inte följas/avrefereras
100
Voidpekare
• int tal;
int * ip;
void * vp;
• vp = (void *) &tal; /* voidpekaren får adressen till tal */
• (void *) kallas ”type cast” och gör att man kan göra om typer
• GÖR DETTA ENBART NÄR DU VET EXAKT VAD SOM HÄNDER!
• *vp = 17; /* avreferera voidpekare med tilldelning av heltal, fel! */
• ip = (int *) vp;
/* läs vp med type cast */
• *ip = 17;
/* avreferera int-pekare, går bra */
• *( (int *) vp ) = 4711; /* krångligt men korrekt */
101
Snabbfrågor
• Antag följande definitioner:
int *p;
int i;
int k;
i = 42;
k = i;
p = &i;
• Vilka av följande kommer ändra på variabel i till 75?
•
•
•
•
•
k = 75;
*k = 75;
p = 75;
*p = 75;
Två eller fler av svaren ovan kommer ändra i till 75
102
byte-vektor, textsträng, char [ ]
• En vektor av bytes
• char text[14] = "Hello, world!";
/* 14 bytes, kom ihåg nolltecknet på slutet! */
• med index 0 – 13
• &text[0] – adress till första tecknet,
pointer to char
• text – också adress till första tecknet,
pointer to char
103
bytevektor, pointer to char
• char text[14] = "Hello, world!";
/* 14 bytes, kom ihåg nolltecknet på slutet! */
• char * cp1 = &text[0];
*cp1 = 'Y';
Nu är strängen "Yello, world!"
• char * cp2 = text;
*cp2 = 'C';
Nu är strängen "Cello, world!"
104
Lagring av textsträng, char [ ]
adress 0 1 byte
'H'
'e'
'l'
'l'
'o'
','
''
'w'
'o'
'r'
'l'
'd'
'!'
text[13] 0x00
text[0]
C-kod:
char text[] = ”Hello, world!”;
Assemblerkod:
.data
text: .asciz ”Hello World!”
adress
0xffffffff
105
Pekar-aritmetik: char
• char text[14] = "Hello, world!";
/* 14 bytes, kom ihåg nolltecknet på slutet! */
• char * cp1 = &text[0];
cp1 = cp1 + 1;
Nu är cp1 adressen till 'e'
• *cp1 = 'u';
Nu är strängen "Hullo, world!"
106
Pekar-aritmetik: int (överkurs)
• int v[27];
v[0] = 1234;
• int * ip = &v[0];
• ip = ip + 1;
*ip = 65535;
• Ökas med 1 i C-kod, men ökas
med 4 i assembler!
• Skalning med sizeof(int)
adress10byte
adress för v[0]: 0x800b70 0xd2
1234 (decimalt) =
0x000004d2 (hex)
0x04
0x00
0x00
och här kommer v[1]: 0x800b74
65535 (decimalt) =
0x0000ffff (hex)
0xff
0xff
0x00
0x00
adress för v[2]: 0x800b78
...
adress
0xffffffff
107
memcpy i C-kod
(block data transfer)
void memcpy (void * dst, void * src, int num)
{
int i;
char * localdst = (char *) dst;
char * localsrc = (char *) src;
for (i=0; i<num, i=i+1)
{
*localdst = *localsrc;
localdst = localdst + 1;
localsrc = localsrc + 1;
}
}
108
memcpy i C-kod
(block data transfer)
void memcpy (void * dst, void * src, int num)
{
int i;
char * localdst = (char *) dst;
char * localsrc = (char *) src;
for (i=0; i<num, i=i+1)
{
*localdst++ = *localsrc++;
/* Svårläst! Undvik operatorn ++ */
}
}
109
Matriskopiering i C
#define ROWSIZ 17
#define COLSIZ 27
int enamatrisen[ROWSIZ][COLSIZ];
int andramatrisen[ROWSIZ][COLSIZ];
void matcpy (int* dst, int* src)
{
int i, j;
for (i=0; i<ROWSIZ, i=i+1) /* rad-nr */
for (j=0; j<COLSIZ, j=j+1) /* kolumn-nr */
dst[i][j] = src[i][j];
}
110
Omvänd ordning på rader och kolumner: blir det någon
skillnad?
#define ROWSIZ 17
#define COLSIZ 27
int enamatrisen[ROWSIZ][COLSIZ];
int andramatrisen[ROWSIZ][COLSIZ];
void matcpy (int* dst, int* src)
{
int i, j;
for (j=0; j<COLSIZ, j=j+1) /* kolumn-nr */
for (i=0; i<ROWSIZ, i=i+1) /* rad-nr */
dst[i][j] = src[i][j];
}
111
Omvänd ordning på rader och kolumner
• Adressberäkning
• För att hitta dst[i][j] ska datorn beräkna
(i * ROWSIZ + j) * elementstorleken
och addera det till startadressen för matrisen
• Används matrisen radvis,
i samma ordning som lagringen, så kan kompilatorn ta bort
adressberäkningen
• Adderar elementstorleken sizeof(int)
till adresserna inför varje ny iteration
112
Pekare för att komma åt I/O-enheter
• I/O-enheter är avbildade i adressrymden precis som variabler.
• De är speciella eftersom t.ex.
• en In-enhet kan ändra värde mellan två läsningar
• En Ut-enhet kan ta emot flera värden efter varandra
• Exempel:
• Ut-enhet som tänder/släcker lysdioder (LEDs) beroende på bitvärde
• In-enhet där man kan se hur ett antal strömbrytare är satta
113
Enkel I/O-programmering i C
#define LED 0x4408 // fiktiv adress
#define SWITCH 0x440C // också fiktiv adress
unsigned int *switches = SWITCH;
unsigned int *leds = LED;
int swtch_temp;
// Läs inporten och mata ut till LED
swtch_temp = *switches; // läs switch
swtch_temp = 0x3ff;
// 18 minst signifikanta
*leds = swtch_tmp;
// tänd/släck leds
114
Snabbfrågor
• Antag följande kod:
unsigned char *inport = PORT_ADDRESS;
while ((*inport & 0x80) && (i<100)) {
while (!(*inport & 0x80)) ;
tecken[i++] = (*inport & 0x7f);
}
• Hur många iterationer exekverar den yttre slingan?
•
•
•
•
0 gånger, villkoret kan aldrig vara sant
1 gång, den fastnar i den inre slingan
Det går inte att fastställa men max 100 gånger
100 gånger
115
Den här föreläsningen
• Enkla datatyper och datarepresentation: Heltal, flyttal,
• Mer komplicerade datatyper
• Uttryck, satser, block
• Pekare, pekar-aritmetik
• Lagring av globala och lokala variabler
• Stack
• Heap
116
Lagring av globala och lokala
variabler
Mats Brorsson
KTH
Den här föreläsningen
• Enkla datatyper och datarepresentation: Heltal, flyttal,
• Mer komplicerade datatyper
• Uttryck, satser, block
• Pekare, pekar-aritmetik
• Lagring av globala och lokala variabler
• Stack
• Heap
118
Variabler i minnet (register)
Viktiga aspekter att ta med
• Allokering av minne vid kompilering
at compile-time
• Allokering av minne vid exekvering
at run-time
• global / lokal variabel
• code-area, data-area, heap-area, stack-area
• push och pop för stacken
• malloc() och free() för heapen
119
Statisk allokering av minne
at compile time
Vid kompilering+länkning reserveras minne för
• Programkoden – text
• Globala data
initialized (.data)
uninitialized (.bss) (Block Started by Symbol)
• Heap-area
• Stack-area
120
Minnesuppdelning
PC
program
kod
data
bss
adress 0
SP
stack
program
kod
data
bss
heap
PC
heap
SP
stack
Exempel 1
adress 2n-1
Exempel 2
121
Programstart
körbar fil
minne
header
programkod
kopiering vid
programstart
programkod
data
data
bss
symboltabell
heap
122
Dynamisk allokering av minne
at execution/run -time
• Vid exekvering allokeras minne på stack och används för
• Parametrar vid funktionsanrop
• Returadress vid funktionsanrop
• Lokala variabler i anropad funktion
• Skydd av register vid registerbrist
(r16 – r23)
123
Dynamisk allokering av minne
at execution/run time
• Vid exekvering allokeras minne på heap och används för
• Dynamiska data-strukturer
124
malloc() och free()
dynamisk allokering på heap
/* Tänkbar bakgrund */
struct elephant
{
char[13] name;
int weight;
double area;
};
/* 16 bytes ? ! */
/* 4 bytes */
/* 8 bytes */
struct elephant dumbo;
125
malloc() och free()
dynamisk allokering på heap
struct elephant * ep;
...
ep = (struct elephant *) malloc (sizeof (struct elephant));
...
* ep = dumbo;
...
free (ep);
/* ep MÅSTE ha samma värde som från malloc */
126
Snabbfrågor
• Vilken eller vilka av följande allokeringar gör vad du troligen vill givet
deklarationen?
struct node {
int key;
struct node * next; };
• struct node * x = malloc(sizeof(*x));
• struct node * x = malloc(sizeof(* struct node ));
• struct node * x = malloc(sizeof(struct node));
Lokala variabler
• Deklareras i ett block
• Tillgänglig endast i samma block
som deklarationen
• Startvärdet skrivs när funktionen
anropats
• Anger man inget startvärde så
blir startvärdet odefinierat
• Exempel
#include <stdio.h>
int main()
{
int g = 4711;
printf( "g: %d \n", g );
g = 17;
printf( "g: %d \n", g );
return( 0 );
}
• Utmatning
g: 4711
g: 17
128
Globala variabler
• Deklareras utanför alla
funktioner
• Tillgängliga i alla funktioner
• Startvärde lagras i körbar fil
("exe-filen")
• Anger man inget startvärde så
blir startvärdet noll
• Utnyttja inte detta!
• Svårläst kod
• Exempel
#include <stdio.h>
int g = 4711;
int main()
{
printf( "g: %d \n", g );
g = 17;
printf( "g: %d \n", g );
return( 0 );
}
• Utmatning
g: 4711
g: 17
129
Deklaration av global variabel: kompilering på
riktigt
•C
#include <stdio.h>
int g = 4711; /* global */
int main()
{
printf( "g: %d \n", g );
g = 17;
printf( "g: %d \n", g );
return( 0 );
}
• Förväntad kompilering
.global g
.data
.align 2
g: .word 4711
• Verklig kompilering
.global g
.section
.sdata,"aws",@progbits
.align
2
.type
.size
g:
.long
g, @object
g, 4
4711
• Ger mer hjälpsam info
till länkningen
130
Minnesdisposition
(minnes-karta)
• Programkod
.text
PC
• Globala data
adress 0
data
.data
(indelad i fler delar)
• Dynamiska data
• Stack
program
kod
heap
SP
stack
adress 2n-1
Unix v7
131
Globala variabler: hur funkar det?
• Lagras i data-arean
• Global Pointer GP
• GP innehåller adress till första
globala variabeln
• Exempel
#include <stdio.h>
int valle;
int * pelle;
int main()
{
pelle = &valle;
*pelle = 17;
printf( "valle: %d \n",
valle );
return( 0 );
}
132
REPRIS
Handöversättning till assembler
• int valle;
int * pelle;
• Assembler
.data
.align 2
.global valle
valle: .word 0
• .data
.align 2
.global pelle
pelle: .word 0
adress 0
4 byte
pelles adress
0x800b60
valles adress
0x800b98
adress
0xffffffff
133
Handöversättning till assembler
int valle;
int * pelle;
adress 0
Assembler
.data
.align 2
.global valle
valle: .word 0
.data
.align 2
.global pelle
pelle: .word 0
Obs! Realistiska
exempel på adresser.
gp innehåller 0x800b90
valles adress 0x800b94
pelles adress 0x800b98
r26/gp
134
0x800b90
adress
0xffffffff
4 byte
Handöversättning till assembler
• int main()
{
adress 0
• Assembler
.text
.align 2
.global main
main:
gp innehåller 0x800b90
valles adress 0x800b94
pelles adress 0x800b98
0x800b90
gp
135
adress
0xffffffff
4 byte
Handöversättning till assembler
•C
pelle = &valle;
adress 0
• Assembler
• addi r2,gp,4
• stw r2,8(gp)
gp innehåller 0x800b90
valles adress 0x800b94
pelles adress 0x800b98
0x800b90
gp
136
adress
0xffffffff
4 byte
Steg för steg
• pelle = &valle;
• Assembler
adress 0
gp
0x800b90
4 byte
addi r2,gp,4
• Resultat:
r2 = 0x800b94
• Assembler
stw r2,8(gp)
• Skriver 0x800b94 till det
minnesord som har
adress gp+8
gp innehåller 0x800b90
valles adress 0x800b94
pelles adress 0x800b98
adress
0xffffffff
0x800b94
137
Kompilering på riktigt
• Handöversättningen av pelle = &valle är
addi r2,gp,4
stw r2,8(gp)
• Kompilering ger
• addi
r2, gp, %gprel(valle)
Om valles adress är gp+4
så översätts %gprel(valle) till 4
• stw
r2, %gprel(pelle)(gp)
Är pelles adress gp+8 så är %gprel(pelle) 8
och denna rad blir stw r2,8(gp)
138
Handöversättning till assembler
•C
adress 0
*pelle = 17;
4 byte
• Assembler
• ldw r3,8(gp)
• movi r2,17
• stw r2,0(r3)
gp innehåller 0x800b90
valles adress 0x800b94
pelles adress 0x800b98
gp
0x800b94
0x800b90
adress
0xffffffff
139
Steg för steg
*pelle = 17;
adress 0
(1) Läs pekarens värde
(innehållet i pelle)
ldw r3,8(gp)
(2) Lägg 17 i ett register
movi r2,17
(3) Skriv (följ pekaren)
(2)
r2
r3
4 byte
17
0x800b94
(1)
gp innehåller 0x800b90
valles adress 0x800b94
(3)
pelles adress 0x800b98
17
0x800b94
stw r2,0(r3)
r26
0x800b90
adress
0xffffffff
140
Kompilering på riktigt
• Handöversättningen av *pelle = 17 är
ldw r3,8(gp)
movi r2,17
stw r2,0(r3)
• Kompilering ger
• ldw r3, %gprel(pelle)(gp)
movi r2, 17
stw r2, 0(r3)
%gprel(pelle) är 8 i vårt exempel
141
Testkod med många parametrar
• Börjar här
#include <stdio.h>
int fun(int,int,int,int,int,int);
int main()
{
int r;
r = fun( 1, 2, 3, 4, 5, 6 );
printf( "result = %d\n", r );
return( 0 );
}
• Funktionen fun finns i en annan fil
142
Den här föreläsningen
• Enkla datatyper och datarepresentation: Heltal, flyttal,
• Mer komplicerade datatyper
• Uttryck, satser, block
• Pekare, pekar-aritmetik
• Lagring av globala och lokala variabler
• Stack
• Heap
143