Presentasjon lastes. Vennligst vent

Presentasjon lastes. Vennligst vent

Krasjkurs i C (pluss litt matlab)

Liknende presentasjoner


Presentasjon om: "Krasjkurs i C (pluss litt matlab)"— Utskrift av presentasjonen:

1 Krasjkurs i C (pluss litt matlab)

2 Innhold Litt om Matlab C Funksjoner Strukturer (Structs)
Noen matlab-funksjoner dere kan få bruk for C Kompilatorer Kompilering C syntaks Funksjoner i C Variable i C Operatorer i C Pekere Arrays og Strukturer Makroer Dynamisk Minne-allokering Lesing og skriving av binær fil, eksempel på å lese en wav-fil i C. (Nyttig for oppgaven..!) Om du ønsker, kan du sette inn navn, tittel på foredraget, o.l. her.

3 Matlab Antar en del kunnskap i matlab: For mer grundig introduksjon henvises til: Matlab skiller seg fra språk som java og C ved at matlab prosesserer linje for linje, Matlab håndterer mange oppgaver, som du selv må ha kontroll på i C! (håndtering av datatyper, minneallokering o.l) Matlab er bra til simulering pga. enkel syntaks, koden er enkel å debugge, og det finnes mange funksjoner for visualisering av data. Matlab har mange ferdiglagde funksjoner, men tenk på å skrive matlab kode som uten for store endringer kan skrives om til C ! Om du ønsker, kan du sette inn navn, tittel på foredraget, o.l. her.

4 “geom()” er en funksjons-navnet. Funskjonen må lagres som geom.m
Funksjoner i Matlab En funksjon er en “.m” fil som starter med function. Funksjon kan ta variable inn, utføre en eller flere operasjoner, og returnere et vilkårlig antall variable. Akkurat som funksjoner i java eller C Variabel (ev. variable) som skal returneres. “geom()” er en funksjons-navnet. Funskjonen må lagres som geom.m function ssum =geom(a,N) % funksjon som beregner summen til en % geometrisk rekke n=0:N; ssum= sum(a.^n) Funksjons-argumenter Include directives use <x.h> (brackets) to indicate that the compiler should look in a “standard” place, such as /usr/include/… They use “x.h” (double quotes) to indicate that it should look first in the current directory. Kommentarer i Matlab beynner med %

