int main(void)

DTEK2003
Järjestelmäohjelmointi C++kielellä 5 op
DTEK2003 Perustiedot 2011
• Luennoitsija: TkT Jerker Björkqvist
• Åbo Akdemi, ICT Talo, huone B4052
• [email protected]
• Luennot, 24.10.2011 – 16.12.2011
• Keskiviikkoisin 12-14, Lambda
• Perjantaisin 10-12, Alpha
• https://xprog28.cs.abo.fi/ro.nsf/W/
cprogf
• Tentit;
• 19.12.2011,
• 30.1.2012
• 5.3.2012
• Ilmoittautuminen: nettiopsu
DTEK2003 Perustiedot 2011
• Harjoitukset, huone A2065, 3.11.2011 – 9.12.2011
• Torstaisin 8-12
• Perjantaisin 12-14
• 6 kertaa
• 4 x setti harjoituksia
• ville.utu.fi
• 2 x kertaa laajempi tehtävä
• moodle.utu.fi (tarvittava koodi: c++s11)
• Tenttioikeus:
• Ville-tehtävät minimi 60% suoritettu
• Ensimmäinen laajempi teht. suoritettu (ja hyväksytty)
• Arviointi / hyväksyminen
• Kirjallinen tentti
• (ville-tehtävät suoritettu min 60%)
• Molemmat laajemmat tehtävät suoritettu
• Kaikki harjoitusasiat hoitaa: Peter Larsson ([email protected])
Kirjat / resurssit
•
•
C:
– Kernighan&Ritchie: The C programming language
C++:
– Frank Brokken: C++ Annotations
• http://www.icce.rug.nl/documents/cp
lusplus/
– Stephen Prata: C++ Primer Plus
• www.cplusplus.com
•
C++ ympäristö (kirjastot + GUI)
– Qt (Cross-Platform GUI toolkit)
• qt.nokia.com
• Cross-platform
• Useampi ohjelmointikieli
• wxWidget (aikaisemmin)
• www.wxwidgets.org
Miksi opetella C/C++ ?
Source: http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html
Source: http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html
C - käyttöalueet
• Järjestelmäohjelmointi
– Käyttöjärjestelmät:
• Ydin
• Ajurit
– Tietokantamoottrit
– Middleware eri laajuudessa (myös C++)
• Sulautetut järjestelmät
– DSP
– Muut pienet tietokonejärjestelmät
• Yleensä missä
– Suorituskyky on tärkeämpi kun tuottavuus
– Halutaan kontrolloida tarkasti lopputulosta
Joitakin järjestelmiä C-kielellä
Käyttöjärjestelmien ytimet:
Linux, Windows 7, MacOS, RTOS (uC/OS-II),
RTEMS
Skriptikielien tulkit
Python, Perl, PHP, Ruby,...
Sulautetut järjestelmät
ECU:s (Elektroniset ohjausjärjestelmät)
DSP (Digitaaliset signaaliprosessorit)
Mikrokontrollerit
.... Pitkä lista...
C - Lyhyt historia
• UNIX:n ensimmäiset versiot kirjoitettiin assemblerilla
– Liian työlästä
• Tyypitön kieli BCPL (Richards) 1960-luvun
keskivaiheilla
• Ken Thompson kehitti BCPL kielen perusteella
järjestelmäohjemointia varten B-kielen
• Dennis Ritchie kehitti B, nimeksi tuli C, 1970-luvun
alussa  tietotyyppien lisääminen
• 1978: Brian Kernighan, Dennis Ritchie kirja: K&R C:
De facto standardi
• 1983: ANSI aloitti C-kielen standardointia, 1989
syntyi ANSI C89
• 1999: Uusi versio C99
• 2011: Draft C1X standardi (ei vielä valmis)
http://www.ixonos.com/easydata/customers/ixonos/files/OuluLinuxPresentations/Linux_seminar_stroustrup.pdf
C-kielen etuja
• Suora muistinkäyttö helppoa – myös muiden
laitekomponenttien käyttö helppa
• Käyttöjärjstelmät / peruskirjastot C-kielellä,
suoraviivaista kutsua C-ohjelmasta
• C-kirjastojen laajuus
• On olemassa monta hyviä C-kääntäjiä, jotka generoi
tehokasta koodia
• On saatavilla melkein kaikille alustoille
(mikrokontrollerista superkoneeseen)
• On luultavasti käytettävissä monta vuotta eteenpäin
• Monet muut kielet perustuvat C-kieleen (tai
syntaksiin); C++, Java, C#, Objective-C, Javascript,
PHP,...
C kielen huonoja puolia
• Liian helppoa tehdä katastrofaalisia
ohjelmointivirheitä
• C-ohjelmien virheenetsintä on usein vaikeaa
• Matalan tason kieli: vaikeaa hallita suuria
ohjelmakokonaisuuksia
• Houkutus oikoteille (hacks) on suuri, joka
johtaa vaikeuksiin myöhemmin
Asiaan - HelloWorld C-kielellä
/* helloword.c */
#include <stdio.h>
int main(void) {
printf("Hello, world!\n");
return 0;
}
Kääntäminen ja ajo
% gcc helloworld.c –o helloworld
% ./helloworld
C-kielen järjestelmäläheisyys
• Helloworld.c ajo:
main() {
printf(”Hello World\n”);
}
– 25 järjestelmäkutsua (kutsuja ytimeen), strace -käyttäen
• Vastaava HelloWorld.java ajo
public class HelloWorld {
public static void main(String args[])
{
System.out.println("Hello
World!");
}
}
– 2248 järjestelmäkutsua !!
C-kielen järjestelmäläheisyys (2)
$> strace ./helloWorld
execve("./helloWorld", ["./a.out"], [/* 24 vars */]) = 0
uname({sys="Linux", node="borken", ...}) = 0
brk(0)
= 0x804962c
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =
0x40016000
open("/etc/ld.so.preload", O_RDONLY)
= -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)
= 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=82445, ...}) = 0
old_mmap(NULL, 82445, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40017000
close(3)
= 0
open("/lib/libc.so.6", O_RDONLY)
= 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0 \304\1"..., 1024) = 1024
fstat64(3, {st_mode=S_IFREG|0755, st_size=5735106, ...}) = 0
old_mmap(NULL, 1267176, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x4002c000
mprotect(0x40158000, 38376, PROT_NONE) = 0
old_mmap(0x40158000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x12b000) =
0x40158000
old_mmap(0x4015e000, 13800, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS,
-1, 0) = 0x4015e000
close(3)
= 0
munmap(0x40017000, 82445)
= 0
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =
0x40017000
write(1, "Hello world\n", 12Hello world
)
= 12
munmap(0x40017000, 4096)
= 0
_exit(0)
= ?
C perusteet
• Rakenteeltaan Pascal tai Fortrantyyppinen (proceduraalinen kieli)
• Muuttujia käytetään datan
varastointiin
• Ohjelmarakenne luodaan
käyttämällä ja kutsumalla funktioita
• Ohjelman ajo kontrolloidaan
käyttämäälla silmukoita,
ehtolauseita (if) ja kutsumalla
funktioita
• Pointterien (epäsuora osoitus)
käyttö keskeistä (mutta tämä
myöhemmin)
/* test.c */
#include <stdio.h>
void test(int a);
int main(void) {
int a=23;
int *b = &a;
test(a);
}
void test(int a) {
if (a>54) {
printf(“parameter for test() is
larger than 54\n”);
}
}
C – kieli - tiedostot
• Lähdetiedostot (.c)
– Sisältää funktioiden implementaatiot
– Ohjelma jaetaan mieluiten useampaan
lähdetiedostoon
• Header-tiedosto (.h)
– Funktioiden prototyypit, datatyyppien määrittelyt
– Prototyypit ja määrittelyt käytetään (sisällettytään
lähdekoodiin) käyttämällä #include-lauseella
lähdekoodissa
Prototyyppi: Funktioiden deklaraatiot,
mukaanlukien parametrit ja niiden tyypit
int myfunc(float a, int b);
C-ohjelman perusrakenne
•
•
•
•
Ohjeet esikääntäjälle :#include <stdio.h>
Muuttujien deklaraatiot: int a;
Funktioprototyypit: int main(void);
Funktioiden määritelmät (implementaatio)
int main(void) {
....
}
• C on case-sensitive
• Muuttujia pitää deklaroide ennen kuin niitä voi käyttää
• Funktioita voi kutsua ilman että vastaava prototyyppi
on käytettävissä (muttei suositella)
• Kommentit: /* ... */ (ANSI C99 myös //)
Tietotyypit - kokonaisluvut
• char – min 1 tavu
• short – min 2 tavu
• int – min 2 tavua, yleensä 4 tavua
• Mutta 64-bittinen arkitehtuuri: 8 tavua
•
•
•
•
long – min 4 tavua
long long – min 8 tavua
Tyyppien numeroavaruudet: limits.h
Vakiot tekstissä
– 2, 6L, 88LL, 0x20, 0775
C-koodisaa voidaan tarkentaa mikä datatyyppi
halutaan että C-kääntäjä pitäisi muodstaa (eli ASCIItekstin jäsentelyn yhteydessä)
Esim: L – long, LL - long long, 0x – hexadecimal ....
Tietotyypit - liukuluvut
• float – yleensä 4 tavua  1037, 6 numeron
tarkkuus
• double – min kuten float
• long double – min kuten double
• Vakiot tekstissä
– 3.14, 0.5e-7, 12.4f, 145.67L
• Numeroavaruudet float.h
Datatyypit - boolean
• C-kielessä ei ole erillisiä totuusarvomuuttujia
(mutta ANSI C99 toisaalta on)
• Kokonausluvut toimivat totuusarvoina  0 on
false, muut true
a=99;
if (a) do_something();
Taulukot
• Taulukko: yksi ulottuvuus
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int lotto[7]; /* 7 numeroa per rivi*/
for (i=0; i<7; i++)
lotto[i] = rand() % 37;
return 0;
}
• Useampi ulottuvuus
#include <stdio.h>
int lottolomake[10][7];
lottolomake[2][5] = rand() % 37;
Taulukot
• Taulukko pitää alustaa ennen käyttöä
#include <stdio.h>
#include <stdlib.h>
int main() {
int lotto[7];
printf(“%d\n”, lotto[5]); /* undefined */
printf(“%d\n”, lotto[33]); /* typical buffer
overrun, varying result: Undefined /
segmentation fault / general protection fault
*/
}
HUOM: Kääntäjän näkökulmasta kaikki OK!
Merkkijonot
• C-kielessä ei ole erityistä merkkijonotyyppiä.
Merkkijono on vaan tavallinen taulukko, jossa
merkkijonon päättää ’0’-merkki
char
a[0]
a[1]
a[2]
a[3]
a[] = "abc";
= ’a’
= ’b’
= ’c’
= ’\0’
• Merkkijonoja tuetaan LIB-C kirjastossa, josta
löytyy useampi funktio. header-tiedostossa
string.h löytyy funktioiden prototyypit
Tyyppimuunnokset
#include <stdio.h>
int main(void)
{
int i,j = 12;
/* only j is initialized*/
float f1,f2 = 1.2;
i = (int) f2;
f1 = i;
/* explicit: i <- 1, 0.2 lost */
/* implicit: f1 <- 1.0 */
f1 = f2 + (float) j; /* explicit: f1 <- 1.2 + 12.0 */
f1 = f2 + j;
/* implicit: f1 <- 1.2 + 12.0 */
}
Tyyppimuunnokset
• Yllätykset / väärin ajateltu??
int a, b=1;
a = 2 * (b / 2);
/* a has now value 0 */
MUTTA:
a = 2 * (b / 2.0); /* a has now value 1 */
---------------int sum=7, count=3;
float average;
average = sum/count; /* gives average 2.0 */
MUTTA:
average = (float)sum/count; /* gives average 2.3333 */
Operaattorit
• Aritmeettiset
• int i = i+1; i++; i--; i *= 2;
• +, -, *, /, %
• Relaatiot, logiikka
• <, >, <=, >=, ==, !=
• &&, ||, &, |, !
• Bittioperaatiot
• & - and, | - or, ~ - complement
• <<,>> - left-shift, right-shift
• ^ - xor
• HUOM
• = arvon asetus
• == vertailu
Rakenteet
• if (expr) statement
– Jos expr arvo on eri kun 0 niin statement
suoritetaan
• if (expr) statement else
statement2
– Jos expr arvo on 0 niin statement2
suoritetaan
statement on joko yksinkertainen: a=2;, tai
yhdistetty: {a=2; b=1;}
if - esimerkki
/*
if.c
*/
#include <stdio.h>
int main(void)
{
int a = 2;
int b = 3;
if(a > b) printf("a on suurempi kuin b!\n");
else printf("a ei ole suurempi kuin b!\n");
return 0;
}
while
• while (expr) statement
– statement suoritetaan niin kauan kun expr arvo
on eri kuin 0
• do statement while (expr)
– Eri versio samasta rakenteesta
for
• for (pre_expr; loop_expr;
post_expr) statement
• Silmukkarakenne
– Ensin suoritetaan pre_expr
– Niin kauan kun loop_expr on eri kuin 0,
suoritetaan ensin statement ja sitten
post_expr
• Tehokas rakenne
• Melkein kaiken tyyppiset silmukat voidaan tehdä
for avulla
for - esimerkki
int a,i,n=5;
a=0;
for (i=0; i<n; i++) a += i*i;
Vastaa
int a,i,n=5;
for (a=0,i=0; i<n; a += i*i++) ;
Switch - rakenne
switch (expr) {
case constant-expression :
statement
break
case constant-expression:
statements
break;
default:
}
Funktiot
• Ohjelma jaetaan pienempiin osiin funktioiden
avulla
– Modulaarisuus – edesauttaa
• Koodausta, debuggausta
– Koodin uusiokäyttö
• Funktion deklaraatio – prototyyppi
• Funktion määrittely – prototyyppi + koodi =
implementaatio
Funktiomäärittelyt
•
•
•
•
type name(parameter_list){
declarations
statements
}
Jos funktion ei palauta arvoa, tyypiksi
annetaan void
Parametrilista koostu tyypistä +
muuttujanimestä (int a, float b)
Funktiomäärittelyssä a ja b on formaaleja
parametreja
Funktiokutsu f(x,4) yhteydssä x ja 4 ovat
todelliset parametri, eli argumentitent
Funktiokutsu
• Ennen kuin funktio kutsutaan koodissa,
suositellaan että funktion prototyyppi on
tiedossa (eli kääntäjä tuntee prototyyppiä)
• Paluuarvon tyyppi oikea
• Parametrit voidaan validoida
• Yleensä käytetään header-tiedosto, eli
#include <stdio.h>
• Funktiokutsu = funktion nimi + parametrit
suluissa pow(2,4)
• Funktiokutsut tapahtuu aina call-by-value
• Tästä voidaan olla eri mieltä…
Funktiota - esimerkkejä
#include <stdio.h>
int sum(int a, int b);
/* function prototype at start of file */
void main(void){
int total = sum(4,5); /* call to the function
*/
printf(“The sum of 4 and 5 is %d”, total);
}
int sum(int a, int b){
/* the function itself
- arguments passed by value*/
return (a+b);
/* return by value */
}
main() -funktio
• main() – funktiosta normaalin ohjelman
suoritus alkaa
• Tarvittava startup-koodi linkataan
känntämis/linkkausvaihessa niin, että tietyn
alustuksen jälkeen kutsutaan main()-funktiota
• Argumentit: main (int argc, char *argv[])
• argc –montako parametrejä on annettu
ohjelman käynnistäessä
• argv[] - pointteri argumenttilistaan
• argv[0] – ohjelmanimi
• argv[1] – ensimmäinen argumentti
Muuttjat - näkyvyysalue
• Oletus C-kielessä on että muuttujien
näkyvyys on rajoittunut tiedostoon
• Funktion ulkopuolella deklaroitu muuttuja on
globaali käännöslohkon sisällä
– Funktion sisällä deklaroitu muuttuja on lokaali
funktion sisällä
– Rakenteessa (for, while, if) deklaroitu
muuttuja on lokaali rakenteen sisällä
– Muuttuja joka deklaroidaan funktion sisällä on
lokaali funktion sisällä
Näkyvyysalue - esimerkki
#include <stdio.h>
int gCommonPar; /* Globaali muuttuja */
void main(void){
int i=0; /* Lokaali funktion sisällä */
for (i=0; i<5;i++) {
int j; /* Lokaali rakenteen sisällä */
j=i*5+gCommonPar;
printf(“Laskutoimituksen tulos: %i\n”, j);
}
j=3; /* Käännösvirhe, muuttuja j ei tässä
rakenteessa */
}
Muuttuja – elinikä/tallennus
• static, auto määrittää muuttujien elinikä
(ja tallennus)
– static – muuttuja sijoittuu heap:iin, saa oman
paikkansa muistiavaruudessa, ja on olemassa
ohjelman suorituksen alusta loppuun
– auto – muuttuja sijoitetaan pinoon (stack)
muistipaikka on käytössä vaan niin kauan kuin
ollaan kyseisen funktion sisällä
– auto on lokaalien muuttujien oletus – static on
globaalien muuttujien oletus
Elinikä / esimerkki
#include <stdio.h>
int gimmenext() {
static int next=0; /* Heap */
return ++next;
}
int gimmenext2() {
int next=0; /* Auto(stack) */
return ++next;
}
int main() {
printf("%i\n",gimmenext());
printf("%i\n",gimmenext());
printf("%i\n",gimmenext2());
printf("%i\n",gimmenext2());
}
% ./gimmenext
1
2
1
1
Muut tallenusluokat
• register – ohje kääntäjälle sijoittaa
muuttujan, jos vaan mahdollista, processorin
rekisteriin
• volatile – ohje kääntäjälle jättää
optimoinnit liittyen tähän muuttujaan
tekemättä
– esim muuttujia jotka suoraan osoittavat rautaan
• extern – muuttuja on saanut säilytystilaa
toisen kännöslohkon kautta, eli ohje
kääntäjälle käyttää tätä muualla deklroitua
muuttujaa
• const – muuttuja on määritelty vakioksi, ei
kääntäjä ei salli muutoksia muuttujan arvoon
Esimerkki - extern
/* fil_a.c */
int g_CommonParameter = 5;
/* fil_b.c */
#include <stdio.h>
extern int g_CommonParameter;
int main() {
printf(“Parameter=%i\n”, g_CommonParameter);
}
% gcc fil_a.c fil_b.c –o prog
% ./prog
Parameter=5
Struktuurit - tietueet
• C:ssä voidaan implementoidaan tietueita
#include <string.h>
#define MAX_STRING 30
struct t_person {
char name[MAX_STRING];
int age;
int shoesize;
};
int main() {
struct t_person Person; /* Tietue pinossa */
Person.age = 23; /* Tietueen kenttä
“.”-merkin avulla*/
strcpy(Person.name, “kalle”);
}
Tietueet tietueen sisällä
#define MAX_PARTICIPANTS 100
#define MAX_STRING 30
struct t_course {
char name[MAX_STRING];
int nPersons;
struct t_person Persons[MAX_PARTICIPANTS];
};
main() {
struct t_course Course;
strcpy(Course.name, “Programming C++”);
Course.nPersons = 1;
Course.Persons[0].age = 77;
strcpy(Course.Persons[0].name, “Axel Eklund”);
}
Datatyypin synonyymi
typedef unsigned short WORD;
WORD wIndex; /* sama kuin ”unsgined short wIndex” */
typedef struct t_person Person;
Person Jag;
/* struct t_person Jag; */
typedef struct t_point {
WORD x;
WORD y;
} Point;
Point point;
point.x = 12; point.y=44;
Esimerkki
#include <stdio.h>
int main(void)
{
int nstudents = 0; /* Initialisering */
printf(“How many students does ÅA have ?:”);
scanf (“%d”, &nstudents); /* Läs input */
printf(“ÅA has %d students.\n”, nstudents);
return 0;
}
$How many students does ÅA have ?: 5000 (enter)
ÅA has 5000 students.
$
Muuttujat muistissa / osoitteet
Muuttjalla on aina vastaavuus
Fyysisessa muistissa
Datatyyppi / tietty arvo
 Tietty bittisekvenns muistissa
int x = 5, y = -10;
float f = 12.5, g = 9.8;
char c = „c‟, d = „d‟;
Muuttujan arvo (datatyypin bittirepresentaatio)
5
-10
0x00000005
4300
12.5
0xFFFFFFF6
4304
9. 8
0x41480000
4308
Binäärinen muistisekvenssi
Osoite muistiavaruudessa
c
0x411CCCCD
4312
d
0x63 0x64
4316 4317
Osoittimet
• Pointteri/osoitin= muuttja, jonka arvo osoittaa tiettyyn
paikkaan muistissa
float f;
float *f_addr;
f
/* datamuuttuja */
/* osoitinmuuttuja */
f_addr
any float
?
?
?
4300
4304
any address
f_addr = &f; /* & = osoiteoperaattori */
f_addr
f
?
4300
4300
4304
Osoittimet / esimerkki
#include <stdio.h>
void main(void) {
int j;
/* j = <undefined> */
int *ptr;
/* ptr = <undefined> */
ptr=&j;
/* alusta ennen käyttöä */
/* *ptr=4 ei alusta osoitinta */
*ptr=4;
/* j <- 4 */
j=*ptr;
}
/* j <- ??? */
Osoittimet 2
*f_addr = 3.2;
f
/* epäsuora osoitus*/
f_addr
3.2
4300
4300
4304
float g=*f_addr; /* indirektion:g on nyt 3.2 */
f = 1.3;
f
f_addr
1.3
4300
4300
4304
Osoittimet – datatyypin vaihto
#include <stdio.h>
int main(void) {
float f = 3.141592;
void *ptr; /* yleinen osoitin, ei tyyppiä */
float *f_ptr;
int *i_ptr;
ptr = (void *)&f;
f_ptr = &f;
i_ptr = (int *)&f
3.141592
0x40490FD8
printf(“Val: %f, adress: %p, : int %i\n”,
*f_ptr, ptr, *i_ptr);
}
% Val: 3.141592, adress: 0xbf82e238, int: 1078530008
sizeof()-operaattori
• sizeof(x) – anta tietotyypin x koko (bytes)
• esim (oletetaan 32 bittinien arkkitehtuuri)
•
•
•
•
sizeof(int) = 4
sizeof(float) = 4
sizeof(int *) = 4
sizeof(Person) = 40
char[30] + int + int + alignment
• sizeof(char a[20]) = 20
Taulukot uudestaan
int main(void) {
int lotto[7];
lotto[3] = 17;
}
?
?
?
17
?
?
?
4300
muuttuja lotto on käytännössä osoitin, ja
osoittaa taulukon ensimmäiseen elementtiin
lotto[3] voidaan laskea myös
*(lotto + 3 * sizeof(int))
Taulukon osoittimet
Kompilaattori tekee seuraavat laskutoimitukset
&(lotto[3]) = lotto + sizeof(integer)*3
Taulukko voidaan myös accessoida seuraavasti:
*(lotto+3) = 17 /* vastaa lotto[3]=17 */
Mutta:
*lotto+3 = <int>
Taulukkoa voidaan myös käyttää normaalin osoittimen kautta:
int *lotto_ptr = lotto;
lotto_ptr[3] = 17;
Taulukot lisää
Mikä seuraavien ero?
int lotto[7];
JA
int *lotto_ptr;
lotto : Vakio osoitin taulukkoon, samalla on
tilaa data varteen varattu. Osoittimien arvo ai
voit muuttaa.
lotto_ptr: Muuttuja, jonka arvo voidaan muuttaa.
Lisää osoittimia
int month[12]; /* month is a pointer to base address 430*/
int *ptr;
month[3] = 7; /* month address + 3 * int elements
=> int at address (430+3*4) is now 7 */
ptr = month + 2; /* ptr points to month[2],
=> ptr is now (430+2 * int elements)= 438 */
ptr[5] = 12;
/* ptr address + 5 int elements
=> int at address (438+5*4) is now 12.
Thus, month[7] is now 12 */
ptr++;
/* ptr <- 438 + 1 * size of int = 442 */
(ptr + 4)[2] = 12;
/* accessing ptr[6] i.e., month[9] */
• Nyt month[6], *(month+6), (month+4)[2],
ptr[3], *(ptr+3) om itse asiassa sama muuttuja!!
Tietueosoittimet
typedef struct t_person {
char name[30];
int age;
int shoesize;
} Person;
typedef Person * pPerson;
int main() {
Person pers;
pPerson ptr_pers = &pers;
}
name
(30)
age
(4)
Padding till hela 4 byte
shoesize
(4)
By-value / by-reference
Sanottiin aikaisemmin että KAIKKI parametrit Ckielessä ovat by-value
Miten toimii esim. taulukot?
char str1[30] = “Hei”, str2[30];
strcpy(str2, str1);
Arvo mikä siirretään funktioon on
osoitinmuuttujan arvo, eli osoite missä
merkkitaulukko sijaitsee muistissa. Eli osoitin
siirretään ”by-value” (str1, str2 on
(vakio)osoittimia), tämä voitaisiin kutsua myös
by-reference
By-value / by-reference
C-kielessä ohjelmoijalla on aina kontrolli siihen
muistiin mitä olet allokoinut:
 Kutsutut funktiot eivät voi muuttaa
allokoimasi muistia ilman ”lupaasi” (eli jotta
funktio voisi muttaa toisen funktion sisäisiä
muuttujia, se pitää saada käyttöönsä osoitin
tähän muuttujaan (by reference))
By value / by reference
int inc(int a) { /* tämä funktio ei voi muuttuu kutsujan
käytössä oleva muuttujaa */
return a+1;
}
void inc_ref(int *a) { /* tämä funktio on saanut
käyttöönsä osoittimen alkuperäiseen muuttujaan, eli se
voi muuttaa alkuperäistä muuttujaa
*/
*a++;
}
int main() {
int b=0;
b = inc(b);
inc_ref(&b);
}
/* by value: muuttujan arvo */
/* by ”value”: osoitin muuttujaan */
By value / by reference
#include <stdio.h>
void swap(int *, int *);
main() {
int num1 = 5, num2 = 10;
swap(&num1, &num2);
printf(“num1 = %d and num2 = %d\n”, num1, num2);
}
void swap(int *n1, int *n2) { /* passed and returned by
reference */
int temp;
temp = *n1;
*n1 = *n2;
*n2 = temp;
}
Taulukot parametreina
void init_array(int array[], int size) ;
int main(void) {
int list[5];
}
init_array(list, 5);
for (i = 0; i < 5; i++)
printf(“next:%d”, array[i]);
void init_array(int array[], int size) { /* miksi koko
annetaan ? */
/* taulukot siirretään ”by-reference”- eli
osoitin taulukon ensimmäiseen arvoon */
int i;
for (i = 0; i < size; i++)
array[i] = 0;
}
Funktio-osoittimet
Osoitinmuuttuja voi myös osoittaa funktioon, eli
funktio-osoitin
int
int
int
int
func(); /*function returning integer*/
*func(); /*function returning pointer to integer*/
(*func)(); /*pointer to function returning integer*/
*(*func)(); /*pointer to func returning ptr to int*/
• Fördel ? mera flexibilitet
Funktioosoitin - esimerkki
#include <stdio.h>
void myproc (int d);
void mycaller(void (* f)(int), int param);
void main(void) {
myproc(10);
/* call myproc with parameter 10*/
mycaller(myproc, 10); /* and do the same again ! */
}
void mycaller(void (* f)(int), int param){
(*f)(param);
/* call function *f with param */
}
void myproc (int d){
. . .
/* do something with d */
printf(“Value: %d\n”, d);
}
Mihin funktio-osoittimet käytetään?
Esimerkki 1:
Funktion rekisteröinti toisessa ohjelmassa, mikä voi
suorittaa kuva-käsittelyä (tyypillisesti plug-in)
Esimerkki 2:
Funktion rekisteröinti, mikä reagoi signaaleihin,
signaalikäsittelijän käyttöönotto
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
Vielä laajemmin
Tehtävä
Luo taulukko jossa N osoitinta funktioihin, jotka palauttavat osoittimet
funktioihin jotka palauttavat osoittimet merkkeihin
Version 1. char *(*(*a[N])())();
Version 2. Build the declaration up in stages, using
typedefs:
typedef char *pc; /* pointer to char */
typedef pc fpc(); /* function returning pointer to char
*/
typedef fpc *pfpc; /* pointer to above */
typedef pfpc fpfpc(); /* function returning... */
typedef fpfpc *pfpfpc; /* pointer to... */
pfpfpc a[N]; /* array of... */