Maskinnära programmering i C Föreläsning 5 Mats Brorsson
IS1200 Datorteknik Assemblerprogram C In- och utmatning F1 F2 Ö1 F3 Ö2 F4 Ö3 lab nios2time Assemblerprogram C F5 Ö4 hemlab C In- och utmatning F6 Ö5 Ö6 lab nios2io Avbrott och "trap" F7 Ö7 lab nios2int Cacheminnen F8 Ö8 hemlab cache F9 Ö9 hemlab trådar Trådar, synkronisering F10 Ö10 tentamen
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 is1200@ict.kth.se och boka Ge alltid flera alternativa tider
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
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
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/
C för dig som kan Java main(int argc, char *argv[]) { Datorsystemteknik torsdag den 6 april 2017 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!"); } }
Datatyper i C 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
Hexadecimala talsystemet Datorsystemteknik torsdag den 6 april 2017 Hexadecimala talsystemet Lär dig denna tabell utantill! Ex: 0110 1000 1010 0001 = 0x68A1 Ett tal som börjar med 0x är i hexadecimal notation
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
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
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;
Deklaration och definition int a; /* Deklaration och definition, kompilatorn reserverar plats i minnet för variabeln a */ int b = 17; /* Definition med initiering, kompilatorn reserverar plats och lägger in startvärdet */ int b; /* Deklaration igen, anger samma typ, tillåtet! */ int b = 85; /* Ny definition, förbjudet! */
Datatyper Kombinationstyper: Grundtyper: array heltal struct flyttal union (ej behandlat här) Grundtyper: heltal flyttal
Datatyper: heltal signed char short int long long long unsigned char unsigned short unsigned int unsigned long unsigned long long minst 8 bit minst 16 bit minst 16 bit (oftast 32 bit) minst 32 bit minst 64 bit C99 5.2.4.2.1 sida 21-23 Filen limits.h ger värden för aktuell kompilator #include <limits.h> Exempel: INT_MAX kan ha värdet 2147483647
Exempel: gcc 3.4.6 för Nios II (Nios II har 32-bits adresser) char short int long long long float double 8 bit (1 byte) 16 bit (2 byte) 32 bit (4 byte) 64 bit (8 byte) minst 8 bit minst 16 bit minst 32 bit minst 64 bit
Exempel: gcc 4.4.3 för amd64 (AMD64 har 64-bits adresser) char short int long long long float double 8 bit (1 byte) 16 bit (2 byte) 32 bit (4 byte) 64 bit (8 byte) minst 8 bit minst 16 bit minst 32 bit minst 64 bit
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 );
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)
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)
Binärkod för värdet minus ett Koden för + 1 0000 0000 .... 0001 Bitvis invertering 1111 1111 .... 1110 Addition av ett + 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)
Värdet av kod för negativt tal Koden för negativt tal 1111 .... 1010 0101 Bitvis invertering 0000 .... 0101 1010 Addition av ett + 0000 .... 0000 0001 Och vi får kod 0000 .... 0101 1011 Som tolkas till värdet 64+16+8+2+1 = 91 Alltså -91
Snabbfrågor Omvandla följande decimala tal till 32-bitars tal i tvåkomplementrepresentation: 512 -1023 -4000000 Skriv talen i hexadecimalt format
Byte-ordning i minnet: little-endian, skrivning little-endian byte-order adress 0 1001 1111 0101 0000 1111 0111 0111 0000 . . . 0111 0000 1111 0111 0101 0000 1001 1111 (lilla änden först i minnet) . . . adress 2n-1
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
Datatyper: flyttal: approximation av reella tal float double long double Oftast 32 bitar (IEEE 754 ) Oftast 64 bitar (IEEE 754 ) ”Mer än double” 80 bit eller (IEEE 754 quad precision) Filen float.h ger värden för aktuell kompilator #include <float.h> Exempel: FLT_MAX kan ha värdet 340282346638528859811704183484516925440.000000 C99 5.2.4.2.2 sida 23-28
Decimalt flyttal -047,120 decimalt fixtal -04,7120 * 101 samma flyttal tecken mantissa exponent
Binärt flyttal -0101.110 binärt fixtal -0101,110 * 20 binärt flyttal -01,01110 * 22 normaliserat flyttal exponent tecken mantissa
Binärt flyttal: float (32 bit) -0101,110 binärt fixtal -0101,110 * 20 binärt flyttal -01,01110 * 22 normaliserat flyttal exponenten lagras med 8 bit i excess-127-kod tecken: 1:a för negativa tal bara bråksiffrorna av mantissan lagras, men inte "1,"
Binärt flyttal: double (64 bit) -0101,110 binärt fixtal -0101,110 * 20 binärt flyttal -01,01110 * 22 normaliserat flyttal tecken: 1:a för negativa tal bara bråksiffrorna av mantissan lagras, men inte "1," exponenten lagras med 11 bit i excess-1023-kod
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
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 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 Lös ut M från ekvationen: 1.M*278 = 4,712*1023 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
Varför Excess-127/1023? För då kan man använda heltalsinstruktioner för att avgöra vilket tal som är störst
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
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 0 0 0 0 0 0 0 0 0 0 0 0
Information, 64 bitar adress / värde Little endian byte ordering DOUBLE: 0x31 0xC0 0x00 0x00 - + 0x00 0x00 0x00 0x00 0x00 0x00 0x31 0xC0 -17 i double-format: 0x C0 31 00 00 00 00 00 00
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
Addition/subtraktion av flyttal Datorsystemteknik torsdag den 6 april 2017 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
Multiplikation av flyttal Datorsystemteknik torsdag den 6 april 2017 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
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
Mer komplicerade datatyper i C 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
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! */
Array i minnet Heltals-vektor Resultat i minnet (little-endian) adress 0 Heltals-vektor int v[27]; v[0] = 1234; v[1] = 4711; Resultat i minnet (little-endian) 1 byte adress för v[0]: 0x800b70 0xd2 0x04 1234 (decimalt) = 0x000004d2 (hex) 0x00 0x00 och här kommer v[1]: 0x800b74 0x67 0x12 4711 (decimalt) = 0x00001267 (hex) 0x00 0x00 adress för v[2]: 0x800b78 . . . adress 0xffffffff
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.
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
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' }
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."
Obs! Exempel på adresser. I minnet (exempel) Exempel i C: char c = 'Q'; int x = 4711; Assembler: .data .global c c: .byte 'Q' .align 2 .global x x: .word 4711 adress 0 1 byte adress för c: 0x800b62 0x51 Obs! Exempel på adresser. adress för x: 0x800b64 0x67 0x800b65 0x12 0x800b66 0x00 0x800b67 0x00 4711 (decimalt) = 0x00001267 (hex) adress 0xffffffff
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;
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
struct-exempel användning: 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
Handöversättning till assembler C: struct threadinfo thisthread; Assembler: .data .align 2 .global thisthread thisthread: .word 0 .word 0
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
Obs! Exempel på adresser. I minnet (exempel) adress 0 1 byte De två del-variablerna i en struct ligger efter varandra i minnet Ordningen är alltid samma som i C-programmet thisthread: 0x800b90 0x00 sp 0x800b91 0x00 0x800b92 0x00 0x800b93 0x00 0x800b94 0x67 id 0x800b95 0x12 0x800b96 0x00 0x800b97 0x00 Obs! Exempel på adresser. adress 0xffffffff
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!
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
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;
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
Uttryck, Satser och Block i C 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
Datorsystemteknik torsdag den 6 april 2017 If-satser
Datorsystemteknik torsdag den 6 april 2017 Relationsoperatorer
Datorsystemteknik torsdag den 6 april 2017 Repetitionsslingor
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;
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;
Ett komplett program #include <stdio.h> int n; p(int k) { Datorsystemteknik torsdag den 6 april 2017 Ett komplett program Genom direktivet include kan man göra sina program mer lättlästa. #include <stdio.h> int n; p(int k) { printf(“n = %d\n”, k) } int f(int a) { int b; b = a + 2; return b; /* Huvudprogram */ #define PARAMETER 10 main(){ n = f(10); p(n); } #define är ett sätt att definiera konstanter. Namnet main betyder huvudprogram Parametrar till funktioner och subrutiner deklareras på samma sätt som variabler Du har möjlighet att deklarera variabler inne i en funktion/subrutin return används för att avsluta en funktion och för att tala om vad som är returvärdet
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...
Stil: korthuggen programkod Utskrift av sträng int i; char s[20]; for( i = 0; s[i]; i = i + 1 ) putchar( s[i] ); 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’.
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;
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?
Snabbfrågor Hur många repetitioner kommer följande slinga exekvera? char teckenstr = ”aaaaaaabbbbbcc”; i = 0; tecken = teckenstr[0]; while (tecken = ’a’) { // behandla tecknet a … tecken = teckenstr[i++]; } 1 gång 7 gånger Oändligt antal
Block En sats (statement) kan alltid bytas mot ett block Uttryck kan inte bytas mot block C99 och senare standarder kan blanda deklarationer och satser Block: { deklarationer först i varje block satser efter alla deklarationer }
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
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;
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
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
Bitwise operations Logical operations A & B bitvis AND A | B bitvis OR A ^ B bitvis XOR ~A bitvis NOT (invertering) A && B logisk AND A || B logisk OR !A logisk NOT Notera INGEN operator ^^ i C (INGEN logisk XOR)
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;
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
Pekare och pekararitmetik i C 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
Obs! Bara exempel på adresser. Pekare En pekare är en sorts variabel Pekaren innehåller en adress Exempel int valle; int * pelle; pelle = &valle; *pelle = 17; adress 0 4 byte pelles adress 0x800b60 0x800b98 Obs! Bara exempel på adresser. valles adress 0x800b98 17 adress 0xffffffff
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"
Exempel på handöversättning adress 0 int valle; int * pelle; Assembler .data .align 2 .global valle valle: .word 0 .data .align 2 .global pelle pelle: .word 0 4 byte pelles adress 0x800b60 valles adress 0x800b98 adress 0xffffffff
Handöversättning till assembler C pelle = &valle; Assembler .text .align 2 movia r8,valle movia r9,pelle stw r8,0(r9) adress 0 4 byte pelles adress 0x800b60 valles adress 0x800b98 adress 0xffffffff
Steg för steg: pelle = &valle; Assembler .text .align 2 movia r8,valle adress 0 4 byte pelles adress 0x800b60 valles adress 0x800b98 r8 r8 0x800b98 r9 adress 0xffffffff
Steg för steg: pelle = &valle; Assembler .text .align 2 movia r8,valle movia r9,pelle adress 0 4 byte pelles adress 0x800b60 valles adress 0x800b98 r8 r8 0x800b98 r9 0x800b60 adress 0xffffffff
Steg för steg: pelle = &valle; Assembler .text .align 2 movia r8,valle movia r9,pelle stw r8,0(r9) adress 0 4 byte pelles adress 0x800b60 0x800b98 valles adress 0x800b98 r8 r8 0x800b98 r9 0x800b60 adress 0xffffffff
Handöversättning till assembler C *pelle = 17; Assembler .text .align 2 movia r8,17 movia r9,pelle ldw r10,0(r9) stw r8,0(r10) adress 0 4 byte pelles adress 0x800b60 0x800b98 valles adress 0x800b98 adress 0xffffffff
Steg för steg: *pelle = 17; Assembler: .text .align 2 adress 0 4 byte pelles adress 0x800b60 0x800b98 valles adress 0x800b98 r8 r8 r9 r10 adress 0xffffffff
Steg för steg: *pelle = 17; Assembler: .text .align 2 movia r8,17 adress 0 4 byte pelles adress 0x800b60 0x800b98 valles adress 0x800b98 r8 r8 17 r9 r10 adress 0xffffffff
Steg för steg: *pelle = 17; Assembler: .text .align 2 movia r8,17 movia r9,pelle adress 0 4 byte pelles adress 0x800b60 0x800b98 valles adress 0x800b98 r8 r8 17 r9 0x800b60 r10 adress 0xffffffff
Steg för steg: *pelle = 17; Assembler: .text .align 2 movia r8,17 movia r9,pelle ldw r10,0(r9) adress 0 4 byte pelles adress 0x800b60 0x800b98 valles adress 0x800b98 r8 r8 17 r9 0x800b60 r10 0x800b98 adress 0xffffffff
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 4 byte pelles adress 0x800b60 0x800b98 valles adress 0x800b98 17 r8 r8 17 r9 0x800b60 r10 0x800b98 adress 0xffffffff
Pekartyper Exempel: pointer to char pointer to int pointer to double pointer to struct h men struct h ska förstås vara definierad tidigare char * cp; int * ip; double * dp; struct h * hp;
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
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
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
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 */
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
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
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!"
Lagring av textsträng, char [ ] adress 0 1 byte text[0] 'H' C-kod: char text[] = ”Hello, world!”; Assemblerkod: .data text: .asciz ”Hello World!” 'e' 'l' 'l' 'o' ',' ' ' 'w' 'o' 'r' 'l' 'l' 'd' '!' text[13] 0x00 adress 0xffffffff
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!"
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) adress 0 1 byte adress för v[0]: 0x800b70 0xd2 0x04 1234 (decimalt) = 0x000004d2 (hex) 0x00 0x00 och här kommer v[1]: 0x800b74 0xff 0xff 65535 (decimalt) = 0x0000ffff (hex) 0x00 0x00 adress för v[2]: 0x800b78 . . . adress 0xffffffff
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; } }
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 ++ */ } }
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]; }
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]; }
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
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
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
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
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
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
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
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
Minnesuppdelning program- kod data bss heap data bss stack adress 0 adress 2n-1 Exempel 1 Exempel 2 data bss PC SP
Programstart körbar fil minne header program- kod kopiering vid programstart program- kod data bss data symboltabell heap
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)
Dynamisk allokering av minne at execution/run time Vid exekvering allokeras minne på heap och används för Dynamiska data-strukturer
malloc() och free() dynamisk allokering på heap /* Tänkbar bakgrund */ struct elephant { char[13] name; /* 16 bytes ? ! */ int weight; /* 4 bytes */ double area; /* 8 bytes */ } ; struct elephant dumbo;
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 */
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
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
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 g, @object .size g, 4 g: .long 4711 Ger mer hjälpsam info till länkningen
Minnesdisposition (minnes-karta) Programkod .text Globala data .data (indelad i fler delar) Dynamiska data Stack PC program- kod adress 0 data heap SP stack adress 2n-1 Unix v7
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 ); }
Handöversättning till assembler 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
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 Obs! Realistiska exempel på adresser. gp innehåller 0x800b90 valles adress 0x800b94 pelles adress 0x800b98 r26/gp 0x800b90 adress 0xffffffff
Handöversättning till assembler int main() { Assembler .text .align 2 .global main main: adress 0 4 byte gp innehåller 0x800b90 valles adress 0x800b94 pelles adress 0x800b98 gp 0x800b90 adress 0xffffffff
Handöversättning till assembler C pelle = &valle; Assembler addi r2,gp,4 stw r2,8(gp) adress 0 4 byte gp innehåller 0x800b90 valles adress 0x800b94 pelles adress 0x800b98 gp 0x800b90 adress 0xffffffff
Steg för steg Assembler addi r2,gp,4 Resultat: r2 = 0x800b94 pelle = &valle; Assembler addi r2,gp,4 Resultat: r2 = 0x800b94 Assembler stw r2,8(gp) Skriver 0x800b94 till det minnesord som har adress gp+8 adress 0 gp 0x800b90 4 byte gp innehåller 0x800b90 valles adress 0x800b94 pelles adress 0x800b98 0x800b94 adress 0xffffffff
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)
Handöversättning till assembler C *pelle = 17; Assembler ldw r3,8(gp) movi r2,17 stw r2,0(r3) adress 0 4 byte gp innehåller 0x800b90 valles adress 0x800b94 pelles adress 0x800b98 0x800b94 gp 0x800b90 adress 0xffffffff
Steg för steg Läs pekarens värde (innehållet i pelle) ldw r3,8(gp) Lägg 17 i ett register movi r2,17 Skriv (följ pekaren) stw r2,0(r3) adress 0 4 byte r2 17 (2) r3 0x800b94 (1) gp innehåller 0x800b90 valles adress 0x800b94 17 (3) 0x800b94 pelles adress 0x800b98 r26 0x800b90 adress 0xffffffff
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
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
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