5 Strukturer i Matlab struct: Ikke så forskjellig fra et objekt i java, men en struct er uten metoder. En struct består av av felter med tilhørende verdier, der de forskjellige feltene kan inneholde forskjellige datatyper. >>opt=struct('gamma',0.9,'nbits',4,'stepsize',2.0) opt = gamma: nbits: 4 stepsize: 2 >> opt.gamma ans = 0.9000 >> opt.alpha=0.9 alpha: Felter blir aksessert med ‘.’ notasjon. Packing? The layout of a structure in memory is actually architecture-dependent. The order of the fields will always be preserved; however, some architectures will pad fields so that they are word-aligned. Word alignment is important for some processor architectures because they do not support unaligned memory access, e.g. accessing a 4 byte int that is not aligned to a 4 byte boundary. It is also usually more efficient to process values on word boundaries. So in general, structs are architecture-specific. For instance, on x86 platforms, all fields are word-aligned, so struct { uint16_t x; uint32_t y; }; Will have a hidden two bytes of padding between x and y. However, on an atmel atmega128 this padding will not be present. One way to address this is to predict it, and explicity add the padding fields. Another solution is to add __attribute__ ((“packed”)) after the structure declaration to force the compiler to generate code to handle unaligned accesses. This code will operate slower but this is often the easiest solution to dealing with cross-platform compatibility. The other common problem with cross-platform compatibility is endianness. X86 machines are little-endian; many modern processors such as the PXA (xscale) can select their endianness. The functions ntohl, htonl, etc. can be used to convert to a standard network byte order (which is big endian). =========== Why a zero length array? A zero length array is a typed “pointer target” that begins at the byte after the end of the struct. The zero length array takes up no space and does not change the result of sizeof(). This is very useful for referring to data following the struct in memory, for example if you have a packet header and a packet payload following the header: struct hdr { int src; int dst; uint8_t data[0]; If you have a buffer of data containing the packet and the payload: uint8_t buf[200]; int buf_len; /* cast the buffer to the header type (if it’s long enough!) */ if (buf_len >= sizeof(struct hdr)) { struct hdr *h = (struct hdr *)buf; /* now, h->data[0] is the first byte of the payload */ } Nye felter kan enkelt legges til. Struct er en grei datatype å bruke for å samle tilhørende data.

6 Noen matlab-funksjoner dere kanskje får bruk for
Wavrecord: Ta opp lyd i matlab. Wavread: Les inn en ’wav’-fil til en vektor i matlab Wavwrite: skriv en vektor til en ’wav’-fil Soundsc: Spill av en vektor i matlab Wavplay: Spill av en wavfil i matlab (Kan også gjøres med først wavread og deretter soundsc) Randn: Generer normalfordelte tilfeldige tall i matlab, kan brukes når støy-generatoren skal implementeres Er du usikker på hvordan disse funskjonene brukes, skriv help funksjonsnavn i matlab.

7 C-Kompilator C er i motseteting til matlab et kompilatorspråk på samme måte som java (javac). C-kompilatorer finnes gratis tilgjengelig på internett. I prinsippet kan dere bruke den dere liker best.. Forslag: Gnu’s kompilator gcc. Denne er egentlig laget for unix, men kan kjøres på windows i et program som heter cygwin Fremgangmåte i windows: Innstaller Cygwin Et tips er å legge katalogen hvor bin-filen til cygwin ligger (vanligvis C:/cygwin/bin) i environment-variablen ”system path”, slik at kompilatoren gcc kan kjøres uansett hvilken katalog man er i. Får du ikke kompilatoren til å fungere, kom å spør oss om hjelp!  Gcc compiler options: -Wall tells the compiler to generate ALL “warnings”. These warnings will often identify stupid mistakes. -g tells the compiler to generate debugging information If you don’t supply a –o option to set an output filename, it will create an executable called “a.out” ./? To run a program in the current directory use ./program ‘.’ is the current directory. Otherwise the shell will only run program that are in your “PATH” variable, which is usually “standard” paths like /bin and /usr/bin What if it doesn’t work? If your program compiles but doesn’t work, there are many debugging tools at your disposal. “strace” traces all system calls made by your program. This will tell you when you call into the OS, e.g. open(), read(), write(), etc. “ltrace” traces all standard library calls made by your program. “gdb” is a source-level debugger. With this tool you can analyse core dumps, and step through your program line by line as it runs. “valgrind” is an x86 virtual machine that is useful for catching memory errors such as use of freed memory, bad pointers, etc. Another common way to trace a program is “printf debugging”, where you put printf statements throughout your code to see what’s going on.

8 Kompilering med gcc Kjør kompilatoren for å konvertere programmet fra kildekoden til en binær kjørbar fil: $ gcc my_program.c –o my_program Opsjonen –o betyr at det lages en fil som heter my_program.exe i windows, eventuelt my_program.out i unix. Hvis ikke denne brukes lages en fil med navn a.out (som er default) Kan programmet ha mer enn en c fil? Can your program have more than one .c file? A ‘.c’ file is called a “module”. Many programs are composed of several ‘.c’ files and libraries that are ‘linked’ together during the compile process. Det kan være lurt å lage en makefil som definerer regler for kompilering og lenking av alle filer som inngår i et program. Da kan hele systemet enkelt kompileres med å skrive make. Mer om hvordan man lager en slik makefil neste gang.

9 Kompilator Program Kompileringer skjer i to steg:
Preprossesering og kompilering. Preprosessering #include <stdio.h> /* The simplest C Program */ int main(int argc, char **argv) { printf(“Hello World\n”); return 0; } I preprossesering blir kildekoden ekspandert til lengre kode som er enklere for kompilatorene å forstå. Alle linjer som start med # blir tolket av preprosessoren #include inkluderer en fil. #define definerer en makro. Kommentarer blir fjernet ( /* */ , // ) kodelinjer over flere linjer blir satt sammen til en linje ( \ ) __extension__ typedef unsigned long long int __dev_t; __extension__ typedef unsigned int __uid_t; __extension__ typedef unsigned int __gid_t; __extension__ typedef unsigned long int __ino_t; __extension__ typedef unsigned long long int __ino64_t; __extension__ typedef unsigned int __nlink_t; __extension__ typedef long int __off_t; __extension__ typedef long long int __off64_t; extern void flockfile (FILE *__stream) ; extern int ftrylockfile (FILE *__stream) ; extern void funlockfile (FILE *__stream) ; int main(int argc, char **argv) { printf(“Hello World\n”); return 0; } Why preprocess? Having two phases to a process with different properties often helps to make a very flexible yet robust system. The underlying compiler is very simple and its behavior is easily understood. But because of that simplicity it can be hard to use. The preprocessor lets you “customize” the way your code “looks and feels” and avoid redunancy using a few simple facilities, without increasing the complexity of the underlying compiler. Macros can be used to make your code look clean and easy to understand. They can also be used for “evil”. What are continued lines? Continued lines are lines that need to be very long. When the preprocessor encounters a ‘\’ (that is not inside a string), it will ignore the next character. If that character is a newline, then this “eats” the newline and joins the two lines into one. This is often used when defining macros with #define, because a macro must be all on one line. Be careful! If you have a space after a ‘\’, it will eat the space and will NOT join the lines. Program Kompilatoren endrer så teksten til binær kode som CPU’en kan kjøre direkte Kompilering

10 C Syntax, og Hello World #include setter inn en fil. “.h” filer er såkalte “header-filer”. Header-filer definerer grensenittet (interface i java) til bibliotek og kode i andre c-filer. stdio.h er et av de vanligste biliotekene brukt u C, og inneholder funskjoner for standard input and output. Hva menes med < > ? Kommentarer i C beynner med /* og avsluttes med */. int main() funksjonen er alltid! Det stedet hvor programmet starter å kjøre. (Uavhengig hvor main funksjonen finnes i koden) #include <stdio.h> /* The simplest C Program */ int main(int argc, char **argv) { printf(“Hello World\n”); return 0; } Blokker med kode markeres med { … }. En blokk eller et skop har egne lokale variable, som bare er tilgjengelige innenfor blokken What do the <> mean? Include directives use <x.h> (brackets) to indicate that the compiler should look in a “standard” place, such as /usr/include/… They use “x.h” (double quotes) to indicate that it should look first in the current directory. Can your program have more than one .c file? A ‘.c’ file is called a “module”. Many programs are composed of several ‘.c’ files and libraries that are ‘linked’ together during the compile process. Returner ‘0’. main() returnerer enten 0 eller 1, der programmet kan returnere 1 til operativsystemet hvis det har oppstått en feil. Skriv ut en melding til “stdout”. ‘\n’ betyr ny linje. Alle “Statements” må avsluttes med semikolon.

11 “main()” er en funksjon.
Funksjoner Funksjoner i C er veldig likt funksjoner i java. Man sender Argumenter til funksjonen og den retunerer en Verdi. Return type, or void “main()” er en funksjon. Funksjons-argumenter #include <stdio.h> /* The simplest C Program */ int main(int argc, char **argv) { printf(“Hello World\n”); return 0; } Å kalle en funksjon “printf()” er bare en annen funksjon, som main(). Den er definert i “biblioteket” stdio.h, en samling funksjoner som kan kalles fra programmet. Include directives use <x.h> (brackets) to indicate that the compiler should look in a “standard” place, such as /usr/include/… They use “x.h” (double quotes) to indicate that it should look first in the current directory. Returnering av verdi

12 Hva er en Variabel Symbol-tabell En Variabel navngir en plass i minnet hvor man lagrer en Verdi av en bestemt Datatype. Symbol Addr Verdi 1 2 3 x 4 ? y 5 ‘e’ (101) 6 7 8 9 10 11 12 Man Deklarerer en variabel ved å gi den et navn og ved å spesifisere datatypen, eventuelt kan man sette en initialverdi (startverdi) Datatypen er en enkelt char (“character”) extern? static? const? Navn Hvilke navn kan brukes? Initiell verdi Initiell verdi til x er udefinert char x; char y=‘e’; Kompilatoren legger dem et sted i minnet. Symbol table: The “Symbol Table” is the mapping from symbol to address. You can extract this from a compiled program (if debugging is enabled aqt compile time with –g) using the nm utility. Declaration and definition mean slightly different things in C. Declaration is mapping a name to a type, but not to a memory location. Definition maps a name and type to a memory location (which could be the body of a function). Definitions can assign an initial value. Declarations can point to something that never actually gets defined, or is defined in a library external to your program. What names are legal? Symbols in C (function and variable names) must start with an alphabetic character or ‘_’. They may contain numeric digits as well but cannot start with a digit. Common naming strategies are ‘CamelCase’, ‘lowerUpper’, and ‘under_bar’. Some people use complicated schemes but I prefer under_bar. extern, static, and const are modifiers of variable declarations. ‘extern’ means the variable being declared is defined elsewhere in some other program module (.c file). ‘static’ means that the variable cannot be accessed outside of the current scope. ‘const’ means the variable’s value cannot be changed (directly).

13 Hva er minne? Addr Value 1 2 3 4 ‘H’ (72) 5 ‘e’ (101) 6 ‘l’ (108) 7 8
1 2 3 4 ‘H’ (72) 5 ‘e’ (101) 6 ‘l’ (108) 7 8 ‘o’ (111) 9 ‘\n’ (10) 10 ‘\0’ (0) 11 12 Datamaskin-minne kan sammenlines med en stor tabell med nummererte bokser. Nummeret til boksen er Addressen. En byte kan lagres i hver boks 72? Noen “logiske” datatyper bruker mer enn en boks, slik som strengen “Hello\n” som består av flere “charcters”, altså en tabell av datatypen char. En Datatype navngir en logisk måte å lagre verdier i minnet of memory. Noen datatyper er: What’s 72? ASCII is the coding scheme that maps bytes to characters. Type ‘man ascii’ at the shell to get a listing of the mapping. Not always… Types such as int vary in size depending on the architecture. On a 32 bit or 64 bit platform, ints are 4 bytes (32 bits). On an 8 or 16 bit platform, an int is 2 bytes (16 bits). To be safe, it’s best to specify types exactly e.g. int32_t, int16_t., int8_t. Defined in #include <inttypes.h> Signed? Signedness is a common source of problems in C. It’s always clearest to specify signness explicitly (and size) using the [u]int[8,16,32]_t types. Signedness can introduce surprises when casting types because of “sign extension”. For example, char is signed (although it’s normally used as unsigned 0-255). If you cast char to int, it will be a signed integer and the sign will be extended. If you then cast that to unsigned, it turns a small negative number into an enormous int value (high bit set). That is, (uint32_t)(int)(char)(128) == (not 128!) One easy solution to this is to always use uint8_t rather than char for byte streams. en enkelt char (1 slot) En tabell med 10 char’s signed 4 byte integer 4 byte floating point signed 8 byte integer char char [10] int float int64_t Ikke alltid… Signed?…

14 Mer om datatyper og minne.
Symbol Addr Value 1 2 3 x 4 ? y 5 ‘e’ (101) 6 7 z 8 9 10 11 12 Forskjellige datatyper bruker forskjellige mengde minne. De fleste datamaskin- arkitekturer lagrer på et heltallig multiplum av størrelsen på en primitiv datatype.(int,char) char x; char y=‘e’; int z = 0x ; “padding” En “vanlig” int bruker 4 bytes (dvs 32 bit integer) 0x betyr at konstanten er skrevet i hex (16-tallsystem,0..9,A..,F) En hex er et tall i 16 talls systemet, som er akkurat 2 opphøyd i 4, d.v.s en hex beskriver 2 byte entydig, en int kan dermed skrives som 4 hexer..

15 Skopet til en variabel (Returner ingenting) Alle Variable er definert innefor et “skop”. En Variabel kan ikke refereres utenfor dette skopet. void p(char x) { /* p,x */ char y; /* p,x,y */ char z; /* p,x,y,z */ } /* p */ /* p,z */ void q(char a) { char b; /* p,z,q,a,b */ { char c; /* p,z,q,a,b,c */ char d; /* p,z,q,a,b,d (not c) */ /* p,z,q */ Skopet til en variabel defineres med klammeparanteser { }, som i java, og tilsvarer begin-end i matlab. Kalles også en blokk Skopet til funksjons-argumentene er kun inne i funskjonen, dvs. innenfor klammeparantesene til funksjonen. Skopet til variablene definert inne i en funksjon starter ved definisjonen og slutter ved slutten av char b? Returns nothing.. “void” is used to denote that nothing is returned, or to indicate untyped data. What if you define a new “char b”? What happens to the old one? If you defile a variable in your local scope that has the same name as a variable in an enclosing scope, the local version takes precedence. Are definitions allowed in the middle of a block? Older versions of C and some derivatives like NesC only allow definitions at the beginning of a scope; d would be illegal. Modern C (gcc 3.0+) allows this, which can make the code easier to read as the definitions are closer to use. However, note that definitions are not always allowed in certain places, such as after a jump target, e.g. { goto target; /* … */ target: int z; /* error */ } legal? Skopet til variable definert utenfor funksjonen starter ved definisjonen og slutter på slutten av filen, kalt gloable variable.

16 Sammenligning og matematiske operatorer
== equal to < less than <= less than or equal > greater than >= greater than or equal != not equal && logical and || logical or ! logical not Obs: Deling: Hvis andre argument er integer, så vil resultatet bli integer !! (rounded): 5 / 10  0 , mens 5 / 10.0  0.5 & bitwise and | bitwise or ^ bitwise xor ~ bitwise not << shift left >> shift right + plus minus * mult / divide % modulo Obs: Forskjell på & and &&.. 1 & 2  0 mens 1 && 2  <true>

17 Mer om operatorer i C Merk forskjellen på ++x og x++:
x = y assign y to x x post-increment x ++x pre-increment x x post-decrement x --x pre-decrement x x += y assign (x+y) to x x -= y assign (x-y) to x x *= y assign (x*y) to x x /= y assign (x/y) to x x %= y assign (x%y) to x Merk forskjellen på ++x og x++: int x=5; int y; y = ++x; /* x == 6, y == 6 */ int x=5; int y; y = x++; /* x == 6, y == 5 */ Ikke bland tilordningsoperator =, og sammenligningsoperator == Despite being syntactically correct, if (x=y) is generally to be avoided because it is confusing. Doing complicated things with ++ and – are also not generally recommended. It is almost never important from an efficiency perspective and it just makes the code harder to understand. int x=5; if (x==6) /* false */ { /* ... */ } /* x is still 5 */ int x=5; if (x=6) /* always true */ { /* x is now 6 */ } /* ... */

18 “While” og “for” syntaks i C
“for” løkka kan ses på som en kortversjon av en while-stuktur float pow(float x, uint exp) { float result=1.0; int i; i=0; while (i < exp) { result = result * x; i++; } return result; int main(int argc, char **argv) { float p; p = pow(10.0, 5); printf(“p = %f\n”, p); return 0; float pow(float x, uint exp) { float result=1.0; int i; for (i=0; (i < exp); i++) { result = result * x; } return result; int main(int argc, char **argv) { float p; p = pow(10.0, 5); printf(“p = %f\n”, p); return 0; For: initsialisering; test; increment;

19 Kan en funksjon modifisere argumentene?
Hva om vi ønsket å implementere en funksjon pow_assign() som modifiserer argumentet, slik at disse er ekvivalente: float p = 2.0; /* p is 2.0 here */ p = pow(p, 5); /* p is 32.0 here */ float p = 2.0; /* p is 2.0 here */ pow_assign(p, 5); /* p is 32.0 here */ void pow_assign(float x, uint exp) { float result=1.0; int i; for (i=0; (i < exp); i++) { result = result * x; } x = result; Ville dette fungere? Nei! I C, blir alle argumenter sendt som verdier, og skopet til den modifiserte variabelen er bare innenfor funskjonen Men, hva om vi sendte argumentet som adressen til variabelen?

20 Å sende adresser Symbol Addr Value 1 2 3 char x 4 ‘H’ (72) char y 5
1 2 3 char x 4 ‘H’ (72) char y 5 ‘e’ (101) 6 7 8 9 10 11 12 Hva om vi hadde en måte å finne adressen til en variabel, og så referere til minnet med adressen. address_of(y) == 5 memory_at[5] == 101 void f(address_of_char p) { memory_at[p] = memory_at[p] - 32; } char y = 101; /* y is 101 */ f(address_of(y)); /* i.e. f(5) */ /* y is now = 69 */

21 En ny datatype: Pekere Dette er akkurat hvordan pekere virker.
“addressen til”/ referingsoperator: & “verdien til”/ dereference operator: * void f(char * p) { *p = *p - 32; } char y = 101; /* y is 101 */ f(&y); /* i.e. f(5) */ /* y er nå = 69 */ void f(address_of_char p) memory_at[p] = memory_at[p] - 32; f(address_of(y)); /* i.e. f(5) */ En “peker type”: peker til char Pekere brukes i C for til mange ting: Sende store objekter uten å kopiere dem Aksessere dynamisk allokert minne Referere til funksjoner

22 “Pointers” Eksempel på pekere Input er en peker til char
“addressen til” : & “verdien til”: * void f(char * p) { *p = *p - 32; } char y = 101; /* y is 101 */ f(&y); /* i.e. f(5) */ /* y is now = 69 */ void f(address_of_char p) memory_at[p] = memory_at[p] - 32; f(address_of(y)); /* i.e. f(5) */ Input er en peker til char “Pointers” En tommelfinger-regel er at hvis input-variablene som sendes til funksjonn skal endres i funksjonen, må de sendes som pekere. Hvis de ikke endres bør de sendes til funksjonen som verdier. NB!: Bruker man pekere som input til funksjoner er det veldig viktig å huske å sende adressen og ikke verdien, da man i tilfelle vil endre på en helt tilfeldig plass i minnet, som kan føre til at hele systemet krasjer.

23 Eksmpel på pekere int *p;
er en peker til en integer. En stjerne i forkant av variabelnavnet deklarerer variabelen til å være en peker til den deklarerte typen (her int) int *p , q deklarerer en peker til int (p), og en int (q). p=&q lagrer adressen til q i p int a; a=*p * er også dereferingsoperator, som betyr verdien til p, altså verdien som ligger i addressen p peker på. I dette tilfellet vil altså variablen a få verdien til q

24 Arrays Arrays i C er en tabell med data av samme datatype, der data blir lagt i minnet etter hverandre. /* define an array of 10 chars */ char x[5] = {‘t’,’e’,’s’,’t’,’\0’}; /* accessing element 0 */ x[0] = ‘T’; /* pointer arithmetic to get elt 3 */ char elt3 = *(x+3); /* x[3] */ /* x[0] evaluates to the first element; * x evaluates to the address of the * first element, or &(x[0]) */ /* 0-indksert */ #define COUNT 10 char y[COUNT]; int i; for (i=0; i<COUNT; i++) { /* process y[i] */ printf(“%c\n”, y[i]); } [5] spesifiserer antallet elementer. Initielle verdier kan settes I { }. Arrays i C er 0-indeksert (her, 0..9) Symbol Addr Value char x [0] 100 ‘t’ char x [1] 101 ‘e’ char x [2] 102 ‘s’ char x [3] 103 char x [4] 104 ‘\0’ x[3] == *(x+3) == ‘t’ Hva er forskjellen på char x[count] og char *x? char x[] and char *x are subtlely different. First, what’s the same: both will evaluate to an address that is type char *. In both cases, x[n] == *(x+n) and x == &(x[0]). The difference lies in the fact that char x[10] allocates memory for that data and both &x and x always evaulates to the base address of the array, whereas char *x allocates only space for a pointer, and x simply evaulates to the value of that pointer. Thus, in the case of char x[10], &x == x by definition. However this is almost never true for char *x because x contains the address of the memory where the characters are stored, &(x[0]), and &x is the address of that pointer itself!

25 Repetisjon, pekere og arrays
int main(int argc,char **argv) { int *p ; int a=5; p=&a printf("Addressen til p: %d\n",p); printf("Addressen til a: %d\n",&a); printf("Verdien i p: %d\n",(*p)); printf("Addressen til p: %d\n",(&p)); a=p; /* Kompilator gir warning!*/ printf("Verdien til a: %d\n",&a); a=3; printf("Verdi til p: %d\n",(*p)); printf("Verdien p[0]!: %d\n",p[0]); int c[5]; c[0]=0; c[2]=2; printf("Verdien til \"c\": %d\n",c); printf("addressen til c: %d\n",&c); printf("addressen c[0]: %d\n",(&c[0])); printf("addressen c[1]: %d\n",&c[1]); printf("addressen c[2] : %d\n",&c[2]); printf("Verdi c[0]: %d\n",c[0]); printf("Verdi c[2]: %d\n",c[2]); return 0;} Når a settes til p, blir a lik adressen lagret i p, som ofte er et veldig stort tall. Kompilatoren vil gi en warning om at et int blir satt til en pekerverdi uten ”casting”. Selv om ikke a er en peker kan addressen refereres med &a Setter a til verdien 3, som også medfører at p peker på verdien 3. (p peker på a). Selv om p ikke er definert som et array kan p tolkes som et endimensjonalt array, og verdien til p kan refereres med p[0]. Merk at addressen til a kan sendes hvis a skal endres inne i en annen funksjon Casting vil si å endre datatypen fra en type til en annen, dette krever at man vet hva man holder på med.. Verre å sette p=a ..! Adressen i p settes til 5 som i kan inneholde hva som helst. Og verre endrer man på verdien til p, vet man ikke hva som kan skje.., fare for krasj! c=&c=&c[0] som er adressen til det første elementet. c er altså egentlig en peker til det første elementet arrayet .

26 Lese og skrive til fil Før en fil kan bli aksessert, må den åpnes med fopen. Fopen returner en peker til en definert datatype FILE (eller en null-peker hvis noe går galt) int main(int argc,char **argv) { FILE *f,*f_new; /*2 filpeker2*/ long offset=44; f=fopen("ae_a01.wav","rb"); f_new=fopen(“test.wav",“wb"); fseek(f,offset,0); short value[1]; /*buffer*/ for (int i=0;i<100; i++) fread(&value,sizeof(short),1,f); printf("%d\n",value[0]) fwrite(&value,sizeof(short),1,f_new); } fclose(f); return 0; ’rb’ vil si å åpne en binær fil for lesing, ’wb’ for å åpne for skriving. (Og ev. lage filen hvis den ikke eksisterer) En “.wav” inneholder 44 bit som er info om lydfilen. (Samplefrekvenes o.l, som egentlig også burde vært lest). For å lese selve taledataene flyttes “posisjonen” til filpekeren med funksjonen fseek. Dataene I en wav-fil er normalt int16 (16 bits int), dvs. en short i C (2 byte). Merk at hvis f ikke hadde vært en peker(adresse), ville ikke f være forandret utenfor funksjonen. Merk og at etter fread er ”posisjonen” til filpekeren på plassen hvor den stoppet og lese, og fortsetter derfra neste gang det leses(med mindre man bruker fseek i mellomtida) Funksjonen fread tar inn en adresse/peker (&value), og leser N (i dette tilfellet 1) * size (sizeof(short)) fra filen definert med filpekeren f. fclose lukker filen og sletter filpekeren.

27 Casting i C Noen ganger ønsker man en variabel av en bestemt type (x bytes), og så tilordne denne til en variabel av en annen datatype (y bytes). Dette kalles “(type)casting”. #include <stdio.h> #include <math.h> int main() { char c; int i; float f; c = 'a'; i = c; f = i; printf("c = %d (%c)\n",c,c); printf("i = %d (%c)\n",i,i); printf("f = %f\n", f); double x= int y,y2; y=(int) x; Y2=(int) round(x); printf(”x = %lf\n", x); printf(”y = %d\n", y); printf(”y2 = %d\n", y); int *p; int q=5; q=p; //kompilator gir warning q=(int) p; //ingen warning } Enkle eksempler er å gjøre en char om til en int, og en int om til en float. Dette kan gjøres bare med vanlig tilordning i C. Både i=c og f=i er en ”typecast”-operasjon. Casting gjøres ved å skrive den nye typen i parantes foran variabelnavnet. En casting fra double/float til int gjør at verdien bli rundet av nedover. (kutter desimalene) Formater for printf: lf:double,c:char,d:int,f_float Selv om c gjør noen “typecasts” automatisk, er det en god regel å “caste” hvis vi er i tvil, som er en måte å si til kompilatoren at vi (tror vi) vet hva vi holder på med.

28 Strenger i C En string i C er definert som en nullterminert array av char, det vil si en array av typen char med en “0”-char til slutt for å definere slutten på strengen #include <stdio.h> #include <string.h> int main(int argc,char **argv){ FILE *fp; char id[5]; fp = fopen("h wav","rb"); fread(id, sizeof(char), 4, fp); id[4]=’\0’; //id[4]=0x00; id[4]=0; if (strcmp(id, "RIFF")){ printf(“Dette er en Wavfil”) /* Noen flere strenger */ const char* navn1=”Hans”; char navn2[10]={’H’,’a’,’n’,’s’,’\0’} navn2=”Petter”; /* Ikke mulig!’/ navn2[4]=’a’; navn2[5]=’\0’; printf(”%s\n”,navn2); } De 4 første bytene i et en wav-fil, er av typen char og inneholder tegnene: RIFF. Hvis vi ønsker å lese inn de 4 første bytene og lagre dem i en streng trenger vi en tabell av størrels 5, i og med at vi må ha en ”nullbyte” til slutt! Det er flere måter å definere en streng på I C. Men obs! Forskjell på initsialiserig av en streng og tilordning, En streng må håndteres som en array av char..! RIFF står for: Resource Interchange File Format id[4] er 5. og siste element i tabellen id (obs!) Funkskjoner for å operere på strenger fins i string.h

29 Strukturer struct: En måte å sette sammen eksisterende typer til en struktur. Pakking? #include <sys/time.h> /* declare the struct */ struct my_struct { int counter; float average; struct timeval timestamp; uint in_use:1; uint8_t data[0]; }; /* define an instance of my_struct */ struct my_struct x = { in_use: 1, timestamp: { tv_sec: 200 } x.counter = 1; x.average = sum / (float)(x.counter); struct my_struct * ptr = &x; ptr->counter = 2; (*ptr).counter = 3; /* equiv. */ structen timeval er definert i denne h-filen. En struct definerer en struktur med fields En struct kan inneholde andre structer Fields kan spesifisere spesifikke bit-størrelser. A ny-definert struct initialiseres med klammeparanteser. Alle felt som ikke blir satt, blir satt til 0 Pakking? Hvordan en struct lagres i minne er arkitektur avhengig. Rekkefølgen av feltene vil alltid bli beholdt. Noen arkitekturer vil imidlertid hoppe over( padde) deler av minnet for å holde feltene synkronsiert med “ord-grenser” . Word alignment er viktig fordi noen prosessor arkitekturer ikke støtter lesing av data som ikke starter på en grense. Lesing fra grenser er vanligvis mer effektivt. Dettte kan håndteres ved å eksplisitt padde minnet som ønskes ”tomt”, eller man kan legge til attrinbutten __((”packed”)) etter deklarasjonen For å genere kode for å håndter ualigned data. Denne koden vil gå senere, men sørge for kryss-plattform kompabilitet Fields blir aksessert med ‘.’ notasjon. En peker til en struct. Felter blir aksessert med ‘->’ notasjon, eller (*ptr).counter

30 Input fra kommandolinja
main-funksjonen tar inn to variable fra kommandolinja ved definsjon, argc er antall strenger som er blitt skrevet, mens char ** argv er en peker til streng, og kan ses på som en tabell av strenger. ”argv” inneholder alle strenger som er blitt skrevet på kommandolinja, inkludert program- navnet(argv[0]) #include < stdio.h> #include < stdlib.h> int main(int argc, char** argv) /*f.eks c:\ cmdline -a 2 -b 3.0 kjører programmet cmdline*/ /* main vil motta argc=7 argv[0]=cmdline argv[1]=-a argv[2]=2 argv[3]=-b argv[4]= */ /* Start at i = 1 to skip the cmd name.*/ for (i = 1; i < argc; i++) { if (argv[i][0] == '-') {//check next char switch (argv[i][1]) { case 'a': a_value=atoi(argv[++i]);break; case 'b': b_value=atof(argv[++i]);break; default: fprintf(stderr, "Unknown switch %s\n", argv[i]); } En vanlig konvensjon for input-parametre, er å angi en input med en ”switch” som vil si en ”-” og så et navn for opsjonen. Deretter kommer verdien som ønskes som input til programmet. Å navngi input-opsjonene er bra for at programmet skal kunne håndtere input-verdiene på en robust måte, samtidig som det er brukervennlig. Funksjonene atoi og atof er definert i stdlib.h og konverterer en streng til hhv. int og double

31 Makroer Makroer blir utført av preprosessoren i kompilatoren, som ekspanderer koden ved å sette inn de definerte makroene. Makroer er en måte å gjøre koden lettere å lese gjør koden lettere å lese, men hvis mulig: bruk heller en static inline funksjon. Makroer og static inline funksjoner må inkluders i en hver fil som bruker dem, som regel ved en header-fil. Vanlig bruk for makroer: /* Makroer brukes for å definere kontsanter */ #define FUDGE_FACTOR #define MSEC_PER_SEC #define INPUT_FILENAME “my_input_file” /* Makroer brukes for å gjøre aritmetikk med konstanter */ #define TIMER_VAL (2*MSEC_PER_SEC) /* Makroer brukes for å hente informasjon fra kompilatoren */ #define DBG(args...) \ do { \ fprintf(stderr, “%s:%s:%d: “, \ __FUNCTION__, __FILE__, __LINENO__); \ fprintf(stderr, args...); \ } while (0) /* ex. DBG(“error: %d”, errno); */ Float konstanter må ha et komma, ellers er de av typen int Sett beregningene i parentes. Hvorfor? NB! Ikke bruk x=value.. Forskjellen mellom en makro og en static inline funksjon. Begge konstruksjoner har en del felles egenskaper: Begge må inkluderes i en modul som bruker dem, de kan ikke bli linket til, og de er generelt ”inlined” i programmet. Hoved- Forskjellen er at makroer prosesseres av forprosessoren, mens static inline er en vanlig funksjon. Grunnen til at den må inkluders er at den er deklarert som static. Static inline funksjoner bli evaluert før de blir brukt. Mens en makro er ren tekst-sunstitusjon. Argumenter til static inline er type-sjekket av kompilatoren, og kan både kjøre statements og returnere en verdi,men makro må enten kjøre en statemnet eller returnere en verdi. Hvorfor er makroer alltid i parentes? Pga. presedens overraskelser..! F.eks #define VALUE int c = VALUE*3; What was meant was (2+45)*3 .. But what we got was 2+(45*3) Det er også en ide å putte paranteser rundt argumentene til en makro for å unngå lignende problem Hvorfor Do while: For å unngå overraskelser hvis makroen blir kjørt som en del av en if(): Makroer over flere linjer må deles opp med \ Sett makroer over flere linje i do{}while(0)

32 Typedef og noen ”Modifiers”
Det er mulig å definere egne typer ved å bruke typedef Nye typer kan være helt nye typer som f.eks en struct, eller de kan være en alias for en annen standard type. Det er regnet som god praksis å bruke store bokstaver for en ny type definert med typedef: f.eks: typedef char* STRING; ”Auto” og ”Static”: Ved default blir en variabel i en funksjon definert som auto. C genererer autovariable når funksjonen blir kalt, og sletter dem igjen når funksjonen har returnert. Hvis man ønsker at en variabel definert i en funksjon ikke skal bli slettet, må den defineres som static. Dette må brukes hvis f.eks funksjonen returnerer en peker til en variabel definert inne i funksjonen. Variable som blir deklarert utenfor en funksjon blir definert som static ved default. En funksjon som defineres som static betyr noe helt annet. En static funksjon kan bare kalles i den filen hvor den er definert. En variabel definert som const, betyr at den ikke kan brukes på venstre-siden i en tilordning. Å bruke const er ikke en 100% beskyttelse av variabelen, da den kan endres ved bruk av pekere.

33 Enkel Makefile Gitt et system med to c-filer og en header fil: hellomake.c, hellofunc.c og hellofunc.h #include “c:/cpath/hellofunc.h" int main() { //Kall en funksjon fra en annen fil myPrintHelloMake(); return(0); } Program som kaller en funksjon fra c-filen hellofunc. Dette systemet kan enkelt kompileres med: gcc hellomake.c hellofunc.c –o hellomake En makefil er en fil som inneholder regler for hvordan systemet skal kompileres. Et program, som heter make, leser filen med navn “makefile” (default), og utfører de kompilerings-reglene som er definert i makefilen. Det vil si at hvis du har laget en makefil, kan du kompilere ved å kjøre make! Den enkleste makefilen som kan lages for dette systemet er: Make -f filnavn kan angi en annen fil Flagget –I . Angir at kompilatoren skal lete i gjeldende katalog id[4] er 5. og siste element i tabellen id (obs!) Ennå flere regler og makroer kan brukes, se makefil3 hellomake: hellomake.c hellofunc.c gcc hellomake.c hellofunc.c –o hellomake –I . Den første linjen inneholder alle filer programmet er avhengig av. Dette bruker make til bare å kompilere de filer som er endret siden sist. NB: make krever en tab før hver linje som den skal utføre.

34 Forts. Makefile En litt mer kompilsert makefil:
CC=gcc CFLAGS=-I. hellomake: hellomake.o hellofunc.o $(CC) -o hellomake hellomake.o hellofunc.o $(CFLAGS) Nå er kompilatoren og flaggene definert som makroer (konstanter). Ved å sette kompilatoren til å være avhengig av o-filene, så vet make at den må kompilere c-filene først, og så lenke filene etterpå. Denne typen makefil er bra nok for et lite prosjekt, men en ting mangler…, avhengigheten til h-filen! CC=gcc CFLAGS=-I . DEPS = hellomake.h %.o: %.c $(DEPS) $(CC) -c -o $< $(CFLAGS) hellomake: hellomake.o hellofunc.o gcc -o hellomake hellomake.o hellofunc.o -I. Makroer i parantes. Begynner å bli komplisert, innebygde makroer i make. Runder av der, les mer på make selv, hvis interessert Makroen ($deps) inneholder all h-filene som c-filene avhenger av. Så lager vi en regel som sier at o-filene avhenger av c-filene og h-filene i $(deps), og at make skal generere o-filene som make trenger for å kompilere c-filene. –c flagget i gcc betyr kompiler men ikke link, –o betyr alle filene til venstre for :, og $< betyr den første filen i avh.-listen.

35 Dynamsk Minne-allokering
Så langt har alle eksemplene allokert variable statisk ved å definere dem i programmet. Men hva om vi ønsker å allokere variable basert på dynamiske hendelser/input mens programmet kjøres? Dette krever dynamisk allokering. Funksjonen malloc(N) allokerer minne for N bytes calloc(N,k) allokerer minne for N elementer av en størrelse k. %m: When %m is included in a printf format string, it prints a string naming the most recent error. A global variable ‘errno’ always contains the most recent error value to have occurred, and the function strerr() converts that to a string. %m is shorthand for printf(“%s”, strerr(errno)) Emstar tips: Emstar includes glib which includes various useful functions and macros. One of these is g_new0(). g_new0(typename, count)  (typename *)calloc(count, sizeof(typename)); Emstar also provides elog() and elog_raw(), logging functions that integrate with emstar’s logging facilities. elog() works like printf, but takes one additional loglevel argument. You do not need to include a ‘\n’ in elog() messages as it is implied. elog_raw() takes a buffer and a length and dumps the bytes out in a prettyprinted fashion. Emstar includes the useful “buf_t” library that manages automatically growing buffers. This is often the easiest way to allocate memory. NB! Minne som allokeres dynamisk må senere frigjøres med funksjonen free()

36 Fallgruver med dynamisk minne
Ved allokering av dynamisk minne, må man planlegge hvordan minnet senere skal bli frigjort. Allokert minne som man mister “kontakt” med kalles “memory leak”, og betyr at programmet “spiser” minne. Garbage collection Det er lett å glemme en peker til dynamisk minne som har blitt frigjort. Når man frigjør dynamisk minne, er det sikrest å slette alle pekere til det. Fordi dynamiske minne alltid bruker pekere, er det ingen måte for kompilatoren å sjekke at bruken av minnet er riktig, dette betyr at feil som kan detekteres med statisk allokering, ikke kan oppdages med dynamisk allokering. Hint: unngå dynamisk allokering hvis mulig, bruk heller statiske variable og arrays Reference counting is one approach to tracking allocated memory. However, there is no perfect scheme. The main problem with reference counting is that correctly handling cyclically linked structures is very difficult, and likely to result in mis-counting in one direction or the other. Garbage collection is another solution; this is used by Java and other languages. However, this often causes performance problems and requires a very strict control over typing in the language, which C can’t easily provide by its nature of having direct access to the low levels. Emstar uses a static reference model, where there is exactly one canonical reference to each object, and that reference will be automatically cleared when the object is destroyed. The only requirements are: the reference must be in stable allocation (static or dynamic, but you can’t move the memory) and you must always access the object via that single reference. We have found this scheme to be quite workable, as long as you are cognizant of these requirements.


Laste ned ppt "Krasjkurs i C (pluss litt matlab)"

Liknende presentasjoner


Annonser fra Google