<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4818234838483192587</id><updated>2012-01-25T10:16:33.432+02:00</updated><category term='linkit'/><category term='packed array'/><category term='pointterit'/><category term='calloc()'/><category term='osoittimet'/><category term='VLAN'/><category term='C'/><category term='bitset'/><category term='mutexit'/><category term='jump table'/><category term='tietotyypit'/><category term='ANSI C'/><category term='atomiset operaatiot'/><category term='segmentation fault'/><category term='watchpoint'/><category term='muistivuoto'/><category term='olio-ohjelmointi'/><category term='realloc()'/><category term='taulukko'/><category term='C++'/><category term='ohjelmointi tekniikka'/><category term='säikeet'/><category term='pthread'/><category term='valgrind'/><category term='invalid read'/><category term='monisäikeiset ohjelmat'/><category term='vlan interface'/><category term='funktio-osoittimet'/><category term='winsock'/><category term='reitit'/><category term='Allocointi'/><category term='malloc()'/><category term='muistin ylikirjoitus'/><category term='enum'/><category term='bittivektori'/><category term='dynaaminen muistinvaraus'/><category term='select()'/><category term='osoitteet'/><category term='gdb'/><category term='php'/><category term='sockets'/><category term='Maz'/><category term='static'/><category term='invalid write'/><category term='trim()'/><category term='tyyppimäärittelyt'/><category term='explode()'/><category term='debuggeri'/><category term='MazBotV4'/><category term='pakattu taulukko'/><category term='RTM_NEWLINK'/><category term='thread safe'/><category term='Muisti'/><category term='netlink'/><category term='semaforit'/><category term='callback function'/><category term='reentrant'/><category term='typedef'/><category term='otus C'/><title type='text'>C - ohjelmoijan ajatuksia.</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://c-ohjelmoijanajatuksia.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://c-ohjelmoijanajatuksia.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Maz</name><uri>http://www.blogger.com/profile/03774313944609044806</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>20</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4818234838483192587.post-6823549415350991480</id><published>2011-09-16T16:33:00.001+03:00</published><updated>2011-09-16T16:35:22.305+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='vlan interface'/><category scheme='http://www.blogger.com/atom/ns#' term='linkit'/><category scheme='http://www.blogger.com/atom/ns#' term='C'/><category scheme='http://www.blogger.com/atom/ns#' term='reitit'/><category scheme='http://www.blogger.com/atom/ns#' term='osoitteet'/><category scheme='http://www.blogger.com/atom/ns#' term='netlink'/><category scheme='http://www.blogger.com/atom/ns#' term='VLAN'/><category scheme='http://www.blogger.com/atom/ns#' term='RTM_NEWLINK'/><title type='text'>Netlink soketit</title><content type='html'>Netlink soketti rajapinta.&lt;br /&gt;&lt;br /&gt;Viime keväänä sain tehtäväkseni laajentaa linux:n päälle tehdyn ohjelmistoalustamme IPv4 konfigurointituen koskemaan myös IPv6:tta. Kyseessä on siis reittien, aliasosotteiden, VLAN interfacejen jne konfigurointi. IPv4 osuus oli tehty pääosin linuxin ioctl rajapintaa käyttäen, mutta IPv6 tukea pohtiessani totesin tämän olevan riittämätön. &lt;br /&gt;&lt;br /&gt;Pienen tutkinnan ja hutkinnan jälkeen, päädyin netlink soketti rajapinnan käyttämiseen. Man sivut ja useat muut lähteet kehoittivat käyttämään matalan tason netlink API:n sijasta libnl kirjastoa. Kääntelin libnl kirjaston omalle alustalleni, ja aloin tutkimaan miten se toimii. Jostain syystä libnl:n dokumentaatio ei kuitenkaan millään avautunut minulle. Aikani tuskailtuani löin hanskat tiskiin ja totesin että minun on keksittävä jokin toinen keino. Niinpä palasin takaisin raakaan netlink rajapintaan.&lt;br /&gt;&lt;br /&gt;Netlink rajapinnalle löysin varsin kattavanoloisen dokumentaation man sivuista, sekä jopa RFC:n joka kuvasi netlink rajapinnan routtaus protokollan. Ei siis muuta kun hihat rullalle ja hommiin... Itse asiassa, perusjutut oli varsin suoraviivaisia, ja moni asia onnistui varsin kivuttomasti, jo pelkän pienen googlailun ja man sivujen tutkinnan perusteella. Lopulta kuitenkin törmäsin aukkoihin dokumentaatiossa, jotka lopulta pakottivat minut kääntämään oman debuggiprinteillä varustetun kernelin... Näitä aukkoja on tarkoitus paikata tässä blogitekstissä. Samalla luultavasti lisään oman pienen palaseni muuten rikkaaseen netlink sokettien perusdokumentaatioon - jota tosin voi olla vaikeaa löytää suomeksi??&lt;br /&gt;&lt;br /&gt;Netlink socketit ovat itse asiassa enemmän kuin vain IP konfigurointien rajapinta. Ne ovat varsin geneerinen keino vaihtaa dataa user ja kernelspacen välillä. Netlink sockettien avulla voi myös mm. säätää palomuureja ja "neighbour cachea". Tässä blogissa keskityn kuitenkin vain kolmeen netlink sockettien viestityyppiin/konfiguroitavaan ominaisuuteen.&lt;br /&gt;&lt;br /&gt;1. Linkkeihin (kuten eth0, vlan linkit kuten eth0.20 jne), jotka ovat ikäänkuin ovia systeemin ulkopuolelle / ulkopuolelta.&lt;br /&gt;2. Osoitteisiin (kuten 192.168.255.2/24 tai fe80::211:43ff:fe26:2b6c/64), jotka ovat ikäänkuin lappuja ovissa, kertoen systeemin ulkopuolelle kuka oven takana on, ja systeemin sisäpuolelle, mihin ulkopuolelta pääsee.&lt;br /&gt;3. Reitteihin, (kuten 0.0.0.0 via dev eth0, tai 10.34.143.0 255.255.255.0 gw 192.168.1.55 metric 2), jotka voivat osoitteiden lisäksi antaa erityisiä ohjeita siitä, mistä ovesta mihinkin kohteeseen matkalla oleva paketti täytyy potkaista menemään.&lt;br /&gt;&lt;br /&gt;Nyt minulle tuli pakoittava tarve kertoa hiukan tarkemmin reiteistä. Saattaa olla, että Vesa ei ihan ymmärtänyt mitä tuo reitti tarkoittaa ;) Jos reitit on tuttua kauraa, tai eivät kiinnosta, niin ole hyvä ja skippaa neljä seuraavaa kappaletta.&lt;br /&gt;&lt;br /&gt;Reittien ideana on sanoa, että "tällaiseen osoitteeseen matkalla oleva paketti täytyy tuupata tuosta interfacesta ulos". Jos palaamme stackissa muutaman rivin taaksepäin, huomaamme kohdan jossa kerroin osoitteen olevan ikäänkuin lappu siitä, mitä jonkin linkin (interfacen) takana on. Ts. jos eth0 linkkimme osoite on 192.168.255.2/24, tämä kertoo meille että oven (linkin) eth0 takana on "verkko" (osoitteet) 192.168.255.XXX. Miksi? Koska osoitteen loppuosa /24 kertoo ns netmaskin. Se siis kertoo että oven takana oleva verkko, johon siis meidänkin ovemme kuuluu, koostuu osoitteista joissa 24 ensimmäistä bittiä on kiinteitä, loppujen 8 vaihtuessa. Ts, 192.168.255 on verkkomme vakio-osa. Jokainen pisteiden välissä oleva numerosarja kun voi IPv4 osoitteessa vaihdella 0-255 välillä - Ts. muodostuu kahdeksasta bitistä. 4 tällaista osaa tarkoittaa 4*8 = 32 bittiä, joka siis on koko osoite. Esimerkissämme 24 ensimmäistä bittiä on kiinteitä, ts. kolme ensimmäistä numerosarjaa on kiinteitä. Jos maski olisi /23, tarkoittasi se että 23 bittiä olisi kiinteitä. Tällöin osoitteet verkossa voisivat olla 192.168.255.XXX tai 192.168.254.XXX /8 tarkoittaisi osoitteiden olevan 192.XXX.XXX.XXX jne.&lt;br /&gt;&lt;br /&gt;Nyt siis osaamme ilman erillisiä reittejä jo sanoa, että osoitteeseen 192.168.255.66 matkalla oleva paketti, tulisi esimerkkitapauksessamme tuupata ovesta eth0 ulos. Mutta entäs jos meillä olisi useampi linkki (esim. useampi verkkokortti, tai vaikka VLAN virtuaali-interface) vaikkapa eth0 ja eth1, olkoot osoitteet vaikka 192.168.255.2/24 ja 192.168.1.2/24, ja pakettimme olisi menossa osoitteeseen 10.24.45.22? Nyt kummankaan linkin osoite ei kerro että linkin takaa löytyisi kyseinen kohde... Tarvitaan siis erillinen reititysohje kertomaan kummanko linkin kautta paketti tulee paiskata pihalle. Katsotaampa siis ensimmäistä esimerkkireittiä:&lt;br /&gt;&lt;br /&gt;0.0.0.0 via dev eth0&lt;br /&gt;0.0.0.0 on reitin kohde osoite. 0.0.0.0 on erikoisasemassa oleva osoite, ja kohdeosoitteena se siis tarkoittaa ns. oletusreittiä. Ts. kyseinen reitti kertoo, että mikäli mikään muu reititysohje ei paketin kanssa toimi, tulee se toimittaa eth0 linkkiin.&lt;br /&gt;&lt;br /&gt;Toinen esimerkkireitti oli 10.34.143.0 255.255.255.0 gw 192.168.1.55 metric 2&lt;br /&gt;Tässä kohdeosoite on 10.34.143.0, ja nettimaski on 255.255.255.0. Tämä tarkoittaa samaa kuin 255.255.255.0/24, eli maski kertoo merkitsevät numerot osoitteessa. Gw tarkoittaa gatewayta, joka kertoo, että paketti tulee toimittaa koneen 192.168.1.55 kautta, ja tuo kone tuntee tien lopulliseen määränpäähän. Metric 2 kertoo kuinka suosittu reitti on. Ts, mikäli johonkin osoitteeseen on määritetty useampi kuin yksi reitti, valitaan ensisijaisesti reitti jolla on tarkempi maski. Esim jos paketti on lähetetty osoitteeseen 192.168.255.124, ja reitteja on määritetty osoitteeseen 192.168.255.0 maskilla 255.255.255.0, sekä reitti osoitteeseen 192.168.255.124 maskilla 255.255.255.255, valitaan ensisijaisesti jälkimmäinen reitti, joka siis on tarkka. Ns. "hostireitti". Mikäli kuitenkin reittejä olisi kaksi vaihtoehtoista, eikä maskeissa olisi eroja, voidaan "suosituimmuus" määrittää metric arvolla. Mitä pienempi metric, sitä suositumpi reitti. Ts, paketti toimitetaan ennemmin pienemmän metricin omaavaan reittiin.&lt;br /&gt;&lt;br /&gt;IPv6:n kanssa reititys on hieman monimutkaisempaa, sillä itse osoitteet kantavat osan aliverkkotiedosta. Mutta se reiteistä. Mennäänhän päivän epistolaan. Netlink rajapinta on siis tyypillinen viestirajapinta. Sockettiin lähetetään pyyntöjä, ja vastauksia kuunnellaan. Tässä jutussa käsittelen kolmen eri tyypin pyyntöjä/vastauksia - Osoite (ADDRESS), reitti (ROUTE) ja linkki (LINK). Kaikille perheille on määritelty viestityypit joilla voidaan&lt;br /&gt;&lt;br /&gt;luoda uusi &amp;lt; osoite/linkki/reitti &amp;gt; &lt;br /&gt;poistaa olemassaoleva &amp;lt; osoite/linkki/reitti &amp;gt; &lt;br /&gt;hakea olemassaolevat &amp;lt; osoitteet/linkit/reitit &amp;gt; &lt;br /&gt;&lt;br /&gt;Tarkat viestin headeriin täytettävät viestityypit ovat&lt;br /&gt;&lt;br /&gt;RTM_NEWADDR (uusi osoite)&lt;br /&gt;RTM_DELADDR (osoitteen poisto)&lt;br /&gt;RTM_GETADDR (olemassaolevien osoitteiden haku)&lt;br /&gt;&lt;br /&gt;RTM_NEWROUTE (uusi reitti)&lt;br /&gt;RTM_DELROUTE (...poisto)&lt;br /&gt;RTM_GETROUTE (...haku)&lt;br /&gt;&lt;br /&gt;RTM_NEWLINK (uusi linkki)&lt;br /&gt;RTM_DELLINK (...poisto)&lt;br /&gt;RTM_GETLINK (...haku)&lt;br /&gt;&lt;br /&gt;ja linkeille myös&lt;br /&gt;&lt;br /&gt;RTM_SETLINK &lt;br /&gt;&lt;br /&gt;jolla luodun linkin tilaa voidaan säätää.&lt;br /&gt;&lt;br /&gt;Tässäpä minulle tulikin ensimmäiset ongelmat... Vasta kernelin koodeja tutkimalla opin, että NEWLINK reqiestit joissa struct ifinfomsg viestistructissa oli kenttiä täytetty - hylättiin.&lt;br /&gt;&lt;br /&gt;Varsinainen netlink viesti koostuu:&lt;br /&gt;&lt;br /&gt;1. normaali viestittelyssä käytetystä messuheaderista tyyppiä struct msghdr. Oletan että tämä on tuttu.&lt;br /&gt;2. netlinkin messuheaderista tyyppiä struct nlmsghdr (alla)&lt;br /&gt;3. viestityypistä (esim route, addres tai link) riippuva structi (tässä esitelty struct ifaddrmsg /  struct ifinfomsg / struct rtmsg)&lt;br /&gt;4. mahdollisesti sekalainen joukko atribuutteja.&lt;br /&gt;&lt;br /&gt;Netlink message header&lt;br /&gt;struct nlmsghdr&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;__u32&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   nlmsg_len;  /* Viestin pituus headeri mukaanlukien */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;__u16&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   nlmsg_type; /* Viestin tyyppi (kts. yllä) */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;__u16&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   nlmsg_flags;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/* Flagit */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;__u32&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   nlmsg_seq;  /* Sequenssi numero */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;__u32&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   nlmsg_pid;  /* Lähettävän processin portti ID */&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;1. Eli nlmsg_len siis kertoo koko viestin pituuden, karvoinen kaikkineen. Netlink viestien dynaamisten kokojen takia tämä kenttä on hyvin keskeinen.&lt;br /&gt;2. Viestin tyyppi, esim RTM_NEWLINK.&lt;br /&gt;3. Flagit...&lt;br /&gt;&lt;br /&gt;...Suoraan /usr/include/linux/netlink.h:sta kaiveltuna/käänneltynä:&lt;br /&gt;&lt;br /&gt;  /* Flagit */&lt;br /&gt;&lt;br /&gt;NLM_F_REQUEST&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/* Viesti on requesti   */&lt;br /&gt;NLM_F_MULTI&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/* Useampiosainen viesti, viimeinen tyyppiä NLMSG_DONE */&lt;br /&gt;NLM_F_ACK&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;  /* ack - viesti, sisältönä nolla tai virhekoodi */&lt;br /&gt;NLM_F_ECHO&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Pyyntö "echottaa" viesti */&lt;br /&gt;&lt;br /&gt;/* GET requestin liput */&lt;br /&gt;NLM_F_ROOT&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* palauta kaikki data, ei vain yhtä "entryä"*/&lt;br /&gt;NLM_F_MATCH&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/* palauta kaikki täsmäävät - todennäköisesti ei toteutettu  */&lt;br /&gt;NLM_F_ATOMIC&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   /* atominen "snapshotin" haku   */&lt;br /&gt;NLM_F_DUMP /* Dumppaa koko taulu */&lt;br /&gt;&lt;br /&gt;/* NEW requestin liput */&lt;br /&gt;NLM_F_REPLACE&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;  /* Korvaa olemassaoleva */&lt;br /&gt;NLM_F_EXCL&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Älä koske jos on jo olemassa   */&lt;br /&gt;NLM_F_CREATE&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   /* Luo jos ei ole olemassa */&lt;br /&gt;NLM_F_APPEND&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   /* Lisää listan hännille   */&lt;br /&gt;&lt;br /&gt;Esimerkiksi &lt;b&gt;kaikkien pyyntöjen tulee olla varustettuna lipulla NLM_F_REQUEST.&lt;/b&gt; NLM_F_MATCH ei luultavasti ole toteutettuna, ja GET requestisi palauttanee kaikki mahdolliset reitit/linkit tai osoitteet jotka on tehtynä - riippumata niistä tarkennuksista joita requestisi structissa/atribuuteissa mainitsit. Lisätarkennusta flageihin saatat löytää man sivuilta.&lt;br /&gt;&lt;br /&gt;4. sequenssinumero, joka auttaa parittamaan vastaukset oikeiden pyyntöjen kanssa - tyypillisesti juokseva numerointi (esim atomisesti kasvatettava globaali).&lt;br /&gt;5. prosessin identifiointi. Tyypillisesti&lt;br /&gt;(pthread_self() &amp;lt;  &amp;lt; 16|getpid());&lt;br /&gt;Kernelin vastauksissa pid on 0.&lt;br /&gt;&lt;br /&gt;Huomaathan että oletusarvoisesti kerneli ei lähetä vastausta onnistuneisiin pyyntöihin (paitsi tietysti GET pyyntöihin). Minä kuitenkin haluan saada varmistuksen sille, että pyyntöni on kuultu. Siksi höystänkin kaikki muut paitsi GET requestini NLM_F_ACK lipulla. Tämä lippu saa kernelin vastaamaan muihinkin kuin GET pyyntöihin. Onnistunut suoritus kuitataan viestillä, jonka tyyppi on NLMSG_ERROR, ja jossa nlmsghdr structin perässä on struct nlmsgerr, jossa error kenttä sisältää arvon 0. Samaa viestiä kerneli käyttää raportoimaan virheet, mutta silloin error kenttä sisältää virhekoodin. Virhetilanteet raportoidaan NLM_F_ACK lipusta riippumatta.&lt;br /&gt;&lt;br /&gt;struct nlmsghdr:n jälkeen seuraa requestikohtainen structi. &lt;br /&gt;&lt;br /&gt;Osoitteille:&lt;br /&gt;&lt;br /&gt;struct ifaddrmsg&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;__u8&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ifa_family;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;__u8&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ifa_prefixlen;  /* Prefixin pituus (maski) &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;__u8&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ifa_flags;  /* Liput&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;__u8&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ifa_scope;  /* Osoitteen skooppi&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;__u32&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   ifa_index;  /* Linkin ID numero (ifindex) &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   */&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;jossa ifa_family on siis IPv4 tilanteessa AF_INET, ja IPv6 tilanteessa AF_INET6.&lt;br /&gt;ifa_prefixlen on maskin pituus bitteinä - IPv4 tapauksessa 0-32 ja IPv6 tapauksessa 0-128 (eli tämä siis kertoo millainen osoiteavaruus on sen interfacen takana, jonne linkki johon osoite on sidottu johtaa.&lt;br /&gt;ifa_flags - liput &lt;br /&gt;&lt;br /&gt;/* ifa_flags */&lt;br /&gt; IFA_F_SECONDARY&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt; IFA_F_TEMPORARY&lt;br /&gt;&lt;br /&gt; IFA_F_NODAD&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt; IFA_F_OPTIMISTIC&lt;br /&gt; IFA_F_DADFAILED&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt; IFA_F_HOMEADDRESS&lt;br /&gt; IFA_F_DEPRECATED&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt; IFA_F_TENTATIVE&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt; IFA_F_PERMANENT&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Joista en ole oikeastaan koskaan tarvinnut yhtään.&lt;br /&gt;&lt;br /&gt;ifa_scope - "skooppi" - en oikeastaan ole jaksanut tutkia mitä tämä pitää sisällään. Tähänasti olen tyytynyt asettameen skoopin nollaksi.&lt;br /&gt;ifa_index - sen linkin indexinumero johon osoite on sidottu. (kts man sivut funkkarille unsigned if_nametoindex(const char *ifname) jos tämä on outo juttu)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Linkit:&lt;br /&gt;&lt;br /&gt;struct ifinfomsg&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unsigned char   ifi_family;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unsigned char   __ifi_pad;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unsigned short  ifi_type;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   /* ARPHRD_* */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ifi_index;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;  /* Link index   */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unsigned&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ifi_flags;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;  /* IFF_* flags  */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unsigned&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ifi_change;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* IFF_* change mask */&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;Kas niin. Murheenkryynini. Kuten jo mainitsin, hakkasin itse päätä seinään tekemällä RTM_NEWLINK requestin jossa struct ifinfomsg:n kenttiä oli täyteltynä. VLAN interfaceni luonti hylättiin tylysti kerta toisensa jälkeen.. Lopulta kernelikoodeja lukemalla tajusin, että NEWLINK requestissa ei oikeastaan saa olla mitään näistä täytettynä, vain attribuutteja kertomassa mitä oikeastaan yritetään. Myöhemmin linkin tilan voi muuttaa RTM_SETLINK requestilla. Itse käytin RTM_SETLINK:iä vain muuttamaan linkin tilan "ylös" tilaan (IFF_UP). Ja tämänkin tein laiskasti tuuppaamalla vain requestiin ifi_index kentäksi luodun linkin id:n, ifi_changen 0xffffffff:ksi (kuten man sivut neuvoo) ja ifi_flags kentän suoraan IFF_UP:ksi. Oikeampi tapa olisi varmaan ollut lukea linkin tila, ja sitten lisätä tai-operaatiolla (|) IFF_UP bitit lippuun.&lt;br /&gt;&lt;br /&gt;Reitit:&lt;br /&gt;&lt;br /&gt;struct rtmsg&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unsigned char&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   rtm_family;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unsigned char&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   rtm_dst_len;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unsigned char&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   rtm_src_len;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unsigned char&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   rtm_tos;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unsigned char&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   rtm_table;  /* Routtaus taulun id */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unsigned char&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   rtm_protocol;   /* Routtaus protokolla (alla)  */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unsigned char&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   rtm_scope;  /* skooppi (alla) */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unsigned char&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   rtm_type;   /* tyyppi (alla) */&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unsigned&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;rtm_flags;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Missä tyyppi voi olla:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTN_UNSPEC, /* määrittelemätön */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTN_UNICAST,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/* Gateway tai suora reitti  */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTN_LOCAL,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;  /* Paikallinen &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTN_BROADCAST,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;  /* Paikallinen bcast,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   lähetä broadcastina */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTN_ANYCAST,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/* Hyväksy paikallisesti broadcastina,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   mutta lähetä unicastina */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTN_MULTICAST,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;  /* Multicast reitti&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;  */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTN_BLACKHOLE,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;  /* Tiputa paketit;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTN_UNREACHABLE,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/* Määränpää ei tavoitettavissa   */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTN_PROHIBIT,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   /* Pääsy kielletty  */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTN_THROW,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;  /* Ei tässä taulussa&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTN_NAT,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/* Muunna NAT:illa   */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTN_XRESOLVE,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   /* Käytä erillsitä paikannusta - ei mahda olla toteutettu&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/&lt;br /&gt;&lt;br /&gt;Luultavasti normaaleille reiteille haluat käyttää RTN_UNICASTia. Vaikkakin toisinaan spammin määrää ihmetellessä RTN_BLACKHOLEn käyttö olisi houkuttelevaa... ;)&lt;br /&gt;&lt;br /&gt;Protokollat (kertovat siitä miten reitti on syntynyt):&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#define RTPROT_UNSPEC   0   /* määrittelemätön */&lt;br /&gt;#define RTPROT_REDIRECT 1   /* ICMP redirectien tekemä reitti;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   Ei tällähetkellä IPv4:llä käytössä */&lt;br /&gt;#define RTPROT_KERNEL   2   /* kernelin tekemä reitti&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/&lt;br /&gt;#define RTPROT_BOOT 3   /* bootissa tehty reitti &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;  */&lt;br /&gt;#define RTPROT_STATIC   4   /* Adminin tekemä reitti */&lt;br /&gt;&lt;br /&gt;/* &lt;br /&gt;    Kerneli ei tutki protokollia  &amp;gt; = RTPROT_STATIC;&lt;br /&gt; */&lt;br /&gt;&lt;br /&gt;#define RTPROT_GATED&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;8   /* GateD */&lt;br /&gt;#define RTPROT_RA   9   /* RDISC/ND router advertisements */&lt;br /&gt;#define RTPROT_MRT  10  /* Merit MRT */&lt;br /&gt;#define RTPROT_ZEBRA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;11  /* Zebra */&lt;br /&gt;#define RTPROT_BIRD 12  /* BIRD */&lt;br /&gt;#define RTPROT_DNROUTED 13  /* DECnet routing daemon */&lt;br /&gt;#define RTPROT_XORP 14  /* XORP */&lt;br /&gt;#define RTPROT_NTK  15  /* Netsukuku */&lt;br /&gt;#define RTPROT_DHCP 16&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;  /* DHCP client */&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Minä käytin arvoa RTPROT_STATIC, joka siis vastaa tilannetta jossa käyttäjä luo reitin käyttämällä komentoa "ip route add".&lt;br /&gt;&lt;br /&gt;skoopit:&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RT_SCOPE_UNIVERSE=0,&lt;br /&gt;/* User defined values  */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RT_SCOPE_SITE=200,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RT_SCOPE_LINK=253,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RT_SCOPE_HOST=254,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RT_SCOPE_NOWHERE=255&lt;br /&gt;&lt;br /&gt;Jos reittisi ei ole tarkoitettu paikalliseksi, lienee syytä käyttää RT_SCOPE_UNIVERSEa.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/* rtm_flags */&lt;br /&gt;&lt;br /&gt;#define RTM_F_NOTIFY&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0x100   /* Ilmoita jos reitti muuttuu  */&lt;br /&gt;#define RTM_F_CLONED&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0x200   /* Kloonattu reitti&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; */&lt;br /&gt;#define RTM_F_EQUALIZE&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;  0x400   /* Multipath equalizer: NI  */&lt;br /&gt;#define RTM_F_PREFIX&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0x800   /* Prefix addresses&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; */&lt;br /&gt;&lt;br /&gt;Minä asetin liput nollaksi - en siis tarvinnut näitä omassa käytössäni.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Nyt viestimme siis koostuu netlink headerista, jossa ilmoitetusta tyypistä riippuen meillä on viestissä tyyppikohtainen strukti. Jotta homma ei jäisi liian simppeliksi, lisätään hieman muuttuvia osasia.. =)&lt;br /&gt;&lt;br /&gt;Attribuutit.&lt;br /&gt;&lt;br /&gt;Viestin loppuun voidaan lisätä sekalainen joukko attribuutteja joilla tarkennetaan luotavan/poistettavan reitin/linkin/osoitteen ominaisuuksia. Attribuutit alkavat structilla:&lt;br /&gt;&lt;br /&gt;struct rtattr&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unsigned short  rta_len;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unsigned short  rta_type;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;jossa siis kerrotaan attribuutin koko, ja tyyppi. Structin jälkeen on varsinainen attribuutin data, jota siis on rta_len-sizeof(struct rtattr) verran. Attribuutit on vielä alignoitu 4:n tavun mukaan. Kuten C-koodari äkkiä huomaa, tällaisesta virityksestä saattaa tulla sekavahko hanskattava. Siksipä meillä onkin valmiit makrot attribuuttien hanskaukseen:&lt;br /&gt;&lt;br /&gt;RTA_ALIGN(len)&lt;br /&gt;RTA_OK(rta,len)&lt;br /&gt;RTA_NEXT(rta,attrlen)&lt;br /&gt;RTA_LENGTH(len)&lt;br /&gt;RTA_DATA(rta)&lt;br /&gt;RTA_PAYLOAD(rta)&lt;br /&gt;&lt;br /&gt;RTA_ALIGN(len) Pyöristää annetun pituuden neljän tavun alignmenttiin. (Miten tuo sanotaan suomeksi???) Eli siis&lt;br /&gt;RTA_ALIGN(1) palauttaisi 4 kuten myös RTA_ALIGN(4). RTA_ALIGN(5) taas palauttaisi 8 jne.&lt;br /&gt;&lt;br /&gt;RTA_OK(rta,len) tarkastaa että annettu attribuutti rta on Ok. Yleinen tapa läpikäydä viestijonon attribuutit on hakea aina seuraava attribuutti makrolla RTA_NEXT, ja tarkistaa että atribuuttia on turvallista tutkia, kutsumalla RTA_OK:ta. Makroille annettavan pituuden (len), on ensimmäisellä kutsulla oltava attribuuttibufferin koko. Jokaisella RTA_NEXT kutsulla kokoa päivitetään. &lt;br /&gt;&lt;br /&gt;RTA_LENGHT(len) palauttaa koon, joka tarvitaan jotta voidaan muodostaa atribuutti jonka datan koko on len. Eli RTA_LENGHT siis lisää annettuun kokoon struct rtattr:n koon ja alignoi sen sitten neljän tavun mukaiseksi. &lt;br /&gt;&lt;br /&gt;RTA_DATA(rta) palauttaa pointterin attribuutin rta datan alkuun.&lt;br /&gt;RTA_PAYLOAD(rta) palauttaa attribuutin rta sisältämän datan pituuden.&lt;br /&gt;&lt;br /&gt;Kuulostaa sekavalta? Älä huoli, näytän myöhemmin esimerkin jolla attribuutteja voi läpikäydä.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Nyt kun selvät asiat on läpikäyty, voidaan mennä seikkaan joka sai minun hermoni melkein riekaleiksi.. Tieto siitä miten netlink sokettien avulla tehdään VLAN interface, eli virtuaalinen linkki joka lähettää VLAN tagilla varustettuja paketteja, ei löytynyt ihan helpolla. Google lauloi kun etsin:&lt;br /&gt;"VLAN interface netlink sockets", "RTM_NEWLINK VLAN". "Create VLAN via netlink". "VLAN netlink attribute"... Näillä hauilla löysin tarpeeksi tietoa kertomaan, että kyllä - VLAN linkin pystytys on ilmeisesti mahdollista, ja että se onnistunee jollain attribuutilla... No lopullinen selvuus löytyi lisäämällä debuggiprinttejä kerneliin, ja yrittämällä &amp; erehtymällä. Eli tavanomaisten attribuuttien lisäksi on ns. nested attribuutteja, eli sisäkkäisiä attribuutteja.  Mutta katsellaanpa tuota myöhemmin. &lt;br /&gt;&lt;br /&gt;Attribuutit joita eri requestit tukevat:&lt;br /&gt;&lt;br /&gt;Osoitteet:&lt;br /&gt;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFA_UNSPEC,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFA_ADDRESS,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFA_LOCAL,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFA_LABEL,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFA_BROADCAST,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFA_ANYCAST,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFA_CACHEINFO,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFA_MULTICAST,&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Reitit:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTA_UNSPEC,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTA_DST,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTA_SRC,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTA_IIF,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTA_OIF,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTA_GATEWAY,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTA_PRIORITY,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTA_PREFSRC,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTA_METRICS,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTA_MULTIPATH,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTA_PROTOINFO, /* no longer used */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTA_FLOW,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTA_CACHEINFO,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTA_SESSION, /* no longer used */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTA_MP_ALGO, /* no longer used */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTA_TABLE,&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Linkit:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_UNSPEC,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_ADDRESS,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_BROADCAST,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_IFNAME,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_MTU,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_LINK,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_QDISC,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_STATS,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_COST,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_COST&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_PRIORITY,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_PRIORITY&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_MASTER,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_MASTER&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_WIRELESS,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_WIRELESS&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_PROTINFO,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_PROTINFO&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_TXQLEN,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_TXQLEN&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_MAP,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_MAP&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_WEIGHT,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_WEIGHT&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_OPERSTATE,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_LINKMODE,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_LINKINFO,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_LINKINFO&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;IFLA_NET_NS_PID,&lt;br /&gt;&lt;br /&gt;osaan löydät dokumentaation rtnetlinkin man sivuilta sektiosta 7 - osaan et...&lt;br /&gt;&lt;br /&gt;Ja nyt helpotukseksi heille jotka taistelevat VLAN linkkien teon kanssa... Minä sain VLAN linkin pystytettyä seuraavien attribuuttien avulla.&lt;br /&gt;&lt;br /&gt;IFLA_LINK, datan koko on intin verran. Sisältää VLANin alla olevan oikean interfacen indexin.&lt;br /&gt;IFLA_IFNAME, uuden VLAN linkin nimi. Itse käytin muotoa &amp;lt; alkuperäinen_interface &amp;gt; . &amp;lt; vlan Id &amp;gt;&lt;br /&gt;&lt;br /&gt;sisäkkäiset attribuutit:&lt;br /&gt;IFLA_LINKINFO joka siis sisältää&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;attribuutin IFLA_INFO_KIND, jossa datans stringi 'vlan', pituuden ollessa tekstin 'vlan' pituus + 4:n tavun alignointi.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;toinen nested attribute IFLA_INFO_DATA, joka sisältää:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;attribuutin IFLA_VLAN_ID, jossa datana 16-bittinen vlan id.&lt;br /&gt;&lt;br /&gt;Sekavan kuulosita? Jep.. Toisinaan ihmettelen onko insinöörit todella olleet NOIN humalassa speksatessaan rajapintoja... (http://xkcd.com/323/)&lt;br /&gt;&lt;br /&gt;No helpotuksena(?) pätkä koodia jolla atribuutit voi hanskata (pahoittelut kielestä - en jaksa ainakaan nyt kääntää suomeksi, jos joku muu viitsii, niin otan kiitollisena päivitetyn koodin vatsaan! (kommenttina tai osoitteessa Mazziesaccount@gmail.com)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#define NLMSG_BOTTOM(nlmsg) ((struct rtattr *)(((void *)(nlmsg)) + NLMSG_ALIGN((nlmsg)- &amp;gt; nlmsg_len)))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;static int addAttr(struct nlmsghdr *nl_req, int attrlabel, const void *data, int datalen)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;struct rtattr *attr=NLMSG_BOTTOM(nl_req));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unsigned int attrlen=RTA_LENGTH(datalen); /* sizeof(struct rtattr) + datalen + align */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(NULL==nl_req || (datalen &amp;gt; 0 &amp;&amp; NULL==data))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf("NULL arg detected!");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return -1;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;attr- &amp;gt; rta_type=attrlabel;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;attr- &amp;gt; rta_len=attrlen;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;memcpy(RTA_DATA(attr),data,datalen);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;nl_req- &amp;gt; nlmsg_len=NLMSG_ALIGN(nl_req- &amp;gt; nlmsg_len)+RTA_ALIGN(attrlen);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 0;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;static struct rtattr * addNestedAttr(struct nlmsghdr *nl_req, int attrlabel)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;struct rtattr *nested = NLMSG_BOTTOM(nl_req);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(!addAttr(nl_req, attrlabel, NULL, 0))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return nested;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return NULL;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;static void endNestedAttr(struct nlmsghdr *nl_req, struct rtattr *nested)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;nested- &amp;gt; rta_len = (void *)NLMSG_BOTTOM(nl_req) - (void *)nested;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/* ...snip - Add attributes to the nlmsg msg */&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;struct rtattr *attr1, *attr2;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(addAttr(msg,IFLA_LINK,&amp;orig_ifindex,sizeof(int)))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf("IFLA_LINK %d adding as rtattr to req failed!",orig_ifindex);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;retval=-1;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else if(addAttr(msg,IFLA_IFNAME,ifname,strlen(ifname)))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf("IFLA_IFNAME  %s adding as rtattr to req failed!",ifname);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;retval=-1;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else if(NULL==(attr1=addNestedAttr(msg,IFLA_LINKINFO)))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf("addNestedAttr IFLA_LINKINFO FAILED!");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;retval=-1;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else if(addAttr(msg,IFLA_INFO_KIND,"vlan", strlen("vlan")))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf("IFLA_INFO_KIND \"vlan\" adding FAILED!");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   retval=-1;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else if(NULL==(attr2=addNestedAttr(msg,IFLA_INFO_DATA)))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf("addNestedAttr IFLA_INFO_DATA FAILED!");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;retval=-1;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else if(addAttr(msg,IFLA_VLAN_ID,&amp;vlanid,sizeof(unsigned short)))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf("IFLA_VLAN_ID  %hu adding as rtattr to req failed!",vlanid);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;retval=-1;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;endNestedAttr(msg,attr2);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;endNestedAttr(msg,attr1);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf("VLAN ID %hu, orig ifindex %d and new ifname %s added as attrs",vlanid,orig_ifindex,ifname);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Tämä koodi olettaa että nlmsghdr structissa oleva viestin pituus on ajantasalla. Ts, ensimmäistä atribuuttia lisättäessä, pituuden on oltava nlmsghdr struktin koko + requesti spesifisen structin koko. Jokainen attribuutin lisäys kutsu edelleen päivittää tätä pituuskenttää. (kts. makro NLMSG_BOTTOM() )&lt;br /&gt;&lt;br /&gt;Nyt alkaa tämän lyhyen intron loppu häämöttää. Esitän vielä oikeaa/pseudokoodia jotta messujen lähetyksen/vastaanoton idea tulisi selväksi.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;struct sockaddr_nl kernproc;&lt;br /&gt;struct msghdr msg;&lt;br /&gt;struct nlmsghdr *netlinkresp;&lt;br /&gt;struct iovec iov;&lt;br /&gt;&lt;br /&gt;memset(&amp;msg,0,sizeof(msg));&lt;br /&gt;&lt;br /&gt;memset(&amp;kernproc,0,sizeof(kernproc));&lt;br /&gt;memset(&amp;iov,0,sizeof(iov));&lt;br /&gt;&lt;br /&gt;kernproc.nl_family = AF_NETLINK;&lt;br /&gt;&lt;br /&gt;msg.msg_name=(void *)&amp;kernproc;&lt;br /&gt;msg.msg_namelen=sizeof(kernproc);&lt;br /&gt;&lt;br /&gt;netlinkresp= &amp;lt; vastaukselle allokoitu bufferi &amp;gt; ;&lt;br /&gt;&lt;br /&gt;/* Lisää NLMSG_F_ACK jos vastausta ei ole odotettavissa */&lt;br /&gt;&lt;br /&gt;iov.iov_base=(void *)netlinkresp;&lt;br /&gt;iov.iov_len= &amp;lt; nlmsg_len &amp;gt; ;&lt;br /&gt;&lt;br /&gt;msg.msg_iov=&amp;iov;&lt;br /&gt;msg.msg_iovlen = 1; /* vain yksi iov structi */&lt;br /&gt;iov.iov_len= &amp;lt; vastausbufferin koko &amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;retry:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;retval=recvmsg(sock, &amp;msg, 0);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(0 &amp;gt; =retval)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(errno==EINTR)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;goto retry;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf("Error when receiving from netlink sock!");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/* handle error */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/* ...reply received */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Siis&lt;br /&gt;1. Tarkista vastauksen pituus recv:n paluuarvosta - älä ylitä sitä. &lt;br /&gt;2. Tarkista msghdr (EI SIIS nlmsghdr) nähdäksesi sopiko koko viesti vastaanottobufferiin.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(msg.msg_flags&amp;MSG_TRUNC)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;   ... varaa lisää tilaa ja yritä uudelleen...&lt;br /&gt;3. Tallenna pointteri nlmsghdr messun headeriin.&lt;br /&gt;4. aloita silmukka ja tarkista että messu on ok NLMSG_OK() makrolla. Jos ei, sinulla ei ole mitään hanskattavaa.&lt;br /&gt;5. Tarkista nlmsg:n tyyppi, ja pituus. jos pituus on pidempi tai yhtäsuuri kuin nlmsg+tyyppi specifinen headeri, käytä &lt;br /&gt;NLMSG_DATA makroa saadaksesi pointterin varsinaiseen messuun. Castaa ja tallenna pointteri.&lt;br /&gt;5. Tarkista informaatio jota olit vailla. jos koko messu ei ole vielä käsitelty (pituus), silloin perässä on todennäköisesti attribuuttijoukkio...&lt;br /&gt;6. Ota pointteri ensimmäiseeb attribuuttiin lisäämällä messu spesifisen structin koko to NLMSG_DATA();n palauttamaan osoittimeen.&lt;br /&gt;7. aloita silmukka ja tarksista atribuutti  RTA_OK() makrolla. Jos testi epäonnistuu, hyppää kohtaan 9&lt;br /&gt;8. Tarkista attribuutin tyyppi ja data.&lt;br /&gt; RTA_NEXT - &amp;gt;  lopeta silmukka ja hyppää kohtaan 7.&lt;br /&gt;9. Kun viimeinen attribuutti on tarkistettu, katso onko lippu NLM_F_MULTI asetettu nlmsghdr headerissa, ja että onko viimeisen viestin tyyppi NLMSG_DONE jos ei, hae seuraava nlmsg makrolla NLMSG_NEXT() ja hyppää uudelleen silmukkaan kohtaan 4&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Viestin lähetys käyttää samaa generistä iovec mekanismia:&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;struct sockaddr_nl kernproc;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;struct msghdr msg;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;struct nlmsghdr *netlinkreq;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;struct iovec iov;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;memset(&amp;msg,0,sizeof(msg));this- &amp;gt; mypid&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;memset(&amp;kernproc,0,sizeof(kernproc));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;memset(&amp;iov,0,sizeof(iov));&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;kernproc.nl_family = AF_NETLINK;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;msg.msg_name=(void *)&amp;kernproc;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;msg.msg_namelen=sizeof(kernproc);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;netlinkreq= &amp;lt; pointteri varattuun ja täytettyyn viestiin &amp;gt; ;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;netlinkreq- &amp;gt; nlmsg_pid= (pthread_self() &amp;lt;  &amp;lt; 16|getpid());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;netlinkreq- &amp;gt; nlmsg_seq= atomicallyIncrementSeqId(seqid);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#ifdef debug&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;debugprint_msg(netlinkreq);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#endif&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;iov.iov_base=(void *)netlinkreq;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;iov.iov_len=netlinkreq- &amp;gt; nlmsg_len;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;msg.msg_iov=&amp;iov;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;msg.msg_iovlen = 1; /* vain yksi iov struct  */&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;retval =sendmsg(sock,&amp;msg,0);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(retval &amp;lt; =0)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf("sendmsg() FAILED!");&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return retval;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Debuggiprinttifunktioni läpikäy lähetettävän viestin seuraavalla koodilla:&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(netlinkreq- &amp;gt; nlmsg_flags &amp; NLM_F_ACK)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf("Msg contains f_ack!");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(!NLMSG_OK(netlinkreq,netlinkreq- &amp;gt; nlmsg_len))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf("Looks like we're sending invalid nlmsg!! NLMSG_OK() == false at send!");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"sending NLMSG: len %u, type %hu, flags %hu, seq %u pid %u",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;netlinkreq- &amp;gt; nlmsg_len,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;netlinkreq- &amp;gt; nlmsg_type,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;netlinkreq- &amp;gt; nlmsg_flags,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;netlinkreq- &amp;gt; nlmsg_seq,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;netlinkreq- &amp;gt; nlmsg_pid&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;switch(netlinkreq- &amp;gt; nlmsg_type)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case RTM_NEWROUTE:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case RTM_DELROUTE:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case RTM_GETROUTE:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"Req is route req (new %u, del %u, get %u)",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTM_NEWROUTE,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTM_DELROUTE,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTM_GETROUTE&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"family %u, dstlen %u, srclen %u, tos %u, table %u, proto %u, scope %u, type %u, flags %u",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(unsigned int)((struct rtmsg *) NLMSG_DATA(netlinkreq) )- &amp;gt; rtm_family,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(unsigned int)((struct rtmsg *) NLMSG_DATA(netlinkreq) )- &amp;gt; rtm_dst_len,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(unsigned int)((struct rtmsg *) NLMSG_DATA(netlinkreq) )- &amp;gt; rtm_src_len,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(unsigned int)((struct rtmsg *) NLMSG_DATA(netlinkreq) )- &amp;gt; rtm_tos,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(unsigned int)((struct rtmsg *) NLMSG_DATA(netlinkreq) )- &amp;gt; rtm_table,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(unsigned int)((struct rtmsg *) NLMSG_DATA(netlinkreq) )- &amp;gt; rtm_protocol,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(unsigned int)((struct rtmsg *) NLMSG_DATA(netlinkreq) )- &amp;gt; rtm_scope,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(unsigned int)((struct rtmsg *) NLMSG_DATA(netlinkreq) )- &amp;gt; rtm_type,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(unsigned int)((struct rtmsg *) NLMSG_DATA(netlinkreq) )- &amp;gt; rtm_flags&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int len=netlinkreq- &amp;gt; nlmsg_len;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;struct rtattr *at=(struct rtattr *)((char *)NLMSG_DATA(netlinkreq)+sizeof(struct rtmsg));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while(NULL!=at &amp;&amp; RTA_OK(at,len))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;char tmp[100];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;switch(at- &amp;gt; rta_type)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case RTA_DST:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"dst is set to %s",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;inet_ntop&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(at- &amp;gt; rta_len &amp;gt; 8)?AF_INET6:AF_INET,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTA_DATA(at),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;tmp,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;100&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case RTA_SRC:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; printf&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"src is set to %s",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;inet_ntop&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(at- &amp;gt; rta_len &amp;gt; 8)?AF_INET6:AF_INET,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTA_DATA(at),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;tmp,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;100&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case RTA_GATEWAY:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"gw is set to %s",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;inet_ntop&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(at- &amp;gt; rta_len &amp;gt; 8)?AF_INET6:AF_INET,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTA_DATA(at),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;tmp,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;100&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case RTA_OIF:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"OIF is set to %u",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*(unsigned int *)RTA_DATA(at)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case RTA_PRIORITY:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"Priority is set to %u",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*(unsigned int *)RTA_DATA(at)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;default:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf("rta_type %u, len %u",at- &amp;gt; rta_type,at- &amp;gt; rta_len);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;at=RTA_NEXT(at,len);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case RTM_NEWADDR:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case RTM_GETADDR:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case RTM_DELADDR:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"Req is ADDR req (new %u, del %u, get %u)",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTM_NEWADDR,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTM_DELADDR,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTM_GETADDR&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"ifa_family %u, ifa_prefixlen %u, ifa_flags %u, ifa_scope %u, ifa_index %d",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(unsigned int)((struct ifaddrmsg *) NLMSG_DATA(netlinkreq) )- &amp;gt; ifa_family,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(unsigned int)((struct ifaddrmsg *) NLMSG_DATA(netlinkreq) )- &amp;gt; ifa_prefixlen,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(unsigned int)((struct ifaddrmsg *) NLMSG_DATA(netlinkreq) )- &amp;gt; ifa_flags,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(unsigned int)((struct ifaddrmsg *) NLMSG_DATA(netlinkreq) )- &amp;gt; ifa_scope,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(int)((struct ifaddrmsg *) NLMSG_DATA(netlinkreq) )- &amp;gt; ifa_index&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int len=netlinkreq- &amp;gt; nlmsg_len;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;struct rtattr *at=(struct rtattr *)((char *)NLMSG_DATA(netlinkreq)+sizeof(struct ifaddrmsg));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while(NULL!=at &amp;&amp; RTA_OK(at,len))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;char tmp[100];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;switch(at- &amp;gt; rta_type)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case IFA_ADDRESS:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"IFA_ADDRESS is set to %s",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;inet_ntop&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(at- &amp;gt; rta_len &amp;gt; 8)?AF_INET6:AF_INET,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTA_DATA(at),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;tmp,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;100&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case IFA_LOCAL:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"IFA_LOCAL is set to %s",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;inet_ntop&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(at- &amp;gt; rta_len &amp;gt; 8)?AF_INET6:AF_INET,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTA_DATA(at),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;tmp,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;100&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case IFA_BROADCAST:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"IFA_BROADCAST is set to %s",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;inet_ntop&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(at- &amp;gt; rta_len &amp;gt; 8)?AF_INET6:AF_INET,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTA_DATA(at),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;tmp,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;100&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case IFA_LABEL:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"IFA_LABEL is set to '%s'",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(char *)RTA_DATA(at)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case IFA_ANYCAST:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"IFA_ANYCAST is set to %s",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;inet_ntop&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(at- &amp;gt; rta_len &amp;gt; 8)?AF_INET6:AF_INET,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RTA_DATA(at),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;tmp,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;100&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;default:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf("rta_type %u, len %u",at- &amp;gt; rta_type,at- &amp;gt; rta_len);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;at=RTA_NEXT(at,len);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;.&lt;br /&gt;./* Tänne voi lisätä uusia viestityyppejä */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Tähän loppuu lyhyt introni netlink viestien maailmaan. Lisäinfoa löytyy mm. man sivuilta man 7 rtnetlink ja man 3 netlink &lt;br /&gt;&lt;br /&gt;Jos sattuu että ĺöysit jotain hyödyllistä täältä, tai jos sinulla on jotain&lt;br /&gt;parannusehdotuksia / suomenkielisiä koodiesimerkkejä, niin nakkaa minua&lt;br /&gt;viestillä täällä tai osoitteessa Mazziesaccount@gmail.com&lt;br /&gt;&lt;br /&gt;Ja jos käytät koodiani jossain, tai jos julkaiset sitä netissä, niin ole&lt;br /&gt;ystävällinen ja mainitse minut (Maz) ja tämä sivusto lähteenäsi =)&lt;br /&gt;&lt;br /&gt;Have fun!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4818234838483192587-6823549415350991480?l=c-ohjelmoijanajatuksia.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-ohjelmoijanajatuksia.blogspot.com/feeds/6823549415350991480/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4818234838483192587&amp;postID=6823549415350991480' title='0 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/6823549415350991480'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/6823549415350991480'/><link rel='alternate' type='text/html' href='http://c-ohjelmoijanajatuksia.blogspot.com/2011/09/netlink-soketit.html' title='Netlink soketit'/><author><name>Maz</name><uri>http://www.blogger.com/profile/03774313944609044806</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4818234838483192587.post-2633455949961562981</id><published>2010-07-07T10:57:00.003+03:00</published><updated>2011-09-13T14:53:22.198+03:00</updated><title type='text'>Repositoryn muutos</title><content type='html'>Hep Hep! Mikään ei ole niin pysyvää kuin muutos... Bottiprojektini juuttui jäihin ajanpuutteen vuoksi, ja repository on ammuttu alas. Viimeisimmät sorsat löytyy&lt;br /&gt;&lt;br /&gt;&lt;a href="http://xp-dev.com/svn/MazBotV4/trunk/"&gt;http://xp-dev.com/svn/MazBotV4/trunk/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;paketista.&lt;br /&gt;&lt;br /&gt;Olen laajalti viljellyt linkkejä bottini svn repositoryyn, jossa säilön koodipätkiäni.&lt;br /&gt;Jouduin tekemään joitain muutoksia repositoryyn, ja monet linkit vanhenivat. Seuraavalla muutoksella homman pitäisi taas pelata:&lt;br /&gt;&lt;br /&gt;Vanha linkki kehitysrepon juureen:&lt;br /&gt;http://blackdiam.net/svn/MazBot/&lt;br /&gt;&lt;br /&gt;Uusi linkki:&lt;br /&gt;http://blackdiam.net/svn/MazBot/trunc/&lt;br /&gt;&lt;br /&gt;Ja stabiilimpi ympäristö:&lt;br /&gt;http://blackdiam.net/svn/MazBot/releases/0.2.1/&lt;br /&gt;&lt;br /&gt;Toivottavasti löydätte etsimänne ;)&lt;br /&gt;&lt;br /&gt;-Maz&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4818234838483192587-2633455949961562981?l=c-ohjelmoijanajatuksia.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-ohjelmoijanajatuksia.blogspot.com/feeds/2633455949961562981/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4818234838483192587&amp;postID=2633455949961562981' title='0 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/2633455949961562981'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/2633455949961562981'/><link rel='alternate' type='text/html' href='http://c-ohjelmoijanajatuksia.blogspot.com/2010/07/repositoryn-muutos.html' title='Repositoryn muutos'/><author><name>Maz</name><uri>http://www.blogger.com/profile/03774313944609044806</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4818234838483192587.post-6253359459975473428</id><published>2010-06-29T14:53:00.012+03:00</published><updated>2011-09-15T15:59:46.678+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C'/><category scheme='http://www.blogger.com/atom/ns#' term='bittivektori'/><category scheme='http://www.blogger.com/atom/ns#' term='packed array'/><category scheme='http://www.blogger.com/atom/ns#' term='pakattu taulukko'/><category scheme='http://www.blogger.com/atom/ns#' term='bitset'/><category scheme='http://www.blogger.com/atom/ns#' term='MazBotV4'/><category scheme='http://www.blogger.com/atom/ns#' term='Maz'/><title type='text'>Lisää työkaluja C-koodina.</title><content type='html'>&lt;span style="font-size:100%;"&gt;&lt;br /&gt;13.09.2011: HepHep!&lt;br /&gt;Sorsat saatavilla svn reposta &lt;/span&gt;&lt;a href="http://xp-dev.com/svn/MazBotV4/trunk/"&gt;http://xp-dev.com/svn/MazBotV4/trunk/&lt;/a&gt;&lt;br /&gt;(generic/src kansiosta)&lt;br /&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;Kappas vain miten aika rientää. Edellisestä raapustelusta onkin mennyt jo tovi, mutta lohdutukseksi voin sanoa etten ole unohtanut kumpaakaan teistä rakkaista lukijoistani. :p&lt;br /&gt;&lt;br /&gt;No nyt sitten tuli taas mieleen, että voisin jakaa noita räpellyksiäni myös muiden harmiksi. Tälläkertaa tiputetaan ilmoille muistin säästämiseksi tehdyt bittivektori ja pakattu taulukko.&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Bittivektori:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;Bittivektorilla tarkoitan tässäyhteydessä simppeliä tapaa säästää muistia. Oletetaan että meillä on liuta asioita, joilla on kaksi tilaa. Vaikkapa kouluaiheisena esimerkkinä oppilaiden koetulokset. Jokainen koe voi olla joko suoritettu tai suorittamatta. Ja koska nykypäivänä kaikki koetetaan sulloa samaan purkkiin, on oppilaita hirmuinen joukko.&lt;br /&gt;&lt;br /&gt;Oletetaan nyt että olet tekemässä ohjelmaa, joka pitää kirjaa oppilaiden kokeiden suorittamisesta. Nopein mieleentuleva keino tallettaa tieto suureen taulukkoon, ja antaa oppilaille id-numero jolla taulukko voidaan indeksoida. Sovitaan nyt vaikka että oppilaita on 10000.&lt;br /&gt;&lt;br /&gt;Ensimmäinen viritys voisi siis olla&lt;br /&gt;int koe_suoritettuna[10000];&lt;br /&gt;taulukko. Nyt pohditaan tovi. Kuka tietää kuinka monta bittiä int-tyypin tieto vaatii? Ei kukaan ;) Se on hieman konearkkitehtuurista riippuvaa.. Jos nyt otetaan esimerkkimasiinaksi intelin/amd:n x86 arkkitehtuuri, niin tyypillisesti int tietotyyppi vie 32 bittiä/luku. Eli yksi suorittamatta/suoritettu tieto vie siis 32 bittiä. Jos oikein asiaa ajatellaan, niin eihän tuon tiedon tallettamiseen tarvita kuin yksi bitti, joko 0(=suorittamatta) tai 1(=suoritettu). Eli jokaisen oppilaan kohdalla hukataan 31 bittiä tilaa. 10000 oppilasta =&amp;gt; 310000 bittiä eli 38,750 tavua, eli n. 37.8 kilotavua.&lt;br /&gt;&lt;br /&gt;No PC maailmassa tämä ei kuulosta pahalta, mutta kun vastaavat ratkaisut viedään sulautettuun ohjelmointiin, tai käsitellään vielä suurempia tietomääriä, alkaa softan muistinkulutus päästä M$:n tuotteiden tasolle...&lt;br /&gt;&lt;br /&gt;Muistinkulutus saataisiin heti 4-kertaa pienemmäksi muuttamalla tietotyyppi chariksi. Mutta haluttaessa yhä optimaalisempaa muistinkulutusta, joudutaan rakentamaan jokin oma viritys. Tällainen on bittivektori. Bittivektorissa voidaan yksittäisiä bittejä varata tarpeellinen määrä (poislukien alignointibitit joilla ei yleensä näissä muistimäärissä ole merkitystä), ja muuttaa/lukea yksittäisen bitin arvo. Tähän tarkoitukseen koodailin bitsetin irc-bottiani varten.&lt;br /&gt;&lt;br /&gt;Koodi löytyy bottini repositoriosta:&lt;br /&gt;&lt;a href="http://xp-dev.com/svn/MazBotV4/trunk/generic/src/"&gt;http://xp-dev.com/svn/MazBotV4/trunk/generic/src/&lt;/a&gt;&lt;br /&gt;Tiedostot MbotBitset.h ja MbotBitset.c sisältävät bittivektorin. Pakattu taulukko on tiedostoissa MbotPackedArray.c ja MbotPackedArray.h&lt;br /&gt;&lt;br /&gt;HUOM! Tämä bitsettiviritys ei ole koeponnistettu 64-bittisillä koneilla, enkä takaa sen toimivuutta moisessa ympäristössä. Mikäli kokeilet sitä 64-bittisellä arkkitehtuurilla, niin olethan ystävällinen ja raportoit tulokset vaikka kommentoimalla tätä blogia!&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Pakattu taulukko:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;No jos nyt jatketaan edellistä esimerkkiä kokeen suorittamisesta, niin phditaanpa hieman vnhanmallin yliopistoarvosanojen tallennusta. Eli kokeen suorittamisesta seuraa numero, ja numero tulee tietysti myös tallentaa. Numero voi kuitenkin olla muutakin kuin 1 tai 0. Wanhaan hyvään aikaan yliopistoissa arvosanat olivat hylätty, 1,2 tai 3.&lt;br /&gt;Eli nyt yhden arvosanan esittämiseen tarvitaan kaksi bittiä.&lt;br /&gt;(00) = hylätty&lt;br /&gt;(01) = 1&lt;br /&gt;(10) = 2&lt;br /&gt;(11) = 3&lt;br /&gt;&lt;br /&gt;Tällaiseen tarkoitukseen rakentelin bitsetin kaveriksi pakatun taulukon, joka kysyy suurimman mahdollisen tallennettavan luvun, ja laskee sen jälkeen tarvittavien bittien määrän &amp;amp;&amp;amp; varaa muistin ja indeksoi sen sisäisesti oikean kokoisiin palasiin. Tämän jälkeen pakatusta taulukosta/taulukkoon voi lukea/kirjoittaa arvon halutulle oppilas-id:lle.&lt;br /&gt;&lt;br /&gt;Pakattu taulukko löytyy samasta paikasta kuin bittivektori, mutta tiedostot ovat MbotPackedArray.h ja MbotPackedArray.c&lt;br /&gt;&lt;br /&gt;Huom! pakattu taulukko on myös täysin testaamatta 64-bittisellä alustalla (ja oletuksena on ettei se toimi oikein 64-bit ympäristössä). Lisäksi 32 bittinen ympäristökin on hieman heikohkosti testattu...&lt;br /&gt;Joten kaikki havainnot pyydetään raportoimaan.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Oh, ja sekä bitsetistä, että pakatusta taulukosta löydetyt bugit voi kirjailla minulle osoitteeseen Mazziesaccount@gmail.com / kommenttina tänne blogiposteihin.&lt;br /&gt;&lt;br /&gt;(MazBot projektille)&lt;br /&gt;&lt;br /&gt;-Maz&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4818234838483192587-6253359459975473428?l=c-ohjelmoijanajatuksia.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-ohjelmoijanajatuksia.blogspot.com/feeds/6253359459975473428/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4818234838483192587&amp;postID=6253359459975473428' title='2 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/6253359459975473428'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/6253359459975473428'/><link rel='alternate' type='text/html' href='http://c-ohjelmoijanajatuksia.blogspot.com/2010/06/lisaa-tyokaluja-c-koodina.html' title='Lisää työkaluja C-koodina.'/><author><name>Maz</name><uri>http://www.blogger.com/profile/03774313944609044806</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4818234838483192587.post-2944662655124497083</id><published>2009-04-16T21:49:00.013+03:00</published><updated>2009-05-04T20:18:42.331+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='otus C'/><category scheme='http://www.blogger.com/atom/ns#' term='C'/><category scheme='http://www.blogger.com/atom/ns#' term='olio-ohjelmointi'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>C on olio-ohjelmointi kieli! (osa 2)</title><content type='html'>*********** EDIT ************&lt;br /&gt;Kirjoituksen lopussa olevat koodit on nyt lisätty zippiin, jonka voit ladata sivun oikeasta sarakkeesta. (Koodi ei todellakaan ole luettavaa blogisivulla...)&lt;br /&gt;*****************************&lt;br /&gt;&lt;br /&gt;Jos joku teistä on lukenut Englanninkielistä blogiani, niin tämä juttu ei anna juuri lisää tietoa teille. Oikeastaan olen lisännyt vain lotto koodin joka löytyy aivan tekstin lopusta.&lt;br /&gt;&lt;br /&gt;Vuosien saatossa minua on SUUNNATTOMASTI alkanut ärsyttää C++ ja JAVA koodareiden olio-ohjelmointi hypetys. Keskustelupalstat ovat täynnä teinipoikia, jotka kertovat siitä, miten C++ (tai JAVA) hakkaa C:n mennen tullen. Ja perusteluksi heille tuntuu riittävän se, että C++ ja JAVA ovat olio-ohjelmointikieliä.&lt;br /&gt;&lt;br /&gt;Siis kertauksena: C++ koodarit tuppaavat uskomaan (ja tuputtamaan uskomustaan pahaa aavistamattomille aloittelijoille), että C++ on loistava kieli olio-ominaisuuksiensa takia, ja että C vastaavasti on huono kieli, koska se ei ole olio-ohjelmointiin soveltuva.&lt;br /&gt;&lt;br /&gt;No nyt on korkea aika kertoa teille kaikille oliohypettäjille, että myös C tukee olio-ohjelmointia. Repikää siitä.&lt;br /&gt;&lt;br /&gt;Lisäksi C on kaukana huonosta kielestä. Maailman ehkä parhaat ohjelmat kun on kirjoitettu C-kielellä, ja lisää C koodia tehdään edelleen päivittäin. Tiesitkö, että esim. Linux on kirjoitettu C:llä? Lisäksi suurin osa sulautettujen järjestelmien softista on tehty C:llä. Tiesitkö myös että nykyaikaisessa autossa lieenee saman verran koodirivejä kuin windows XP:ssä? Pelottava ajatus...&lt;br /&gt;&lt;br /&gt;Joo, Vesa, koetahan ottaa happea, muutut pikkuhiljaa punaiseksi...&lt;br /&gt;&lt;br /&gt;Te jotka juuri ja juuri putositte penkiltä järkytyksestä, ja olette jo lähdössä hakemaan erinäisiä teräaseita opettaaksenne minulle tapoja/oikaistaksenne harhaoppejani, miettikää ensin hetki, mitkä piirteet saavat ohjelmointikielestä oliokielen. Sillävälin minä haen kahvia...&lt;br /&gt;&lt;br /&gt;Itse asiassa, C++:ssa on vain muutamia asioita, joita ei C:llä voi tehdä (syntaksi on tietysti erilainen. mutta samat ideat voidaan toteuttaa). Tärkeimpinä rajoituksina tulee mieleen operaattoreiden ylikuormaus, templatet ja destructorit. Näitä kun ei paljaalla C:llä oikein voi tehdä.&lt;br /&gt;&lt;br /&gt;Mitä se Vesa huutelee? Ai raivoat jotain luokista, perinnästä ja virtuaalisuudesta? Otahan taas happea, kerron kohta.&lt;br /&gt;&lt;br /&gt;Löysittekö muuten vastaukset kysymykseeni, että mikä tekee kielestä oliokielen? Tärkeimpinä piirteinä sanotaan olevan datan ja toiminnallisuuden paketointi samaan pakettiin, sekä instanssien luonti. (Ts. luokka joka kokoaa muuttujat ja funktiot, ja josta voidaan luoda mielin määrin instansseja.)&lt;br /&gt;&lt;br /&gt;Englanninkielisestä blogistani kopioidut esimerkit menevät näin:&lt;br /&gt;&lt;br /&gt;C++:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;const int AGE_STOMACH_RATIO 5&lt;br /&gt;class dog&lt;br /&gt;{&lt;br /&gt;private:&lt;br /&gt;EColour furColour;&lt;br /&gt;int age;&lt;br /&gt;unsigned int stomach_state;&lt;br /&gt;bool check_condition();&lt;br /&gt;int calculateStomachCapacity();&lt;br /&gt;void die();&lt;br /&gt;public:&lt;br /&gt;dog(EColour colour);&lt;br /&gt;void feed(int food_amount);&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;dog::dog(EColour colour)&lt;br /&gt;{&lt;br /&gt;furColour=colour;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;bool dog::check_condition()&lt;br /&gt;{&lt;br /&gt;if(calculateStomachCapacity() &amp;lt;&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Ja vastaava C:llä:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;struct Sdog;&lt;br /&gt;int dog_check_condition(Sdog *_this);&lt;br /&gt;&lt;br /&gt;typedef int (*check_conditionF)(Sdog *_this);&lt;br /&gt;typedef int (*calculateStomachCapacityF)(Sdog *_this);&lt;br /&gt;typedef void (*dieF)(void);&lt;br /&gt;typedef void (*dogF)(Sdog *_this, EColour colour);&lt;br /&gt;typedef void (*feedF)(Sdog *_this);&lt;br /&gt;typedef struct Sdog&lt;br /&gt;{&lt;br /&gt;   EColour furColour;&lt;br /&gt;   int age;&lt;br /&gt;   unsigned int stomach_state;&lt;br /&gt;   check_conditionF check_condition;&lt;br /&gt;   calculateStomachCapacityF calculateStomachCapacity;&lt;br /&gt;   dieF die;&lt;br /&gt;   dogF dog;&lt;br /&gt;   feedF feed;&lt;br /&gt;}Sdog;&lt;br /&gt;&lt;br /&gt;int dog_check_condition(Sdog *_this)&lt;br /&gt;{&lt;br /&gt;    //starvation or overeating&lt;br /&gt;    if(_this-&amp;ft;stomach_state == 0 ||&lt;br /&gt;      _this-&amp;gt;calculateStomachCapacity(_this) &lt;br /&gt;      &amp;lt;stomach_state &lt;br /&gt;    )&lt;br /&gt;        return 0;&lt;br /&gt;    return 1;&lt;br /&gt;}&lt;br /&gt;int dog_&lt;br /&gt;void initDog(Sdog *_this, EColour colour)&lt;br /&gt;{&lt;br /&gt;   _this-&amp;gt;furColour=colour;&lt;br /&gt;   _this-&amp;gt;age=1;&lt;br /&gt;   _this-&amp;gt;stomach_state=3;&lt;br /&gt;   _this-&amp;gt;check_condition=&amp;dog_check_condition;&lt;br /&gt;   .&lt;br /&gt;   .&lt;br /&gt;   .&lt;br /&gt;   and so on..&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Toivottavasti näette mitä ajan takaa :)&lt;br /&gt;&lt;br /&gt;Mitenkäs sitten se seuraava, olio-ohjelmoinnin hiiiiieno paradigma? (Olen aina halunnut päästä käyttämään sanaa paradigma :) )&lt;br /&gt;&lt;br /&gt;Instanssin luonti:&lt;br /&gt;&lt;br /&gt;Nyt siis pitäisi voida tuosta dog "luokasta" kaulita haukkuja vaikka talon täydeltä...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt;  Sdog *lassie;&lt;br /&gt;&lt;br /&gt;  lassie=malloc(sizeof(Sdog));&lt;br /&gt;  lassie-&amp;amp;gtMdog=&amp;initDog;&lt;br /&gt;  *(lassie-&amp;gt;dog)(lassie,EColor_brown);&lt;br /&gt;  *(lassie-&amp;gt;feed)(lasie,2);&lt;br /&gt;  .&lt;br /&gt;  .&lt;br /&gt;  // or as a local variable:&lt;br /&gt;  /*&lt;br /&gt;  Sdog lassie;&lt;br /&gt;  *lassie.dog=&amp;initDog;&lt;br /&gt;  *lassie.dog(&amp;amp;lassie,EColor_brown);&lt;br /&gt;  *lassie.feed(&amp;amp;lassie,2);&lt;br /&gt;  .&lt;br /&gt;  .&lt;br /&gt;  */&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Hei Vesa, miksi noin hiljaa? Veikö koira kielen? ;)&lt;br /&gt;Seuraavaksi sitten ehkä heikoimmin C:ssä toimiva OOP vaatimus,&lt;br /&gt;&lt;br /&gt;tiedon "kapselointi":&lt;br /&gt;&lt;br /&gt;Tässä on kyllä hieman huijauksen makua, mutta tarkoitushan olikin vain näyttää, että C:llä PYSTYY kirjoittamaan olioajatukset täyttävää koodia. Jaamme siis Sdog struktin kahteen osaan, julkiseen ja yksityiseen struktiin.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;typedef struct Sdog&lt;br /&gt;{&lt;br /&gt;  dogF dog;&lt;br /&gt;  feedF feed;&lt;br /&gt;  char internalData[1];&lt;br /&gt;}Sdog;&lt;br /&gt;&lt;br /&gt;typedef struct S_dog&lt;br /&gt;{&lt;br /&gt;//As abowe.&lt;br /&gt;}S_dog;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Nyt sijoitamme julkisen struktin esittelyn&lt;br /&gt;(structin joka siis sisältää funktiot ja datan, jota oliomme&lt;br /&gt;käyttäjän on päästävä käsittelemään), headeriin, joka tarjotaan&lt;br /&gt;oliomme käyttäjälle. Privaatti structin taas esittelemme&lt;br /&gt;headerissa, jota ei tarjota asiakkaallemme. .c tiedostossa&lt;br /&gt;jossa otustamme sitten käsittelemme, inkuloimme molemmat&lt;br /&gt;headerit. (Tai jos emme jaa oliomme toteutusta useampaan&lt;br /&gt;tiedostoon, voimme jopa esitellä privaatti osat c-tiedostossamme,&lt;br /&gt;ja tehdä funktioista sekä mahdollisista globaaleistamme staticeja&lt;br /&gt;- silloin kapselointi toimii erinomaisesti).&lt;br /&gt;&lt;br /&gt;Teemme siis dog_init() funktion, joka varaa tilan koiruudelle, ja&lt;br /&gt;palauttaa osoittimen luotuun olioon käyttäjälle. Instanssia&lt;br /&gt;tehtäessa siis allokoimme muistiköntin, johon sopii sekä public,&lt;br /&gt;että privaatti struktit, ja sijoitamme ne peräkäin, siten että&lt;br /&gt;julkinen osa tulee köntin alkuun. Käyttäjälle palautamme osoittimen&lt;br /&gt;julkisen struktin alkuun. Tai voimme myös käyttää realloccia, ja&lt;br /&gt;kasvattaa käyttäjän varaamaa oliota, mikäli emme halua lähteä&lt;br /&gt;puhtaalle osoitinlinjalle.&lt;br /&gt;&lt;br /&gt;placeholder[1] joka esimerkissä näkyy, on tehty helpoittamaan&lt;br /&gt;struktin käyttöä. castaamalla osoittimen placeholderiin privaatti&lt;br /&gt;struktiksi, pääsemme käsittelemään oikaa kohtaa ilman&lt;br /&gt;laskutoimituksia.&lt;br /&gt;&lt;br /&gt;Ai keksitkö Vesa jotain uutta, vai mikä noin hymyilyttää? Että&lt;br /&gt;siis mitenkä C:llä hoidetaan...&lt;br /&gt;&lt;br /&gt;Periytys:&lt;br /&gt;&lt;br /&gt;Helppoa kuin heinän teko. Edellistä esimerkkiä jatkaakseni,&lt;br /&gt;määrittelemme nyt structin Sdoberman, jonne sijoitamme&lt;br /&gt;dobermannille ominaisen tiedon ja toiminnallisuuden. Yleiset&lt;br /&gt;koiramatskut pidämme Sdogissa. Sitten edellisen esimerkin tapaan,&lt;br /&gt;sijoitamme Sdoberman structin Sdog structin perään, ja lähestymme&lt;br /&gt;asiaa osoittimin. Tahdottaessa käyttää geneerisiä koiramaisuuksia,&lt;br /&gt;käytämme Sdog struktia, ja dobermannille ominaisissa asioissa&lt;br /&gt;asioimme Sdobermannin kanssa. Eli osoitteiden laskentaa ja casteja.&lt;br /&gt;Kyllä se siitä lähtee.&lt;br /&gt;&lt;br /&gt;Ja vielä kuuluu Vesasta pihinää... Mitä...? Ok.&lt;br /&gt;&lt;br /&gt;Virtuaali luokat:&lt;br /&gt;&lt;br /&gt;Pysytään edelleen koiramaisissa tunnelmissa. Eli pidämme geneerisen&lt;br /&gt;Sdog structimme, ja teemme lisäksi Sdoberman, Schihuahua ja&lt;br /&gt;Ssekarotuinen struktit. Lisäämme geneeriseen struktiimme kentän,&lt;br /&gt;joka kertoo minkätyyppinen koira on kyseessä (esim. enumin). Sitten&lt;br /&gt;chihuahuaa, dobermannia tai sekarotuista luotaessa, täytämme&lt;br /&gt;tyyppitiedon oikein. Kaikki kutsut tehdään jälleen Sdog struktia&lt;br /&gt;käyttäen, mutta virtuaalitoiminnot suoritetaan Sdoberman, Schihuahua&lt;br /&gt;ja Ssekarotuinen struktista, tyypin tarkistuksen ja oikean&lt;br /&gt;castauksen jälkeen.&lt;br /&gt;&lt;br /&gt;Vielä pari sanaa Destructoreista ja C:stä.&lt;br /&gt;&lt;br /&gt;Oletko ikinä kuullut sanaa "memory pool"? Hyyyvin usein varsinkin&lt;br /&gt;linux maailmassa, joudutaan köntti muistia allokoimaan johonkin&lt;br /&gt;tarkoitukseen, ja jakamaan siitä palasia aina pyydettäessä.&lt;br /&gt;Esimerkkinä olkoon vaikka jaettu muisti prosessien välillä, johon&lt;br /&gt;siis päästään kirjoittamaan useammasta kuin yhdestä prosessista&lt;br /&gt;yhtä aikaa. Tällöin on tarpeen pitää kirjaa siitä, mitkä palikat&lt;br /&gt;muistista on milloinkin käytössä, ettei useampi prosessi kirjoittele&lt;br /&gt;samaan kohtaan omia juttujaan.&lt;br /&gt;&lt;br /&gt;Ehkä tyypillisin tapa tehdä tämä on:&lt;br /&gt;1. Allokoida köntti.&lt;br /&gt;2. jakaa se pienempiin palasiin, ja tehdä kirjanpito siitä, mitkä&lt;br /&gt;palat ovat käytössä.&lt;br /&gt;&lt;br /&gt;Käyttäjän pyytäessä muistia, lasketaan montako palasta käyttäjän&lt;br /&gt;tarvitsema tila vie, etsitään vapaat palat ja merkitään ne&lt;br /&gt;varatuiksi, kirjoitetaan ensimmäisen palan alkuun määrä jonka&lt;br /&gt;käyttäjä varasi ja palautetaan käyttäjälle osoitin tämän "headerin"&lt;br /&gt;(kirjanpidon) jälkeiseen muistiosoitteeseen. Käyttäjä sitten käyttää&lt;br /&gt;muistia miten tahtoo, ja lopulta pyytää vapauttamaan muistin.&lt;br /&gt;Vapautettaessa sitten tutkitaan käyttäjän antamaa "vapautettavaa&lt;br /&gt;osoitetta" edeltävä paikka, johon kirjoitimme varatun määrän, ja&lt;br /&gt;merkitsemme vastaavat palaset jälleen vapaiksi.&lt;br /&gt;&lt;br /&gt;Nyt tähän sisäiseen kirjanpitoon voidaan lisätä tila&lt;br /&gt;funktio-osoittimelle, ja antaa palan varanneen käyttäjän rekisteröidä&lt;br /&gt;callback funktio, jonka osoite siis tallennetaan tähän headeriin.&lt;br /&gt;Käyttäjän nyt pyytäessä muistipaikan vapautusta, suoritetaan samalla&lt;br /&gt;Em. callback, ja voila. Meillä on destructoin tapainen ominaisuus&lt;br /&gt;C:ssä. Tämä kuitenkin eroaa destructorista siinä, että mikäli&lt;br /&gt;käyttäjä instantioi otuksensa stackista, eikä näinollen joudu sitä&lt;br /&gt;erikseen vapauttamaan, me emme voi tehdä destructoria&lt;br /&gt;(ainakaan helposti).&lt;br /&gt;&lt;br /&gt;LottoEsimerkki:&lt;br /&gt;&lt;br /&gt;Ja loppuun vielä esimerkki jonka tekaisin äkkipäätä väitteitäni&lt;br /&gt;tukemaan. Hirveän kauhean inhottavan näköinen lottokötöstys-C-olio,&lt;br /&gt;joka siis arpoo lottonumeroita. Ikävä kyllä, minulla ei ole mitään&lt;br /&gt;muistikuvaa siitä, montako numeroa Viking lotossa oikeasti on, eikä&lt;br /&gt;myöskään jokereiden määrästä, joten lotto-otukseni ei liene ihan&lt;br /&gt;vastaa todellisuutta. (tosin defineitä muuttamalla siitä pitäisi&lt;br /&gt;tulla toimiva ;) ) Ainoa julkinen (käyttäjälle tarkoitettu) headeri&lt;br /&gt;on nyt lotto.h&lt;br /&gt;&lt;br /&gt;Huom! Kapselointi paranisi mikäli peruslotto.c ja vikinglotto.c&lt;br /&gt;olisi sisällytetty lotto.c tiedostoon, ja static avainsanaa olisi&lt;br /&gt;käytetty näiden kanssa. Selkeyden vuoksi laitoin ne kuitenkin omiin&lt;br /&gt;tiedostoihinsa, ja tein niistä siis globaaleja funktioita. Lisäksi&lt;br /&gt;tilanne paranisi mikäli muista tiedostoista paitsi lottoTesti.c:stä&lt;br /&gt;käännettäisiin kirjasto, ja käyttäjälle annettaisiin vain kirjasto&lt;br /&gt;ja lotto.h headeri käyttöön.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;/* ******************************************************** */&lt;br /&gt;/*                                                          *&lt;br /&gt;*      Implementation of a lotto engine written in C        *&lt;br /&gt;*      Main purpose is to demonstrate that C can be used as *&lt;br /&gt;*      object oriented programming language.               *&lt;br /&gt;*                                                          *&lt;br /&gt;*      Written by  Maz (2009)                              *&lt;br /&gt;*      http://maz-programmersdiary.blogspot.com/           *&lt;br /&gt;*      http://c-ohjelmoijanajatuksia.blogspot.com/         *&lt;br /&gt;*                                                          *&lt;br /&gt;*      You're free to use this piece of code.              *&lt;br /&gt;*      You can also modify it freely, but if you           *&lt;br /&gt;*      improve this, you must write the improved code      *&lt;br /&gt;*      in comments at:                                     *&lt;br /&gt;*      http://maz-programmersdiary.blogspot.com/           *&lt;br /&gt;*      or at:                                              *&lt;br /&gt;*      http://c-ohjelmoijanajatuksia.blogspot.com/         *&lt;br /&gt;*      or mail the corrected version to me at              *&lt;br /&gt;*      Mazziesaccount@gmail.com                            *&lt;br /&gt;*                                                          *&lt;br /&gt;*      I do not want to enforce any copyright for using    *&lt;br /&gt;*      this piece unmodified, but I would be glad if I     *&lt;br /&gt;*      heard of you - in case this was informative/usefull *&lt;br /&gt;*      to you.                                             *&lt;br /&gt;*                                                          *&lt;br /&gt;*      Revision History:                                   *&lt;br /&gt;*                                                          *&lt;br /&gt;*      -v0.0.1 16.09.2008/Maz                              *&lt;br /&gt;*                                                          */&lt;br /&gt;/* ******************************************************** */&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;lotto.h:&lt;br /&gt;&lt;br /&gt;#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;&lt;br /&gt;#ifndef LOTTOKONE_H&lt;br /&gt;#define LOTTOKONE_H&lt;br /&gt;&lt;br /&gt;struct lottokone;&lt;br /&gt;&lt;br /&gt;typedef struct lottotulos&lt;br /&gt;{&lt;br /&gt;int lottoNroMaara;&lt;br /&gt;int jokeriNroMaara;&lt;br /&gt;int *lottonumerot;&lt;br /&gt;int *jokeri;&lt;br /&gt;}lottotulos;&lt;br /&gt;&lt;br /&gt;typedef enum ElottoType&lt;br /&gt;{&lt;br /&gt;ElottoType_Perus = 0,&lt;br /&gt;ElottoType_Viking,&lt;br /&gt;ElottoType_PerusJaJokeri,&lt;br /&gt;ElottoType_VikingJaJokeri,&lt;br /&gt;ElottoType_Max&lt;br /&gt;}ElottoType;&lt;br /&gt;&lt;br /&gt;struct lottokone *lotto_luo( ElottoType lotonTyyli);&lt;br /&gt;typedef void (*tuhoF)(struct lottokone **lotto);&lt;br /&gt;&lt;br /&gt;typedef void (*arvontaF)(struct lottokone *lotto);&lt;br /&gt;typedef lottotulos (*tulosF)(struct lottokone *lotto);&lt;br /&gt;&lt;br /&gt;typedef struct lottokone&lt;br /&gt;{&lt;br /&gt;ElottoType ltype;&lt;br /&gt;arvontaF arvo;&lt;br /&gt;tulosF haeArvonnanTulos;&lt;br /&gt;tuhoF lotto_tuhoa;&lt;br /&gt;}lottokone;&lt;br /&gt;&lt;br /&gt;#endif&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;lottoTesti.c:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/* ******************************************************** */&lt;br /&gt;/*                                                          *&lt;br /&gt;*      Implementation of a lotto engine written in C        *&lt;br /&gt;*      Main purpose is to demonstrate that C can be used as *&lt;br /&gt;*      object oriented programming language.               *&lt;br /&gt;*                                                          *&lt;br /&gt;*      Written by  Maz (2009)                              *&lt;br /&gt;*      http://maz-programmersdiary.blogspot.com/           *&lt;br /&gt;*      http://c-ohjelmoijanajatuksia.blogspot.com/         *&lt;br /&gt;*                                                          *&lt;br /&gt;*      You're free to use this piece of code.              *&lt;br /&gt;*      You can also modify it freely, but if you           *&lt;br /&gt;*      improve this, you must write the improved code      *&lt;br /&gt;*      in comments at:                                     *&lt;br /&gt;*      http://maz-programmersdiary.blogspot.com/           *&lt;br /&gt;*      or at:                                              *&lt;br /&gt;*      http://c-ohjelmoijanajatuksia.blogspot.com/         *&lt;br /&gt;*      or mail the corrected version to me at              *&lt;br /&gt;*      Mazziesaccount@gmail.com                            *&lt;br /&gt;*                                                          *&lt;br /&gt;*      I do not want to enforce any copyright for using    *&lt;br /&gt;*      this piece unmodified, but I would be glad if I     *&lt;br /&gt;*      heard of you - in case this was informative/usefull *&lt;br /&gt;*      to you.                                             *&lt;br /&gt;*                                                          *&lt;br /&gt;*      Revision History:                                   *&lt;br /&gt;*                                                          *&lt;br /&gt;*      -v0.0.1 16.09.2008/Maz                              *&lt;br /&gt;*                                                          */&lt;br /&gt;/* ******************************************************** */&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#include "lotto.h"&lt;br /&gt;#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;int main(void)&lt;br /&gt;{&lt;br /&gt;int i;&lt;br /&gt;lottokone *lotto;&lt;br /&gt;lottotulos tulos;&lt;br /&gt;lotto=lotto_luo(ElottoType_Perus);&lt;br /&gt;lotto-&amp;gt;arvo(lotto);&lt;br /&gt;tulos=lotto-&amp;gt;haeArvonnanTulos(lotto);&lt;br /&gt;/*&lt;br /&gt;Ollaksemme Wielä Enemmän OlioOtuksia, voisimme &lt;br /&gt;tunkea tulos structiin sekalaisen joukon funktioita, &lt;br /&gt;joilla arvotut numerot voisi esim. tulostaa&lt;br /&gt;*/&lt;br /&gt;for(i=0;i&amp;lt;tulos.lottoNroMaara;i++)&lt;br /&gt;{&lt;br /&gt;printf("nro %d = %u\n",i+1,tulos.lottonumerot[i]);&lt;br /&gt;}&lt;br /&gt;return 0;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;lotto_sisainen.h&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/* ******************************************************** */&lt;br /&gt;/*                                                          *&lt;br /&gt;*      Implementation of a lotto engine written in C        *&lt;br /&gt;*      Main purpose is to demonstrate that C can be used as *&lt;br /&gt;*      object oriented programming language.               *&lt;br /&gt;*                                                          *&lt;br /&gt;*      Written by  Maz (2009)                              *&lt;br /&gt;*      http://maz-programmersdiary.blogspot.com/           *&lt;br /&gt;*      http://c-ohjelmoijanajatuksia.blogspot.com/         *&lt;br /&gt;*                                                          *&lt;br /&gt;*      You're free to use this piece of code.              *&lt;br /&gt;*      You can also modify it freely, but if you           *&lt;br /&gt;*      improve this, you must write the improved code      *&lt;br /&gt;*      in comments at:                                     *&lt;br /&gt;*      http://maz-programmersdiary.blogspot.com/           *&lt;br /&gt;*      or at:                                              *&lt;br /&gt;*      http://c-ohjelmoijanajatuksia.blogspot.com/         *&lt;br /&gt;*      or mail the corrected version to me at              *&lt;br /&gt;*      Mazziesaccount@gmail.com                            *&lt;br /&gt;*                                                          *&lt;br /&gt;*      I do not want to enforce any copyright for using    *&lt;br /&gt;*      this piece unmodified, but I would be glad if I     *&lt;br /&gt;*      heard of you - in case this was informative/usefull *&lt;br /&gt;*      to you.                                             *&lt;br /&gt;*                                                          *&lt;br /&gt;*      Revision History:                                   *&lt;br /&gt;*                                                          *&lt;br /&gt;*      -v0.0.1 16.09.2008/Maz                              *&lt;br /&gt;*                                                          */&lt;br /&gt;/* ******************************************************** */&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#ifndef SISAINEN_LOTTO_H&lt;br /&gt;#define SISAINEN_LOTTO_H&lt;br /&gt;&lt;br /&gt;#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;#include "lotto.h"&lt;br /&gt;#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;&lt;br /&gt;#define LOTTO_NRO_MAARA 10&lt;br /&gt;#define VIKINGLOTTO_NRO_MAARA 6&lt;br /&gt;#define JOKERI_NRO_MAARA 7&lt;br /&gt;#define VIKINGJOKERI_NRO_MAARA 7&lt;br /&gt;#define LOTTO_MAX 45&lt;br /&gt;#define VIKINGLOTTO_MAX 45&lt;br /&gt;&lt;br /&gt;typedef struct SlottoPerus&lt;br /&gt;{&lt;br /&gt;lottokone virtlotto;&lt;br /&gt;int jokerimukana; &lt;br /&gt;/* itse asiassa kaikille lottotyypeille &lt;br /&gt;voitaisiin käyttää samaa typedef structia, tai C++ &lt;br /&gt;tyyliin, esim jokerimukana muuttuja voitaisiin siirtää &lt;br /&gt;"base" luokkaan (eli lottokone structiin, tai muuhun &lt;br /&gt;yhteiseen (sisäiseen) structiin) &lt;br /&gt;*/&lt;br /&gt;arvontaF arvo;&lt;br /&gt;tulosF haeArvonnanTulos;&lt;br /&gt;char tulosplaceholder[1];&lt;br /&gt;}SlottoPerus;&lt;br /&gt;&lt;br /&gt;typedef struct SlottoViking&lt;br /&gt;{&lt;br /&gt;lottokone virtlotto;&lt;br /&gt;int jokerimukana;&lt;br /&gt;arvontaF arvo;&lt;br /&gt;tulosF haeArvonnanTulos;&lt;br /&gt;char tulosplaceholder[1];&lt;br /&gt;&lt;br /&gt;}SlottoViking;&lt;br /&gt;&lt;br /&gt;typedef struct SperusNumerot&lt;br /&gt;{&lt;br /&gt;int lottoNumerot[LOTTO_NRO_MAARA];&lt;br /&gt;}SperusNumerot;&lt;br /&gt;&lt;br /&gt;typedef struct SvikingNumerot&lt;br /&gt;{&lt;br /&gt;int vikingNumerot[VIKINGLOTTO_NRO_MAARA];&lt;br /&gt;}SvikingNumerot;&lt;br /&gt;&lt;br /&gt;typedef struct SperusJokeriNumerot&lt;br /&gt;{&lt;br /&gt;int jokeriNumerot[JOKERI_NRO_MAARA];&lt;br /&gt;}SperusJokeriNumerot;&lt;br /&gt;&lt;br /&gt;typedef struct SvikingJokeriNumerot&lt;br /&gt;{&lt;br /&gt;int vikinJokeriNumerot[VIKINGJOKERI_NRO_MAARA];&lt;br /&gt;}SvikingJokeriNumerot;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;lottotulos vikingtulos(lottokone *siht_);&lt;br /&gt;void vikingarvonta(lottokone *siht_);&lt;br /&gt;void perusarvonta(lottokone *siht_);&lt;br /&gt;lottotulos perustulos(lottokone *siht_);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#endif&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;lotto.c&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/* ******************************************************** */&lt;br /&gt;/*                                                          *&lt;br /&gt;*      Implementation of a lotto engine written in C        *&lt;br /&gt;*      Main purpose is to demonstrate that C can be used as *&lt;br /&gt;*      object oriented programming language.               *&lt;br /&gt;*                                                          *&lt;br /&gt;*      Written by  Maz (2009)                              *&lt;br /&gt;*      http://maz-programmersdiary.blogspot.com/           *&lt;br /&gt;*      http://c-ohjelmoijanajatuksia.blogspot.com/         *&lt;br /&gt;*                                                          *&lt;br /&gt;*      You're free to use this piece of code.              *&lt;br /&gt;*      You can also modify it freely, but if you           *&lt;br /&gt;*      improve this, you must write the improved code      *&lt;br /&gt;*      in comments at:                                     *&lt;br /&gt;*      http://maz-programmersdiary.blogspot.com/           *&lt;br /&gt;*      or at:                                              *&lt;br /&gt;*      http://c-ohjelmoijanajatuksia.blogspot.com/         *&lt;br /&gt;*      or mail the corrected version to me at              *&lt;br /&gt;*      Mazziesaccount@gmail.com                            *&lt;br /&gt;*                                                          *&lt;br /&gt;*      I do not want to enforce any copyright for using    *&lt;br /&gt;*      this piece unmodified, but I would be glad if I     *&lt;br /&gt;*      heard of you - in case this was informative/usefull *&lt;br /&gt;*      to you.                                             *&lt;br /&gt;*                                                          *&lt;br /&gt;*      Revision History:                                   *&lt;br /&gt;*                                                          *&lt;br /&gt;*      -v0.0.1 16.09.2008/Maz                              *&lt;br /&gt;*                                                          */&lt;br /&gt;/* ******************************************************** */&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;//#include "lotto.h"&lt;br /&gt;#include "lotto_sisainen.h"&lt;br /&gt;&lt;br /&gt;static lottotulos virtuaalitulos(lottokone *lotto);&lt;br /&gt;static void virtuaaliarvonta(lottokone *_this);&lt;br /&gt;&lt;br /&gt;static void lotto_tuhoa(lottokone **lotto)&lt;br /&gt;{&lt;br /&gt;free(*lotto);&lt;br /&gt;lotto=NULL;&lt;br /&gt;}&lt;br /&gt;lottokone *lotto_luo(ElottoType lotonTyyli)&lt;br /&gt;{&lt;br /&gt;void *paluuarvo;&lt;br /&gt;switch(lotonTyyli)&lt;br /&gt;{&lt;br /&gt;case ElottoType_Perus:&lt;br /&gt;{&lt;br /&gt; SlottoPerus *peruslotto;&lt;br /&gt; peruslotto=malloc&lt;br /&gt; (&lt;br /&gt;  sizeof(SlottoPerus)+sizeof(SperusNumerot)-1&lt;br /&gt; );&lt;br /&gt; paluuarvo=(void *)peruslotto;  if(peruslotto==NULL)&lt;br /&gt; {&lt;br /&gt;  printf("lotto_alusta() : pannahinen, muisti loppu?");&lt;br /&gt;  break;&lt;br /&gt; }&lt;br /&gt; peruslotto-&amp;gt;virtlotto.ltype=lotonTyyli;&lt;br /&gt; peruslotto-&amp;gt;virtlotto.lotto_tuhoa=&amp;amp;lotto_tuhoa;&lt;br /&gt; peruslotto-&amp;gt;virtlotto.arvo=&amp;virtuaaliarvonta;&lt;br /&gt; peruslotto-&amp;gt;virtlotto.haeArvonnanTulos=&amp;virtuaalitulos;&lt;br /&gt; peruslotto-&amp;gt;jokerimukana=0; //tämä on kyllä huijausta, en jakasa leikkiä olioihmistä ja tehdä jokeri oliota, tahi eri oliota lotolle ja lotolle jokerin kera...&lt;br /&gt; peruslotto-&amp;gt;arvo=&amp;perusarvonta;&lt;br /&gt; peruslotto-&amp;gt;haeArvonnanTulos=&amp;perustulos;&lt;br /&gt; break;&lt;br /&gt;}&lt;br /&gt;case ElottoType_Viking:&lt;br /&gt;{&lt;br /&gt; SlottoViking *vikinglotto;&lt;br /&gt; vikinglotto=malloc&lt;br /&gt; (&lt;br /&gt;   sizeof(SlottoViking)+&lt;br /&gt;   sizeof(SvikingNumerot)-1&lt;br /&gt; );&lt;br /&gt; paluuarvo=(void *)vikinglotto;&lt;br /&gt; if(vikinglotto==NULL)&lt;br /&gt; {&lt;br /&gt;  printf("lotto_alusta() : pannahinen, muisti loppu?");&lt;br /&gt;  break;&lt;br /&gt; }&lt;br /&gt; vikinglotto-&amp;gt;virtlotto.ltype=lotonTyyli;&lt;br /&gt; vikinglotto-&amp;gt;virtlotto.lotto_tuhoa=&amp;amp;lotto_tuhoa; &lt;br /&gt; vikinglotto-&amp;gt;virtlotto.arvo=&amp;virtuaaliarvonta;&lt;br /&gt; vikinglotto-&amp;gt;virtlotto.haeArvonnanTulos=&amp;virtuaalitulos;&lt;br /&gt; vikinglotto-&amp;gt;jokerimukana=0; &lt;br /&gt;/*&lt;br /&gt;  tämä on kyllä huijausta, en jakasa leikkiä olioihmistä ja&lt;br /&gt;  tehdä jokeri oliota, &lt;br /&gt;  tahi eri oliota lotolle ja lotolle jokerin kera...&lt;br /&gt;*/&lt;br /&gt; vikinglotto-&amp;gt;arvo=&amp;vikingarvonta;&lt;br /&gt; vikinglotto-&amp;gt;haeArvonnanTulos=&amp;vikingtulos;&lt;br /&gt; break;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;break;&lt;br /&gt;case ElottoType_PerusJaJokeri:&lt;br /&gt;{&lt;br /&gt; SlottoPerus *peruslotto;&lt;br /&gt; peruslotto=malloc&lt;br /&gt; (&lt;br /&gt;   sizeof(SlottoPerus)+&lt;br /&gt;   sizeof(SperusNumerot)+&lt;br /&gt;   sizeof(SperusJokeriNumerot)-1&lt;br /&gt; );&lt;br /&gt; paluuarvo=(void *)peruslotto;&lt;br /&gt; if(peruslotto==NULL)&lt;br /&gt; {&lt;br /&gt;  printf("lotto_alusta() : pannahinen, muisti loppu?");&lt;br /&gt;  break;&lt;br /&gt; }&lt;br /&gt; peruslotto-&amp;gt;virtlotto.ltype=lotonTyyli;&lt;br /&gt; peruslotto-&amp;gt;virtlotto.arvo=&amp;virtuaaliarvonta;&lt;br /&gt; peruslotto-&amp;gt;virtlotto.lotto_tuhoa=&amp;amp;lotto_tuhoa;&lt;br /&gt; peruslotto-&amp;gt;virtlotto.haeArvonnanTulos=&amp;virtuaalitulos;&lt;br /&gt; peruslotto-&amp;gt;jokerimukana=1; &lt;br /&gt; //tämä oli myös huijausta&lt;br /&gt; peruslotto-&amp;gt;arvo=&amp;perusarvonta;&lt;br /&gt; peruslotto-&amp;gt;haeArvonnanTulos=&amp;perustulos;&lt;br /&gt; break;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;break;&lt;br /&gt;case ElottoType_VikingJaJokeri:&lt;br /&gt;{&lt;br /&gt; SlottoViking *vikinglotto;&lt;br /&gt; vikinglotto=malloc&lt;br /&gt; (&lt;br /&gt;   sizeof(SlottoViking)+&lt;br /&gt;   sizeof(SvikingNumerot)+&lt;br /&gt;   sizeof(SvikingJokeriNumerot)-1&lt;br /&gt; );&lt;br /&gt; paluuarvo=(void *)vikinglotto;&lt;br /&gt; if(vikinglotto==NULL)&lt;br /&gt; {&lt;br /&gt;  printf("lotto_alusta() : pannahinen, muisti loppu?");&lt;br /&gt;  break;&lt;br /&gt; }&lt;br /&gt; vikinglotto-&amp;gt;virtlotto.ltype=lotonTyyli;&lt;br /&gt; vikinglotto-&amp;gt;virtlotto.lotto_tuhoa=&amp;amp;lotto_tuhoa;&lt;br /&gt; vikinglotto-&amp;gt;virtlotto.arvo=&amp;virtuaaliarvonta;&lt;br /&gt; vikinglotto-&amp;gt;virtlotto.haeArvonnanTulos=&amp;virtuaalitulos;&lt;br /&gt; vikinglotto-&amp;gt;jokerimukana=1;&lt;br /&gt; vikinglotto-&amp;gt;arvo=&amp;vikingarvonta;&lt;br /&gt; vikinglotto-&amp;gt;haeArvonnanTulos=&amp;vikingtulos;&lt;br /&gt; break;&lt;br /&gt;}&lt;br /&gt;break;&lt;br /&gt;default:&lt;br /&gt; printf&lt;br /&gt; (&lt;br /&gt;  "lotto_alusta() : Pannahinen, annoit olemattoman lottotyypin!\n"&lt;br /&gt;  );&lt;br /&gt; paluuarvo=NULL;&lt;br /&gt;break;&lt;br /&gt;}&lt;br /&gt;return (lottokone *)paluuarvo;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;static void virtuaaliarvonta(lottokone *_this)&lt;br /&gt;{&lt;br /&gt;if(*(unsigned int*)_this&amp;lt;ElottoType_Max)&lt;br /&gt;{&lt;br /&gt;/*&lt;br /&gt; Jos tekisin tätä helpolla ja yksinkertaisella C -&lt;br /&gt; tyylillä, tässä olisi hyppytaulu eri &lt;br /&gt; lottoarvontafunktioihin, indexoituna &lt;br /&gt; ElottoType:llä&lt;br /&gt;&lt;br /&gt; koska tämän on kuitenkin tarkoitus matkia C++:n &lt;br /&gt; virtuaaliluokkaa, kutsutaan täältä "aliluokan &lt;br /&gt; sisällä olevaa" funktiota. Nyt siis tämä if-else &lt;br /&gt; runko olisi voitu korvata seuraavalla switch&lt;br /&gt; rakenteella, mutta if-else rakenne on täällä, &lt;br /&gt; jotta hyppytaulun käyttöidea näkyisi. Ts. tässä &lt;br /&gt; voisi olla:&lt;br /&gt; arvontaF arvontafunktiot[ElottoType_Max];&lt;br /&gt; arvontafunktiot[ElottoType_Perus]=&lt;br /&gt; &amp;perusarvontafunktio;&lt;br /&gt; arvontafunktiot[ElottoType_Viking]=&lt;br /&gt; &amp;viikinkiarvontafunktio;&lt;br /&gt; arvontafunktiot[ElottoType_PerusJaJokeri]=&lt;br /&gt; &amp;perusjokeriarvontafunktio;&lt;br /&gt; arvontafunktiot[ElottoType_VikingJaJokeri]=&lt;br /&gt; &amp;viikinkijokeriarvontafunktio;&lt;br /&gt; *arvontafunktiot[*(unsigned int*)_this](_this);&lt;br /&gt;*/&lt;br /&gt;switch(*(unsigned int*)_this&amp;lt;ElottoType_Max)&lt;br /&gt;{&lt;br /&gt; case ElottoType_Perus:&lt;br /&gt;  return ((SlottoPerus*)_this)-&amp;gt;arvo(_this);&lt;br /&gt; break;&lt;br /&gt; case ElottoType_Viking:&lt;br /&gt;  return ((SlottoViking*)_this)-&amp;gt;arvo(_this);&lt;br /&gt; break;&lt;br /&gt; case ElottoType_PerusJaJokeri:&lt;br /&gt;  return ((SlottoPerus*)_this)-&amp;gt;arvo(_this); &lt;br /&gt; break;&lt;br /&gt; case ElottoType_VikingJaJokeri:&lt;br /&gt;  return ((SlottoViking*)_this)-&amp;gt;arvo(_this); &lt;br /&gt; break;&lt;br /&gt; default:&lt;br /&gt;  printf("Mahdoton Tapahtui!!! %s:%d\n",__FILE__,__LINE__);&lt;br /&gt;  fflush(stdout);&lt;br /&gt;  exit(-1);&lt;br /&gt; break;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;else&lt;br /&gt;{&lt;br /&gt;printf&lt;br /&gt;(&lt;br /&gt; "lottoarvonta : pannahinen, joku meni poskelleen, lotto olio viallinen!"&lt;br /&gt;);&lt;br /&gt;fflush(stdout);&lt;br /&gt;exit(-1);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;static lottotulos virtuaalitulos(lottokone *_this)&lt;br /&gt;{&lt;br /&gt; switch(*(unsigned int*)_this&amp;lt;ElottoType_Max)&lt;br /&gt; {&lt;br /&gt; case ElottoType_Perus:&lt;br /&gt;  return ((SlottoPerus*)_this)-&amp;gt;haeArvonnanTulos(_this);&lt;br /&gt; break;&lt;br /&gt; case ElottoType_Viking:&lt;br /&gt;  return ((SlottoViking*)_this)-&amp;gt;haeArvonnanTulos(_this);&lt;br /&gt; break;&lt;br /&gt;case ElottoType_PerusJaJokeri:&lt;br /&gt; return ((SlottoPerus*)_this)-&amp;gt;haeArvonnanTulos(_this); &lt;br /&gt;break;&lt;br /&gt;case ElottoType_VikingJaJokeri:&lt;br /&gt; return ((SlottoViking*)_this)-&amp;gt;haeArvonnanTulos(_this); &lt;br /&gt;break;&lt;br /&gt;default:&lt;br /&gt; printf&lt;br /&gt; (&lt;br /&gt;  "lottoHaeTulos : pannahinen, joku meni poskelleen, lotto olio viallinen!"&lt;br /&gt; );&lt;br /&gt; fflush(stdout);&lt;br /&gt; exit(-1);&lt;br /&gt;break;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;peruslotto.c&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/* ******************************************************** */&lt;br /&gt;/*                                                          *&lt;br /&gt;*      Implementation of a lotto engine written in C        *&lt;br /&gt;*      Main purpose is to demonstrate that C can be used as *&lt;br /&gt;*      object oriented programming language.               *&lt;br /&gt;*                                                          *&lt;br /&gt;*      Written by  Maz (2009)                              *&lt;br /&gt;*      http://maz-programmersdiary.blogspot.com/           *&lt;br /&gt;*      http://c-ohjelmoijanajatuksia.blogspot.com/         *&lt;br /&gt;*                                                          *&lt;br /&gt;*      You're free to use this piece of code.              *&lt;br /&gt;*      You can also modify it freely, but if you           *&lt;br /&gt;*      improve this, you must write the improved code      *&lt;br /&gt;*      in comments at:                                     *&lt;br /&gt;*      http://maz-programmersdiary.blogspot.com/           *&lt;br /&gt;*      or at:                                              *&lt;br /&gt;*      http://c-ohjelmoijanajatuksia.blogspot.com/         *&lt;br /&gt;*      or mail the corrected version to me at              *&lt;br /&gt;*      Mazziesaccount@gmail.com                            *&lt;br /&gt;*                                                          *&lt;br /&gt;*      I do not want to enforce any copyright for using    *&lt;br /&gt;*      this piece unmodified, but I would be glad if I     *&lt;br /&gt;*      heard of you - in case this was informative/usefull *&lt;br /&gt;*      to you.                                             *&lt;br /&gt;*                                                          *&lt;br /&gt;*      Revision History:                                   *&lt;br /&gt;*                                                          *&lt;br /&gt;*      -v0.0.1 16.09.2008/Maz                              *&lt;br /&gt;*                                                          */&lt;br /&gt;/* ******************************************************** */&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;#include &amp;lt;time.h&amp;gt;&lt;br /&gt;#include &amp;lt;string.h&amp;gt;&lt;br /&gt;#include "lotto_sisainen.h"&lt;br /&gt;&lt;br /&gt;lottotulos perustulos(lottokone *siht_)&lt;br /&gt;{&lt;br /&gt;SlottoPerus *_this = (SlottoPerus *)siht_;&lt;br /&gt;lottotulos tulos;&lt;br /&gt;memset(&amp;amp;tulos,0,sizeof(lottotulos));&lt;br /&gt;if&lt;br /&gt;(&lt;br /&gt; *(unsigned int*)_this!=ElottoType_Perus &amp;amp;&amp;amp;&lt;br /&gt; *(unsigned int*)_this!=ElottoType_PerusJaJokeri &lt;br /&gt;)&lt;br /&gt;{&lt;br /&gt; printf("perustuloshae : lottokone rikki, soittakaa Wirallisille Walvojille!!");&lt;br /&gt; fflush(stdout);&lt;br /&gt; exit(-1);&lt;br /&gt;}&lt;br /&gt;if(_this-&amp;gt;jokerimukana)&lt;br /&gt;{&lt;br /&gt; tulos.jokeriNroMaara=JOKERI_NRO_MAARA;&lt;br /&gt; tulos.jokeri=&lt;br /&gt; &amp;amp;(((int *)_this-&amp;gt;tulosplaceholder)[LOTTO_NRO_MAARA]);&lt;br /&gt;}&lt;br /&gt;tulos.lottoNroMaara=LOTTO_NRO_MAARA;&lt;br /&gt;tulos.lottonumerot=(int *)(_this-&amp;gt;tulosplaceholder);&lt;br /&gt;return tulos;&lt;br /&gt;}&lt;br /&gt;void perusarvonta(lottokone *siht_)&lt;br /&gt;{&lt;br /&gt;SlottoPerus *_this = (SlottoPerus *)siht_;&lt;br /&gt;&lt;br /&gt;int i, ok;&lt;br /&gt;&lt;br /&gt;srand(time(NULL));&lt;br /&gt;if&lt;br /&gt;(&lt;br /&gt; *(unsigned int*)_this!=&lt;br /&gt; ElottoType_Perus &amp;amp;&amp;amp;&lt;br /&gt; *(unsigned int*)_this!=&lt;br /&gt; ElottoType_PerusJaJokeri &lt;br /&gt;)&lt;br /&gt;{&lt;br /&gt; printf&lt;br /&gt; (&lt;br /&gt;  "perusarvonta : lottokone rikki, soittakaa Wirallisille Walvojille!!"&lt;br /&gt; );&lt;br /&gt; fflush(stdout);&lt;br /&gt; exit(-1);&lt;br /&gt;}&lt;br /&gt;for(i=0;i&amp;lt;LOTTO_NRO_MAARA;i++)&lt;br /&gt;{&lt;br /&gt;ok=0;&lt;br /&gt;while(!ok)&lt;br /&gt;{&lt;br /&gt; int j;&lt;br /&gt; ok=1;&lt;br /&gt; ((int *)_this-&amp;gt;tulosplaceholder)[i]=&lt;br /&gt; (rand()%LOTTO_MAX+1);&lt;br /&gt; //varmista että arvottu numero on uniikki&lt;br /&gt; for(j=0;j&amp;lt;i;j++)&lt;br /&gt; {&lt;br /&gt;  if&lt;br /&gt;  (&lt;br /&gt;    ((int *)_this-&amp;gt;tulosplaceholder)[i] == &lt;br /&gt;    ((int *)_this-&amp;gt;tulosplaceholder)[j]&lt;br /&gt;  )&lt;br /&gt;  {&lt;br /&gt;   //sama numero arvottiin kahteen kertaan =&amp;gt; hylkää!&lt;br /&gt;   ok=0;&lt;br /&gt;   //koska tupla-arvo löytyi jo, ei kannata jatkaa.&lt;br /&gt;   break;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;if(_this-&amp;gt;jokerimukana)&lt;br /&gt;{&lt;br /&gt;for(i=0;i&amp;lt;JOKERI_NRO_MAARA;i++)&lt;br /&gt;{&lt;br /&gt; ok=0;&lt;br /&gt; while(!ok)&lt;br /&gt; {&lt;br /&gt;  int j;&lt;br /&gt;  ok=1;&lt;br /&gt;  ((int *)_this-&amp;gt;tulosplaceholder)[LOTTO_NRO_MAARA+i]=&lt;br /&gt;  (rand()%LOTTO_MAX+1);&lt;br /&gt;  //varmista että arvottu numero on uniikki&lt;br /&gt;  for(j=0;j&amp;lt;i;j++)&lt;br /&gt;  {&lt;br /&gt;   if(&lt;br /&gt;   ((int *)_this-&amp;gt;tulosplaceholder)[LOTTO_NRO_MAARA+i]==&lt;br /&gt;   ((int *)_this-&amp;gt;tulosplaceholder)[LOTTO_NRO_MAARA+j]&lt;br /&gt;   )&lt;br /&gt;   {&lt;br /&gt;    //sama numero arvottiin kahteen kertaan =&amp;gt; hylkää!&lt;br /&gt;    ok=0;&lt;br /&gt;    //koska tupla-arvo löytyi jo, ei kannata jatkaa.&lt;br /&gt;    break;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;vikinglotto.c&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/* ******************************************************** */&lt;br /&gt;/*                                                          *&lt;br /&gt;*      Implementation of a lotto engine written in C        *&lt;br /&gt;*      Main purpose is to demonstrate that C can be used as *&lt;br /&gt;*      object oriented programming language.               *&lt;br /&gt;*                                                          *&lt;br /&gt;*      Written by  Maz (2009)                              *&lt;br /&gt;*      http://maz-programmersdiary.blogspot.com/           *&lt;br /&gt;*      http://c-ohjelmoijanajatuksia.blogspot.com/         *&lt;br /&gt;*                                                          *&lt;br /&gt;*      You're free to use this piece of code.              *&lt;br /&gt;*      You can also modify it freely, but if you           *&lt;br /&gt;*      improve this, you must write the improved code      *&lt;br /&gt;*      in comments at:                                     *&lt;br /&gt;*      http://maz-programmersdiary.blogspot.com/           *&lt;br /&gt;*      or at:                                              *&lt;br /&gt;*      http://c-ohjelmoijanajatuksia.blogspot.com/         *&lt;br /&gt;*      or mail the corrected version to me at              *&lt;br /&gt;*      Mazziesaccount@gmail.com                            *&lt;br /&gt;*                                                          *&lt;br /&gt;*      I do not want to enforce any copyright for using    *&lt;br /&gt;*      this piece unmodified, but I would be glad if I     *&lt;br /&gt;*      heard of you - in case this was informative/usefull *&lt;br /&gt;*      to you.                                             *&lt;br /&gt;*                                                          *&lt;br /&gt;*      Revision History:                                   *&lt;br /&gt;*                                                          *&lt;br /&gt;*      -v0.0.1 16.09.2008/Maz                              *&lt;br /&gt;*                                                          */&lt;br /&gt;/* ******************************************************** */&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;#include &amp;lt;time.h&amp;gt;&lt;br /&gt;#include &amp;lt;string.h&amp;gt;&lt;br /&gt;&lt;br /&gt;#include "lotto_sisainen.h"&lt;br /&gt;&lt;br /&gt;lottotulos vikingtulos(lottokone *siht_)&lt;br /&gt;{&lt;br /&gt;SlottoViking *_this = (SlottoViking *)siht_;&lt;br /&gt;lottotulos tulos;&lt;br /&gt;memset(&amp;amp;tulos,0,sizeof(lottotulos));&lt;br /&gt;if&lt;br /&gt;(&lt;br /&gt; *(unsigned int*)_this!=&lt;br /&gt; ElottoType_Perus &amp;amp;&amp;amp;*(unsigned int*)_this!=&lt;br /&gt; ElottoType_PerusJaJokeri &lt;br /&gt; )&lt;br /&gt;{&lt;br /&gt;printf&lt;br /&gt;(&lt;br /&gt; "perustuloshae : lottokone rikki, soittakaa Wirallisille Walvojille!!"&lt;br /&gt; );&lt;br /&gt; fflush(stdout);&lt;br /&gt; exit(-1);&lt;br /&gt;}&lt;br /&gt;if(_this-&amp;gt;jokerimukana)&lt;br /&gt;{&lt;br /&gt;tulos.jokeriNroMaara=JOKERI_NRO_MAARA;&lt;br /&gt;tulos.jokeri=&lt;br /&gt;&amp;amp;(((int *)_this-&amp;gt;tulosplaceholder)[VIKINGLOTTO_NRO_MAARA]);&lt;br /&gt;}&lt;br /&gt;tulos.lottoNroMaara=VIKINGLOTTO_NRO_MAARA;&lt;br /&gt;tulos.lottonumerot=(int *)(_this-&amp;gt;tulosplaceholder);&lt;br /&gt;return tulos;&lt;br /&gt;}&lt;br /&gt;void vikingarvonta(lottokone *siht_)&lt;br /&gt;{&lt;br /&gt;SlottoViking *_this = (SlottoViking *)siht_;&lt;br /&gt;&lt;br /&gt;int i, ok;&lt;br /&gt;&lt;br /&gt;srand(time(NULL));&lt;br /&gt;if&lt;br /&gt;(&lt;br /&gt; *(unsigned int*)_this!=&lt;br /&gt; ElottoType_Viking &amp;amp;&amp;amp;*(unsigned int*)_this!=&lt;br /&gt; ElottoType_VikingJaJokeri &lt;br /&gt;)&lt;br /&gt;{&lt;br /&gt;printf&lt;br /&gt;(&lt;br /&gt; "perusarvonta : lottokone rikki, soittakaa Wirallisille Walvojille!!"&lt;br /&gt;);&lt;br /&gt; fflush(stdout);&lt;br /&gt; exit(-1);&lt;br /&gt;}&lt;br /&gt;for(i=0;i&amp;lt;VIKINGLOTTO_NRO_MAARA;i++)&lt;br /&gt;{&lt;br /&gt;ok=0;&lt;br /&gt;while(!ok)&lt;br /&gt;{&lt;br /&gt; int j;&lt;br /&gt; ok=1;&lt;br /&gt; ((int *)_this-&amp;gt;tulosplaceholder)[i]=&lt;br /&gt; (rand()%LOTTO_MAX+1);&lt;br /&gt; //varmista että arvottu numero on uniikki&lt;br /&gt; for(j=0;j&amp;lt;i;j++)&lt;br /&gt; {&lt;br /&gt;  if&lt;br /&gt;  (&lt;br /&gt;   ((int *)_this-&amp;gt;tulosplaceholder)[i]== &lt;br /&gt;   ((int *)_this-&amp;gt;tulosplaceholder)[j]&lt;br /&gt;  )&lt;br /&gt;  {&lt;br /&gt;   //sama numero arvottiin kahteen kertaan =&amp;gt; hylkää!&lt;br /&gt;   ok=0;&lt;br /&gt;   //koska tupla-arvo löytyi jo, ei kannata jatkaa.&lt;br /&gt;   break;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;if(_this-&amp;gt;jokerimukana)&lt;br /&gt;{&lt;br /&gt;for(i=0;i&amp;lt;VIKINGJOKERI_NRO_MAARA;i++)&lt;br /&gt;{&lt;br /&gt; ok=0;&lt;br /&gt; while(!ok)&lt;br /&gt; {&lt;br /&gt;  int j;&lt;br /&gt;  ok=1;&lt;br /&gt;  ((int *)_this-&amp;gt;tulosplaceholder)[VIKINGLOTTO_NRO_MAARA+i]=&lt;br /&gt;  (rand()%LOTTO_MAX+1);&lt;br /&gt;  //varmista että arvottu numero on uniikki&lt;br /&gt;  for(j=0;j&amp;lt;i;j++)&lt;br /&gt;  {&lt;br /&gt;   if&lt;br /&gt;   (&lt;br /&gt;    ((int *)_this-&amp;gt;tulosplaceholder)[VIKINGLOTTO_NRO_MAARA+i]==&lt;br /&gt;    ((int *)_this-&amp;gt;tulosplaceholder)[VIKINGLOTTO_NRO_MAARA+j]&lt;br /&gt;   )&lt;br /&gt;   {&lt;br /&gt;    //sama numero arvottiin kahteen kertaan =&amp;gt; hylkää!&lt;br /&gt;    ok=0;&lt;br /&gt;    //koska tupla-arvo löytyi jo, ei kannata jatkaa.&lt;br /&gt;    break;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/span&gt;Kääntäminen gcc:llä onnistuu käskyllä:&lt;br /&gt;&lt;br /&gt;gcc -Wall -o lottoEsimerkki lottoTesti.c lotto.c  peruslotto.c vikinglotto.c&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Ja vielä teille jotka kahlasitte tänne asti...&lt;br /&gt;&lt;br /&gt;Eli, loppusanat:&lt;br /&gt;&lt;br /&gt;C++ on hyvä kieli, ja olio-ohjelmointi on tuonut joitain hienoja piirteitä tullessaan. Mutta Olio-Ohjelmointi ei automaattisesti tarkoita hyvää asiaa. Monet olio-ohjelmoijat ovat uponneet liian syvälle oliokuoppaansa, eivätkä näe enää metsää puilta. Liian pitkälle menevä perintä lienee yksi kauheimmista asioista mitä tiedän. Olen joskus joutunut porautumaan oliopohjalta tehtyyn koodiin, ja tekemään sen päälle jotain.. Usein huomasin vasta päiviä puuhasteltuani, että sama asia jota tein kaikkien periytettyjen oliokerrosten päälle, olijo (&lt;=kirjoitusvirhe tarkoituksellinen) toteutettu jossain alempien kerrosten syövereissä. Ja kaikenkaikkiaan, nykyään tuntuu, että Olio-ohjelmointi nähdään liian usein itse tarkoituksena, ei suinkaan työkaluna joksi se on tarkoitettu. On tilanteita joissa oliokielet ovat hyviä, mutta on myös tilanteita joissa proseduraalisella kielellä saavutetaan tavoite paljon pienemmällä koodimäärällä, yksinkertaisemmin, ylläpidettävämmin ja ymmärrettävämmin.  Siis muistakaa tytöt ja pojat, (niin, sinäkin Vesa): Olio-ohjelmoijien ja ohjelmoinnin päätarkoitus ei oikeasti ole vain kikkailla ja keksiä entistä hienompia olio-ismejä.&lt;br /&gt;&lt;br /&gt;Ja viimeinen kommentti. Koodiesimerkit tuppaavat menemään rikki tässä html wysiwyg maailmassa - olen pahoillani. Koetan saada lottokone-esimerkin pakattua zippiin, ja jonnekkin palvelimelle, mikäli joku haluaa oikeasti nähdä sen / koettaa sitä :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4818234838483192587-2944662655124497083?l=c-ohjelmoijanajatuksia.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-ohjelmoijanajatuksia.blogspot.com/feeds/2944662655124497083/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4818234838483192587&amp;postID=2944662655124497083' title='5 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/2944662655124497083'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/2944662655124497083'/><link rel='alternate' type='text/html' href='http://c-ohjelmoijanajatuksia.blogspot.com/2009/04/c-on-olio-ohjelmointi-kieli-osa-2.html' title='C on olio-ohjelmointi kieli! (osa 2)'/><author><name>Maz</name><uri>http://www.blogger.com/profile/03774313944609044806</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4818234838483192587.post-9106494089601447405</id><published>2009-04-03T22:44:00.004+03:00</published><updated>2011-09-19T12:22:39.767+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C'/><category scheme='http://www.blogger.com/atom/ns#' term='monisäikeiset ohjelmat'/><category scheme='http://www.blogger.com/atom/ns#' term='semaforit'/><category scheme='http://www.blogger.com/atom/ns#' term='säikeet'/><category scheme='http://www.blogger.com/atom/ns#' term='thread safe'/><category scheme='http://www.blogger.com/atom/ns#' term='atomiset operaatiot'/><title type='text'>Atomiset operaatiot.</title><content type='html'>HepHep!&lt;br /&gt;&lt;br /&gt;Sorsat saatavilla reposta&lt;br /&gt;&lt;a href="http://xp-dev.com/svn/MazBotV4/branches/oldprojects/atomic_example.tar.gz"&gt;http://xp-dev.com/svn/MazBotV4/branches/oldprojects/atomic_example.tar.gz&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Nyt mukaan pakettiin livautettu myös esimerkkisorsa, jossa käytetään ++ - operaatiota tavalliseen muuttujaan ja atomista operaatiota. Näitä muuttujia käsitellään esimerkissä 10 - säikeestä, ja ainakin minun koeajoissa nähtiin miten normaalin globaalin kasvattaminen ++-operaatiolla, ei ollut turvallinen monisäikeisessä ympäristössä.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Säikeistä (aiheen pohjustus)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Tunnen suurta syyllisyyttä siitä, että esittelin blogissani säikeet eli threadit. Ne ovat kuin Star Warsin "voiman", paha, "pimeä puoli". Niiden käyttöön on helppo lipsahtaa. Ne houkuttelevat siinä aivan käden ulottuvilla, tarjoten nerokkaalta tuntuvia ratkaisuja - ja kun kerran kokeilet, jäät helposti koukkuun.&lt;br /&gt;&lt;br /&gt;No joo. Hieman karrikoitua, sitäpaitsi tälläkertaa en aikonut kirjoittaa säikeistä varsinaisesti, mutta kun kerran atomiset operaatiot liittyvät kiinteästi säikeisiin, kirjoitetaan nopeasti ylös muutamia asioita joita on hyvä pitää mielessä...&lt;br /&gt;&lt;br /&gt;Säikeet siis tarjoavat tavan suorittaa näennäisen yhtäaikaisesti useampaa kohtaa ohjelmakoodissa. Toisinaan tämä auttaa yksinkertaistamaan asioita merkittävästi. Yhden säikeen voi esim. luoda odottamaan käyttäjältä syötettä, toisen säikeen suorittaessa taustalla jotain tarpeellista tehtävää. Miksi sitten avasin tekstini syyttämällä säikeitä maailman suurista onnettomuuksista?&lt;br /&gt;&lt;br /&gt;Vaikka säikeet tuntuvat helpolta tavalta hanskata blokkaavat I/O kutsut ja tehdä monia yhdenaikaisia toimenpiteitä, on säikeissä omat nurjat puolensa.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Synkronointi.&lt;/span&gt;&lt;br /&gt;Joskus asioita vaan tulee tehdä tietyssä järjestyksessä. Maitoa ei voi kaataa lasiin, ennen kuin purkki on avattu. Uunin peltejä ei voi laittaa kiinni, ennen kuin tuli on sammunut. Ja ehkä tyypillisimmillään ohjelmoinnissa, palvelua ei voida käyttää, ennen kuin se on alustettu (palautuu yleensä siihen seikkaan, että muistiin ei voi kirjoittaa ennen kuin se on varattu) jne. Käytettäessä useampia säikeitä, eri säikeiden suoritusjärjestys uskotaan schedulerin haltuun. Ts. suoritusjärjestys on jokseenkin epämääräinen, ja voi vaihdella ajokerrasta toiseen. Tyypillisesti tässä käytetään apuna semaforeja, mutexeja ja conditional variableja.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Yhteisten resurssien suojaus.&lt;/span&gt;&lt;br /&gt;Eli mikäli useampi kuin yksi säie päivittää jotain yhteistä resurssia (esim, globaalia muuttujaa), voi tulla ongelmia jolloin säikeet ovat päivittämässä samaa paikkaa yhtäaikaa. Perusesimerkkinä globaalin muuttujan lisäys yhdellä - mutta aloitankin varsinaisen "atomic operation" matskun sillä, jahka sinne asti pääsen.&lt;br /&gt;&lt;br /&gt;Ja viimeisenä, mutta ei varmastikkaan mitenkään vähäpätöisenä tulee &lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;br /&gt;ohjelman vikojen korjaus ja ylläpito.&lt;/span&gt;&lt;br /&gt;Totuushan on, että virheetöntä koodia ei ole, käytettiin säikeitä tai ei. Mutta se mikä monisäikeisessä ohjelmassa saa hiukset nousemaan pystyyn, ja naaman kalpenemaan, on "viuhupointterin" löytämisen hankaluus. Verrattaessa useassa säikeessä ajettavaa ohjelmaa, esim. useassa prosessissa ajettavaan, huomataan yleensä ensimmäisenä se, että saman prosessin kaikilla säikeillä on pääsy kaikkien säikeiden osoiteavaruuteen. Prosesseillahan tuo osoiteavaruus on suojattu. Eli sen lisäksi että globaalit muuttujat on käytössä kaikissa säikeissä, voidaan osoittimilla sohia sujuvasti toisten säikeiden pinoihin (ja luonnollisesti sinne yhteiseen kekoon). Datan jakaminen on siis helppoa, mutta myös koodiin lipsahtanut viuhupointteri saattaa sujuvasti muuttaa dataa toisen threadin tontilta, saaden aikaan merkillisiä vikoja jossain ihan muualla kuin siellä missä viuhupointteri on. Hankalampaa kuin prosessien kanssa sanon minä, sillä mikäli viuhupointteri osoittaisi toisen prosessin osoiteavaruuteen, saataisiin välittömästi generoitua SIGSEGV eli segmentation faultti (tai access violation wintoosalla).&lt;br /&gt;&lt;br /&gt;No niin... Siinäpä sitä säieasiaa jo tulikin, kirjoitukseen jonka ei ollut tarkoitus käsitellä säikeitä...&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Miksi atomisuus??&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Aiemmin mainitsin ongelman, jossa kaksi säiettä yrittää yhtä aikaa kasvattaa globaalia muuttujaa yhdellä. Siis kumpikin säie ajaa koodia&lt;br /&gt;&lt;br /&gt;globaali++;&lt;br /&gt;&lt;br /&gt;Mietitäänpä hetki. Mitä globaali++; oikeastaan tekee? Se:&lt;br /&gt;&lt;br /&gt;1. lukee globaali muuttujan arvon muistista.&lt;br /&gt;2. kasvattaa arvoa yhdellä.&lt;br /&gt;3. kirjoittaa tuloksen takaisin globaali muuttujaan.&lt;br /&gt;&lt;br /&gt;Jos kaksi säiettä nyt tekee tätä yhtäaikaa, voi lopputulos olla seuraava:&lt;br /&gt;&lt;br /&gt;Säie1 aloittaa. Se:&lt;br /&gt;&lt;br /&gt;lukee globaalin arvon&lt;br /&gt;laskee globaali+1&lt;br /&gt;&lt;br /&gt;ja nyt scheduleri päättääkin antaa säikeelle 2 vuoron edetä, ja pysäyttää säikeen 1.&lt;br /&gt;&lt;br /&gt;säie 2 lukee globaali muuttujan arvon (joka siis on vielä sama minkä säie 1 luki)&lt;br /&gt;säie 2 laskee globaali+1&lt;br /&gt;ja kirjoittaa tuloksen muuttujaan globaali.&lt;br /&gt;&lt;br /&gt;tovin päästä scheduleri antaa säikeelle 1 luvan jatkaa, ja se kirjoittaa edellä tekemänsä laskutoimituksen summan muuttujaan globaali, onnellisen tietämättömänä siitä, että säie 2 on kasvattanut globaalia välillä. Lopputulos siis uhmaa koulumatematiikkaa, ja jos oletetaan globaalin olleen alussa voikka 534, laskutoimituksen päättyessä, 534+1+1 = 535. Ja kun lisätään soppaan se, että tämä voi todellakin tapahtua vasta kun globaalia on päivitetty 534 kertaa onnistuneesti (mahdollisesti tuntien toiminnan jälkeen), ja se, että ohjelma ei välttämättä kaadu virheeseen.. Voihan olla että joka viidessadas pankkiautomaatilla laskuja maksava asiakas aikaansaa yhden sentin virheen pankin kirjanpidossa... ... ... ... Koetan vain sanoa, että vika on usein äärimmäisen vaikeaa paikantaa.&lt;br /&gt;&lt;br /&gt;Edellinen esimerkki oli vieläpä äärimmäisen yksinkertainen. Todelliset ongelmat ovat usein paljon monimutkaisempia operaatioita, jota ei suotaisi katkaistavan.&lt;br /&gt;&lt;br /&gt;Tässävaiheessa sivistyneempi teistä kahdesta lukijastani (se et edelleenkään ole sinä Vesa) lienee huomannut yhtäläisyyden kuvaamani ongelman ja sanan atominen kanssa. Atominen (atomic) tarkoittaa jakamatonta. Ja atominen operaatio siis operaatiota, jota ei voi jakaa... Eli, jos lue, lisää, kirjoita olisikin käsky, joka tapahtuisi "jakamattomasti" siten, että välissä ei voida tehdä muuta, olisimme autettuja.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Atomisuuden vaihtoehdot&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Lääkettä vaivaan sanoi entinen mies kun loippaa otti. Ongelmaan onkin tarjolla useita ratkaisuja.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Keskeytysten disablointi.&lt;/span&gt;&lt;br /&gt;Koodarinplanttu voi vetää scheduleria nenästä disabloimalla keskeytykset, jolloin scheduleri ei pääse scheduloimaan muita säikeitä / prosesseja ajoon. Kuulostaa hyvältä. Ikävä kyllä, keskeytysten disabloinnilla on sivuvaikutuksensa. Masiina nimittäin käyttää keskeytyksiä paljon muuhunkin, muunmuassa kommunikointiin raudan ja käyttöjärjestelmän välillä. Lisäksi monet operaatiot ovat riippuvaisia prosesseista, jotka ovat taustalla ajossa. Lisäksi voi koneessa pyöriä prosesseja, joiden on päästävä ajoon... Siis keskeytysten disabloinnin tulisi aina olla äärimmäisen lyhytkestoista, ja tällä välin ohjelmalla on rajoitettu määrä operaatioita käytettävissään... Ja... Vesakin varmaan tietää, että nykyään useissa koneissa on enemmän kuin yksi prosessori. Siis vaikka toisella prosessorilla scheduleri lukittaisiin, toisella prosessorilla voi ajoon päästä jotain, joka sotkee aristoteleen ympyrät...&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Mutexit ja semaforit.&lt;/span&gt;&lt;br /&gt;Niistä olenkin kertonut jo aiemmassa blogitekstissäni. Ja ne ovat nerokkaita viritelmiä - jotka useimmiten perustuvat atomisiin operaatioihin. Mutta mitä pidempään mutexi on yhdellä säikeellä lukossa, sitä kauemmin muut säikeet joutuvat odottelemaan. Puhumattakaan vaaroista kuten deadlockit ja starvation.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Atomiset operaatiot (Joo, nyt pääsin ihan oikeasti asiaan)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Onneksemme (oh, ylistäkäämme insinöörien nerokkuutta [minähän en muuten siihen sakkiin kuulu, toisin kuin Vesa]) suurin osa prosessoreista tukee muutamia ihan perustavanlaatuisia atomisia käskyjä, jotka voidaan asm:lla koodailla C-koodin joukkoon. (Eli useisiin prosessoreihin on rakennettu ihan "rautatuki" takaamaan tiettyjen yksinkertaisten operaatioiden keskeyttämättömyys). Ja kun muutama tällainen peruspalikka on käytössä, pystyy koodari kikkailemaan lisää käskyjä, jotka ulospäin näyttävät atomisilta.&lt;br /&gt;&lt;br /&gt;Kaikki lhtee yleensä liikkeelle 32 bittisen (int) luvun kirjoittamisesta / lukemisesta muistiin/muistista. Onko siis luku / kirjoitus atominen? Savolainen osaisi varmasti sanoa tähän jotain näppärää ja tulkitsematonta, mutta minä sensijaan sanon, että: "Se vähän riippuu siitä, mistä se roikkuu."&lt;br /&gt;&lt;br /&gt;Ok. Helpotetaan hiukan. Nykykoneilla joissa väylät ovat yleensä vähintään 32 bittisiä, luku ja kirjoitus on yleensä atomisia. Vanhemmilla/erikoisemmilla koneilla ei välttämättä. Pitempien lukujen kohdalla taas 32 bittiset koneet usein lukevat/kirjoitttavat kahdessa osassa, ja tällaisen luvun / kirjoituksen sekaan sössiminen usealla säikeellä, saa aikaan lukuja joissa on puolet yhtä, ja toinen puoli toista, eli jotka siis ovat ihan mitä sattuu. Ja 32 bittisiäkin lukuja jäsiteltäessä on hyvä muistaa, että kääntäjä saattaa optimoida koodia siten, että lukuja käytetäänkin rekistereistä tai cachesta... Tällaisten sekaannuksien välttämiseksi, atomisiin operaatioihin tarkoitetut muuttujat on hyvä höystää "volatile" avainsanalla, joka kertoo kääntäjälle, että olisi hyvin suotavaa, mikäli tämä luku pidettäisiin aina muistissa, eikä sitä optimoitaisi rekistereihin jne.&lt;br /&gt;&lt;br /&gt;No niin Vesa, lopetappa se volatilen viljely, ja ota kääntäjän manuska karvaiseen kouraasi.&lt;br /&gt;&lt;br /&gt;Volatile (ja register) sanoja kun käytetään oikeastaan vaan vihjeenä kääntäjälle. Osa kääntäjistä hoitaa homman kotiin, osa antaa palttua koodarin toiveille, ja osa jotain siltä väliltä. Kannattaa siis katsoa, ennen kuin katuu.&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Compare and Swap&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Oikeastaan tässä alkaa olla jo tarinaa joka vinkkaa oikeaan suuntaan, mutta paukutetaan vielä pari juttua. En nimittäin malta olla esittelemättä parasta kaveriani, atomista "CMS" eli "CompareAndSwap" käskyä. Joo Vesa, komealtahan se nimi kuulostaa, mutta se ei ollut syy miksi haluan esitellä juuri compare and swapin. Syy on se, että kun olet toteuttanut atomisen compare and swap käskyn, voit rakentaa sen avulla kokonaisen ison liudan muita atomisia käskyjä.&lt;br /&gt;&lt;br /&gt;Compare and swap siis tekee seuraavan tempun. Se ottaa käyttäjältä parametreinaan muistiosoitteen, ja kaksi lukua. Se tarkistaa muistiosoitteen sisällön, vertaa sitä annettuun lukuun, ja mikäli sisältö on sama kuin annettu luku, se vaihtaa muistiosoitteen sisällön toiseen annettuun lukuun. Lopuksi se palauttaa arvon, joka kertoo tehtiinkö swap vai ei. Ja kaiken tämän vieläpä atomisesti. Paluuarvona on tyypillisesti joko true/false tai arvo joka muistipaikasta luettin. (Siis, jos muistipaikan sisältö oli sama kuin annettu vertailumuuttuja =&amp;gt; uusi arvo kirjoitettiin =&amp;gt; vertailumuuttujaa vastaava arvo palautetaan. Muuten palautettava arvo on eri kuin vertailuarvo).&lt;br /&gt;&lt;br /&gt;Todennäköisesti lähes kaikki nykyiset prosessorit tukevat atomista CMS käskyä. Ainakin x86, AMD ja ne PPC prosessorit joihin olen törmännyt.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Uusien atomisten käskyjen rakentelu&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;No niin. Mainitsinkin jo, että CMS käskyn avulla voidaan toteutta useita hienoja atomisia operaatioita. Vesakin voi lopettaa nauramisen, se ei ollut vitsi. Atomisuus on siis käsite, joka toteuttaa seuraavat ehdot.&lt;br /&gt;&lt;br /&gt;Jokin sarja operaatioita voidaan suorittaa siten, että ympäröivä maailma ei tiedä muutoksista joita se aiheuttaa ennen kuin se on kokonaisuudessaan päättynyt.&lt;br /&gt;Ja jos jossakin operaation vaiheessa sössitään, koko operaatio epäonnistuu jälkiä jättämättä.&lt;br /&gt;&lt;br /&gt;CMS:ää hyödyntäen voidaan nyt seuraavalla kaavalla toteutta iso joukko erilaisia:&lt;br /&gt;"vertaa/manipuloi muistipaikassa olevaa dataa, ja kirjoita tulos takaisin muistiin"&lt;br /&gt;operaatioita, jotka ovat ulospäin atomisia.&lt;br /&gt;&lt;br /&gt;1. lue muistipaikan alkuperäinen arvo, ja talleta se väliaikaiseen muuttujaan.&lt;br /&gt;2. vertaile / manipuloi dataa (toisessa väliaikaisessa muuttujassa)&lt;br /&gt;3. Tee compareAndSwap, josa vertaat muistipaikassa tällähetkellä olevaa arvoa arvoon, joka siellä oli atomista funktiotasi kutsuttaessa. Laita swapattawaksi arvoksi manipulointisi tulos.&lt;br /&gt;Toista loopissa kunnes swap onnistuu.&lt;br /&gt;&lt;br /&gt;Eli, mikäli jokin muu säie pääsi muuttamaán muistipaikassa olevaa arvoa manipuloinnin/vertailun aikana, compare and swap funktiossa alkuperäinen, talletettu arvo ja manipuloinnin jälkeinen arvo eivät täsmää (operaatio ei ollut atominen) =&amp;gt; swap ei onnistu. =&amp;gt; uusi yritys.&lt;br /&gt;&lt;br /&gt;Jos taas swap onnistuu, se tarkoitta, että kukaan ei kesken operaation käpistellyt muistipaikan sisältämää dataa, ja operaatio oli siis atominen.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Ja tässä vielä loppuhöysteenä tuo paljon hypettämäni compare and swap, ix86 prosessorille. (muistakaa, että intti jonka osoite funktiolle annetaan TÄYTYY olle volatile).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;int __INLINE__ atominenCAS( int *ptr, int cmp, int new)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        unsigned char ret;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        __asm__ __volatile__ (&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                "  lock\n"&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                "  cmpxchgl %2,%1\n"&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                "  sete %0\n"&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                : "=q" (ret), "=m" (*ptr)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                : "r" (new), "m" (*ptr), "a" (cmp)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                : "memory");&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        return ret;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4818234838483192587-9106494089601447405?l=c-ohjelmoijanajatuksia.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-ohjelmoijanajatuksia.blogspot.com/feeds/9106494089601447405/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4818234838483192587&amp;postID=9106494089601447405' title='0 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/9106494089601447405'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/9106494089601447405'/><link rel='alternate' type='text/html' href='http://c-ohjelmoijanajatuksia.blogspot.com/2009/04/atomiset-operaatiot.html' title='Atomiset operaatiot.'/><author><name>Maz</name><uri>http://www.blogger.com/profile/03774313944609044806</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4818234838483192587.post-7722281646357477834</id><published>2009-03-23T19:05:00.003+02:00</published><updated>2009-03-23T19:15:25.029+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C'/><category scheme='http://www.blogger.com/atom/ns#' term='debuggeri'/><category scheme='http://www.blogger.com/atom/ns#' term='gdb'/><category scheme='http://www.blogger.com/atom/ns#' term='watchpoint'/><title type='text'>Gdb watch pointit - pysähdy kun muistipaikassa oleva data muuttuu</title><content type='html'>&lt;span style="font-size:130%;"&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Gdb watchpointit - Stoppaa suoritus kun data muuttuu&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;br /&gt;Oikeastaan kirjoitin tämän tekstin alunperin tuohon aiempaan blogitekstiini (debuggauksesta ylensä), mutta koska se paisui kuin pullataikina, päätin laittaa tästä ihan oman osion.&lt;br /&gt;&lt;br /&gt;Usein ohjelmissa on bugeja, joissa kirjoitetaan yli allokoidun tilan siten, että kirjoitus menee kerta toisensa jälkeen samaan paikkaan. (Esim, ylikirjoitettaessa staattisesti varattu taulukko). Mikäli sattuu niin ikävästi, että ylikirjoitettu muisti on varattu ohjelman käyttöön, voi olla, ettei käyttöjärjestelmä huomaa mitään virhettä, vaan ohjelma kyykkää myöhemmin mitä kummallisimpiin vikoihin. Tosi jänniä ilmiöitä saa aikaiseksi muunmuassa ylikirjoiottamalla jonkin tallennetun callbackfunktion osoittimen -&gt; prosessorille annetaan suoritettavaksi mitä erikoisempia käskyjä :) Tällaisten "viuhupointtereiden" jäljittäminen voi olla todella työlästä.&lt;br /&gt;&lt;br /&gt;Tällaisissa tapauksissa usein toivoisi, että muistiosoitteen voisi merkitä "seurattavaksi", eli jonkin vahtimaan osoitetta ja pysäyttämään suorituksen osoitteen päivittyessä. Näin saisi viuhupointterin käyttäjän kiinni itse teosta.&lt;br /&gt;&lt;br /&gt;gdb antaa tähän valmiudet watchpointtien avulla. Tämä kuitenkin vaatii pari riviä lisäselvitystä.&lt;br /&gt;&lt;br /&gt;Watchpoint on siis periaatteeltaan samanlainen kuin breakpoint, mutta nyt suoritus pysäytetään tietyn muuttujan (tai muistipaikan) arvoa luettaessa/kirjoitettaessa.&lt;br /&gt;&lt;br /&gt;Gdb:ssä on tuki kahdenlaisille watchpointeille, "rauta" ja "softa" watchpointeille. Näissä kuitenkin on muutamia eroja:&lt;br /&gt;&lt;br /&gt;Rautawatchpointit hyödyntävät prosessoriin tehtyä watchpoint tukea (johon en tarkemmin nyt mene). Kuitenkaan kaikissa prosessoreissa ei ole tukea tälle, tai gdb ei pysty kaikkien prosessorien tarjoamaa tukea hyödyntämään. Perus x86, ppc, ym prosessorien kanssa tämä tuki tulisi kuitenkin olla saatavilla.&lt;br /&gt;&lt;br /&gt;Defaulttina rautawatchpointit on käytössä, mikäli ne on tuettuina. Tähän on pari syytä. Ensinnäkin, rautawatchpointit ovat nopeita. Niiden käyttö ei vaadi ylimääräistä prosessointiaikaa, sillä varsinainen työ tehdään jo rautatasolla. Lisäksi rautawatchpointit mahdollistavat ohjelman pysäytyksen "vahdittavaksi" määritettävää muuttujaa/muistiosoitetta luettaessa.&lt;br /&gt;&lt;br /&gt;Softawtchpointit tätä lukemisen havaitsemista ei voida toteuttaa, ilman että jokainen käsky tutkittaisiin debuggerin toimesta, ja lukupyyntö tutkittaisiin. Tämä hidastaisi ohjelman suoritusta toivottoman paljon. Jo pelkkä muistipaikkojen kirjoituksen tutkiminen hidastaa ohjelman ajoa melkoisesti (Ts. softawatchpoittien käyttö hidastaa ohjelmaa merkittävästi jo nyt).&lt;br /&gt;&lt;br /&gt;Vielä yksi ero softa ja rautawatchpointtien välillä tulee esiin monisäikeisiä ohjelmia debugattaessa. Softawatchpointti pystyy havaitsemaan kirjoituksen vain sen säikeen kontextista, jossa watchpointti asetettiin. Rautawatchpointti taas pystyy havaitsemaan luvut ja kirjoitukset säikeestä riippumatta, tosin rautawatchpointin luonnissa voidaan täsmentää minkä säikeen kontekstissa tulevista luvuista/kirjoituksista ollaan kiinnostuneita.&lt;br /&gt;&lt;br /&gt;Pieni tarkennus on kuitenkin vielä paikallaan. Mikäli käytät muuttujan nimeä watchpointtia asetettaessa, watchpoint pysyy validina vain niin kauan, kun ohjelman suoritus pysyy siinä (funktion/tiedoston)kontekstissa, jossa muuttuja on näkyvillä. Jos haluat tutkia esim. jotain keosta varattua paikallista muuttujaa läpi koko ohjelman ajon, tulee sinun asettaa watchpointti muistialueen osoitteen, ei muuttujan nimen perusteella.&lt;br /&gt;&lt;br /&gt;Ja nyt watchpointin syntaksiin:&lt;br /&gt;Watchpoint muuttujan int foo sisällölle nimen ja osoitteen perusteella:&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;br /&gt;watch foo [threadinnumero mikäli rautawatchpointti]&lt;br /&gt;watch (int)0xB00BBABE [threadinnumero mikäli rautawatchpointti]&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Eli jälkimmäisessä siis oletetaan, että muuttuja foo on talletettu osoitteeseen 0xB00BBABE. Oikean osoitteen saat selville käskyllä&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;br /&gt;print &amp;amp;foo&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Huomaa siis, että mikäli haluat ohjelman suorituksen pysähtyvän kun johonkin muistiosoitteeseen kirjoitetaan jotain, tulee sinun suorittaa oikeanlainen castaus, jotta gdb tietää minkäkokoista muistikönttiä annetusta osoitteesta eteenpäin halutaan tarkkailla. Laitetaanpä viekä selvennykseksi toinen esimerkki. Olkoon osoitteessa 0x00BADDAD nyt taulukko long long int *foo[5], eli taulukollinen osoittimia jotka osoittavat long long int tyyppiseen dataan.&lt;br /&gt;&lt;br /&gt;Mikäli haluaisimme seurata sitä arvoa, johon taulukon kolmas osoitin osoittaa, komentaisimme:&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;br /&gt;watch *((long long int *)0x00BADDAD+2) [threadinnumero]&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;a href="http://c-ohjelmoijanajatuksia.blogspot.com/2009/03/c-kieli-vikojen-etsinta.html"&gt;Lisaa gdb:sta&lt;/a&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4818234838483192587-7722281646357477834?l=c-ohjelmoijanajatuksia.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-ohjelmoijanajatuksia.blogspot.com/feeds/7722281646357477834/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4818234838483192587&amp;postID=7722281646357477834' title='0 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/7722281646357477834'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/7722281646357477834'/><link rel='alternate' type='text/html' href='http://c-ohjelmoijanajatuksia.blogspot.com/2009/03/gdb-watch-pointit-pysahdy-kun.html' title='Gdb watch pointit - pysähdy kun muistipaikassa oleva data muuttuu'/><author><name>Maz</name><uri>http://www.blogger.com/profile/03774313944609044806</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4818234838483192587.post-2909152703170309036</id><published>2009-03-21T22:59:00.003+02:00</published><updated>2009-03-21T23:02:51.085+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='otus C'/><category scheme='http://www.blogger.com/atom/ns#' term='C'/><category scheme='http://www.blogger.com/atom/ns#' term='ohjelmointi tekniikka'/><category scheme='http://www.blogger.com/atom/ns#' term='funktio-osoittimet'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>C:tä C++ tyyliin.</title><content type='html'>&lt;BR /&gt;&lt;br /&gt;&lt;BR /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Hi deee Ho!&lt;br /&gt;&lt;br /&gt;Jaa-a. Tälläkertaa kirjoitan aiheesta, josta en ole koskaan aiemmin kirjoittanut. Enkä lukenut. Aiheesta, josta minulla on varsin rajalliset kokemukset. Aiheesta jota on hyvin vaikeaa rajata, aiheesta johon en voi sanoa "Näin se on. Tämä on oikein. Tämä taas väärin". Myönnetään, kysymyksenne "Kannattaako sellaisesta aiheesta kirjoittaa?" on aiheellinen. Mutta vastausta kysymykseenne etta saa muuten, kuin lukemalla tämän kirjoituksen - minä en voi sitä kertoa, sillä en itsekään sitä tiedä.&lt;br /&gt;&lt;br /&gt;Viime aikoina olen useammassa kuin yhdessä C:llä koodaamassani komponentissa käyttänyt hieman tavallisuudesta poikkeavaa lähestymistapaa. Oikeastaan tämä tapa saattaa olla tutumpi C++ kuin C koodareille. Ja tämä tapa (en tiedä onko se yleisesti käytetty/tunnettu, enkä tiedä onko sillä nimeä) sanotaan sitä vaikka otus C:eeksi, minun on nyt tarkoitus esitellä.&lt;br /&gt;&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;&lt;br /&gt;Miettivä hiljaisuus. En tiedä miten etenisin. Taidan heittää tähän väliin esimerkin.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Viimeisin suuri koodaamani kokonaisuus on testisofta useille eri ajastimille. Enkä nyt tarkoita mitään rannekelloja, vaan ohjelmistoalustan applikaatiokoodarin käyttöön tarjoamia ajastimia. Mainitaan vielä sen verran, että systeemi tukee useamman eri käyttöjärjestelmän päälle rakennettuja, erilaisella rajapinnalla varustettuja ajastimia.&lt;br /&gt;&lt;br /&gt;Kas joitain aikoja sitten olin tilanteessa, jossa katsoin että useiden erilaisten ajastimien testaus olisi hyväksi. Tuijotin jossain määrin toivottomana noin viittätoista erilaista ajastimen luontifunktiota, joille kaikille pystyi vielä antamaan varsin erilaisia toimintamoodeja parametreilla. Lisäksi ajastimet käyttivät kahta täysin erilaisia ajanlaskusysteemiä, toisessa tiksuteltiin numeroa aina vain isommaksi, tietyn ajan välein, toinen taas muistutti seinäkelloa, (joka siis koostuu sekunneista, minuuteista ja tunneista, jotka kaikki pyörähtävät ympäri kasvattaen suurempaa yksikköä, kunnes vuorokauden vaihtuessa kello pyörähtää ympäri alkutilaansa) tosin kolmen erilaisen aikayksikön sijasta tässä oli viisi erilaista yksikköä, ja "kellon ympäri" mentiin hieman alle minuutissa.&lt;br /&gt;&lt;br /&gt;Mietin miten ihmeessä saisin kaikki ajastimet testattua suunnilleen samalla tavalla.&lt;br /&gt;&lt;br /&gt;Mietittyäni tovin huomasin kaikkien ajastimien testauksen lopulta vaativan tietyt samankaltaiset askeleet.&lt;br /&gt;&lt;br /&gt;1. ajastimen luonti.&lt;br /&gt;2. ajastimen "expiroitumisen" oikeanaikaisuuden tarkistaminen.&lt;br /&gt;3. mahdollisen jaksoittaisen ajastimen peruuttaminen.&lt;br /&gt;&lt;br /&gt;Hieman tarkennettuna lista näytti tältä:&lt;br /&gt;&lt;br /&gt;1. ajastimen parametrien generointi&lt;br /&gt;2. ajastimen parametrien oikeellisuuden tarkistus. (ajastimen toiminta virheellisiä parametreja käytettäessä on toki myös tarkistettava, mutta testisysteemin tulee osata odottaa oikeaa käytöstä), ja ajastimen jaksollisuuden tarkistus.&lt;br /&gt;3. Ajastimen luonti&lt;br /&gt;4. oletetun laukeamisajan laskeminen&lt;br /&gt;5 laukeamisen odottaminen ja laukeamisajan tarkistus&lt;br /&gt;6 uuden laukeamisajan laskenta jaksolliselle ajastimelle&lt;br /&gt;7.jaksollisen ajastimen (ja laukeamatta jääneen) ajastimen peruuttaminen.&lt;br /&gt;8. virhetilanteen raportointi, ja paluuarvon generointi.&lt;br /&gt;&lt;br /&gt;Mekaaninen älämietivaantee C tyylin lähestymistapa olisi ollut kirjoittaa tällainen runko, kopioida se 15 eri tiedostoon, ja lisätä joka runkoon eri ajastintyypille sopivat funktiot + kirjoittaa mahdollisesti vielä sekakäyttöstressitesti.&lt;br /&gt;&lt;br /&gt;Totesin, että ei millään, noin tappavan tylsään urakkaan en lähde!&lt;br /&gt;&lt;br /&gt;Mieleeni tuli C++:n virtuaaliluokat, joiden avulla C++ koodari saisi kaiken menemään yhteen runkoon. Ja voila...&lt;br /&gt;&lt;br /&gt;Aloitin ensin viidestä samankaltaisesta ajastimesta, ja kirjoitin C structin, jota käytin C++:n luokan tyylisesti, eli sijoitin struktiin sekä toiminnallisuuden, että datan. Toisin sanoen, aloitin toteuttamalla yllälistatut funktiot, ja sijoitin struktiin osoittimet ylläminitun tyyppisiin funktioihin. Parametrina kaikille funktioille taas annettiin osoitin tähän struktiin. Lisäksi sijoitin struktiin funktiopointterit struktin "init ja uninit" funktioihin. Kaikki funktiosta toiseen välitettävät datamuuttujat sitten sijoitin myös kyseiseen struktiin.&lt;br /&gt;&lt;br /&gt;Ehh.. Nyt kun luen tuon tekstin, en ymmärrä itsekkään mitä oikein tarkoitan :D Eli kirjoitan hyyyvin yksinkertaisen esimerkin:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;typedef void (*lampunSytytin)(lamppuesimerkki *_this);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;typedef void (*lampunSammutin)(lamppuesimerkki *_this);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;typedef void (*lampunAlustin)(lamppuesimerkki *_this);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;typedef void (*lampunPoistin)(lamppuesimerkki *_this);&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;typedef struct lamppuesimerkki&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    Slamppu *lamppuStrukti;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    lampunSytytin sytyta;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    lampunSammutin sammuta;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    lampunAlustin alusta;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    lampunPoistin poista;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}lamppuesimerkki;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;jossa siis sytyta, sammuta, alusta ja poista ovat funktio-osoittimia.&lt;br /&gt;&lt;br /&gt;Esim:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;void sytyta(lamppuesimerkki* _this)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    _this-&gt;paalla=1;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;void sammuta(lamppuesimerkki* _this)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    _this-&gt;paalla=0;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;void alusta(lamppuesimerkki *_this)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    _this-&gt;lamppuStrukti=getLamppu();&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    _this-&gt;sytyta=&amp;sytyta;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    _this-&gt;sammuta=&amp;sammuta;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    _this-&gt;alusta=&amp;alusta;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    _this-&gt;poista=&amp;poista;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;]&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;void poista(lamppuesimerkki *_this)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    releaseLamppu(_this-&gt;lamppuStrukti);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Ja samanlaiset funktiot energiansäästölampulle.&lt;br /&gt;&lt;br /&gt;Sitten tein rungon, jossa ensin kutsutaan alustusfunktiota, ja sitten kutakin testifunktiota, ja lopuksi palautetaan tulos. Tai itse asiassa, erotin jo tässä vaiheessa parametrien generoinnin omaksi pieneksi for-looppi-häkkyräksi, joka ei ollut osa struktia. Tästä for looppi häkkyrästä sitten välitettiin ajastimen tyyppi ja parametrit "testienginelle", joka osasi sitten kutsua oikeaa alustusfunktiota, ja antaa parametrit sille. (sijoitin parametrit Em struktiin data-alueelle, ja parametrien generoinnin jälkeen, kun "testi-instanssi" tehtiin (strukti luotiin), siihen sijoitettiin parametristrukti jo ennen init funktion kutsumista. Huomautettavahan se on tässävaiheessa, että kaikille ajastimille minun ei suinkaan tarvinnut toteuttaa kaikkia funktioita erikseen. Osa funktioista sopi useammalle kuin yhdelle ajastimelle.&lt;br /&gt;&lt;br /&gt;Lisäsin tässä vaiheessa testirunkoon vielä uuden säikeen generoinnin / instanssi, jolloin for-loopista voitiin laukaista useita yhdenaikaisia testi-instansseja pyörimään.&lt;br /&gt;&lt;br /&gt;Seuraavaksi huomasin, että lisäämällä struktin data-alueelle muutaman uuden muuttujan, sain parilla uudella funktiolla tehtyä toimivan testin vielä viidelle muulle ajastimelle.&lt;br /&gt;&lt;br /&gt;Ja sitten vielä muutama uusi funktio, ja loputkin ajastimet voitiin testata käyttämällä samaa testirunkoa, hieman erilaisilla parametrien generointi loopeilla. Samoin pelkkää parametrigeneraattoria muuttamalla pystyi testaamaan erilaisten ajastimien yhteiskäyttöä.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Kun testit olivat valmiit, huomasin tässä struktitoteutuksessa vielä erään edun. Ajastinstrukti sisälsi oikeastaan kaiken (missä tahansa testin vaiheessa) tarvittavan datan, ja osoitin struktiin meni jokaisen funktion argumentiksi. Nyt siis pysäyhdyttäessä debuggerilla mihin tahansa testin kohtaan, on oikeastaan lähes kaikki info myös testin aikaisemmasta suorituksesta tallessa ja näkyvissä. Lisäksi erilaisten debuggi-infojen keräily on helppoa, koska kiinnostavissa kohdissa testin tarvitsee vain välittää kopio structista infoja keräilevälle ja tallettavalle prosessille. Niinpä testin kylkeen tuli koodailtua "event" systeemi, eli kiinnostavissa kohdissa testi dumppaa struktin kopion rengaspuskuriin, jota matalampi prioriteettinen säie purkaa. Purkavalle säikeelle on mahdollista rekisteröidä callback-fuinktioita kiinnostavista kohdista tulevan datan käsittelyyn, ja näin voidaan testistä saada ulos mm. erilaisia statistiikkoja, testin suorituksen juurikaan kärsimättä.&lt;br /&gt;&lt;br /&gt;Ehkä suuriimat hyödyt tämänkaltaisesta suunnittelusta saadaan silloin, kun toteutettavana on lukuisia toisistaan poikkeavia kikkuloita, jotka kuitenkin käyttäytyvät saman formaatin mukaan. Ja toisaalta, tällaisella toteutuksella/suunnittelulla saadaan systeemeistä sellaisia, että niihin voidaan helposti muuttaa / lisätä toteutusta. Vielä jos varsinaisessa "rungossa" käytetään sopivasti void  osoitinta, sen sijaan että käytetään osoitinta structiin , ja castataan se oikeaksi tyypiksi jonkin ulkoisen syötteen perusteella, voidaan systeemiä muuntaa melkein loputtomiin - joskin sen ymmärtäminen ja sitä mukaa ylläpitäminen saattaa muuttua varsin hankalaksi... (Ts. tämä mahdollistaisi useiden erityyppisten structien käytön saman "enginen" sisällä).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4818234838483192587-2909152703170309036?l=c-ohjelmoijanajatuksia.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-ohjelmoijanajatuksia.blogspot.com/feeds/2909152703170309036/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4818234838483192587&amp;postID=2909152703170309036' title='0 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/2909152703170309036'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/2909152703170309036'/><link rel='alternate' type='text/html' href='http://c-ohjelmoijanajatuksia.blogspot.com/2009/03/cta-c-tyyliin.html' title='C:tä C++ tyyliin.'/><author><name>Maz</name><uri>http://www.blogger.com/profile/03774313944609044806</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4818234838483192587.post-1632171562402536491</id><published>2009-03-19T21:43:00.014+02:00</published><updated>2009-03-20T00:31:29.715+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='valgrind'/><category scheme='http://www.blogger.com/atom/ns#' term='invalid read'/><category scheme='http://www.blogger.com/atom/ns#' term='muistin ylikirjoitus'/><category scheme='http://www.blogger.com/atom/ns#' term='invalid write'/><category scheme='http://www.blogger.com/atom/ns#' term='segmentation fault'/><category scheme='http://www.blogger.com/atom/ns#' term='muistivuoto'/><title type='text'>Valgrind yksinkertainen esimerkki.</title><content type='html'>&lt;span style="font-size:130%;"&gt;Valgrind Esimerkki.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Eli tässä nyt edellisessä blogitekstissä lupaamani esimerkki valgrindin käytöstä. Demotakseni valgrindin toimintaa, kirjoitin seuraavanlaisen yksinkertaisen koodinpätkän, joka äkkinäisestä lukijasta saattaa näyttä ihan hyvältä. Siihen on kuitenkin "piilotettu" pari hyvin arkista bugia, jotka valgrind saattaa meille paljastaa...&lt;br /&gt;&lt;br /&gt;yliluku.c:&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;br /&gt;#include &amp;lt;string.h&amp;gt;&lt;br /&gt;#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;#define TAULUN_KOKO 4&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt;  size_t taulun_koko=0xFFFFFFFF;&lt;br /&gt;  const char merkkijono[]="foo bar foo bar lorum ipsum foo bar";&lt;br /&gt;  char taulukko[TAULUN_KOKO];&lt;br /&gt;  char *taulu2;&lt;br /&gt;  taulu2=malloc(TAULUN_KOKO);&lt;br /&gt;  memset(taulukko,0,TAULUN_KOKO);&lt;br /&gt;  memcpy(taulukko,merkkijono,TAULUN_KOKO);&lt;br /&gt;  taulun_koko=strlen(taulukko);&lt;br /&gt;  memcpy(taulu2,taulukko,taulun_koko);&lt;br /&gt;  printf("taulukon sisältö \"%s\"\n",taulukko);&lt;br /&gt;  printf("taulu2:n sisältö \"%s\"\n",taulu2);&lt;br /&gt;  printf("Taulun Koko Define = %u\n",TAULUN_KOKO);&lt;br /&gt;  printf("Taulun Koko strlen =  %u\n",taulun_koko);&lt;br /&gt;  printf("Taulu2:n koko strlen = %u\n",strlen(taulu2));&lt;br /&gt;  return TAULUN_KOKO-taulun_koko;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;Kas tässä. Huomaatko heti bugit? Et välttämättä, vaikka kyse on näin lyhyestä ja yksinkertaisesta pätkästä. Nyt jos tähän lisätään 10000 kertainen määrä koodia, lukuisia threadeja joilla on pääsy yhteisiin resursseihin, ja paljon monimutkaisempia rakenteita... Ja satunainen crashi... Mahdollisesti sellainen joka tapahtuu vasta tuntien toiminnan jälkeen, tai vain kerran 10 000 ajolla... Voit kuvitella miten työlästä sellaisen selvittäminen on. (Ja jos ongelma tulee esiin kerran 10 000 ajolla, ja mikäli teet laadukasta softaa sillä mahdollisesti tulee jossain vaiheessa olemaan 10 000 käyttäjää.. Sitä paitsi, ohjelmistoteollisuudessa Mr. Murphy osaa aina näytellä osansa. Yleensä ikävimmät viat tuuppaavat tulemaan esiin silloin, kun ne eivät saisi. Esim. juuri kun demoat ohjelmaasi firman tärkeimmälle asiakkaalle..)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;No joo. Katsotaan siis mitä valgrind tuumaa. Annetaan komento:&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;br /&gt;&lt;br /&gt;valgrind --log-file=ylilukulogi --leak-check=full -v ./yliluku&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;Ohjelman tulostus:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;[Matti@home ~]$ valgrind --log-file=ylilukulogi2 --leak-check=full -v ./yliluku&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;taulukon sisältö "foo foo bar foo bar lorum ipsum foo bar"&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;taulu2:n sisältö "foo foo bar foo bar lorum ipsum foo bar"&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;Taulun Koko Define = 4&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;Taulun Koko strlen =  39&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;Taulu2:n koko strlen = 40&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Hmm.. "yllättäen" taulun koot eivät olekkaan yhtäsuuret, ja ne vieläpä vaihtelevat ajokertojen välillä. Myös viimeinen merkki (tai viimeiset merkit) tulostuksessa ovat hieman erikoisia... (Erikoinen merkki ei välttämättä näy selaimessa...)&lt;br /&gt;&lt;br /&gt;Tutkitaanpa sitte valgrindin kirjoittama logitiedosto:&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;==5488== Memcheck, a memory error detector.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== Copyright (C) 2002-2008, and GNU GPL'd, by Julian Seward et al.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== Using LibVEX rev 1884, a library for dynamic binary translation.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== Copyright (C) 2004-2008, and GNU GPL'd, by OpenWorks LLP.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== Using valgrind-3.4.1, a dynamic binary instrumentation framework.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== Copyright (C) 2000-2008, and GNU GPL'd, by Julian Seward et al.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== My PID = 5488, parent PID = 10064.  Prog and args are:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    ./yliluku&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- Command line&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--    ./yliluku&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- Startup, with flags:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--    --log-file=ylilukulogi2&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--    --leak-check=full&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--    -v&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- Contents of /proc/version:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--   Linux version 2.6.27.5-117.fc10.i686 (mockbuild@x86-7.fedora.phx.redhat.com) (gcc version 4.3.2 20081105 (Red Hat 4.3.2-7) (GCC) ) #1 SMP Tue Nov 18 12:19:59 EST 2008&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- Arch and hwcaps: X86, x86-sse1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- Page sizes: currently 4096, max supported 4096&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- Valgrind library directory: /home/Matti/valgrind_new//lib/valgrind&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- Reading syms from /lib/ld-2.9.so (0x4a7000)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- Reading debug info from /usr/lib/debug/lib/ld-2.9.so.debug ..&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- Reading syms from /home/Matti/yliluku (0x8048000)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- Reading syms from /home/Matti/valgrind_new/lib/valgrind/x86-linux/memcheck (0x38000000)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--    object doesn't have a dynamic symbol table&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- Reading suppressions file: /home/Matti/valgrind_new//lib/valgrind/default.supp&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- REDIR: 0x4c0440 (index) redirected to 0x3803cbc3 (vgPlain_x86_linux_REDIR_FOR_index)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- Reading syms from /home/Matti/valgrind_new/lib/valgrind/x86-linux/vgpreload_core.so (0x4000000)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- Reading syms from /home/Matti/valgrind_new/lib/valgrind/x86-linux/vgpreload_memcheck.so (0x4003000)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== WARNING: new redirection conflicts with existing -- ignoring it&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--     new: 0x004c0440 (index               ) R-&gt; 0x040071b0 index&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- REDIR: 0x4c0610 (strlen) redirected to 0x4007460 (strlen)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- Reading syms from /lib/libc-2.9.so (0x4cc000)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- Reading debug info from /usr/lib/debug/lib/libc-2.9.so.debug ..&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- REDIR: 0x5437f0 (rindex) redirected to 0x4007090 (rindex)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- REDIR: 0x53f6c0 (malloc) redirected to 0x4006c80 (malloc)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- REDIR: 0x5450d0 (memset) redirected to 0x4008350 (memset)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- REDIR: 0x5455e0 (memcpy) redirected to 0x40078b0 (memcpy)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- REDIR: 0x543370 (strlen) redirected to 0x4007440 (strlen)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== Invalid write of size 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4007A07: memcpy (mc_replace_strmem.c:402)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x8048545: main (yliluku.c:18)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==  Address 0x402d02c is 0 bytes after a block of size 4 alloc'd&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4006D3E: malloc (vg_replace_malloc.c:207)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x80484E6: main (yliluku.c:14)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== Invalid write of size 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4007A0F: memcpy (mc_replace_strmem.c:402)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x8048545: main (yliluku.c:18)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==  Address 0x402d02d is 1 bytes after a block of size 4 alloc'd&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4006D3E: malloc (vg_replace_malloc.c:207)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x80484E6: main (yliluku.c:14)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== Invalid write of size 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4007A18: memcpy (mc_replace_strmem.c:402)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x8048545: main (yliluku.c:18)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==  Address 0x402d02e is 2 bytes after a block of size 4 alloc'd&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4006D3E: malloc (vg_replace_malloc.c:207)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x80484E6: main (yliluku.c:14)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== Invalid write of size 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4007A21: memcpy (mc_replace_strmem.c:402)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x8048545: main (yliluku.c:18)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==  Address 0x402d02f is 3 bytes after a block of size 4 alloc'd&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4006D3E: malloc (vg_replace_malloc.c:207)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x80484E6: main (yliluku.c:14)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== Invalid write of size 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4007A57: memcpy (mc_replace_strmem.c:402)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x8048545: main (yliluku.c:18)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==  Address 0x402d04c is not stack'd, malloc'd or (recently) free'd&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- REDIR: 0x548120 (strchrnul) redirected to 0x4008420 (strchrnul)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- REDIR: 0x545130 (mempcpy) redirected to 0x4008480 (mempcpy)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== Invalid read of size 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4007453: strlen (mc_replace_strmem.c:242)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x50F4CF: vfprintf (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x51591F: printf (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x804856B: main (yliluku.c:20)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==  Address 0x402d02c is 0 bytes after a block of size 4 alloc'd&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4006D3E: malloc (vg_replace_malloc.c:207)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x80484E6: main (yliluku.c:14)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== Invalid read of size 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x5367DC: _IO_file_xsputn@@GLIBC_2.1 (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x50ED5C: vfprintf (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x51591F: printf (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x804856B: main (yliluku.c:20)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==  Address 0x402d04f is not stack'd, malloc'd or (recently) free'd&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== Invalid read of size 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x5367F4: _IO_file_xsputn@@GLIBC_2.1 (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x50ED5C: vfprintf (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x51591F: printf (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x804856B: main (yliluku.c:20)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==  Address 0x402d04e is not stack'd, malloc'd or (recently) free'd&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== Invalid read of size 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4008575: mempcpy (mc_replace_strmem.c:677)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x5368C2: _IO_file_xsputn@@GLIBC_2.1 (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x50ED5C: vfprintf (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x51591F: printf (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x804856B: main (yliluku.c:20)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==  Address 0x402d02c is 0 bytes after a block of size 4 alloc'd&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4006D3E: malloc (vg_replace_malloc.c:207)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x80484E6: main (yliluku.c:14)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== Invalid read of size 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4008568: mempcpy (mc_replace_strmem.c:677)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x5368C2: _IO_file_xsputn@@GLIBC_2.1 (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x50ED5C: vfprintf (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x51591F: printf (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x804856B: main (yliluku.c:20)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==  Address 0x402d02d is 1 bytes after a block of size 4 alloc'd&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4006D3E: malloc (vg_replace_malloc.c:207)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x80484E6: main (yliluku.c:14)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== Invalid read of size 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4007453: strlen (mc_replace_strmem.c:242)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x804859D: main (yliluku.c:23)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==  Address 0x402d02c is 0 bytes after a block of size 4 alloc'd&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4006D3E: malloc (vg_replace_malloc.c:207)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x80484E6: main (yliluku.c:14)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- REDIR: 0x53d2b0 (free) redirected to 0x4005aa0 (free)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== ERROR SUMMARY: 181 errors from 11 contexts (suppressed: 14 from 1)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== 1 errors in context 1 of 11:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== Invalid read of size 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x5367DC: _IO_file_xsputn@@GLIBC_2.1 (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x50ED5C: vfprintf (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x51591F: printf (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x804856B: main (yliluku.c:20)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==  Address 0x402d04f is not stack'd, malloc'd or (recently) free'd&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== 3 errors in context 2 of 11:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== Invalid write of size 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4007A57: memcpy (mc_replace_strmem.c:402)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x8048545: main (yliluku.c:18)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==  Address 0x402d04c is not stack'd, malloc'd or (recently) free'd&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== 8 errors in context 3 of 11:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== Invalid write of size 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4007A21: memcpy (mc_replace_strmem.c:402)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x8048545: main (yliluku.c:18)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==  Address 0x402d02f is 3 bytes after a block of size 4 alloc'd&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4006D3E: malloc (vg_replace_malloc.c:207)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x80484E6: main (yliluku.c:14)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== 8 errors in context 4 of 11:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== Invalid write of size 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4007A18: memcpy (mc_replace_strmem.c:402)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x8048545: main (yliluku.c:18)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==  Address 0x402d02e is 2 bytes after a block of size 4 alloc'd&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4006D3E: malloc (vg_replace_malloc.c:207)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x80484E6: main (yliluku.c:14)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== 8 errors in context 5 of 11:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== Invalid write of size 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4007A0F: memcpy (mc_replace_strmem.c:402)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x8048545: main (yliluku.c:18)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==  Address 0x402d02d is 1 bytes after a block of size 4 alloc'd&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4006D3E: malloc (vg_replace_malloc.c:207)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x80484E6: main (yliluku.c:14)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== 8 errors in context 6 of 11:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== Invalid write of size 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4007A07: memcpy (mc_replace_strmem.c:402)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x8048545: main (yliluku.c:18)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==  Address 0x402d02c is 0 bytes after a block of size 4 alloc'd&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4006D3E: malloc (vg_replace_malloc.c:207)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x80484E6: main (yliluku.c:14)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== 18 errors in context 7 of 11:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== Invalid read of size 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4008568: mempcpy (mc_replace_strmem.c:677)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x5368C2: _IO_file_xsputn@@GLIBC_2.1 (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x50ED5C: vfprintf (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x51591F: printf (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x804856B: main (yliluku.c:20)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==  Address 0x402d02d is 1 bytes after a block of size 4 alloc'd&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4006D3E: malloc (vg_replace_malloc.c:207)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x80484E6: main (yliluku.c:14)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== 18 errors in context 8 of 11:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== Invalid read of size 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4008575: mempcpy (mc_replace_strmem.c:677)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x5368C2: _IO_file_xsputn@@GLIBC_2.1 (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x50ED5C: vfprintf (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x51591F: printf (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x804856B: main (yliluku.c:20)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==  Address 0x402d02c is 0 bytes after a block of size 4 alloc'd&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4006D3E: malloc (vg_replace_malloc.c:207)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x80484E6: main (yliluku.c:14)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== 35 errors in context 9 of 11:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== Invalid read of size 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x5367F4: _IO_file_xsputn@@GLIBC_2.1 (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x50ED5C: vfprintf (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x51591F: printf (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x804856B: main (yliluku.c:20)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==  Address 0x402d04e is not stack'd, malloc'd or (recently) free'd&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== 37 errors in context 10 of 11:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== Invalid read of size 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4007453: strlen (mc_replace_strmem.c:242)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x804859D: main (yliluku.c:23)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==  Address 0x402d02c is 0 bytes after a block of size 4 alloc'd&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4006D3E: malloc (vg_replace_malloc.c:207)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x80484E6: main (yliluku.c:14)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== 37 errors in context 11 of 11:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== Invalid read of size 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4007453: strlen (mc_replace_strmem.c:242)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x50F4CF: vfprintf (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x51591F: printf (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x804856B: main (yliluku.c:20)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==  Address 0x402d02c is 0 bytes after a block of size 4 alloc'd&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4006D3E: malloc (vg_replace_malloc.c:207)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x80484E6: main (yliluku.c:14)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- supp:     14 dl-hack3-cond-1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== IN SUMMARY: 181 errors from 11 contexts (suppressed: 14 from 1)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== malloc/free: in use at exit: 4 bytes in 1 blocks.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== malloc/free: 1 allocs, 0 frees, 4 bytes allocated.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== searching for pointers to 1 not-freed blocks.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== checked 48,172 bytes.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== 4 bytes in 1 blocks are definitely lost in loss record 1 of 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4006D3E: malloc (vg_replace_malloc.c:207)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x80484E6: main (yliluku.c:14)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488== LEAK SUMMARY:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    definitely lost: 4 bytes in 1 blocks.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==      possibly lost: 0 bytes in 0 blocks.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    still reachable: 0 bytes in 0 blocks.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==         suppressed: 0 bytes in 0 blocks.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--  memcheck: sanity checks: 0 cheap, 1 expensive&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--  memcheck: auxmaps: 0 auxmap entries (0k, 0M) in use&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--  memcheck: auxmaps_L1: 0 searches, 0 cmps, ratio 0:10&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--  memcheck: auxmaps_L2: 0 searches, 0 nodes&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--  memcheck: SMs: n_issued      = 9 (144k, 0M)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--  memcheck: SMs: n_deissued    = 0 (0k, 0M)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--  memcheck: SMs: max_noaccess  = 65535 (1048560k, 1023M)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--  memcheck: SMs: max_undefined = 0 (0k, 0M)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--  memcheck: SMs: max_defined   = 25 (400k, 0M)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--  memcheck: SMs: max_non_DSM   = 9 (144k, 0M)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--  memcheck: max sec V bit nodes:    0 (0k, 0M)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--  memcheck: set_sec_vbits8 calls: 0 (new: 0, updates: 0)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--  memcheck: max shadow mem size:   448k, 0M&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- translate:            fast SP updates identified: 1,765 ( 89.8%)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- translate:   generic_known SP updates identified: 108 (  5.4%)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- translate: generic_unknown SP updates identified: 92 (  4.6%)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--     tt/tc: 3,939 tt lookups requiring 3,969 probes&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--     tt/tc: 3,939 fast-cache updates, 3 flushes&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--  transtab: new        1,840 (39,574 -&gt; 566,109; ratio 143:10) [0 scs]&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--  transtab: dumped     0 (0 -&gt; ??)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--  transtab: discarded  5 (113 -&gt; ??)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- scheduler: 23,126 jumps (bb entries).&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488-- scheduler: 0/2,143 major/minor sched events.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--    sanity: 1 cheap, 1 expensive checks.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--    exectx: 769 lists, 207 contexts (avg 0 per list)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--    exectx: 397 searches, 216 full compares (544 per 1000)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--    exectx: 0 cmp2, 397 cmp4, 0 cmpAll&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--  errormgr: 22 supplist searches, 981 comparisons during search&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;--5488--  errormgr: 195 errlist searches, 507 comparisons during search&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-size:100%;"&gt;Oh Hoh... johan paukahti.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Ensin näemme nipun valgrindin käynnistystietoja, kuten peruskäskyjen uudelleenohjauksen. Sitten alkaa varsinaiset tuotokset minun koodini osalta. Ensin nippu kirjoituksia häröosoitteisiin&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;==5488== Invalid write of size 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4007A07: memcpy (mc_replace_strmem.c:402)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x8048545: main (yliluku.c:18)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==  Address 0x402d02c is 0 bytes after a block of size 4 alloc'd&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4006D3E: malloc (vg_replace_malloc.c:207)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x80484E6: main (yliluku.c:14)&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;ja kumppanit. Tarkataanpa tätä hetki. Ensin on kerrottu mikä vika on löydetty. Eli kuten jo  totesin, häröosoitteeseen kirjoitus, kirjoitettavan datan ollessa 1 tavu.&lt;br /&gt;Sitten call trace kohdasta jossa ylikirjoitus tapahtui. Eli main() funktiosta riviltä 18 alkoi kutsujen nippu, joka päättyi ylikirjoitukseen memcpy() funktiossa.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;    memcpy(taulu2,taulukko,taulun_koko);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Ja viimeiset pari riviä kertovat kirjoituksen osuneen heti rivillä 14 allokoidun muistipalan perään.&lt;br /&gt;&lt;br /&gt;Eli olemme siis ylikeirjoittaneet memcpy lausekkeessamme taulu2 viimeisen indeksin yli...&lt;br /&gt;&lt;br /&gt;Mutta miksi?&lt;br /&gt;Vastauksen kerron lopussa ;)&lt;br /&gt;&lt;br /&gt;Skipataanpa muiden memcpy:stä tulleiden varoitusten ohi (oh, tämä muuten näyttää sille, että memcpy kirjoitti datan tavu kerrallaan... Aika yllätys minulle). Seuraavaksi tuleekin vastaan Invalid Read:eja&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;==5488== Invalid read of size 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4007453: strlen (mc_replace_strmem.c:242)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x50F4CF: vfprintf (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x51591F: printf (in /lib/libc-2.9.so)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x804856B: main (yliluku.c:20)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==  Address 0x402d02c is 0 bytes after a block of size 4 alloc'd&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4006D3E: malloc (vg_replace_malloc.c:207)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x80484E6: main (yliluku.c:14)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Jälleen ensimmäisenä on tapahtunut virhe, eli luku häröosoitteesta. Sitten jälleen calltrace, ja lähellä majailevan allokoidun muistialueen allokointikohta. Eli rivin 20 printti on siis käyttänyt sisällään strlen() funktiota (tuntuu loogiselta, printti halunnee tietää kuinka pitkästi dataa on printattava käytettäessa %s muotoilua). strlen funktio puolestaan on lukenut yli taulu2 lopun. Toisin sanoen taulu2 muuttujamme ei ole ollut kunnolla "NULL terminoitu", eli ei ole sisältänyt viimeistä NULL merkkiä, ilmoittamassa tekstistringin loppumisesta. Ihan luonnollista koska aiemmin memcpyssä kirjoitimme taulukon lopun yli...&lt;br /&gt;&lt;br /&gt;No nyt on hyvä hetki näyttää myös valgrindin heikkous. Eli ensimmäinen tapahtunut virhe jäi valgrindilta huomaamatta.&lt;br /&gt;&lt;br /&gt;Rivillä 17 (tarkistettaessa taulukko taulukossa olleen tekstistringin pituutta strlen() funktiolla), on ensimmäisen kerran suoritettu luku väärästä paikasta. Ylempänä kopioitaessa tekstiä taulukko muuttujaan, kopioimme koko taulukon mitalta - siis myös viimeisen NULL merkin yli. seuraava strlen() lukee taulukko[0]:n osoitteesta alkaen kunnes löytää '\0' merkin. Koska kirjoitimme juuri sen yli, strlen lukee iloisesti yli taulukko muuttujamme rajojen, saaden stringin pituudeksi huomattavasti pidemmän pätkän kuin pitäisi. Ilmeisesti kuitenkin tämä luku sattui testiprosessimme stackin alueelle, sillä valgrind ei huomannut sitä. Nyt seuraava memcpy kopioi kuitenkin strlen() ilmoittaman (liian pitkän) pituuden verran dataa, joten loppuosa kopioinnista menee taulu2 rajojen yli. Tästä siis valgrindin ensimmäinen herja.&lt;br /&gt;&lt;br /&gt;Loppu onkin jo itsestään selvää. Eli printatessamme taulukko ja taulu2 muuttujien sisällöt tekstinä, printf:n kutsuma strlen() lukee jälleen yli rajojen. Ja kaiken kruunaa vielä valgrind raportin lopussa oleva:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;==5488== 4 bytes in 1 blocks are definitely lost in loss record 1 of 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    at 0x4006D3E: malloc (vg_replace_malloc.c:207)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;==5488==    by 0x80484E6: main (yliluku.c:14)&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Eli allokoidun muistin vapauttaminen pääsi unohtumaan... ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4818234838483192587-1632171562402536491?l=c-ohjelmoijanajatuksia.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-ohjelmoijanajatuksia.blogspot.com/feeds/1632171562402536491/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4818234838483192587&amp;postID=1632171562402536491' title='0 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/1632171562402536491'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/1632171562402536491'/><link rel='alternate' type='text/html' href='http://c-ohjelmoijanajatuksia.blogspot.com/2009/03/valgrind-yksinkertainen-esimerkki.html' title='Valgrind yksinkertainen esimerkki.'/><author><name>Maz</name><uri>http://www.blogger.com/profile/03774313944609044806</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4818234838483192587.post-6652458212756152532</id><published>2009-03-17T23:35:00.009+02:00</published><updated>2009-03-23T19:18:15.844+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='valgrind'/><category scheme='http://www.blogger.com/atom/ns#' term='C'/><category scheme='http://www.blogger.com/atom/ns#' term='debuggeri'/><category scheme='http://www.blogger.com/atom/ns#' term='segmentation fault'/><category scheme='http://www.blogger.com/atom/ns#' term='gdb'/><title type='text'>C - kieli, vikojen etsintä.</title><content type='html'>&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Debuggerit (erityisesti gdb)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Ensimmäistä ohjelmapätkää C:llä koodatessani vietin tunteja etsien indexivirhettä for loopeistani. (Ensimmäinen ohjelmani oli muuten koodinpätkä, joka korvasi parametrina annettuja stringejä, toisilla - tehty silloisten kotisivujeni linkkiosion päivitykseen). Vian etsimiseen käytin printtejä. Tulostin muuttujan arvon toisensa jälkeen, ja yritin löytää kohdan jossa ohjelma kaatui... Printit ovat toki edelleen tehokas keino virheiden etsintään, mutta vuosien aikana olen tutustunut moniin tehokkaisiin ohjelmiin jotka helpottavat vikojen löytämistä.&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;&lt;br /&gt;Debuggerit:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Toivotko joskus että voisit nähdä muuttujien arvot ohjelman suorituksen aikana? Haluaisitko seurata ohjelman ajoa rivi riviltä, nähden miten suoritus hyppii kutsusta toiseen? Tämä on mahdollista debuggerin avulla. Muutamien windows koodaajien olen kuullut kehuvan MS visual studio 2005 mukana tulevaa debuggeria. Itse en ole kyseiseen vempeleeseen tutustunut, sillä windows on kaiken kaikkiaan aika outo alusta minulle. Linuxilla taas tunnetuin debuggeri on gdb, joka yleensä löytyy kaikkien distrojen pakettienhallinnasta. Puhdas gdb voi olla aika hankala käyttää ensi alkuun, sillä se on puhtaasti komentorivipohjainen. Onneksi gdb:n mukana tulee hyvä "helppi", jota voit käyttää myös kesken debuggauksen kirjoittamalla yksinkertaisen komennon "help" tai "help &amp;lt;gdbn_komento&amp;gt;". Lisäksi gdb:tä käyttämään on tehty useita graafisia ohjelmia. Itse olen käyttänyt Eclipse IDE:ä, joka siis sisältää debuggerin lisäksi koodieditorin, käännösnapit jne. Eclipse ei kuitenkaan ole maailman käyttäjäystävällisin mitä tulee projektin luontiin. (ainakaan vanhemmat versiot eivät olleet). Toinen graafinen käyttöliittymä gdb:lle jota olen käyttänyt on ddd. Ddd on oikeastaan vain käyttöliittymä gdb:lle, ilman mitään ylimääräisiä lisäosia.&lt;br /&gt;&lt;br /&gt;Jotta saat täyden hyödyn debuggeristasi, tulee sinun käännösvaiheessa sisällyttä "debuggi-infot" kääntämääsi binääriin. Tällöin debuggeri osaa näyttää kutsuttujen funktioiden nimet, ja kooditiedostot + rivinumerot ajon aikana. Gcc kääntäjällä, debuggi-infot saat binääriin käyttämällä käännöksessä -g (tai -ggdb) flagia. Esim:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;gcc -ggdb -o testiohjelma testiohjelmakoodi.c&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Ja ihan peruskomennot, joilla gdb:n kanssa pääsee alkuun:&lt;br /&gt;&lt;br /&gt;Gdb käynnistetään kertomalla sille ajettavan binäärin nimi, tai vaihtoehtoisesti suoritettvan ohjelman ajokomento parametreineen --args vivun keralla.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;gdb testiohjelma&lt;/span&gt;&lt;br /&gt;tai&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;gdb --args ./testiohjelma parametri1 parametr2 parametri3...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Tämä käynnistää gdbn, joka lukee ajettavan binäärin, ja lataa tarvittavat (ja saatavilla olevat) debuggitiedot. Muista, että C ohjelmasi on tehty standardikirjastojen päälle. Mikäli siis olet asentanut debuggiversiot standardikirjastoista ja linkannut ohjelmasi niitä vasten, voit sujuvasti kurkistella myös standardikirjastojen toteutuksen sisään.&lt;br /&gt;&lt;br /&gt;Tässä vaiheessa ohjelma on siis ladattuna gdb:n sisään, mutta ei ole vielä ajossa. Ohjelman ajon aikana et voi kskyttää gdb:tä, joten nyt on hyvä aika asettaa mahdolliset breakpointit (joihin tultaessa ohjelma pysähtyy), signaalien käsittelyt jne. Sen jälkeen kun ajat ohjelman gdb:n sisällä, voit pysäyttää gdb:n antamalla näppäimistöltä interrupt signaalin SIG_INT (ctrl+c). Tämä tosin pysäyttää ohjelman ajon satunnaiseen paikkaan, mikä harvoin on kannattavaa, ellet halua muuttaa joitain gdb:n asetuksia, kuten breakpointteja tms. (listaan komentoja jäljempänä)&lt;br /&gt;&lt;br /&gt;Tyypillisin ja helpoin gdb:n käyttötapa on seuraava:&lt;br /&gt;&lt;br /&gt;Sinulla on ohjelma foo joka kaatuu segmentation Fault ilmoituksen kanssa. (lue aiemmat tekstini muistinkäytöstä mikäli segmentation fault on hämärä käsite).&lt;br /&gt;&lt;br /&gt;Ladataan ohjelma gdb:hen&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;gdb --args ./foo fooparameters &lt;/span&gt;&lt;br /&gt;tai&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;gdb foo&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;ajetaan ohjelma:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;run&lt;/span&gt;&lt;br /&gt;tai&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;run fooparameters&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;odotetaan että ohjelma saavuttaa kaatumispisteensä...&lt;br /&gt;&lt;br /&gt;Kun MMU (memory management unit) huomaa userspace ohjelman (joita kaikki normaali applikaatiot ovat) pyrkivän accessoimaan muistiosoitteeseen jota sille ei ole "mapattu" käyttöön (esim. kirjoittamalla alustamattomaan osoittimeen, tai taulukon rajojen yli), se herättää kernelin, joka (tavallisesti) lähettää SIGSEGV signaalin ohjelmalle. Normaalisti ohjelman saadessa SIGSEGV signaalin, se lopettaa suorituksensa Segmentation Fault ilmoituksen kera (ellei jotain handleria ole rekisteröity - mihin en nyt puutu). Debuggerilla ajettaessa debuggeri kuitenkin "poimii" signaalin, ja pysäyttää ohjelman suorituksen ongelmakohtaan (kohtaan jossa varsinainen laiton muistiaccessi tapahtui - todellinen ongelma tosin on voinut sattua jo aiemmin). gdb myös näyttää koodirivin jolla kirjoitus/luku tapahtui.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Mikäli kyseisessä kohdassa ei näytä olevan mitään erikoista, tai mikäli ollaan jonkin standardikirjaston sisäisessa kutsussa, käyttäjä yleensä antaa komennon&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;bt&lt;/span&gt;&lt;br /&gt;joka näyttää pinosta funktiot, joiden kautta kaatumispisteeseen on tultu. Kuljetut funktiot on numeroitu siten, että tuoreempi funktiokutsu saa pienemmän numeron. Näitä kutsukohtia voi sitten pomppia eteen ja taaksepäin komennolla&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;frame &lt;numero&gt;&lt;/numero&gt;&lt;/span&gt;&lt;br /&gt;kunnes pääsee kohtaan jossa epäilee ongelman mahdollisesti majailevan. Nyt esim osoittimien osoittaman muistin tilan (tai tavallisen muuttujan sisältämän datan) voit tarkistaa komentamalla&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;inspect muuttujannimi &lt;/span&gt;&lt;br /&gt;tai&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;inspect muistiosoite.&lt;/span&gt;&lt;br /&gt;Mikäli sinulla on osoitin johonkin datarakenteeseen, voit tutkia koko rakenteen datan helposti komennolla&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;inspect *osoitinmuuttujannimi&lt;/span&gt;&lt;br /&gt;tai tietyn jäsenen sisältämä datan&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;inspect osoitinmuuttujannimi-&gt;jäsen.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Mutta ennen kuin tämä osio venyy kohtuuttoman kauas, lopetan yksityiskohtaisen kerronnan, ja sanon pari varoituksen sanaa + listaan liudan käteviä komentoja:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;VaRoItUs!&lt;/span&gt; Erityisesti useampisäikeisissä ohjelmissa on tyypillistä, että koodarit tekevät ikäviä synkronointibugeja, eli sallivat useamman säikeen käpistelevän jotain jaettua resurssia samanaikaisesti. Ikäviä näistä bugeista tekee se, että niiden laukeaminen on hyvin satunnaista, ja erityisen ajoituskriittistä. Kun ohjelmaa ajetaan debuggerissa, ohjelman suoritus hidastuu debuggerin pitäessä kirjaa tapahtumista =&gt; usein ajoituskriittiset viat jotka tulevat esiin ilman debuggeria ajettaessa, eivät tule esiin debuggerin kanssa - ja päinvastoin...&lt;br /&gt;&lt;br /&gt;sitten se lupaamani &lt;span style="font-size:130%;"&gt;&lt;br /&gt;lista gdb:n peruskomennoista:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;ohjelman ajo:&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;run &lt;argumentit&gt;&lt;/argumentit&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;pysäytyskohdan asettaminen:&lt;br /&gt;&lt;span style="font-family:georgia;"&gt;break funktionnimi&lt;/span&gt;&lt;br /&gt;tai&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;break tiedostonnimi:rivinumero&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;ehdollinen pysäyttäminen:&lt;br /&gt;aseta pysäytyskohta kuten edellä, ja pane merkille pysäytyskohdan numero&lt;br /&gt;tee ehto:&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;condition &lt;breakinnumero&gt; &lt;ehto&gt;&lt;/ehto&gt;&lt;/breakinnumero&gt;&lt;/span&gt;&lt;br /&gt;esim:&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;condition 1 muuttuja == 5&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;signaalien käsittely:&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;handle &lt;signaalin&gt;&lt;/signaalin&gt;&lt;/span&gt;&lt;br /&gt;tai&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;handle &lt;signaalin&gt;&lt;/signaalin&gt;&lt;/span&gt;&lt;br /&gt;(kertoo tämänhetkisen signaalin käsittelytavan)&lt;br /&gt;tämän jälkeen voit muuttaa kyseisiä käsittelytapoja määrittämällä pysäytetäänkö ohjelman suoritus signaalin saapuessa, printataanko signaalin tulo ja päästetänkö signaali ajettavalle ohjelmalle.&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;handle &lt;signaalin&gt; &lt;no&gt;stop &lt;no&gt;print &lt;no&gt;pass&lt;/no&gt;&lt;/no&gt;&lt;/no&gt;&lt;/signaalin&gt;&lt;/span&gt;&lt;br /&gt;esim.&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;handle SIGILL&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;handle SIGILL nostop noprint pass&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Säikeet saat listattua (ja tiedon siitä mitä säikeet ovat tekemässä) käyttämällä komentoa:&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;info threads&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;ja säikeestä toiseen voit hypätä komennolla&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;thread numero&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;paikalliset muuttujat saat esille komennolla&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;info locals&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Muuttujien sisältämää dataa pääsee tutkimaan paitsi inspect komennolla, myös komennolla&lt;br /&gt;x&lt;br /&gt;x-komento sallii sinun näyttävän myös haluamasi määrän dataa tietystä muistiosoitteesta (tai osoitinmuuttujan osoittamasta osoitteesta). Erityisen kätevä x komento on kaivettaessa dataa suuresta void muuttujasta, esim muistipoolin alusta. koko syntaksi on&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;x/&lt;näytettävien&gt; &lt;näyttöformaatti&gt; &lt;datatyyppi&gt; osoite/osoitinmuuttuja&lt;/datatyyppi&gt;&lt;/näyttöformaatti&gt;&lt;/näytettävien&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;jossa näyttöformaatti voi olla o=octaali, x=heksadesimaali, d=desimaali, u=unsigned desimaali,  t=binari, f=liukuluku, a=osoite, i=käsky, c=merkki s=merkkijono.&lt;br /&gt;datatyyppi (eli datan koko) voi olla b=tavu, h=2 tavua(short), w=4 tavua(int), g=8 tavua (64 bittiä)&lt;br /&gt;&lt;br /&gt;eli esimerkkinä. Minulla on taulukko int foo[100], jonka alkuun osoittaa osoitin void *bar. Voin siis käskeä esim:&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;x/100xw bar&lt;/span&gt;&lt;br /&gt;tämän pitäisi tulostaa 100 kappaletta (w) 4:n tavun kokoisia datakönttejä (x)heksadesimaalimuodossa.&lt;br /&gt;&lt;br /&gt;Ohjelmasta voit irroittautua komennolla&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;detach&lt;/span&gt;&lt;br /&gt;jolloin debuggeri jättää ohjelman ajelemaan yksikseen.&lt;br /&gt;&lt;br /&gt;tiedostolistauksen saat komennolla&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;list&lt;/span&gt;&lt;br /&gt;joka näyttää 10 riviä kohdasta johon olet juuri pysähtynyt, tai edellisen listauksen loppukohdasta. Voit myös määrittää tiedosto ja rivin jolta alkaen haluat nähdä 10 riviä, tai funktion jonka alusta haluat nähdä 10 riviä komennolla:&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;list funktionnimi&lt;/span&gt;&lt;br /&gt;tai&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;list tiedostonnimi:rivinumero&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Watchpoint muuttujan int foo sisällölle nimen ja osoitteen perusteella:&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;br /&gt;watch foo [threadinnumero mikäli rautawatchpointti]&lt;br /&gt;watch (int)0xB00BBABE [threadinnumero mikäli rautawatchpointti]&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Eli jälkimmäisessä siis oletetaan, että muuttuja foo on talletettu osoitteeseen 0xB00BBABE. Oikean osoitteen saat selville käskyllä&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;br /&gt;print &amp;amp;foo&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;a href="http://c-ohjelmoijanajatuksia.blogspot.com/2009/03/gdb-watch-pointit-pysahdy-kun.html"&gt;Lisää watchpointeista&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Valgrind:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Debuggerin kaverina on hyvä olla jokin muistinanalysointityökalu, jolla voit helposti paikantaa luvut/kirjoitukset yli varatun tilan, alustamattomien muuttujien käytön, muistivuodot jne. Jälleen windows maailma on minulle hieman outo, mutta linuxilla olen käyttänyt mm. valgrind nimistä työkalua.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;valgrind --log-file=foo --leak-check=full -v ./ajettavaohjelma&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;ajaa ohjelman valgrind nimisessä "hiekkalaatikossa", memcheck nimisen muistinanalysointiohjelman alla, tehden foo.prosessiId nimisen logitiedoston, johon liitetään myös täysi muistivuotoanalyysi, ja vielä verbose (mahdollisimman paljon tietoa) moodissa.&lt;br /&gt;Pienen esimerkkilogin ja sen analyysin koetan räpeltää tänne lähipäivinä, kunhan vain ennättäisin...&lt;br /&gt;&lt;br /&gt;To Be Continued... ...later :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4818234838483192587-6652458212756152532?l=c-ohjelmoijanajatuksia.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-ohjelmoijanajatuksia.blogspot.com/feeds/6652458212756152532/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4818234838483192587&amp;postID=6652458212756152532' title='0 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/6652458212756152532'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/6652458212756152532'/><link rel='alternate' type='text/html' href='http://c-ohjelmoijanajatuksia.blogspot.com/2009/03/c-kieli-vikojen-etsinta.html' title='C - kieli, vikojen etsintä.'/><author><name>Maz</name><uri>http://www.blogger.com/profile/03774313944609044806</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4818234838483192587.post-2486917284154988010</id><published>2008-09-16T16:50:00.017+03:00</published><updated>2011-09-19T09:11:58.116+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='ANSI C'/><category scheme='http://www.blogger.com/atom/ns#' term='Maz'/><category scheme='http://www.blogger.com/atom/ns#' term='explode()'/><title type='text'>ANSI C explode.</title><content type='html'>13.09.2011: Allaoleva linkki päivitetty (taas)&lt;br /&gt;16.02.2010 YeeeHaw farmarit! Vai miten se pärstäkirjan suosikkiapplikaatio sen sanoikaan... Jokatapauksessa, SVN on taas pystyssä! &lt;a href="http://xp-dev.com/svn/MazBotV4/trunk/"&gt;http://xp-dev.com/svn/MazBotV4/trunk/&lt;/a&gt;&lt;br /&gt;(generic/src kansiosta löytynee kaikki kiinnostava materiaali)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;09.02.2010 Moro! Ikäviä uutisia, mutta SVN repositorio jota käytän kehitysversion ylläpitoon on tällähaavaa ongelmien kourissa. Ilmoittelen ja korjaan asioita sitämukaa kun ehdin :(&lt;br /&gt;&lt;p&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;02.02.2010: Olinpa aikaansaapa. Tekaisin pikaisen API dokumentaation Cexplodesta ja muista funktioista jotka sisältyvät bottiprojektini "helperseihin". Dokumentaatio löytyy &lt;a href="http://xp-dev.com/svn/MazBotV4/trunk/docs/html/helpers_8h.html"&gt;täältä&lt;/a&gt;. Tämänhetken filut (helpers.h ja helpers.c) ovat &lt;a href="http://xp-dev.com/svn/MazBotV4/trunk/generic/src/"&gt;täällä&lt;/a&gt;. Makefilen joudutte itse kyhäämään, mutta se ei liene kauhean vaikeaa :) Ja jos nyt ihan ylitsepääsemättömiä ongelmia tulee, mutta haluaisitte kuitenkin käyttää funktioita, niin pieni kommentti tänne/sähköposti osoitteeseen Mazziesaccount@gmail.com, ja saatan tehdä tuosta ihan jonkinlaisen ladattavan zipin.&lt;br /&gt;Ja vielä pieni pyyntö: raportoikaa kaikki bugiloiset &lt;a href="http://blackdiam.net/tracker/view_all_bug_page.php"&gt;tänne&lt;/a&gt;, siitä on minulle iiiiso apu! :)&lt;br /&gt;&lt;br /&gt;Hups. Bugin pannahinen piileskelee allaolevassa versiossa - älkää käyttäkö.&lt;br /&gt;Otin tämän Cexploden käyttöön uudessa bottiprojectissani, ja siellä siihen myös ilmaantuu muutamia lisäosasia tarpeen mukaan...&lt;br /&gt;&lt;br /&gt;Täältä löydätte suht testatun ja suht stabiilin version (Cexplode löytyy nyt kansiosta generic/src/, tiedostot helpers.c ja helpers.h.)&lt;br /&gt;http://xp-dev.com/svn/Mazzie-mazbot/&lt;br /&gt;(subversion repository)&lt;br /&gt;Tiedostoissa on joitain muitakin funktioita, ja mikäli käännösherjoja tulee, kannattaa koettaa raapia ne pois.&lt;br /&gt;&lt;br /&gt;Allaolevassa kommentissa on linkki kehitysversioon botista, ja siinä olevan koodin toimivuudesta ei ole mitään takeita... Toisinaan se ei edes käänny :)&lt;br /&gt;&lt;br /&gt;Monet blogini lukijat ovat etsineet C/C++ kielistä toteutusta php:n explode funktiolle. Päätin siis kirjoitella moisen, ja tässä se nyt sitten on.&lt;br /&gt;&lt;br /&gt;Pari sanaa kuitenkin ennen lähdekoodin näyttöä:&lt;br /&gt;&lt;br /&gt;1. Thread safety/re-entrancy.&lt;br /&gt;&lt;br /&gt;Suurimpana ongelmana standardikirjaston strtok:ssa nään sen, ettei se ole monisäikeiseen ohjelmaan soveltuva. (siitä kirjoittelin edellisessä blogitekstissäni). Tämäkään Cexplode ei takaa monisäikeiseen ohjelmointiin soveltuvuutta, sillä siinä käytetään standardikirjaston strlen(), memcmp(), malloc(), free() ja realloc() funktioita. Kuitenkin mikäli teette ohjelmaa normaaliin nykyaikaiseen PC käyttöön, käyttäen normaaleja standardikirjastoja, on melko turvallista olettaa kyseisten funktioiden toimivan myös monisäikeisissä ohjelmissa. (Syynä on se, ettei em. funktioiden implementointi oikeastaan vaadi minkään ei re-entrantin ominaisuuden käyttöä - ja nykyään säikeiden käyttö on enemmänkin sääntö kuin poikkeus =&amp;gt; kirjastot pyritään tekemään monisäikeiseen ohjelmaan soveltuviksi vaikkei C:n standardit sitä vaadi.)&lt;br /&gt;&lt;br /&gt;2. ongelmat Cexplode:n käytössä.&lt;br /&gt;&lt;br /&gt;Lue esimerkki. Mikäli jotain jää epäselväksi, kysy :)&lt;br /&gt;&lt;br /&gt;3. Muokkaus ja käyttö.&lt;br /&gt;&lt;br /&gt;Koodipätkää saa vapaasti käyttää, mutta mikäli muokkaat siitä parannetun version tulee sinun postata parannettu versio kommenttina joko tähän blogiin, tai http://maz-programmersdiary.blogspot.com/ blogiini, tai laittaa se minulle sähköpostilla osoitteeseen Mazziesaccount@gmail.com&lt;br /&gt;&lt;br /&gt;Koetan ehtiä jossain välissä laittaa pakatut tiedostot ladattavaksi, mutta tällähetkellä saatte vain allaolevan copy-pastattavan version.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Cexplode.h&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;/* ******************************************************** */&lt;br /&gt;/*                                                          *&lt;br /&gt;*      Implementation of php's explode written in C        *&lt;br /&gt;*      Written by  Maz (2008)                              *&lt;br /&gt;*      http://maz-programmersdiary.blogspot.com/           *&lt;br /&gt;*                                                          *&lt;br /&gt;*      You're free to use this piece of code.              *&lt;br /&gt;*      You can also modify it freely, but if you           *&lt;br /&gt;*      improve this, you must write the improved code      *&lt;br /&gt;*      in comments at:                                     *&lt;br /&gt;*      http://maz-programmersdiary.blogspot.com/           *&lt;br /&gt;*      or at:                                              *&lt;br /&gt;*      http://c-ohjelmoijanajatuksia.blogspot.com/         *&lt;br /&gt;*      or mail the corrected version to me at              *&lt;br /&gt;*      Mazziesaccount@gmail.com                            *&lt;br /&gt;*                                                          *&lt;br /&gt;*      Revision History:                                   *&lt;br /&gt;*                                                          *&lt;br /&gt;*      -v0.0.1 16.09.2008/Maz                              *&lt;br /&gt;*                                                          */&lt;br /&gt;/* ******************************************************** */&lt;br /&gt;&lt;br /&gt;#ifndef CEXPLODE_H&lt;br /&gt;#define CEXPLODE_H&lt;br /&gt;&lt;br /&gt;#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;#include &amp;lt;string.h&amp;gt;&lt;br /&gt;#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;&lt;br /&gt;typedef struct CexplodeStrings&lt;br /&gt;{&lt;br /&gt;int amnt;&lt;br /&gt;char **strings;&lt;br /&gt;}CexplodeStrings;&lt;br /&gt;&lt;br /&gt;typedef enum ECexplodeRet&lt;br /&gt;{&lt;br /&gt;ECexplodeRet_InternalFailure    = -666,&lt;br /&gt;ECexplodeRet_InvalidParams         = -667&lt;br /&gt;}ECexplodeRet;&lt;br /&gt;&lt;br /&gt;int Cexplode&lt;br /&gt;(&lt;br /&gt;const char *string,&lt;br /&gt;const char *delim,&lt;br /&gt;CexplodeStrings *exp_obj&lt;br /&gt;);&lt;br /&gt;char *Cexplode_getNth(int index,CexplodeStrings exp_obj);&lt;br /&gt;char *Cexplode_getfirst(CexplodeStrings exp_obj);&lt;br /&gt;void Cexplode_free(CexplodeStrings exp_obj);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#endif&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Cexplode.c&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;/* ******************************************************** */&lt;br /&gt;/*                                                          *&lt;br /&gt;*      Implementation of php's explode written in C        *&lt;br /&gt;*      Written by  Maz (2008)                              *&lt;br /&gt;*      http://maz-programmersdiary.blogspot.com/           *&lt;br /&gt;*                                                          *&lt;br /&gt;*      You're free to use this piece of code.              *&lt;br /&gt;*      You can also modify it freely, but if you           *&lt;br /&gt;*      improve this, you must write the improved code      *&lt;br /&gt;*      in comments at:                                     *&lt;br /&gt;*      http://maz-programmersdiary.blogspot.com/           *&lt;br /&gt;*      or at:                                              *&lt;br /&gt;*      http://c-ohjelmoijanajatuksia.blogspot.com/         *&lt;br /&gt;*      or mail the corrected version to me at              *&lt;br /&gt;*      Mazziesaccount@gmail.com                            *&lt;br /&gt;*                                                          *&lt;br /&gt;*      Revision History:                                   *&lt;br /&gt;*                                                          *&lt;br /&gt;*      -v0.0.1 16.09.2008/Maz                              *&lt;br /&gt;*                                                          */&lt;br /&gt;/* ******************************************************** */&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#include "Cexplode.h"&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;int Cexplode&lt;br /&gt;(&lt;br /&gt;const char *string,&lt;br /&gt;const char *delim,&lt;br /&gt;CexplodeStrings *exp_obj&lt;br /&gt;)&lt;br /&gt;{&lt;br /&gt;int stringL = 0;&lt;br /&gt;int delimL  = 0;&lt;br /&gt;int index;&lt;br /&gt;int pieces=0;&lt;br /&gt;int string_start=0;&lt;br /&gt;&lt;br /&gt;char **tmp=NULL;&lt;br /&gt;&lt;br /&gt;//Sanity Checks:&lt;br /&gt;if(NULL==string || NULL==delim || NULL == exp_obj)&lt;br /&gt;{&lt;br /&gt;    printf("Invalid params given to Cexplode!\n");&lt;br /&gt;    return ECexplodeRet_InvalidParams;&lt;br /&gt;}&lt;br /&gt;stringL = strlen(string);&lt;br /&gt;delimL  = strlen(delim);&lt;br /&gt;if(delimL&amp;gt;=stringL)&lt;br /&gt;{&lt;br /&gt;    printf("Invalid params given to Cexplode!\n");&lt;br /&gt;    return 0;&lt;br /&gt;}&lt;br /&gt;for(index=0;index&amp;lt;stringL-delimL;index++)&lt;br /&gt;{&lt;br /&gt;    if(string[index]==delim[0])&lt;br /&gt;    {&lt;br /&gt;        //Check if delim was actually found&lt;br /&gt;        if( !memcmp(&amp;amp;(string[index]),delim,delimL) )&lt;br /&gt;        {&lt;br /&gt;            //token found&lt;br /&gt;            //let's check if token was at the beginning:&lt;br /&gt;            if(index==string_start)&lt;br /&gt;            {&lt;br /&gt;                string_start+=delimL;&lt;br /&gt;                index+=delimL-1;&lt;br /&gt;                continue;&lt;br /&gt;            }&lt;br /&gt;            /*&lt;br /&gt;               if token was not at start, then we&lt;br /&gt;               should add it in CexplodeStrings&lt;br /&gt;            */&lt;br /&gt;            pieces++;&lt;br /&gt;            if(NULL==tmp)&lt;br /&gt;                tmp=malloc(sizeof(char *));&lt;br /&gt;            else&lt;br /&gt;                tmp=realloc(tmp,sizeof(char *)*pieces);&lt;br /&gt;            if(NULL==tmp)&lt;br /&gt;            {&lt;br /&gt;                printf("Cexplode: Malloc failed!\n");&lt;br /&gt;                return ECexplodeRet_InternalFailure;&lt;br /&gt;            }&lt;br /&gt;            //alloc also for \0&lt;br /&gt;            tmp[pieces-1]=malloc&lt;br /&gt;            (&lt;br /&gt;              sizeof(char *)*(index-string_start+1)&lt;br /&gt;            );&lt;br /&gt;            if(NULL==tmp[pieces-1])&lt;br /&gt;            {&lt;br /&gt;                printf("Cexplode: Malloc failed!\n");&lt;br /&gt;                return ECexplodeRet_InternalFailure;&lt;br /&gt;            }&lt;br /&gt;            memcpy(&lt;br /&gt;              tmp[pieces-1],&lt;br /&gt;              &amp;amp;(string[string_start]),&lt;br /&gt;              index-string_start&lt;br /&gt;            );&lt;br /&gt;&lt;br /&gt;            tmp[pieces-1][index-string_start]='\0';&lt;br /&gt;            string_start=index+delimL;&lt;br /&gt;            index+=(delimL-1);&lt;br /&gt;        }//delim found&lt;br /&gt;    }//first letter in delim found from string&lt;br /&gt;}//for loop&lt;br /&gt;&lt;br /&gt;if(memcmp(&amp;amp;(string[index]),delim,delimL))&lt;br /&gt;    index+=delimL;&lt;br /&gt;if(index!=string_start)&lt;br /&gt;{&lt;br /&gt;    pieces++;&lt;br /&gt;    if(NULL==tmp)&lt;br /&gt;        tmp=malloc(sizeof(char *));&lt;br /&gt;    else&lt;br /&gt;        tmp=realloc(tmp,sizeof(char *)*pieces);&lt;br /&gt;    if(NULL==tmp)&lt;br /&gt;    {&lt;br /&gt;        printf("Cexplode: Malloc failed!\n");&lt;br /&gt;        return ECexplodeRet_InternalFailure;&lt;br /&gt;    }&lt;br /&gt;        tmp[pieces-1]=malloc&lt;br /&gt;       (&lt;br /&gt;          sizeof(char *)*(index-string_start+1)&lt;br /&gt;       );&lt;br /&gt;    if(NULL==tmp[pieces-1])&lt;br /&gt;    {&lt;br /&gt;        printf("Cexplode: Malloc failed!\n");&lt;br /&gt;        return ECexplodeRet_InternalFailure;&lt;br /&gt;    }&lt;br /&gt;    memcpy&lt;br /&gt;    (&lt;br /&gt;      tmp[pieces-1],&lt;br /&gt;      &amp;amp;(string[string_start]),&lt;br /&gt;      index-string_start&lt;br /&gt;    );&lt;br /&gt;    tmp[pieces-1][index-string_start+1]='\0';&lt;br /&gt;}&lt;br /&gt;exp_obj-&amp;gt;amnt=pieces;&lt;br /&gt;exp_obj-&amp;gt;strings=tmp;&lt;br /&gt;return pieces;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;char *Cexplode_getNth(int index,CexplodeStrings exp_obj)&lt;br /&gt;{&lt;br /&gt;if(exp_obj.amnt&amp;lt;index)&lt;br /&gt;{&lt;br /&gt;    return NULL;&lt;br /&gt;}&lt;br /&gt;return exp_obj.strings[index-1];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;char *Cexplode_getfirst(CexplodeStrings exp_obj)&lt;br /&gt;{&lt;br /&gt;return Cexplode_getNth(1,exp_obj);&lt;br /&gt;}&lt;br /&gt;void Cexplode_free(CexplodeStrings exp_obj)&lt;br /&gt;{&lt;br /&gt;int i=0;&lt;br /&gt;for(;i&amp;lt;exp_obj.amnt;i++)&lt;br /&gt;    free(exp_obj.strings[i]);&lt;br /&gt;free(exp_obj.strings);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Cexplode_example.c&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;/* ******************************************************** */&lt;br /&gt;/*                                                          *&lt;br /&gt;*      Implementation of php's explode written in C        *&lt;br /&gt;*      Written by  Maz (2008)                              *&lt;br /&gt;*      http://maz-programmersdiary.blogspot.com/           *&lt;br /&gt;*                                                          *&lt;br /&gt;*      You're free to use this piece of code.              *&lt;br /&gt;*      You can also modify it freely, but if you           *&lt;br /&gt;*      improve this, you must write the improved code      *&lt;br /&gt;*      in comments at:                                     *&lt;br /&gt;*      http://maz-programmersdiary.blogspot.com/           *&lt;br /&gt;*      or at:                                              *&lt;br /&gt;*      http://c-ohjelmoijanajatuksia.blogspot.com/         *&lt;br /&gt;*      or mail the corrected version to me at              *&lt;br /&gt;*      Mazziesaccount@gmail.com                            *&lt;br /&gt;*                                                          *&lt;br /&gt;*      Revision History:                                   *&lt;br /&gt;*                                                          *&lt;br /&gt;*      -v0.0.1 16.09.2008/Maz                              *&lt;br /&gt;*                                                          */&lt;br /&gt;/* ******************************************************** */&lt;br /&gt;&lt;br /&gt;#include "stdio.h"&lt;br /&gt;#include "Cexplode.h"&lt;br /&gt;&lt;br /&gt;int main(int argc,char *argv[])&lt;br /&gt;{&lt;br /&gt;char *string;&lt;br /&gt;char *delim;&lt;br /&gt;int retval;&lt;br /&gt;int index=0;&lt;br /&gt;char *token;&lt;br /&gt;CexplodeStrings expString;&lt;br /&gt;if(argc!=3)&lt;br /&gt;{&lt;br /&gt;    printf("Test Command should be:\n");&lt;br /&gt;    printf&lt;br /&gt;    (&lt;br /&gt;      "&amp;lt;testExe&amp;gt; \"original string\" \"delimiter string\""&lt;br /&gt;    );&lt;br /&gt;    return -1;&lt;br /&gt;}&lt;br /&gt;string=argv[1];&lt;br /&gt;delim=argv[2];&lt;br /&gt;&lt;br /&gt;printf("TestString is \"%s\"\n",string);&lt;br /&gt;printf("Test Delimiter is \"%s\"\n",delim);&lt;br /&gt;if(0&amp;gt;(retval=Cexplode(string,delim,&amp;amp;expString)))&lt;br /&gt;{&lt;br /&gt;    printf("CexplodeFailed!\n");&lt;br /&gt;    return -1;&lt;br /&gt;}&lt;br /&gt;else&lt;br /&gt;{&lt;br /&gt;    //Way 1, use expString straight away:&lt;br /&gt;    printf("Way 1, use expString straight away:\n");&lt;br /&gt;    for(index=0;index&amp;lt;expString.amnt;index++)&lt;br /&gt;    {&lt;br /&gt;        printf&lt;br /&gt;        (&lt;br /&gt;          "token %d = %s\n",&lt;br /&gt;          index+1,&lt;br /&gt;          expString.strings[index]&lt;br /&gt;        );&lt;br /&gt;    }&lt;br /&gt;    /*&lt;br /&gt;       Way 2, you can use Cexplode_getNth,&lt;br /&gt;       or Cexplode_getfirst()&lt;br /&gt;    */&lt;br /&gt;    printf(&lt;br /&gt;     "Way 2 use Cexplode_getNth, or Cexplode_getfirst():\n"&lt;br /&gt;    );&lt;br /&gt;    token=Cexplode_getfirst(expString);&lt;br /&gt;    printf("first token %s\n",token);&lt;br /&gt;    index=1;&lt;br /&gt;    while(NULL!=(token=Cexplode_getNth(++index,expString)))&lt;br /&gt;    {&lt;br /&gt;        printf("token %d = %s\n",index,token);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;Cexplode_free(expString);&lt;br /&gt;return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4818234838483192587-2486917284154988010?l=c-ohjelmoijanajatuksia.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-ohjelmoijanajatuksia.blogspot.com/feeds/2486917284154988010/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4818234838483192587&amp;postID=2486917284154988010' title='3 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/2486917284154988010'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/2486917284154988010'/><link rel='alternate' type='text/html' href='http://c-ohjelmoijanajatuksia.blogspot.com/2008/09/ansi-c-explode.html' title='ANSI C explode.'/><author><name>Maz</name><uri>http://www.blogger.com/profile/03774313944609044806</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4818234838483192587.post-6883288448837145697</id><published>2008-09-10T19:06:00.005+03:00</published><updated>2008-09-10T19:12:36.244+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C'/><category scheme='http://www.blogger.com/atom/ns#' term='mutexit'/><category scheme='http://www.blogger.com/atom/ns#' term='reentrant'/><category scheme='http://www.blogger.com/atom/ns#' term='pthread'/><category scheme='http://www.blogger.com/atom/ns#' term='monisäikeiset ohjelmat'/><category scheme='http://www.blogger.com/atom/ns#' term='semaforit'/><category scheme='http://www.blogger.com/atom/ns#' term='säikeet'/><category scheme='http://www.blogger.com/atom/ns#' term='thread safe'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>Monisäikeiset ohjelmat.</title><content type='html'>&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;olen jo aiemmin blogissani esitellyt pthread kirjaston käyttöä säikeiden luontiin. Mainitsin myös semaforien ja mutexien käytön, sekä näytin muutaman esimerkin syntaxista. Nyt tarkoituksenani on kertoa hieman enemmän aiheeseen liittyvistä sudenkuopista.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Re-entrancy ja thread safety:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Pohditaampa hetkinen monisäikeisten ohjelmien toimintaa, ja muuttujien näkyvyyttä. Monisäie ohjelmoinnissahan useampi koodinpätkä voi olla suorituksessa yhtä aikaa. Pohditaanpa hetkinen mitä tämä merkitsee funktiotasolla. Tehdään jälleen ongelman ytimen selkeyttävä ajatusleikki. Kuvitellaanpa että meillä on funktio, joka keittää kahvia. Kahvinkeitin olkoon muuttuja jota funktiomme käyttää. Nyt meillä on kaksi vaihtoehtoa sille, miten kahvinkeittimemme sijoitamme. Voimme luoda kahvinkeittimen funktion sisällä, jolloin joka kerta kun funktiota kutsutaan, luodaan uusi kahvinkeitin. Tai sitten voimme määritellä kahvinkeittimemme globaalina muuttujana, jolloin kahvinkeittimiä on vain yksi, ja aina kun funktiota kutsutaan käytetään samaa kahvinkeitintä. Mititäänpä nyt hetki monisäikeistä ohjelmaa, kumpikohan kahvinkeitinmalli toimisi paremmin?&lt;br /&gt;&lt;br /&gt;Nyt tekisi miei sanoa, että globaali keitin, kukas hullu sitä kahvia niin paljon keittää, ettei yksi keitin riitä? Mutta kun palaamme takaisin ohjelmointiin, huomaamme, että mikäli meillä on vain yksi keitin, ja mikäli kahvinkeittofunktiotamme kutsutaan monesta säikeestä, saattaa eteen tulla tilanne, että säie yksi on juuri keskellä kahvinkeittofunktiota, kun scheduleri päättää antaa ajoaikaa säie 2:lle. Nyt säie kaksi menee kahvinkeittofunktioon, ja ...&lt;br /&gt;&lt;br /&gt;Huomaattehan riskin? on mahdollista, että säie yksi oli juuri täyttänyt kahvinkeittimen vesisäiliön, kun säie kaksi pääsi ajoon. Nyt säie kaksi kaataa säiliöön lisää vettä, ja keitin tulvii yli (kumpikin säiehän käytti samaa keitintä). Säie kaksi lisää kahvin, painaa virtanappulaa ja kaataa kahvin kuppeihin. Säie 2 jatkaa muissa tehtävissä, kunnes scheduleri antaa taas ajoaikaa säikeelle 1. Nyt säie yksi muistaa täyttäneensä veden, lisää kahvin ja painaa virtanappulaa. Mutta koska säie kaksi kävi välissä käyttämässä keitintä, vesisäiliö on nyt tyhjä, ja keitin palaa tuhkaksi...&lt;br /&gt;&lt;br /&gt;Mietitäänpä miten ongelma voidaan välttää. Yksi keino on käyttää funktion sisäistä kahvinkeitin muuttujaa, jolloin aina kun keittofunktiota kutsutaan, kutsuja saa ihka oman keittimen käyttöönsä. Mutta aina tämä ei ole järkevää. Joskus on tarpeen käpistellä samaa muuttujaa/oliota/tms. useasta threadista. Nyt vaihtoehtona on käyttää edellä esiteltyjä semaforeja/mutexeja. Eli laitetaan keitin lukolliseen huoneeseen, johon on vain yksi avain. Nyt kun säie yksi ryhtyy kahvinkeittopuuhiin, se menee huoneeseen sisälle, ottaa avaimen lukosta, ja lukitsee oven perässään. Mikäli scheduleri antaa kesken kahvinkeiton ajoaikaa säikeelle 2, ja mikäli säie kaksi koettaa ryhtyä kahvinkeittoon, se törmää lukittuun oveen ja joutuu odottelemaan vuoroaan kunnes säie yksi on keittänyt kahvinsa. Säie yksi saa taas ajoaikaa, keittää kahvin valmiiksi, poistuu huoneesta ja jättää mennessään avaimen lukkoon. Nyt säie kaksi pääsee kokkailemaan, lukiten oven huoneeseen mennessään...&lt;br /&gt;&lt;br /&gt;re-entrant ja thread safe ovat termejä jotka kuvaavat jonkin funktion soveltuvuutta monisäikeiseen ohjelmointiin. Termeillä on kuitenkin perustavaa laatua oleva ero. ohjelmointi, ja varsinkin C kun ovat ihmeitä täynnä, tuo edellinen lukkoesimerkki ei olekkaan aivan vedenpitävä. Eli lukkoesimerkki on esimerkki thread safe funktiosta, mutta re-entrant se ei ole.&lt;br /&gt;&lt;br /&gt;re-entrant termi vaatii nimittäin, että funktio täyttää myös kvanttimekaniikan mukanaan tuomat kummallisuudet... Tai jotain :D Eli re-entrant funktio on sellainen, joka toimii myös tilanteessa jossa säie hyppää kesken jonkin funktion suorituksen suorittamaan samaa funktiota. Eli kahvinkeittoesimerkissämme säie yksi kesken kahvinkeiton tunneloituisi lukitun oven väärälle puolelle, ja pyrkisi uudelleen keittämään kahvia. Tämähän ei toimisi sillä avainhan jäi kahvinkeittohuoneeseen. Sen sijaan paikallista kahvinkeitintä käyttämällä tämä toimisi. Nyt kun säie yksi hyppäisi uudelleen funktion alkuun, se loisi itsellleen uuden keittimen, keittelisi kahvin ja palaisi sitten jatkamaan keittelyä sillä toisella keittimellä...&lt;br /&gt;&lt;br /&gt;Mikä C:ssä siis saa moisia kvanttimekaanisia ilmiöitä aikaan?&lt;br /&gt;&lt;br /&gt;No perus C:ssä oikeastaan vain rekursiiviset funktiot (eli funktiot jotka kutsuvat itseään). Rekursiivista funktiota rakentaessa ohjelmoija kuitenkin yleensä tietää tilanteen, ja osaa automaattisesti ottaa tällaisen tilanteen huomioon. Mutta tutkitaanpa hieman vaikka posix rajapintaa. (posix = portaple operating system standardi, joka määrittelee usita C-kielen funktioita ja metodeita. Tyypillisiä posix standardin täyttäviä käyttöjärjestelmiä ovat mm. linux ja unix). Posix määrittelee signaalit, joiden voidaan ajatella olevan ohjelmallisia keskeytyksiä. Eli säie määrittää funktion, jonka se suorittaa mikäli tietyntyyppinen keskeytys tulee. Nyt välittömästi kun tällainen signaali generoidaan, säie jättää sen hetkiset tehtävänsä, ja hyppää suorittamaan keskeytyksen käsittely funktiotaan.&lt;br /&gt;&lt;br /&gt;Jos nyt jatketaan kahvinkeitto linjoilla, ja mietitään tilanne jossa olemme rekisteröineet kahvinkeittofunktion jonkin tietyn signaalin käsittely funktioksi. Sitten ohjelmamme huomaa janoisen vieraan, ja nostaa signaalin, jolloin säikeemme jolle kahvinkeittokäsittely oli määrätty, hyppää suorittamaan kahvinkeittofunktiota. Kuinka ollakkaan, yht-äkkiä, kesken kahvin keittelyn, ohjelmamme huomaa uuden janoisen vieraan. Keskeytys generoidaan, ja kahvinkeittokäsittelijämme jättää ensimmäisen keittokerran kesken, ja hyppää uudelleen keittofunktion alkuun. Mikäli meillä nyt on semafori tai mutexi suojaamassa globaalia keitintämme, saamme aikaan deadlockin, sillä semafori/mutex on varattuna (ovi on lukittuna), mutta säie jonka tulisi avata mutex/semafori, odottaa nyt kahvinkeittofunktion alussa, lukon takana... Ts. pattitilanne.&lt;br /&gt;&lt;br /&gt;Eli, re-entrancy on tietyssä mielessä tiukempi vaatimus kuin thread safety. Ja keskeytysfunktioiden kanssa TULEE olla tarkkana.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:100%;" &gt;Eli summa summarum, ongelmakohtia säikeiden kanssa:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;1. saman resurssin käpistely useasta säikeestä. (globaali data, static muuttujat)&lt;br /&gt;2. Deadlock. (Jonkin resurssin odottelu, kun vapauttaja odottelee myös).&lt;br /&gt;&lt;br /&gt;Ja vielä.... STANDARDIKIRJASTON MUUTTUJAT EIVÄT PÄÄSÄÄNTÖISESTI OLE REENTRANTTEJA! Eli keskeytysfunktioissa tulisi välttää standardikirjaston funktioiden käyttöä! Myöskään thread safety ei standardikirjaston funktioille ole tavanomaista! Esim malloc ja free on yleensä toteutettu siten, että ne käpistelevät jotain globaalia resurssia jossa pidetään kirjaa varatuista muistiblockeista! Kuitenkin useista standardikirjaston muuttujista on tehty reentrantit/threadsafet versiot, jotka on myös tyypillisesti nimetty &lt;standardifunktionnimi&gt;_r() ( esim strtok_r(). )&lt;br /&gt;&lt;br /&gt;... Lisää ehkä taas joskus :)&lt;/standardifunktionnimi&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4818234838483192587-6883288448837145697?l=c-ohjelmoijanajatuksia.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-ohjelmoijanajatuksia.blogspot.com/feeds/6883288448837145697/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4818234838483192587&amp;postID=6883288448837145697' title='0 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/6883288448837145697'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/6883288448837145697'/><link rel='alternate' type='text/html' href='http://c-ohjelmoijanajatuksia.blogspot.com/2008/09/monisikeiset-ohjelmat.html' title='Monisäikeiset ohjelmat.'/><author><name>Maz</name><uri>http://www.blogger.com/profile/03774313944609044806</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4818234838483192587.post-2588845009810494159</id><published>2008-08-28T21:17:00.009+03:00</published><updated>2008-09-10T23:25:15.411+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='enum'/><category scheme='http://www.blogger.com/atom/ns#' term='C'/><category scheme='http://www.blogger.com/atom/ns#' term='Muisti'/><category scheme='http://www.blogger.com/atom/ns#' term='static'/><category scheme='http://www.blogger.com/atom/ns#' term='typedef'/><category scheme='http://www.blogger.com/atom/ns#' term='Maz'/><title type='text'>Avainsana static C:ssä.</title><content type='html'>&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;avainsana static on ehkä C:n (ja myös C++:n) huonoimmin suunniteltu ominaisuus. Ja kun puhutaan C:n huonoimmin suunnitellusta osasta, puhumme ehkä maailmankaikkeuden kaikkien ohjelmointikielien sekavimmasta avainsanasta :)&lt;br /&gt;&lt;br /&gt;Syyt ovat edelliseen väittämään ovat hyvin yksinkertaisia. Ensinnäkin C:ssä ja C++:ssa static määrettä käytetään joissain määrin eri tavoin. (C++:ssa static sanalle tulee vielä pari uutta käyttötarkoitusta). Tämä on omiaan sekoittamnaan jo ennestään sekavan koodarin pään. Kun tähän vielä lisätään se, että C:ssä sanalla static on kaksi toisistaan näennäisesti täysin poikkeavaa käyttötarkoitusta, hiipii mieleen ajatus että kielen kehittäjien sanavarasto lienee  ollut hieman vajaa... Ehkä olisi ollut syytä harkita kahden eri sanan käyttämistä...&lt;br /&gt;&lt;br /&gt;No kieli on tehty jo ajat sitten, joten tilannetta on turha itkeä enää. Täytyy siis vain yrittää oppia. Katsotaan ensin yksinkertaisempi, mutta äärimmäisen käytännöllinen static:n käyttötarkoitus. Kaikessa kauneudessaan:&lt;br /&gt;&lt;br /&gt;Laittamalla sana static globaalin muuttujan määrittelyn eteen, muuttujan näkyvyys rajoittuu tiedostoon jossa se on esitelty.&lt;br /&gt;Samalla tavalla laitettaessa static määre funktion esittelyn eteen, saadaan funktion näkyvyys rajoitettua ainoastaan siihen tiedostoon, jossa se on toteutettu.&lt;br /&gt;&lt;br /&gt;esim:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;funktiot.h&lt;/b&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;extern int G_globaali1;&lt;br /&gt;extern int G_globaali2;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;funktiot.c&lt;/b&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;int G_globaali1=1;&lt;br /&gt;static int G_globaali2=2;&lt;br /&gt;static void funktio1();&lt;br /&gt;void funktio2();&lt;br /&gt;&lt;br /&gt;void funktio1()&lt;br /&gt;{&lt;br /&gt;    ...&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void funktio2()&lt;br /&gt;{&lt;br /&gt;    ...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;main.c&lt;/b&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#include "funktiot.h"&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt;    int x;&lt;br /&gt;    x=G_globaali1;&lt;br /&gt;    x=G_globaali2;&lt;br /&gt;    funktio1();&lt;br /&gt;    funktio2();&lt;br /&gt;    return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Ylläoleva esimerkki ei linkkantuisi, sillä funktio1():tä ja G_globaali2:ta ei löytyisi main.c tiedostossa. Voitte kokeilla tätä tekemällä esimerkin mukaiset tiedostot, ja laittamalla jonkinlaisen toteutuksen funktio1:lle ja funktio2:lle. gcc:llä homma onnistuu sitten komennolla&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;gcc -o testi main.c funktiot.c&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Koettakaa sitten ottaa static sanat pois, ja kääntäkää uudestaan...&lt;br /&gt;&lt;br /&gt;Miksi kukaan sitten haluaisi rajoittaa funktion/muuttujan näkyvyyttä tällä tavalla? Pohditaanpa hetki. Minä olen ollut töissä eräässä projektissa, jossa samaa mammuttimaisen suurta ohjelmistoa teki arviolta 200 henkeä. En edes tuntenut kaikkia, saati että olisin tuntenut kaikkien tekemien ohjelmistokomponenttien toteutuksen. Ja ajan oloon unohdin myös itse tekemieni koodien yksityiskohdat.&lt;br /&gt;&lt;br /&gt;ohjelmisto pohjautui viestipohjaiseen reaaliaika käyttöjärjestelmään, ja vain arvata saattaa kuinka moni koodari toteutti toisistaan tietämättä esim. send_msg() viestin. Testikoodeja ohjelmaan oli satojatuhansia rivejä, ja yksittäisi testejä tuhansia. Sopii vain pohtia kuinka monta status tai G_status tai G_test_status globaalia koodeissa esiteltiin. Yksinkertainen keino välttää päällekkäiset määritykset oli käyttää juuri static avainsanaa.&lt;br /&gt;&lt;br /&gt;No niin. Koska tämä näyttää ihan selkeältä ja järkevältä, niin on syytä sekoittaa pakkaa ettei totuus unohtuisi :) Eihän C:n näin helppoa kuulu olla, joten vedetään kehiin static sanan käyttö funktion sisäisten muuttujien esittelyssä. No nythän näkyvyyttä ei ole tarpeen rajoittaa, funktion sisäiset muuttujathan eivät muutenkaan näy funktion ulkopuolelle. Paikallisten muuttujien yhteydessä static tekeekin muuttujista "staattisia". Ts. ne ovat varattuna jo ohjelman käynnistyksestä lähtien, riippumatta siitä, tuleeko ohjelman suoritus koskaan kyseistä funktiota kutsumaan. Staattiset muuttujat myös &lt;b&gt;säilyttävät arvonsa funktion kutsujen välissä&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;Ja valoitetaan jälleen esimerkillä:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;typedef enum ElaskurinToimintamoodi&lt;br /&gt;{&lt;br /&gt;    ElaskurinToimintamoodi_lisaa=0,&lt;br /&gt;    ElaskurinToimintamoodi_vahenna,&lt;br /&gt;    ElaskurinToimintamoodi_palauta&lt;br /&gt;}ElaskurinToimintamoodi;&lt;br /&gt;&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt;    while(laskuri(ElaskurinToimintamoodi_palauta)&amp;lt;10)&lt;br /&gt;    {&lt;br /&gt;        if(laskuri(ElaskurinToimintamoodi_lisaa)==0)&lt;br /&gt;        {&lt;br /&gt;            printf("virhe laskurissa");&lt;br /&gt;            return -1;&lt;br /&gt;        };&lt;br /&gt;        if(laskuri(ElaskurinToimintamoodi_lisaa)==0)&lt;br /&gt;        {&lt;br /&gt;            printf("virhe laskurissa");&lt;br /&gt;            return -1;&lt;br /&gt;        };&lt;br /&gt;        if(laskuri(ElaskurinToimintamoodi_vahenna)==0)&lt;br /&gt;        {&lt;br /&gt;            printf("virhe laskurissa");&lt;br /&gt;            return -1;&lt;br /&gt;        };&lt;br /&gt;    }&lt;br /&gt;    return 0;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;int laskuri(ElaskurinToimintamoodi toiminto)&lt;br /&gt;{&lt;br /&gt;    static int luku=0;&lt;br /&gt;    switch(toiminto)&lt;br /&gt;    {&lt;br /&gt;        case ElaskurinToimintamoodi_lisaa:&lt;br /&gt;          luku++;&lt;br /&gt;        break;&lt;br /&gt;        caseElaskurinToimintamoodi_vahenna:&lt;br /&gt;          luku--;&lt;br /&gt;        break;&lt;br /&gt;        case ElaskurinToimintamoodi_palauta:&lt;br /&gt;          return luku;&lt;br /&gt;        break;&lt;br /&gt;        default:&lt;br /&gt;            printf("Virhe! Tuntematon toimintamoodi annettu");&lt;br /&gt;            return 0;&lt;br /&gt;        break;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Kuutioidaanpa hetki tuota laskuri funktiota. Eli funktiolle annetaan parametrina enumin arvo, joka kertoo mitä laskurin tahdotaan tekevän. Itsestään selvää. Mutta mitä mitä. Nythän laskurin arvo pidetään funktion sisäisessä muuttujassa "luku", eikä globaalissa. Normaalistihan tämä muuttuja menettäisi arvonsa aina kun funktiosta poistutaan. Tässä nyt kuitenkin arvo säilyy, avainsana static:in ansiosta. Pohditaanpa vielä hetki. lukuhan määritellään funktion alussa: static int luku=0; Miksei luku muuttujaan siis joka kutsulla talleteta nollaa, ja sekoiteta näin laskuriamme? Static sanan toinen merkillisyys on siinä, että static muuttuja alustetaan vain kerran, ohjelman suorituksen alkaessa. Koska muuttuja ei ikinä poistu muistista (ikinä on nyt siihen asti kuin ohjelman suoritus kestää, eli esimerkin tapauksessa, nykyajan koneilla muutamia millisekunteja. Suhteellisuusteoria on siis todistettu ajan suhteellisuuden osalta.)&lt;br /&gt;&lt;br /&gt;Ja viimein. C++:ssa static:lla on siis lisäksi omat tarkoituksensa, ja siitä ehkä jokin toinen kerta...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4818234838483192587-2588845009810494159?l=c-ohjelmoijanajatuksia.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-ohjelmoijanajatuksia.blogspot.com/feeds/2588845009810494159/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4818234838483192587&amp;postID=2588845009810494159' title='1 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/2588845009810494159'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/2588845009810494159'/><link rel='alternate' type='text/html' href='http://c-ohjelmoijanajatuksia.blogspot.com/2008/08/avainsana-static-css.html' title='Avainsana static C:ssä.'/><author><name>Maz</name><uri>http://www.blogger.com/profile/03774313944609044806</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4818234838483192587.post-6298437335220377071</id><published>2008-08-23T23:15:00.004+03:00</published><updated>2008-08-24T00:08:13.420+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='trim()'/><category scheme='http://www.blogger.com/atom/ns#' term='C'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><category scheme='http://www.blogger.com/atom/ns#' term='Maz'/><category scheme='http://www.blogger.com/atom/ns#' term='explode()'/><title type='text'>php explode() ja rtrim() funktiot C++:aan ja C:hen.</title><content type='html'>&lt;br /&gt;&lt;br /&gt;Kohta 30 vuotisen taipaleeni varrella olen ehtinyt puuhastella monenmoista. Muun muassa tehnyt virheitä php:llä, mistä todisteena on &lt;a href="www.curlysworldoffreeware.com"&gt; CWF Freeware&lt;/a&gt;. Php:ssä on paljon hienoja funktioita stringien käsittelyyn, ja noita kaipailin joskus C/C++ kielissäkin. No nuorena miehenä koodailin omat versioni noista. Ja vaikka ne on koodattu urani aamunkoitossa, eivätkä luultavasti ole parhaita mahdollisia toteutuksia, on ne kuitenkin toimivia. Ja ajattelin, että kun kerran minä noita kaipasin, niin miksein joku muukin. Joten tässä tulee:&lt;br /&gt;&lt;br /&gt;explode() for C++:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#include &amp;lt;iostream&amp;gt;&lt;br /&gt;#include &amp;lt;vector&amp;gt;&lt;br /&gt;#include &amp;lt;string&amp;gt;&lt;br /&gt;&lt;br /&gt;using std::string;&lt;br /&gt;using std::vector;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;int explode(string tear_me,string cut,vector &amp;store)&lt;br /&gt;{&lt;br /&gt;        unsigned int start=0;&lt;br /&gt;        int len=0;&lt;br /&gt;        int i=0;&lt;br /&gt;        vector&amp;lt;string&amp;gt; temp;&lt;br /&gt;&lt;br /&gt;        len=cut.length();&lt;br /&gt;        if(tear_me.find(cut)==string::npos)&lt;br /&gt;        {&lt;br /&gt;                return -1;&lt;br /&gt;        }&lt;br /&gt;        while( (start=tear_me.find(cut))!=string::npos)&lt;br /&gt;        {&lt;br /&gt;                temp.push_back(tear_me.substr(0,start));&lt;br /&gt;                tear_me.erase(0,start+len);&lt;br /&gt;                i++;&lt;br /&gt;        }&lt;br /&gt;        temp.push_back(tear_me);&lt;br /&gt;        store=temp;&lt;br /&gt;        return i;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;rtrim() for C:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#include &amp;lt;string.h&amp;gt;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;\brief removes trailing whitespaces and tabs from a string.&lt;br /&gt;\returns number of characters removed&lt;br /&gt;*/&lt;br /&gt;int trim(char *text)&lt;br /&gt;{&lt;br /&gt;    int i;&lt;br /&gt;    int len;&lt;br /&gt;    int retval=0;&lt;br /&gt;    len=strlen(text);&lt;br /&gt;    for&lt;br /&gt;    (&lt;br /&gt;      i=len-1;&lt;br /&gt;      i&gt;=0&amp;&amp;( text[i]==' ' || text[i]=='\t' );&lt;br /&gt;      i--&lt;br /&gt;    )&lt;br /&gt;    {&lt;br /&gt;        text[i]=(char)0;&lt;br /&gt;        retval++;&lt;br /&gt;    }&lt;br /&gt;    return retval;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;rtrim on kevennetty versio. Se poistaa vain tabulaattorit ja välilyönnit, mutta yhden argumentin lisäyksellä, ja for ehdon pienellä muokkauksella saavutettaisiin php rtrim():n koko toiminnallisuus. (eli mahdollisuus poistaa myös muita merkkejä).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4818234838483192587-6298437335220377071?l=c-ohjelmoijanajatuksia.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-ohjelmoijanajatuksia.blogspot.com/feeds/6298437335220377071/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4818234838483192587&amp;postID=6298437335220377071' title='0 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/6298437335220377071'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/6298437335220377071'/><link rel='alternate' type='text/html' href='http://c-ohjelmoijanajatuksia.blogspot.com/2008/08/php-explode-ja-trim-funktiot-caan-ja.html' title='php explode() ja rtrim() funktiot C++:aan ja C:hen.'/><author><name>Maz</name><uri>http://www.blogger.com/profile/03774313944609044806</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4818234838483192587.post-1914750307795630575</id><published>2008-08-23T16:36:00.025+03:00</published><updated>2008-09-10T23:35:47.453+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='taulukko'/><category scheme='http://www.blogger.com/atom/ns#' term='calloc()'/><category scheme='http://www.blogger.com/atom/ns#' term='C'/><category scheme='http://www.blogger.com/atom/ns#' term='Muisti'/><category scheme='http://www.blogger.com/atom/ns#' term='dynaaminen muistinvaraus'/><category scheme='http://www.blogger.com/atom/ns#' term='malloc()'/><category scheme='http://www.blogger.com/atom/ns#' term='realloc()'/><category scheme='http://www.blogger.com/atom/ns#' term='osoittimet'/><category scheme='http://www.blogger.com/atom/ns#' term='Maz'/><title type='text'>C ja muisti osa 3 - Muistin varaaminen</title><content type='html'>&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;No niin. Ensimmäiset Segmentation Fault:it siis lienee aikaansaatu. Älkää pelätkö, mikäli harrastatte C-ohjelmointia jatkossakin, tulette näkemään tuon virheen vielä monen monta kertaa. Itse asiassa, kaikkein kokeneinkin ohjelmoija joutuu toisinaan nöyrtymään tuon ilmoituksen edessä...&lt;br /&gt;&lt;br /&gt;Segmentation Fault kaikessa yksinkertaisuudessaan tarkoittaa, että ohjelmanne erehtyi käpistelemään jotain muistialuetta jonne sillä ei olisi ollut asiaa. Yksinkertainen tapa saada tämä aikaan, on kääntää ja ajaa vaikka seuraava koodipätkä:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt;  char *osoitin;&lt;br /&gt;  strcpy&lt;br /&gt;  (&lt;br /&gt;    osoitin,&lt;br /&gt;    "kaadu! Tama kirjoitetaan varaamattomaan muistilohkoon"&lt;br /&gt;  );&lt;br /&gt;  return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Pohditaanpa jälleen hieman muistia ja käyttöjärjestelmää.&lt;br /&gt;&lt;br /&gt;ohjelmaa ajettaessa käyttöjärjestelmä varaamaa sopivan köntin muistia ohjelmaan kirjoitetuille tavallisille muuttujille, tiedolle siitä mistä funktiosta mihinkin on hypätty jne. Tätä ennalta varattua muistikönttiä kutsutaan pinoksi, eli stackiksi. Pino on muistialue, joka pysyy varattuna ohjelman suorituksen ajan.&lt;br /&gt;&lt;br /&gt;Mietitäänpä sitten hieman jotain todellista sovellusta, Esim. tekstieditoria. Nyt ohjelman käynnistyessä ei mitenkään voida tietää tarvittavan muistin määrää, sillä käyttäjän kirjoittamat merkit täytyy tallettaa muistiin siksi aikaa, kunnes käyttäjä tallettaa kirjoittamansa merkit tiedostoon. Miten tällaista ongelmaa lähestytään? Voisimme tietysti päättää, että editoriin sopii maksimissaan X merkkiä, ja varata tämän muistimäärän etukäteen. Tämä kuitenkin olisi kömpelöä, sillä se muodostaisi ylärajan editoitavan tekstin koolle, ja varaisi aina ohjelman käynnistyessä kyseisen määrän - ja koska yleensä käyttäjät kirjoittavat eri mittaisia tiedostoja, olisi suurimman tiedoston vaatima muisti paljon keskimääräisen tiedoston vaatimaa muistia suurempi. Ja kuitenkin joka kerran joutuisi editori käynnistyessään varaamaan muistia suurinta tiedostoa varten... Yksinkertaisesti sanottua, editori kuluttaisi muistia järjettömän paljon, melkein kuin M$:n sovellukset...&lt;br /&gt;&lt;br /&gt;Ratkaisu on nk. dynaaminen muistinvaraus. Eli C-kieli mahdollistaa muistin varaamisen ajon aikana. Tämä dynaaminen varaus tehdään käyttämättömästä muistialueesta "keosta" (heap). Eli kun ohjelma tarvitsee lisää muistia, se pyytää käyttöjärjestelmää varaamaan tarvitsemansa tilan, ja saa paluuarvona varatun tilan alkuosoitteen.&lt;br /&gt;&lt;br /&gt;Jatketaampa ajatusleikkiä. Kuvitellaan, että olemme tehneet webbiselaimen dynaamisen muistinvaraamisen avulla siten, että aina kun käyttäjä avaa sivuston, sivuston vaatima tila on dynaamisesti varattu. Kuvitellaan edelleen, että käyttäjä surffaa koko työpäivän netissä, aukoen ja sulkien sivuja... Tästä päästään toiseen erittäin tärkeään dynaamiseen muistinkäyttöön liittyvään asiaan, eli muistin vapauttamiseen.&lt;br /&gt;&lt;br /&gt;Normaalit muuttujat jotka on otettu pinosta vapautetaan automaattisesti sen funktion suorituksen loputtua, jossa muuttuja on esitelty. (10 psiteen kysymys, mikä vaara piilee osoittimissa, jotka osoittavat näihin nk. lokaaleihin muuttujiin?) Sen sijaan dynaamisesti varattu muisti pysyy varattuna aina ohjelman suorituksen loppuun asti, ellei sitä erikseen vapauteta! Vapauttamatta jääneitä muistilohkoja kutsutaan muistivuodoiksi, ja ohjelmissa joita on suunniteltu suoritettavan pitkän aikaa, muistivuodot voivat olla todellinen riesa, aiheuttaen ajanoloon koko koneen hidastumista ohjelman rohmutessa lisää ja lisää muistia.&lt;br /&gt;&lt;br /&gt;Mutta katsotaanpa nyt se C:n tyypillisin tapa varata muistia:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt;  int *luku;&lt;br /&gt;  char *teksti;&lt;br /&gt;&lt;br /&gt;  luku=(int *)malloc(sizeof(int));&lt;br /&gt;  if(luku==NULL)&lt;br /&gt;  {&lt;br /&gt;    printf("Muistin varaaminen epaonnistui!");&lt;br /&gt;    return -1;&lt;br /&gt;  }&lt;br /&gt;  *luku=5;&lt;br /&gt;  //varataan tilaa sanalle viisi:&lt;br /&gt;  //tarvitaan 6 charia, 5 kirjaimille ja yksi lopun '\0':lle&lt;br /&gt;  teksti=(char *)malloc((5+1)*sizeof(char));&lt;br /&gt;  if(teksti==NULL)&lt;br /&gt;  {&lt;br /&gt;    printf("Muistin varaaminen epaonnistui!");&lt;br /&gt;    return -1;&lt;br /&gt;  }&lt;br /&gt;  strcpy(teksti,"viisi");&lt;br /&gt;  printf("%d eli %s",*luku,teksti);&lt;br /&gt;  //vapautetaan muisti&lt;br /&gt;  free(teksti);&lt;br /&gt;  free(luku);&lt;br /&gt;  return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;&lt;br /&gt;Eli avainsanat muistin varaamiseen ja vapautukseen ovat malloc() ja free(). malloc() varaa muistia niin monta tavua kuin argumenttina annetaan. Eli argumenttina on annettava halutun tietotyypin koko. onnistuessaan muistinvarauksessa malloc palauttaa varatun muistialueen alkuosoitteen, joka talletetaan osoittimeen. Epäonnistuessaan malloc palauttaa NULL:in (eli 0 osoitteen). mallocin paluuarvo on aina syytä tarkistaa, sillä NULL osoitteen käpistely myöhemmin johtaa tuhoon ja hävitykseen...&lt;br /&gt;&lt;br /&gt;free() funktio vapauttaa varatun muistin, mutta jättää osoittimen sojottamaan vapautettuun muistilohkoon. Luonnollisestikin vapautetun muistilohkon käyttö johtaa softan kaatumiseen, joten vapautuksen jälkeen osoitin on käyttökelvoton kunnes se jälleen laitetaan osoittamaan jotain järkevää. Ja viimeksi, myös vapautetun muistilohkon uudelleen vapautus johtaa ohjelman kippaamiseen, samoin kuin pinosta varatun muistipalan vapautus.&lt;br /&gt;&lt;br /&gt;Ihan viho viimeksi mainitsen vielä [] operaattorin. Eli esittelemällä muuttujan muodossa:&lt;br /&gt;&lt;br /&gt;tyyppi nimi[koko];&lt;br /&gt;&lt;br /&gt;saamme muistialueen johon on varattu tilaa koko muuttujalle. Myöhemmin koodissa voimme käyttää [N] operaattoria päästäksemme käsiksi N:nteen taulukkoon talletettuun arvoon.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  int taulukko[10];&lt;br /&gt;  int *osoitin;&lt;br /&gt;&lt;br /&gt;  osoitin=(int *)malloc(10*sizeof(int));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Mikä edellä olevassa on osoittimen ja taulukon ero? Ero on se, että taulukko on nyt varattu stackista, ja sitä EI voi vapauttaa free() kutsulla, vaan se säilyy varattuna koko ohjelman suorituksen ajan. Ja tähän sopii vielä pieni huomautus sizeof() funktiosta, joka siis kertoo montako tavua tilaa jokin tyyppi/muuttuja vie muistista. sizeof() kuitenkin muutetaan luvuksi jo ohjelmaa käännettäessä, joten:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  int taulukko[10];&lt;br /&gt;  int *osoitin;&lt;br /&gt;&lt;br /&gt;  osoitin=(int *)malloc(10*sizeof(int));&lt;br /&gt;  printf&lt;br /&gt;  (&lt;br /&gt;    "taulukko=%d osoitin=%d",&lt;br /&gt;    sizeof(taulukko),&lt;br /&gt;    sizeof(osoitin)&lt;br /&gt;  );&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;tulostaa eri koot taulukolle ja osoittimelle. (taulukolle 10*4 eli 10 int:n viemän tilan ja osoittimelle  arkkitehtuurista riippuen 4 tai 8, eli osoittimen vaatiman tilan). Ja viimeinen esimerkki siitä, miten [] operaattorilla accessoidaan tiettyyn muistipaikkaan:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  char taulukko[10];&lt;br /&gt;  char *osoitin;&lt;br /&gt;&lt;br /&gt;  osoitin=(int *)malloc(10*sizeof(int));&lt;br /&gt;  strcpy(taulukko,"kymmenen!");&lt;br /&gt;  strcpy(osoitin,"kymmenen!");&lt;br /&gt;  osoitin[8]='?';&lt;br /&gt;  taulukko[0]='x';&lt;br /&gt;  printf("taulukko=%s osoitin=%s\n", taulukko, osoitin);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Tämä tulostaisi:&lt;br /&gt;taulukko=xymmenen! osoitin=kymmenen?&lt;br /&gt;&lt;br /&gt;Huomaa siis, että [] operaattoria käytettäessä, taulukon ensimmäinen arvo on 0, ja viimeinen on koko-1. Eli:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  char taulu[10];&lt;br /&gt;  taulu[10]='\0';&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;kirjoittaa '\0':n taulukon ulkopuolelle!&lt;br /&gt;&lt;br /&gt;EDIT: &lt;br /&gt;Jäi vaivaamaan... Nimittäin unohdin mainita realloc() ja calloc() kutsut, jotka malloc():n lisäksi ovat erittäin käyttökelpoisia. Mutta koska kömmin jo sängystä tekemään tämän lisäyksen, en jaksa niistä enempää jaaritella. Kannattaa kuitenkin katsoa ne vaikka man sivuilta, vaikka tuota tällä sivulla olevaa man hakua käyttäen. (toivottavasti se toimii, en voi itse testata, koska se on samalla sellainen mainoshässäkkä, ja olen sitoutunut olemaan käyttämättä sitä :rolleyes:)&lt;br /&gt;Ja nyt alkaa taas uni painaa, joten palataan jälleen joku toinen päivä :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4818234838483192587-1914750307795630575?l=c-ohjelmoijanajatuksia.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-ohjelmoijanajatuksia.blogspot.com/feeds/1914750307795630575/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4818234838483192587&amp;postID=1914750307795630575' title='4 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/1914750307795630575'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/1914750307795630575'/><link rel='alternate' type='text/html' href='http://c-ohjelmoijanajatuksia.blogspot.com/2008/08/c-ja-muisti-osa-3-muistin-varaaminen.html' title='C ja muisti osa 3 - Muistin varaaminen'/><author><name>Maz</name><uri>http://www.blogger.com/profile/03774313944609044806</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4818234838483192587.post-8732088355148908036</id><published>2008-08-23T12:28:00.024+03:00</published><updated>2008-08-23T22:08:02.959+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C'/><category scheme='http://www.blogger.com/atom/ns#' term='pointterit'/><category scheme='http://www.blogger.com/atom/ns#' term='Muisti'/><category scheme='http://www.blogger.com/atom/ns#' term='osoittimet'/><category scheme='http://www.blogger.com/atom/ns#' term='Maz'/><title type='text'>C ja muisti osa 2 - osoittimet eli pointterit.</title><content type='html'>osa 1:ssä pohdittiin hieman muistia noin yleisesti, ja käytettiin osoittimia ihan sujuvasti. Koska aloittelevilla C koodareilla on usein ongelmia ymmärtää osoittimia eli pointtereita, ajattelin lisätä jo ennestään valtaisaan osoitin-tutoriaali tulvaan omani.&lt;br /&gt;&lt;br /&gt;Eli kuten aiemmin kerroin, C-kielessä muistin käsittely on oikeastaan kaiken a ja o. Suora pääsy tiettyyn muistipaikkaan, jonne jotain dataa on jo talletettu mahdollistaa ylimääräisen kopioinnin välttämisen, eli siis nopeuttaa ohjelmaa ja vähentää muistin kulutusta. Samalla se kuitenkin on myös C-ohjelman haastavimpia asioita, ja virheelliset muistiaccessit aiheuttavatkin todella ikäviä ja vaikeasti paikannnettavissa olevia virheitä. Mutta eiköhän tässä nyt tullut pohjustusta tarpeeksi, menen siis seuraavaksi itse asiaan.&lt;br /&gt;&lt;br /&gt;Eli kuten ykkösosassa totesin, käyttöjärjestelmä paloittelee muistin lohkoihin, jotka se numeroi. Eli varsinainen data (esim. muuttujan arvo) on aina talletettu johonkin tiettyyn lohkoon, jota edustaa lohkon numeerinen osoite. normaalia muuttujaa käytettäessä, koodarin ei tarvitse välittää osoitteesta, sillä kääntäjä piiloittaa osoitteen käytön. Tämä kuitenkin tarkoittaa sitä, että mikäli sama data halutaan tallettaa myös toiseen muuttujaan, on data kopioitava toiseen muistilohkoon johon kääntäjä liittää uuden muuttujan nimen.&lt;br /&gt;&lt;br /&gt;Esim:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;int muuttuja1;&lt;br /&gt;int muuttuja2;&lt;br /&gt;&lt;br /&gt;muuttuja1=1; //laitetaan arvo 1 muistiin johonkin lohkoon,&lt;br /&gt;// jota edustaa nimi muuttuja1&lt;br /&gt;&lt;br /&gt;muuttuja2=muuttuja1; //kopioidaan data toiseen muistilohkoon,&lt;br /&gt;// jota edustaa nimi muuttuja2&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Joskus olisi kätevää pystyä linkittämään nimi muuttuja2 samaan muistilohkoon johon nimi muuttuja1 on linkitetty. Tällöin muuttuja1:tä muutettaessa, myös muuttuja2:n arvo luonnollisesti muuttuu. (koska kumpikin osoittaa samaan dataan, ei toiseen muistilohkoon kopioituun dataan). Tämä on mahdollista pointtereiden avulla.&lt;br /&gt;&lt;br /&gt;Pointteri on yksinkertaisuudessaan muuttuja, joka sisältää muistipaikan osoitteen. Lisäksi pointteri sisältää muistipaikkaan talletetun datan tyypin, jotta kääntäjä tietäisi miten monta tavua muistipaikasta on luettava, jotta saadaan selville data johon pointteri osoittaa. (osassa 1 totesimme, että C-kielen eri tietotyypit vaativat eri määrän tilaa.) Nyt tarkkana, näytän miten pointteri tehdään. Ja ikävä kyllä, jälleen syntaxissa on kohta, joka saattaa tuntua aloittelijasta sekavalta...&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;int muuttuja1;&lt;br /&gt;int *muuttuja2;&lt;br /&gt;&lt;br /&gt;muuttuja1=1;&lt;br /&gt;muuttuja2=&amp;muuttuja1;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Näyttää lähes samalta kuin edellinen esimerkki. Tarkkaavainen lukijani (eli et sinä Vesku, vaan Kari), huomaa, että erona on nyt * ja &amp;amp; merkkien käyttö.&lt;br /&gt;&lt;br /&gt;otetaan helpompi ensin. Eli &amp;amp;-merkki. &amp;amp;-merkillä saadaan selville muuttujan (tai funktion) (alku)osoite. Toinen &amp;amp;-merkin käyttötarkoitus on bitwise and operaatio, mutta ei nyt vaivata päätämme sillä... Eli kun tahdomme käyttää muuttujan osoitetta johonkin, laitamme muuttujan nimen eteen &amp;amp;-merkin (C++:ssa &amp;amp; merkkiä voidaan myös käyttää funktion argumentin edessä, funktion prototyypissä, kun tahdomme... Äh. Antaa olla, tämä tuto koskekoon vain C:tä).&lt;br /&gt;&lt;br /&gt;Sitten *. Nyt tarkkana. Äsken käytin *-merkkiä vain yhdessä kohdassa, mutta oikeasti sillä on kaksi osoittimiin liittyvää käyttötarkoitusta. Ensin kun esittelemme, eli luomme osoittimen, *-merkki kuuluu olla muuttujan nimen edessä, kertomassa että tahdomme muuttujan olevan osoitin. datatyyppi osoittimen edessä kertoo, minkä tyyppistä dataa muistipaikka johon osoittimen laitamme sojottamaan sisältää.&lt;br /&gt;&lt;br /&gt;Tämän jälkeen kun tahdomme tallettaa osoitteita muistipaikkaan, emme käytä *-merkkiä. osoittimen esittelyn jälkeen, *-merkki kertoo, että tahdomme käpistellä DATAA joka muistipaikassa on. Esim:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;int muuttuja1;&lt;br /&gt;int *muuttuja2;&lt;br /&gt;&lt;br /&gt;muuttuja1=1;&lt;br /&gt;muuttuja2=&amp;muuttuja1;&lt;br /&gt;*muuttuja2=*muuttuja2+3;&lt;br /&gt;printf("%d",muuttuja1);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Mitähän kyseinen funktio tulostaisi? Käydäämpä tämä tarkasti läpi.&lt;br /&gt;Ensin luomme tavallisen muuttujan tyyppiä int.&lt;br /&gt;Sitten luomme osoittimen nimeltä muuttuja2, ja kerromme, että se tulee osoittamaan int tyyppiseen dataan.&lt;br /&gt;Talletamme muuttuja1:een arvon 1.&lt;br /&gt;Sitten, oikealta vasemmalle luettuna:&lt;br /&gt;otamme &amp;amp;-merkillä muuttuja1 sisältämän datan (eli luvun 1) osoitteen. Ts. Sen muistipaikan osoitteen, jossa muuttuja1:n sisältämä data on talletettuna. Talletamme tämän osoitteen muuttuja2:een, eli pointteriimme.&lt;br /&gt;Sitten jälleen oikealta vasemmalle:&lt;br /&gt;Lisäämme luvun 3, *muuttuja2:een. Eli siihen dataan joka on talletettu muistipaikkaan johon muuttuja2 osoittaa. olipa hankala lause, mutta kun luette sen 3-5 kertaa (Vesalla menee 5), sisäistätte kyllä asian. Nyt meillä siis on luku 1+3, eli 4. Tämän talletamme nyt *muuttuja2:een, eli siihen muistipaikkaan johon muuttuja2 osoittaa. Ts. muistipaikkaan jossa on muuttuja1:n arvo talletettuna. Nyt siis muuttuja1 onkin muuttunut 1:stä 4:ksi.&lt;br /&gt;Ja niinpä viimeinen printf lause tulostaa näytölle numeron 4.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_oWGseshcc_c/SLAC_oplqZI/AAAAAAAAAAM/8y0CqfIAvOc/s1600-h/muisti.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_oWGseshcc_c/SLAC_oplqZI/AAAAAAAAAAM/8y0CqfIAvOc/s320/muisti.png" alt="" id="BLOGGER_PHOTO_ID_5237689658898753938" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Huokaistaanpa nyt siis hetki, ja kuutioidaan mielessämme mihin tätä voi käyttää...&lt;br /&gt;&lt;br /&gt;No niin. Funktio lienee toiselle teistä lukijani jo tuttu käsite. (En tarkoita sinua Vesa). Mietitäänpä hetkinen miten funktion parametrit välitetään funktion sisälle. Ihan oikein Kari, ne välitetään kopioimalla arvo uuteen muistipaikkaan, ja kertomalla tämä uusi muistipaikka funktion sisäiselle muuttujalle. Esim:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;typedef struct kauheastidataa&lt;br /&gt;{&lt;br /&gt;int eka;&lt;br /&gt;int toka;&lt;br /&gt;int kolmas;&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;int sadasviideskymmeneskolmas;&lt;br /&gt;}kauheastidataa;&lt;br /&gt;&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt; kauheastidataa dataa;&lt;br /&gt; dataa.eka=1;&lt;br /&gt; dataa.toka=2;&lt;br /&gt; dataa.kolmas=3;&lt;br /&gt; .&lt;br /&gt; .&lt;br /&gt; .&lt;br /&gt; dataa.sadasviideskymmeneskolmas=153;&lt;br /&gt; tulosta_data(dataa);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void tulosta_data(kauheastidataa data)&lt;br /&gt;{&lt;br /&gt; printf&lt;br /&gt; (&lt;br /&gt;     "eka=%d,&lt;br /&gt;      toka=%d,&lt;br /&gt;      ...,&lt;br /&gt;      sadasviideskymmeneskolmas=%d",&lt;br /&gt;      data.eka,&lt;br /&gt;      data.toka,&lt;br /&gt;      ...,&lt;br /&gt;      data.sadasviideskymmeneskolmas&lt;br /&gt; );&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Mitä edellinen esimerkki tekisi (siis jos se olisi loppuun asti kirjoitettu, eli ... paikat olisi asianmukaisesti täytetty, älä Vesa koeta kääntää sitä tuollaisenaan)?&lt;br /&gt;&lt;br /&gt;Funktion kutsun kohdalla, koko kauheastidataa structi kopioitaisiin uuteen muistilohkoon. Tämä ei kuitenkaan olisi kovin järkevää, sillä kauheastidataa sisältää kauheastidataa, ja kun ohjelma turhaan kopioi kauheastidataa, se hidastuu tarpeettomasti, syö enemmän muistia, ja aiheuttaa pahennusta loppukäyttäjässä. Tämän kopioinnin välttämiseksi voisimme käyttää osoitinta alkuperäiseen kauheastidataa structiin, ja antaa siis tämän kauheastidataa structin muistiosoite tulostusfunktiolle. Tällöin ainoa mitä kopioidaan, on tuo kyseinen muistiosoite. Eli teemme seuraavasti:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;typedef struct kauheastidataa&lt;br /&gt;{&lt;br /&gt;int eka;&lt;br /&gt;int toka;&lt;br /&gt;int kolmas;&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;int sadasviideskymmeneskolmas;&lt;br /&gt;}kauheastidataa;&lt;br /&gt;&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt; kauheastidataa dataa;&lt;br /&gt; dataa.eka=1;&lt;br /&gt; dataa.toka=2;&lt;br /&gt; dataa.kolmas=3;&lt;br /&gt; .&lt;br /&gt; .&lt;br /&gt; .&lt;br /&gt; dataa.sadasviideskymmeneskolmas=153;&lt;br /&gt; tulosta_data(&amp;amp;dataa);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void tulosta_data(kauheastidataa *data)&lt;br /&gt;{&lt;br /&gt; printf&lt;br /&gt; (&lt;br /&gt;     "eka=%d,&lt;br /&gt;      toka=%d,&lt;br /&gt;      ...,&lt;br /&gt;      sadasviideskymmeneskolmas=%d",&lt;br /&gt;      *(data.eka),&lt;br /&gt;      *(data.toka),&lt;br /&gt;      ...,&lt;br /&gt;      *(data.sadasviideskymmeneskolmas)&lt;br /&gt; );&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Structit tarjoavat myös -&gt; operaattorin, jota voidaan käyttää *(structi.jäsen) asemasta. Eli printtifunktion voisi kirjoittaa myös:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;void tulosta_data(kauheastidataa *data)&lt;br /&gt;{&lt;br /&gt; printf&lt;br /&gt; (&lt;br /&gt;     "eka=%d,&lt;br /&gt;      toka=%d,&lt;br /&gt;      ...,&lt;br /&gt;      sadasviideskymmeneskolmas=%d",&lt;br /&gt;      data-&gt;eka,&lt;br /&gt;      data-&gt;toka,&lt;br /&gt;      ...,&lt;br /&gt;      data-&gt;sadasviideskymmeneskolmas&lt;br /&gt; );&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Ja vielä yksi maininnan arvoinen seikka.&lt;br /&gt;oletko ikinä miettinyt miten merkkijonot talletetaan C-kielessä? (Kysymys ei koske sinua Vesa...). Mieti tarkasti, oletko ikinä nähnyt esim. seuraavaa:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;char *merkkijono="merkkejä";&lt;br /&gt;printf("%s", merkkijono);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Kun tuota tarkemmin tutkii, on merkkijonojen syvin olemus oikeastaan aika selvä. Eli itse asiassa C:n merkkijonojen käsittely tapahtuu luomalla osoitin char tietotyyppiin. Sitten C:n merkkijonoja käsittelevät funktiot on rakennettu niin, että ne aloittavat merkkijonon käsittelyn paikasta johon char osoitin osoittaa, ja olettavat merkkijonon jatkuvan kunnes kohtaavat merkin '\0' (eli (char)0, eli arvon 0x00) jossain muistipaikassa. Ts. merkkijonoja käsiteltäessä aletaan paikasta johon osoitin osoittaa, ja luetaan muistia eteenpäin 8-bitin eli yhden tavun palasissa, tulkiten jokainen luettu arvo merkiksi ASCII taulukon osoittamalla tavalla, kunnes törmätään tavuun, jossa kaikki 8 bittiä ovat nollia. Tässä siis piilee vaara. Kun käsittelet mekrrijonoja, varmista aina että viimeinen merkki on '\0'. Muuten tuloksena on .... jotain ei toivottua.&lt;br /&gt;&lt;br /&gt;Ja nyt, mene kokeilemaan. Sitten kun linux koodarit saavat aikaan ensimmäisen kaatunisen virheilmoituksen "Segmentation Fault" kanssa, (ja windows koodarit muuten vaan crashin), tulkaa lukemaan seuraava pätkä muistin varaamisesta ;)&lt;br /&gt;(Koetan kirjoitella senkin tämän viikonlopun aikana, tai viimeistään alkuviikolla).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4818234838483192587-8732088355148908036?l=c-ohjelmoijanajatuksia.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-ohjelmoijanajatuksia.blogspot.com/feeds/8732088355148908036/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4818234838483192587&amp;postID=8732088355148908036' title='0 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/8732088355148908036'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/8732088355148908036'/><link rel='alternate' type='text/html' href='http://c-ohjelmoijanajatuksia.blogspot.com/2008/08/c-ja-muisti-osa-2-osoittimet-eli.html' title='C ja muisti osa 2 - osoittimet eli pointterit.'/><author><name>Maz</name><uri>http://www.blogger.com/profile/03774313944609044806</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_oWGseshcc_c/SLAC_oplqZI/AAAAAAAAAAM/8y0CqfIAvOc/s72-c/muisti.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4818234838483192587.post-7902107638809554338</id><published>2008-08-07T21:17:00.009+03:00</published><updated>2009-03-23T16:22:48.809+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C'/><category scheme='http://www.blogger.com/atom/ns#' term='jump table'/><category scheme='http://www.blogger.com/atom/ns#' term='callback function'/><category scheme='http://www.blogger.com/atom/ns#' term='funktio-osoittimet'/><category scheme='http://www.blogger.com/atom/ns#' term='Maz'/><title type='text'>Funktio-osoittimet C:ssä.</title><content type='html'>&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Suurin osa C ohjelmijista törmää jossain uransa vaiheessa termiin "callback function", "jump tabl" tms. Hieman syvemmälle porauduttuaan, ohjelmoija huomaa, että ideana on tunkea funktio johonkin muuttujaan, josta sitä voidaan tarvittaessa kutsua.. Tai itse asiassa, eihän funktiota mihinkään laiteta, vaan osoitin funktion alkuun. Muistammehan toki, että ohjelma on vain köntti komentoja ja dataa muistissa. Ja jossain päin muistia majailee myös funktiot. Silloinhan on oikeastaan varsin luonnollista tallettaa funktion alkuosoite muuttujaan, ja tarvittaessa pompauttaa ohjelman suoritus tiettyyn kohtaan koodia, riippuen siitä, mikä funktio muuttujaan on talletettu...&lt;br /&gt;&lt;br /&gt;No ihan näin helppoahan se ei kuitenkaan voi olla, eihän kyse muuten olisi C-kielestä.&lt;br /&gt;&lt;br /&gt;ongelmaksi tulee taas tietysti C:n tietotyypit. osa funktioista voi syöda argumentteinaan chareja, osa inttejä, ja osa vaikka mitä... Joihinkin funktioihin ei mene parametreja, toisiin menee useita, ja paluuarvotkin vielä vaihtelee. Siksipä emme voikkaan tehdä "geneeristä" funktio-osoitin tyyppiä, joka kävisi kaikille funktioille, vaan erilaisia argumentteja/palautusarvoja käyttäville funktioille on määritettävä omat funktio-osoitin tyyppinsä. Ja tästä syystä mikäli teemme funktion, joka ottaa argumenttinaan funktiopointterin, on usein käytännöllistä määrittää argumentin tyypiksi "osoitin funktioon joka ottaa argumenttinaan void * osoittimen, ja palauttaa myös sellaisen". Tällöin emme sido liikaa funktiomme käyttäjän käsiä - varsinkaan jos emme tiedä tarkasti mihin tarkoitukseen funktiotamme tullaan käyttämään.&lt;br /&gt;&lt;br /&gt;No niin. Mihinkäs tätä hienoutta nyt sitten voidaan käyttää? Ainakin kaksi asiaa tulee äkkiä mieleen. "Callbackit" ja "jump tablet".&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Callbackit:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Kuvitellaanpa että teet C:llä hienonhienoa matopelimoottoria. Moottori laskee madon paikan ja huolehtii siitä, että käyttäjällä on mahdollisuus suorittaa haluamansa toiminto jos mato eksyy kielletylle alueelle eli seinään. Mutta koska et voi tietää millaista matopeliä käyttäjä onkaan tekemässä, et voi vain summanmutikassa piirtää traagista Game over ruutua. Varsinkaan kun et tiedä, tahdotaanko Game over ruudussa näkyvän verinen kolarin ajanut mato, vaiko kenties täysverinen kyy..&lt;br /&gt;&lt;br /&gt;Yksi mahdollinen tapa tietysti olisi tehdä globaali muuttuja esim int G_matoSeinassa, joka olisi alustettu 0:ksi, ja madon kohdatessa seinän, moottori muuttaisi arvon ykköseksi.Nyt varsinaisen pelin pitäisi kuitenkin jatkuvasti 'pollailla' muuttujaa, nähdäkseen onko sen arvo muuttunut. Tämä luonnollisesti kuluttaisi turhaan arvokasta CPU-aikaa, ja koska käyttöjärjestelmät joiden päällä matopelejä pelataan ovat harvoin reaaliaika systeemeitä, tulisi seinään törmäyksen, ja muuttujaa pollailevan "taskin" suoritukseen mahdollisesti "lagia". Eli mato törmäisi ensin seinään, ja vasta tovin päästä peli huomaisi piirtää Game over ruudun - mahdollisesti päivitettyään ensin madon paikan seinän sisään..&lt;br /&gt;&lt;br /&gt;Callback funktion käyttö on yksi mahdollinen ratkaisu ongelmaan. Eli, matopelimoottorin käyttäjä kirjoittelee funktion, joka hoitaa game over ruudun piirron, ja pysäyttää pelin muun etenemisen seinääntörmäyksen yhteydessä. Tätä funktiota nyt sitten käytetään "callback funktiona". Ts, annamme matopelikoneelle moottorin starttauksen yhteydessä osoittimen tähän funktioon, ja kerromme moottorille että mikäli mato kohtaa seinän, moottorin tulee kutsua kyseistä funktiota. Eli pelimoottoriin pitää siis koodata "callbackin rekisteröinti funktio". Yksinkertaisimmillaan se voisi olla seuraavanlainen temppu:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;int rekisteroi_seina_callback( void (*seinaCBF)()   )&lt;br /&gt;{&lt;br /&gt;   //Tarkista ettei seinaCBF ole NULL&lt;br /&gt;   if(NULL==seinaCBF)&lt;br /&gt;       return 0;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;   /*&lt;br /&gt;   Talleta funktion osoite johonkin moottorin sisäiseen tietorakenteeseen myöhempää käyttöä varten&lt;br /&gt;   */&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;   G_seinacallback=seinaCBF;&lt;br /&gt;   return 1;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ja kohta jossa seinäcallbackkia kutsutaan:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;if( !mato_seinassa_checkki() )&lt;br /&gt;{&lt;br /&gt;   (*G_seinacallback)();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ja näin käyttäjä rekisteröisi seinäcallbackin:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;void tee_tama_jos_seinassa()&lt;br /&gt;{&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void kaynnista_matomoottori()&lt;br /&gt;{&lt;br /&gt;   rekisteroi_seina_callback( &amp;amp;tee_tama_jos_seinassa );&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Tai:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;void kaynnista_matomoottori()&lt;br /&gt;{&lt;br /&gt;   void (*seinaCBF)() = &amp;amp;tee_tama_jos_seinassa;&lt;br /&gt;   rekisteroi_seina_callback( seinaCBF );&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Usein kannattaa kuitenkin käyttäjää ajatellen määritellä tarvittavaa funktio-osoitinta vastaava tyyppi, jolloin käyttäjän ei tarvitse käyttää C:n sekavia &lt;a href="http://c-ohjelmoijanajatuksia.blogspot.com/2008/08/c-kuinka-luet-tyypimrittelyj.html"&gt;tietotyyppimäärityksiä&lt;/a&gt; vaan käyttäjä pystyy tekemään asiat definoidun tyypin nimellä. Eli:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;typedef void (*seina_callback)()&lt;br /&gt;&lt;br /&gt;int rekisteroi_seina_callback( seina_callback seinaCBF  )&lt;br /&gt;{&lt;br /&gt;   //Tarkista ettei seinaCBF ole NULL&lt;br /&gt;   if(NULL==seinaCBF)&lt;br /&gt;       return 0;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;   /*&lt;br /&gt;   Talleta funktion osoite johonkin moottorin sisäiseen tietorakenteeseen myöhempää käyttöä varten&lt;br /&gt;   */&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;   G_seinacallback=seinaCBF;&lt;br /&gt;   return 1;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ja kohta jossa seinäcallbackkia kutsutaan:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;if( !mato_seinassa_checkki() )&lt;br /&gt;{&lt;br /&gt;   (*G_seinacallback)();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ja näin käyttäjä rekisteröisi seinäcallbackin:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;void tee_tama_jos_seinassa()&lt;br /&gt;{&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void kaynnista_matomoottori()&lt;br /&gt;{&lt;br /&gt;   rekisteroi_seina_callback( &amp;amp;tee_tama_jos_seinassa );&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Tai:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;void kaynnista_matomoottori()&lt;br /&gt;{&lt;br /&gt;   seina_callback seinaCBF = &amp;amp;tee_tama_jos_seinassa;&lt;br /&gt;   rekisteroi_seina_callback( seinaCBF );&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Eli tiivisteenä (sis 120% funktio-osoitin asiaa):&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Funktio-osoittimen määrittely: (kts. &lt;a href="http://c-ohjelmoijanajatuksia.blogspot.com/2008/08/c-kuinka-luet-tyypimrittelyj.html"&gt;C:n määrittelyjen luku&lt;/a&gt;)&lt;br /&gt;parametriton, paluuarvoton funkti:&lt;br /&gt;&lt;pre&gt;void (*nimi)();&lt;/pre&gt;&lt;br /&gt;arvon palauttava, parametrillinen funktio:&lt;br /&gt;&lt;pre&gt;&amp;lt;paluuarvon&amp;gt; (*nimi)(&amp;lt;arg1&amp;gt;, &amp;lt;arg2&amp;gt, ..., &amp;lt;argn&amp;gt;)&lt;/pre&gt;&lt;br /&gt;Eli esim.&lt;br /&gt;&lt;pre&gt;double (*laske_summa_ptr)(double, double);&lt;/pre&gt;&lt;br /&gt;tai&lt;br /&gt;&lt;pre&gt;void * (*varaa_muistia_ptr)(size_t maara);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Funktio-osoittimen alustus:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;int *functio(char *foo, int bar)&lt;br /&gt;{&lt;br /&gt;}&lt;br /&gt;...&lt;br /&gt;int *(*osoitin_funktioon)(char *,int);&lt;br /&gt;osoitin_funktioon = &amp;funktio;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&amp;amp; - merkki voidaan useimpien kääntäjien kohdalla jättää pois funktion edestä, mutta portattavuuden takia se kannattaa varalta siinä pitää. Sitäpaitsi, kun kerran funktion osoite on se mitä oikeastikkin haetaan, on kauniimpaa käyttää &amp;amp;-operaattoria, jolla osoite oikeastikkin haetaan.&lt;br /&gt;(osoitin_funktioon = funktio; /* tämä siis luultavasti toimisi myös, mutta on ruma ja laiska tapa jonka käyttäjät tulisi käristää hiljaisella tulella. */ )&lt;br /&gt;&lt;br /&gt;funktio-osoittimen käyttö funktiota kutsuttaessa:&lt;br /&gt;&lt;pre&gt;tulos = (*laske_summa)(1.00,1.00);&lt;/pre&gt;&lt;br /&gt;Ja jälleen on useimmilla kääntäjillä tuettuna myös kerettiläisyyden ruumiillistuma:&lt;br /&gt;tulos = laske_summa (1.00,1.00);&lt;br /&gt;&lt;br /&gt;Ja sitten, ihan vain virkistääksemme muistiamme edelliseen artikkeliin liittyen, katsomme yhden tyyppihirviön:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;          int* (*(*foo)(int *,char *))(double,int *);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Eli, aletaan käymään läpi keskeltä, oikea -vasen - oikea (ellei kohdata sulkeita) systeemillä.&lt;br /&gt;Siis: "Foo on.."&lt;br /&gt;&lt;b&gt;Sulkeet, eli vasemmalta =&amp;gt; osoitin:&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;"foo on osoitin..."&lt;br /&gt;&lt;b&gt;oikealle =&amp;gt; funktio:&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;"foo on osoitin&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;...funktioon..."&lt;br /&gt;&lt;b&gt;vasemmalle =&amp;gt; osoitin,&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;"foo on osoitin&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;...funktioon&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;...joka palauttaa osoittimen..."&lt;br /&gt;&lt;b&gt;oikealle =&amp;gt; funktio&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;"foo on osoitin&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;...funktioon&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;...joka palauttaa osoittimen&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;...funktioon..."&lt;br /&gt;&lt;b&gt;vasemmalle, oikealla ei enää mitään =&amp;gt; osoitin ja int&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;"foo on osoitin&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;...funktioon&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;...joka palauttaa osoittimen&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;...funktioon&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;...joka palauttaa osoittimen int tyypin lukuun"&lt;br /&gt;&lt;br /&gt;... Toivotaan, että tuo meni oikein... Jompi kumpi teistä lukijoistani voisi varmaan tarkistaa tämän?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Ja sitten vielä se Jump Table&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Ja ennen kun lopetan, kerron vielä lyhyesti mikä on hyppytaulu "jump table":&lt;br /&gt;&lt;br /&gt;Eli, kuvitellaan tilanne, jossa meillä on funktio foo, joka palauttaa kokonaisluvun väliltä 0-5, siten, että palautetusta luvusta riippuen haluamme tehdä jonkin tempun. Mikäli temput ovat riittävän yksinkertaisia, eivätkä vaadi kovin paljoa informaatiota alkutilasta, saattaa olla if-else tai switch-case häkkyrää selkeämpi (ja tehokkaampi!) tapa käyttää "jump tablea".&lt;br /&gt;&lt;br /&gt;Eli, luomme temppujen tekoon 5 eri funktiota. Kukin siis suorittaa yhden tempun. Pakotamme funktioille vielä samanlaiset argumentit.&lt;br /&gt;Eli meillä on funktiot:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;void temppu1(void);&lt;br /&gt;void temppu2(void);&lt;br /&gt;void temppu3(void);&lt;br /&gt;void temppu4(void);&lt;br /&gt;void temppu5(void);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Funktio josta saamme paluuarvon olkoot vaikka funktio&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;int mitas_seuraavaksi();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Nyt sitten jump table:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;typedef void (*temppu_osoitin)(void);&lt;br /&gt;&lt;br /&gt;void tee_temput()&lt;br /&gt;{&lt;br /&gt;   temppu_osoitin temput[5];&lt;br /&gt;   temppu[0]=&amp;temppu1;&lt;br /&gt;   temppu[1]=&amp;temppu2;&lt;br /&gt;   temppu[2]=&amp;temppu3;&lt;br /&gt;   temppu[3]=&amp;temppu4;&lt;br /&gt;   temppu[4]=&amp;temppu5;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;/*&lt;br /&gt;Nyt käytämme hyppytaulukkoamme. Vältämme siis funktion paluuarvon vertailun, mikä saattaa olla hyvinkin kriittinen ajansäästö jossain esim kiireellisissä loopissapyöritettävissä funktioissa DSP ympäristössä&lt;br /&gt;*/&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  *(temppu[ mitas_seuraavaksi()])();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Ja lopuksi huomautus kaikille veneilijöille, sekä muille lukijoilleni:&lt;br /&gt;1. Esimerkit ovat jälleen kerran testaamatta, joten bugeja voi olla. Kiitän jokaisesta bugikorjauksesta jonka saan!&lt;br /&gt;2. C++:n luokan jäsenfunktioille funktio-osoittimet ovat hieman erilaisia, mukana roudattavan this osoittimen takia..&lt;br /&gt;&lt;br /&gt;Ehkäpä minun pitäisi seuraavaksi jaaritella C++ funktiopointtereista?&lt;br /&gt;Ehkä, ehkä ei. Nyt on kuitenkin aika taas lopettaa, ja todeta että näemme ehkä ensi jaksossa taas, mitä se sitten pitääkään sisällään :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4818234838483192587-7902107638809554338?l=c-ohjelmoijanajatuksia.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-ohjelmoijanajatuksia.blogspot.com/feeds/7902107638809554338/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4818234838483192587&amp;postID=7902107638809554338' title='3 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/7902107638809554338'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/7902107638809554338'/><link rel='alternate' type='text/html' href='http://c-ohjelmoijanajatuksia.blogspot.com/2008/08/funktio-osoittimet-css.html' title='Funktio-osoittimet C:ssä.'/><author><name>Maz</name><uri>http://www.blogger.com/profile/03774313944609044806</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4818234838483192587.post-7921335343648638427</id><published>2008-08-05T21:45:00.005+03:00</published><updated>2008-09-10T23:56:07.957+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C'/><category scheme='http://www.blogger.com/atom/ns#' term='typedef'/><category scheme='http://www.blogger.com/atom/ns#' term='funktio-osoittimet'/><category scheme='http://www.blogger.com/atom/ns#' term='tyyppimäärittelyt'/><category scheme='http://www.blogger.com/atom/ns#' term='Maz'/><title type='text'>C - kuinka luet tyypimäärittelyjä.</title><content type='html'>&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;C kielen tyyppimäärittelyt ovat usein hyvin kryptisiä, kun siirrytään perustyypien ulkopuolelle. Ei siis liene kummallista, että törmäsin kerran nettisivuun, jossa C-koodin hyvyyden mittayksiköksi kerrottiin koodikatselmoinnissa kuuluvien kommenttien esiintymistiheyttä, wtf/sec. ("Mitä v*ttua?"  sekunnissa). (Nojoo, tämä pätee paljolti muuhunkin kuin tyyppimäärityksiin, mutta ei C:n sekavan näköiset tyypit ainakaan tilannetta helpota).&lt;br /&gt;&lt;br /&gt;Tämän takia muistutankin teitä, että vaikka artikkelin luettuanne tiedättekin muistisäännön jonka avulla saa sekavistakin määrityksistä selkoa, niin typedefien käyttö on suotavaa... Kaikkihan eivät blogiani lue :) &lt;br /&gt;&lt;br /&gt;Katsotaanpa ensin jokin sekavalta näyttävä määrittely:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    char *(*(*foo)[])(char *);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Mikähän olio mahtaa olla kyseessä? Paljastan sen artikkelin lopussa, mikäli et jo siihenmennessä onnistu itse sitä ratkaisemaan.&lt;br /&gt;&lt;br /&gt;Yksinkertainen nyrkkisääntö on: Katso ensin oikealle, sitten vasta vasemmalle - ellei sitten sulkeet muuta järjestystä... Eli, aloitetaan aina kummajaisen nimen kohdalta (mikäli muuttujalle on annettu nimi, muuten pitää vain yrittää löytää sisin tietotyyppi...) Sen jälkeen katsotaan mitä on nimen oikealla puolella, sitten mitä vasemmalla, taas mitä oikealla... Paitsi jos sulkeet muuttavat järjestyksen... Kokeillaan:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    int foo;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Tapaushan on tietysti selviö, mutta ensin on hyvä koettaa että kikka toimii...&lt;br /&gt;&lt;br /&gt;eli aletaan nimestä:&lt;br /&gt;"foo on..."&lt;br /&gt;Katsotaan oikealle - ei mitään. Katsotaan vasemmalle, "..int"&lt;br /&gt;Eli: "foo on int".&lt;br /&gt;YAY! Se toimi!&lt;br /&gt;&lt;br /&gt;Uusi yritys:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;   int *foo[1];&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Aletaan nimestä:&lt;br /&gt;"foo on.."&lt;br /&gt;oikealle:&lt;br /&gt;"foo on taulukko"&lt;br /&gt;ja vasemmalle&lt;br /&gt;"foo on taulukko joka sisältää osoittimen (mikäli koko olisi &amp;gt;1, "foo on taulukko joka sisältää osoittimia")&lt;br /&gt;ja oikealla ei mitään, eli vasemmalle:&lt;br /&gt;"foo on taulukko joka sisältää osoittimia intteihin"&lt;br /&gt;&lt;br /&gt;YAY YAY! edelleen toimii!&lt;br /&gt;&lt;br /&gt;Jatketaan...&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;   void **(*foo)[2]();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;eli...&lt;br /&gt;"foo on..."&lt;br /&gt;nyt huomaamme sulkeet, joten katsomme sulkeiden sisälle ennen oikealle puolelle menemistä:&lt;br /&gt;"foo on osoitin..."&lt;br /&gt;sitten oikealle&lt;br /&gt;"foo on osoitin taulukkoon..."&lt;br /&gt;ja vasemmalle:&lt;br /&gt;"foo on osoitin taulukkoon osoittimia..."&lt;br /&gt;ja oikealle..&lt;br /&gt;Nyt on edessä (). Tämä tarkoittaa tietysti funktiota. Eli:&lt;br /&gt;"foo on osoitin taulukkoon osoittimia funktioihin..."&lt;br /&gt;ja vasemmalle:&lt;br /&gt;osoitin, oikealla ei muuta, vasemmalla vielä void&lt;br /&gt;"foo on osoitin taulukkoon osoittimia funktioihin jotka palauttavat osoittimen 'voidiin'.", eli paremmalla suomella&lt;br /&gt;"foo on osoitin taulukkoon osoittimia funktioihin jotka palauttavat void tyyppisen osoittimen."&lt;br /&gt;&lt;br /&gt;Todella YAY!&lt;br /&gt;&lt;br /&gt;Mutta pakko tunnustaa, että viimeisin tuntui jo aavistuksen epätoivoiselta... Katsotaanpa miten typedefeillä voitaisiin selkeyttää tilannetta:&lt;br /&gt;&lt;br /&gt;typedef void *(*voidfunc)()  //osoitin funktioon joka palauttaa void *:n&lt;br /&gt;typedef voidfunk *osoitinvoidfunktioon //osoitin funktioon joka palauttaa void *:n&lt;br /&gt;&lt;br /&gt;typedef osoitinvoidfunktioon taulukko_voidfunktio_osoittimia[2] //taulukko osoittimia void *:n palauttaviin funktioihin.&lt;br /&gt;taulukko_voidfunktio_osoittimia *foo;&lt;br /&gt;&lt;br /&gt;Ja jo pelkän ensimmäisen typedefin käyttö olisi selkeyttänyt tilannetta...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    voidfunk   *(*foo)[];&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Ja nyt ratkaisu ensimmäiseen tyyppiin... Vastaus on:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;char *(*(*foo)[])(char *);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;"foo on osoitin taulukkoon osoittimia funktioon joka ottaa argumentiksi char osoittimen ja palauttaa osoittimen chariin".&lt;br /&gt;&lt;br /&gt;Ja koska kello on taas liian paljon, en jaksa tarkistaa tätä tänään... Vaan painan pääni tyynyyn, nähden painajaisia huomisesta työpäivästä häröpointtereiden ja gummallisten castausten parissa...&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4818234838483192587-7921335343648638427?l=c-ohjelmoijanajatuksia.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-ohjelmoijanajatuksia.blogspot.com/feeds/7921335343648638427/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4818234838483192587&amp;postID=7921335343648638427' title='2 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/7921335343648638427'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/7921335343648638427'/><link rel='alternate' type='text/html' href='http://c-ohjelmoijanajatuksia.blogspot.com/2008/08/c-kuinka-luet-tyypimrittelyj.html' title='C - kuinka luet tyypimäärittelyjä.'/><author><name>Maz</name><uri>http://www.blogger.com/profile/03774313944609044806</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4818234838483192587.post-3522642253678852716</id><published>2008-08-03T01:57:00.004+03:00</published><updated>2008-08-03T02:11:04.450+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C'/><category scheme='http://www.blogger.com/atom/ns#' term='pthread'/><category scheme='http://www.blogger.com/atom/ns#' term='säikeet'/><category scheme='http://www.blogger.com/atom/ns#' term='sockets'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><category scheme='http://www.blogger.com/atom/ns#' term='Maz'/><title type='text'>Threadit eli säikeet.</title><content type='html'>&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;br /&gt;POSIX Threadit eli pthreads kirjasto&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Kuten edellisessä blogipostissani mainitsin, threadit eli säikeet ovat keino ajaa useampaa kohtaa ohjelmakoodista yhden prosessin sisällä, näennäisesti yhtä aikaa. Yhdenaikaisuuden näennäisyys johtuu siitä, että mikäli koneessa on vain yksi (yksiytiminen) prosessori, ei se luonnollisestikaan voi tehdä kahta asiaa yhtä aikaa. Näennäinen yhdenaikaisuus saadaan aikaan käyttämällä ns. scheduleria, joka paloittelee koodin pienempiin suorituspätkiin, ja ajaa kutakin pätkää vuorollaan. Se mitä pätkää milloinkin ajetaan, ja kuinka paljon, riippuu schedulerin luonteesta. Muutamia yleisiä "hyvätietää pointteja" voin kuitenkin mainita.&lt;br /&gt;&lt;br /&gt;Ensinnäkin, skedulereihin (ja käyttöjärjestelmiin) voidaan tehdä karkea jako, tavalliset käyttöjärjestelmät ja reaaliaika systeemit. Tavallinen käyttöjärjestelmä on "joustavampi". Mikäli teet esimerkiksi ajastimen, jonka tulisi laueta 2 millisekunnin välein, voit olla varma siitä että normaali käyttöjärjestelmä ei tähän kykene. Ja harvoin sen tarvitseekaan. Normaali käytössä tärkeää on muun muassa se, että kun käyttäjä painaa nappia, kone reagoi nopeasti. Eli korkeampi prioriteetti on I/o operaatioilla, ja scheduleri suosiikin I/o prosesseja. Lisäksi aina kun scheduleri vaihtaa toisen threadin (tai prosessin) ajoon, kuluttaa se ajoaikaa läpikäydessään omaa tehtävälistaansa ja hoitaessa muita sisäisiä asioitaan. Siksi kovin tiuhaan tehtävää vaihtava scheduleri hidastaa koneen kokonaistoimintaa. Reaaliaikasysteemit taas on kehitetty vastaamaan tarpeeseen saada ajastimet jne. mahdollisimman tarkoiksi. Ajatellaanpa esim auton tietokonetta, jonka tehtävänä on hoitaa luistonesto mekanismia lisäämällä/vähentämällä vääntöä pyöristä ajo-ominaisuuksien parantamiseksi. Muutaman millisekunnin epätarkkuus saattaisi jo kriittisessä tilanteessa saada auton lähtemään luisuun. Reaaliaikasysteemeissä scheduleri tyypillisesti vaihtaa suoritettavaa tehtävää huomattavasti useammin kuin normaalisysteemeissä. Mutta ennen kun unohdan koko kirjoitukseni alkuperäisen idean, on parasta vain todeta että schedulerit ovat mielenkiintoisia kikkuloita, ja niihin liittyy monia nerokkaita algoritmeja. Alkuun pääset vaikkapa hakemalla googlella tietoa round robinista.&lt;br /&gt;&lt;br /&gt;mmm.. mistä aioinkaan kirjoittaa?&lt;br /&gt;&lt;br /&gt;Ai niin, säikeistä. Yhdenaikainen ohjelmakoodin suoritus aiheuttaa omat ongelmansa. Se mahdollistaa kokonaisen joukon uusia mahdollisia bugeja tehtäväksi. Esim saman resurssin käpistely kahdesta eri säikeestä yhtä aikaa, on melko varma keino saada systeemi nurin...&lt;br /&gt;&lt;br /&gt;Ah. Nyt tarkkaavainen lukijani (joo, taisit olla sinä Kari S., Vesa H. kun ei jaksanut näin pitkälle ;) ) on huomaavinaan aukon selvityksessäni. Aiemminhan minä totesin, että yhdenaikaisuus on näennäestä, prosessori kun tekee vain yhtä asiaa kerrallaan - miten siis samaa resurssia voitaisiin käpistellä yhtä aikaa? Kari hyvä, vaikka kerroinkin ettei prosessori tee kuin yhtä asiaa kerrallaan, niin yhtä C-kielen funktiota saattaa vastata useampi prosessorikohtainen komento. Ja moniprosessori ympäristössä asia mutkistuu entisestään. Sitäpaitsi oli syy mikä tahansa, niin jos teet testiohjelman jossa useampi säie käpistelee samaa osoitetta tiuhaan tahtiin, niin ennemmin tai myöhemmin napsahtaa...&lt;br /&gt;&lt;br /&gt;Usein kuitenkin on tarpeen käyttää samaa resurssia useammasta säikeestä. Miten siis tehdä tämä turvallisesti? No onneksi tähän on myös keksitty ratkaisu. Semaforit, mutexit ja luku/kirjoitus lukot. Mutex on kuin lukko, johon on vain yksi avain. Ensin lukko luodaan ja alustetaan (avain jätetään lukkoon :D) Ts. tehdään 'pthread_mutex_t muuttuja' ja kutsutaan pthread_mutex_init funktiota. Tämän jälkeen kun jokin threadi haluaa käpistellä jotain yhteistä resurssia, se kutsuu lukitusfunktiota (vetää lukon kiinni ja ottaa avaimen mukaansa) pthread_mutex_lock. Nyt lukko on kiinni, ja mikäli jokin toinen säie kutsuu lukkofunktiota mutexin ollessa lukittuna, blokkaa lukkofunktio kutsuvan säikeen suorituksen kunnes aiemmin lukon sulkenut säie avaa sen pthread_mutex_unlock. Finally the mutex is destroyed with pthread_mutex_destroy.&lt;br /&gt;&lt;br /&gt;Semaforit ovat kuten mutexitkin mutta tälläkertaa avaimia onkin yhden sijasta useampi.&lt;br /&gt;&lt;br /&gt;Ja jälleen kerran kun keksittiin uusi hieno ratkaisu ongelmaan, aikaansaatiin myös uusi bugin mahdollisuus. Eli ole varovainen semaforien ja mutexien kanssa, puolihuolimattomalla käytöllä aikaansaadaan ikuinen odotustilanne, eli "deadlock". Deadlock siis on tilanne, missä kahdella (tai useammalla) säikeellä on jokin resurssi hallussaan jota toinen tarvitsee, ja molemmat ovat juuttuneena odottamaan että toinen luovuttaa resurssin toiselle.&lt;br /&gt;&lt;br /&gt;No nyt koetan viimeinkin päästä itse asiaan, eli threadin luontiin ja lopetukseen. Uuden threadin luonti tapahtuu pthread_create() functiolla. pthread_create() funktiolle annetaan argumenttina osoitin funktioon josta uuden säikeen suoritus aloitetaan. Lisäksi pthread_createlle annetaan parametrina starttifunktiolle menevä void * tyypin argumentti. Uusi säie voidaan käynnistää joko"joinablena" tai "detachedina", mutta detach eli irroitus voidaan halutessa tehdä myös myöhemmin, kutsumalla pthread_detach funktiota. Mikäli säiettä ei irroiteta, se voidaan (ja se tulee) 'liittää' kutsuneeseen säikeeseen sen varaamien resurssien vapauttamiseksi ja säikeen palauttaman paluuarvon lukemiseksi. Liittäminen tehdään pthread_join kutsulla, ja se on blokkaava kutsu, eli se pysäyttää liittävän säikeen suorituksen siksi aikaa, kunnes liitettävä säie on lopettanut suorituksensa joko pthread_exit kutsulla, tai palaamalla starttifunktiosta. Irroitettua säiettä ei tarvitse (eikä voi) liittää resurssien vapauttamiseksi, eikä sen paluuarvoa voida tutkia.&lt;br /&gt;&lt;br /&gt;Ja vielä ennen koodiesimerkkiä pieni varoituksen sana:&lt;br /&gt;&lt;br /&gt;Kuten aiemmin mainitsin useista säikeistä koostuvassa ohjelmassa tulee olla tarkkana staattisten sekä globaalien resurssien kanssa. Myös jotkut standardikirjaston funktiot käyttävät allaolevissa toteutuksissaan näitä, eivätkä niinollen sovi monisäikeiseen ohjelmointiin ilman asianmukaista semafori/mutex suojausta! Monisäikeisiin ohjelmiin soveltuvista funktioista käytetään englanninkielistä nimitystä "reentrant". Kallisarvoinen ystävämme google osaa varmasti koostaa sinulle listaa näistä funktioista.&lt;br /&gt;&lt;br /&gt;Mutta nyt esimerkin pariin.&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;br /&gt;Esimerkki 1 - Blokkaavien sokettien käyttö, ohjelmassa joka ei saa jäädä "jumiin".&lt;br /&gt;HUoMAA: Esimerkkikoodia ei ole testattu!&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;#include &amp;lt;inet/arpa.h&amp;gt;&lt;br /&gt;#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;&lt;br /&gt;#define NUKKUAIKA 1&lt;br /&gt;#define YHTEYS_HUKASSA -666&lt;br /&gt;&lt;br /&gt;pthread_mutex_t data_lukko;&lt;br /&gt;void *socketti_data;&lt;br /&gt;size_t datan_koko;&lt;br /&gt;&lt;br /&gt;int main(int argc, char *argv[])&lt;br /&gt;{&lt;br /&gt;   pthread_t thred;&lt;br /&gt;   int paluuarvo;&lt;br /&gt;   datan_koko=0;&lt;br /&gt;   data=NULL;&lt;br /&gt;   char ip[]="127.0.0.1";&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;&lt;br /&gt;/* Alusta mutex käyttöä varten */&lt;br /&gt;&lt;br /&gt;   if(pthread_mutex_init(&amp;amp;data_lukko, NULL))&lt;br /&gt;   {&lt;br /&gt;       //virhekäsittely, mutexia ei voitu alustaa.&lt;br /&gt;   }&lt;br /&gt;   /*&lt;br /&gt;     argumentit: starttifunktio, attribuutit,&lt;br /&gt;     osoitin threadin kahvaan, starttifunktion&lt;br /&gt;     argumentit.&lt;br /&gt;   */&lt;br /&gt;   if(&lt;br /&gt;     pthread_create&lt;br /&gt;     (&lt;br /&gt;       &amp;amp;pollaile_sockettia,&lt;br /&gt;       NULL, &lt;br /&gt;       &amp;amp;thred,&lt;br /&gt;       (void *)ip&lt;br /&gt;     )&lt;br /&gt;   )&lt;br /&gt;   {&lt;br /&gt;     //virhekäsittely, threadin luonti epäonnistui.&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   sleep(NUKKUAIKA);&lt;br /&gt;   while(1)&lt;br /&gt;   {&lt;br /&gt;     //suojaa globaalit muuttujat lukoilla!&lt;br /&gt;     pthread_mutex_lock(&amp;amp;data_lukko);&lt;br /&gt;     if(datan_koko!=0)&lt;br /&gt;     {&lt;br /&gt;       if(*(int *)data==YHTEYS_HUKASSA)&lt;br /&gt;       {&lt;br /&gt;         if( (paluuarvo=pthread_join(thred)) )&lt;br /&gt;         {&lt;br /&gt;           //virhekäsittely, tarkista virhe paluuarvosta&lt;br /&gt;         }&lt;br /&gt;         pthread_mutex_destroy(&amp;amp;data_lukko);&lt;br /&gt;         return 0;&lt;br /&gt;       }&lt;br /&gt;       kasittele_sockettidata();&lt;br /&gt;     }&lt;br /&gt;     pthread_mutex_unlock(&amp;amp;data_lukko);&lt;br /&gt;     tee_tuiki_tärkeä_tehtävää_joka_ei_odotaa_sockettia();&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void *pollaile_sockettia(void *argumentit)&lt;br /&gt;{&lt;br /&gt;  char *ip;&lt;br /&gt;  int socketti;&lt;br /&gt;  size_t luettu;&lt;br /&gt;  char puskuri[RECV_MAX];&lt;br /&gt;&lt;br /&gt;  ip=(char *)argumentit;&lt;br /&gt;   .&lt;br /&gt;   .&lt;br /&gt;   .&lt;br /&gt;/*&lt;br /&gt;  Tässävaiheessa ohjelmaa socketti on luotu&lt;br /&gt;  ja yhdistetty, ja odotellaan vastausta palvelimelta&lt;br /&gt;  toisen säikeen hoitaen samalla tuiki tärkeää tehtävää...&lt;br /&gt;*/&lt;br /&gt; while( 1)&lt;br /&gt; {&lt;br /&gt;   luettu=recv(socketti,puskuri,RECV_MAX,0);&lt;br /&gt;   if(luettu==0||luettu==-1)&lt;br /&gt;   {&lt;br /&gt;   pthread_mutex_lock(&amp;amp;data_lukko);&lt;br /&gt;     data=(void *)YHTEYS_HUKASSA;&lt;br /&gt;   pthread_mutex_unlock(&amp;amp;data_lukko);&lt;br /&gt;     pthread_exit(0);&lt;br /&gt;   }&lt;br /&gt;   pthread_mutex_lock(&amp;amp;data_lukko);&lt;br /&gt;   datan_koko=luettu;&lt;br /&gt;   data=puskuri;&lt;br /&gt;   pthread_mutex_unlock(&amp;amp;data_lukko);&lt;br /&gt;   /*&lt;br /&gt;     nuku hetki että naapurithread ehtii lukea datan,&lt;br /&gt;     ennen uuden datan lukua socketista&lt;br /&gt;   */&lt;br /&gt;   sleep(NUKKUAIKA);&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ja vielä viimeisenä huomautuksena:&lt;br /&gt;käännettäessä gcc kääntäjällä threadeja käyttävää koodia, tule linkkausvaiheessa käyttää flagia -lpthread, eli ottaa mukaan pthread kirjasto.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4818234838483192587-3522642253678852716?l=c-ohjelmoijanajatuksia.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-ohjelmoijanajatuksia.blogspot.com/feeds/3522642253678852716/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4818234838483192587&amp;postID=3522642253678852716' title='2 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/3522642253678852716'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/3522642253678852716'/><link rel='alternate' type='text/html' href='http://c-ohjelmoijanajatuksia.blogspot.com/2008/08/threadit-eli-sikeet.html' title='Threadit eli säikeet.'/><author><name>Maz</name><uri>http://www.blogger.com/profile/03774313944609044806</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4818234838483192587.post-7309015905142753572</id><published>2008-07-31T20:39:00.008+03:00</published><updated>2008-07-31T21:12:38.174+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C'/><category scheme='http://www.blogger.com/atom/ns#' term='tietotyypit'/><category scheme='http://www.blogger.com/atom/ns#' term='Muisti'/><category scheme='http://www.blogger.com/atom/ns#' term='Allocointi'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><category scheme='http://www.blogger.com/atom/ns#' term='Maz'/><title type='text'>C ja muisti, osa 1</title><content type='html'>Tietokoneen prosessorin toiminta voidaan karkeast yksinkertaistaa kahteen osaan.&lt;br /&gt;&lt;br /&gt;1. Muistavaruuden käsittely&lt;br /&gt;2. Komentojen suorittaminen.&lt;br /&gt;&lt;br /&gt;Kohta yksi voidaan jakaa lukuun ja kirjoitukseen. Komennot taas yleensä manipuloivat luettua dataa, ja kirjoittavat lopputuloksen johonkin muistiosoitteeseen. Muistin lukua käytetään myös luettaessa seuraava suoritettava komento muistista. Huomionarvoista on, että prosessori näkee myös kaikki laitteet, levyt, näytön, verkkokortin jne. vain tiettyinä muistiosoitteina.&lt;br /&gt;&lt;br /&gt;Ohjelmointi on prosessorille annettavien komentojen (ja ohjelmaan liittyvän datan) kirjoittamista muistiin, josta prosessori ne lukee ohjelmaa ajettaessa. Mikäli ohjelmointi tulisi tehdä suoraan kirjoittamalla prosessorin käskyt ja data sellaisenaan, olisi "hello word" ohjelman tekokin jo varsin haastavaa, ja vaatisi valtavaa tietämystä koneesta rautatasolla.&lt;br /&gt;&lt;br /&gt;Sen vuoksi on kehitetty erilaisia ohjelmointikieliä, joissa yksinkertaisilla käskyillä suoritetaan useampi prosessorikomento halutun toiminnallisuuden saavuttamiseksi. Lisäksi käyttöjärjestelmä ja laiteajurit peittävät suuren osan alla olevan raudan erityispiirteistä, tarjoten omat (enemmän tai vähemmän) standardoidut keinonsa varsinaisiin laitteisiin käsiksipääsyyn.&lt;br /&gt;&lt;br /&gt;C-kieli on kuitenkin niin sanottu 'matalan tason kieli', mikä tarkoittaa sitä, että vaikka monia asioita on huomattavasti yksinkertaistettu, niin edelleen ollaan kuitenkin varsin lähellä rautatasoa ja prosessorikäskyjä. Tästä seuraa se, että vaikka voit tuottaa ihan toimivaa ohjelmakoodia ymmärtämättä käyttöjärjestelmää/rautatasoa sen paremmin, niin voidaksesi todella ymmärtää miksi jokin toimii kuten toimii, tai voidaksesi todella luoda omaa koodia nauttien kaikista C:n tarjoamista mahdollisuuksista, sinun on tunnettava alusta jolle ohjelmaa kirjoitat.&lt;br /&gt;&lt;br /&gt;Ehkäpä kaikkein eniten ongelmia C:n kirjoituksessa tulee muistinhallinnan kanssa. Tähän liittyy muutamia perusasioita, joita aion läpikäydä tässä blogitekstissäni:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Fyysinen muisti on käyttöjärjestelmän toimesta jaettu pieniin palasiin, ja palaset on numeroitu muistiosoitteiksi. Kuhunkin muistiosoitteeseen mahtuu 8 bittiä tietoa, eli yksi tavu.&lt;br /&gt;&lt;br /&gt;C-kielen eri tietotyypeillä on oma pituutensa, tavanomaisimpien tietotyyppien ollessa:&lt;br /&gt;int 32 bittiä, eli 4 tavua.&lt;br /&gt;short int 16 bittiä, eli 2 tavua&lt;br /&gt;char 8 bittiä, eli yksi tavu.&lt;br /&gt;C:ssä voidaan käyttää myös tietotyyppä void, jonka pituutta ei ole määritetty.&lt;br /&gt;&lt;br /&gt;Tietotyyppien pituuden selvittämiseksi, C:n standardi määrittä sizeof() lauseen. sizeof(int) palauttaa int tietotyypin koon, sizeof(short int) palauttaa short int:n koon jne.&lt;br /&gt;&lt;br /&gt;Huom 1.&lt;br /&gt;sizeof(char *) == sizeof(int *) == sizeof(void *) jne. sizeof(&lt;jotakin&gt; *) palauttaa osoitteen vaatiman tilan, ei sen datan jota osoitteesta alkavaan muistiin on talletettu.&lt;br /&gt;HUOM 2!!! Osoitteen vaatima tila riippuu tietokoneen arkkitehtuurista, tyypillisesti 32 bittisissä koneissa, osoite on 32 bittiä leveä, mutta 64 bittisissä koneissa 64 bittiä leveä. Siis mikäli tahdotte kirjoittaa ohjelmakoodia, joka toimii molemmissa arkkitehtuureissa, älkää muuntako osoitteita int:tyypiksi, sillä 64 bittisessä arkkitehtuurissa osoite ei sovi int tyyppiin, vaan puolet osoitteesta leikkaantuu pois!&lt;br /&gt;&lt;br /&gt;Tämän asian sisäistäminen ja ymmärtäminen on eräs C-ohjelmoinnin kulmakiviä. Vaikka muuttujatyyppiä käytettäessä, kääntäjä osaa itse hoitaa ohjelmakoodin sellaiseksi, että prosessori tietää montako tavua milloinkin on luettava, niin joskus on tarpeen 'kadottaa' tietotyyppi ja palauttaa data myöhemmin takaisin samaksi tyypiksi. Esittelen nyt muutaman tilanteen missä tällaista kikkailua voi joutua harrastamaan.&lt;br /&gt;&lt;br /&gt;Joissain tilanteissa voidaan haluta luoda esim. funktio, jolle voidaan antaa erilaista dataa argumenttina, tilanteesta riippuen. Hyvänä esimerkkinä vaikka pthread kirjasto ja threadin luonti. (Thread eli säie on itsenäinen suorituspolku prosessin sisällä. Eli threadien avulla voidaan haarauttaa ohjelman suoritus suorittamaan useampaa koodia näennäisesti yhtäaikaa.)&lt;br /&gt;&lt;br /&gt;Threadi luodaan kutsumalla pthread_create() funktiota, jolle annetaan parametrina sen funktion osoite, josta uuden säikeen suorituksen tahdotaan jatkuvan. Lisäksi annetaan uuden säikeen alkamisfunktion parametrit. pthread_create funktio on toteutettu siten, että se hyväksyy starttifunktioksi vain funktion, jonka argumentti on tyyppiä void *. Eli 'tyypittömän' datablockin osoite. Syy tällaisen argumentin käyttöön on yksinkertainen. pthread kirjaston tekijät, eivät voi tietää mihin tarkoitukseen kirjaston käyttäjät tahtovat uuden säikeen luoda. Täten on mahdotonta myöskään arvata, millaisia ja minkä tyyppisiä argumentteja starttifunktiolle voidaan antaa.&lt;br /&gt;&lt;br /&gt;Otetaanpa esimerkki. Kuvitellaan, että olemme tekemässä webbiserveriä, jossa käytämme socketteja ihka ensimmäisen blogipostini esimerkin tapaisesti. Kohdassa jossa serverimme hyväksyy kuuntelevaan sockettiin tulevan yhteyden, serverin pitää päästä jatkamaan kuuntelua mahdollisimman nopeasti, jotta muut palvelimemme sisällöstä kiinnostuneet pääsevät katselemaan tarjoamaamme laatupornoa. Kuitenkin ensimmäisen clientin pyynnöt on käsiteltävä.&lt;br /&gt;&lt;br /&gt;ratkaisemme ongelman käyttämällä threadeja. Kun hyväksymme uuden yhteyden, luomme uuden threadin joka aloittaa suorituksensa kasittele_clientin_pyynnot() funktiosta. Samalla annamme alkuperäisen kuuntelijasockettimme palata accept funktioon odottelemaan uusia yhteyspyyntöjä.&lt;br /&gt;&lt;br /&gt;Mitä tietoja kasittele_client_request() funktiomme nyt tarvitsee? Ainakin socketin, jonka kautta clientin kanssa keskustellaan. Lisäksi clientin osoitetiedot on hyvä saada, jotta saadaan ne logitiedostoon talteen. Luultavasti täysiverisessä serverissä tarvittaisiin muutakin dataa, mutta tämän kohdan toteutuskin voisi olla hieman toisenlainen :)&lt;br /&gt;&lt;br /&gt;Mutta kuinka nyt saamme socketin ja clientin tiedot kasittele_clientin_pyynnot() funktiolle? Argumentti jonka annamme on siis muistipaikan osoite. Helpoin mieleentuleva tieto on, sijoittaa socket ja osoitetiedot peräkkäisiin paikkoihin muistissa, ja antaa kasittele_clientin_pyynnot() funktiolle sen muistipaikan alkuosoite, josta datamme alkaa.&lt;br /&gt;&lt;br /&gt;Esimerkki 1: Sekalaisen datan sijoittaminen peräkkäisiin muistipaikkoihin.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;int sock;&lt;br /&gt;struct sockaddr clientin_osoite;&lt;br /&gt;&lt;br /&gt;void *socketti_ja_osoite; //muisti paikka josta data alkaa&lt;br /&gt;&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;&lt;br /&gt;/*&lt;br /&gt;  Allocoidaan, eli varataan muistia sockettia ja osoitetta&lt;br /&gt;  varten, niiden tarvitsema määrä. Malloc() palauttaa &lt;br /&gt;  paluuarvonaan osoitteen,josta varattu tila alkaa:&lt;br /&gt;*/&lt;br /&gt;socketti_ja_osoite=malloc(sizeof(int)+sizeof(struct sockaddr));&lt;br /&gt;/*&lt;br /&gt;  Tarkistetaan että muistn varaus onnistui, varaaminen voi&lt;br /&gt;  epäonnistua mm. jos muistia ei ole vapaana&lt;br /&gt;*/&lt;br /&gt;if(NULL==socketti_ja_osoite)&lt;br /&gt;{&lt;br /&gt;  //virhekäsittely, muistin varaaminen epäonnistui!&lt;br /&gt;}&lt;br /&gt;/*&lt;br /&gt;  Nyt kun muisti on varattu käytettäväksi, täytyy data saada &lt;br /&gt;  sijoitettua muistiin. Talletetaan ensin socketin numero &lt;br /&gt;  muistipaikan alkuun. Tehdään tämä memcpy eli muistin&lt;br /&gt;  kopiointi funktiolla, joka kopioi 1. argumentin osoittamaan&lt;br /&gt;  muistipaikkaan dataa, 2. argumentin osoittamasta&lt;br /&gt;  muistipaikasta,&lt;br /&gt;  3. argumentin kertoman tavumäärän. huom, &amp; merkillä saadaan &lt;br /&gt;  minkä tahansa muuttujan muistipaikan alkuosoite.&lt;br /&gt;*/&lt;br /&gt;memcpy&lt;br /&gt;(&lt;br /&gt;    socketti_ja_osoite, //1. arg, eli mihin kopioidaan.&lt;br /&gt;    (void *)&amp;sock,      //2. arg, eli mistä kopioidaan.&lt;br /&gt;     sizeof(int)        //3. arg, eli montako tavua kopioidaan.&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;/*&lt;br /&gt;  Seuraavaksi meidän tulisi saada kopioitua clientin&lt;br /&gt;  osoitetiedot sockettitiedon perään. Eli koska int tyypin&lt;br /&gt;  tieto vaatii muistia 4 tavua, on osoite johon clientin&lt;br /&gt;  tiedot tahdomme socketti_ja_osoite muistipaikka + 4 tavua.&lt;br /&gt;  HUOMAA: Kun muistipaikkaan lisätään luku, lisää kääntäjä&lt;br /&gt;  itse muistipaikan osoitteeseen niin monta pykälää, kuin on&lt;br /&gt;  tarpeen että &lt;br /&gt;  LUVUN OSOITTAMA MÄÄRÄ OSOITTEESEEN YHDISTETTYÄ TIETOTYYPPIÄ&lt;br /&gt;  VAATII.&lt;br /&gt;  Tämän vuoksi &lt;br /&gt;  VOID * TYYPPISEEN OSOITTEESEE EI VOI LISÄTÄ MITÄÄN!&lt;br /&gt;  Siksi joudumme muuntamaan (castaamaan) socketti_ja_osoite&lt;br /&gt;  muistipaikan toisen tyyppiseksi.&lt;br /&gt;&lt;br /&gt;  Selvennykseksi: Jos meillä on:&lt;br /&gt;  void *foo=0x0,&lt;br /&gt;  niin&lt;br /&gt;  (char *)foo+4 on 4&lt;br /&gt;  (int *)foo+4 on 4*4 eli 16.&lt;br /&gt;&lt;br /&gt;  Siksi epäselvyyksien välttämiseksi minä itse muunnan aina&lt;br /&gt;  osoitteeseen lisätessäni/siitä vähentäessäni osoitteen&lt;br /&gt;  (char *) tyyppiseksi ja lisään sizeof( lisättävä tyyppi )&lt;br /&gt;  koon. Mikäli&lt;br /&gt;  tahdon lisätä useamman kuin yhden kappaleen jotain tietoa, &lt;br /&gt;  kerron sizeof() lausekkeen lukumäärällä.&lt;br /&gt;  VARO KUITENKIN TÄTÄ, mikäli kappalemäärä ei ole tiedossa,&lt;br /&gt;  sillä suurella kappalemäärällä voi sizeof()*kappalemäärä&lt;br /&gt;  kasvaa suuremmaksi kuin 32 bitin kenttään sopii, ja&lt;br /&gt;  sizeof*kappalemäärä voi 'pyörähtää ympäri' aikaansaaden&lt;br /&gt;  hyvin pienen luvun. Erityisen ikävää tämä on muistia&lt;br /&gt;  varattaessa jolloin varausfunktio ei näe virhettä, ja&lt;br /&gt;  luulee muistin varaamisen sujuneen ongelmitta. Varatun&lt;br /&gt;  muistin koko ei kuitenkaan nyt ole haluttu, ja ohjelma&lt;br /&gt;  luultavasti kaatuu myöhemmin, ja jossain aivan muussa&lt;br /&gt;  kohdassa kuin muistinvarauksessa jossa todellinen vika on.&lt;br /&gt;&lt;br /&gt;  Oho. Eksyin sivuraiteille, mutta mennäänpä nyt resinalla &lt;br /&gt;  takaisin pääradalle.&lt;br /&gt;*/&lt;br /&gt;memcpy&lt;br /&gt;(&lt;br /&gt;    (void *)((char *)socketti_ja_osoite)+sizeof(int),&lt;br /&gt;    (void *)&amp;clientin_osoite,&lt;br /&gt;    sizeof(struct sockaddr)&lt;br /&gt;};&lt;br /&gt;/*&lt;br /&gt;  Tai vaihtoehtoisesti:&lt;br /&gt;  memcpy&lt;br /&gt;  (&lt;br /&gt;    (void *)((char *)socketti_ja_osoite)+4,&lt;br /&gt;    (void *)&amp;clientin_osoite,&lt;br /&gt;    sizeof(struct sockaddr)&lt;br /&gt;  );&lt;br /&gt;*/&lt;br /&gt;/*&lt;br /&gt;  Tai vaihtoehtoisesti:&lt;br /&gt;  memcpy&lt;br /&gt;  (&lt;br /&gt;    (void *)((int *)socketti_ja_osoite)[1],&lt;br /&gt;    (void *)&amp;clientin_osoite,&lt;br /&gt;    sizeof(struct sockaddr)&lt;br /&gt;  );&lt;br /&gt; &lt;br /&gt;*/&lt;br /&gt;/*&lt;br /&gt;  Tai vaihtoehtoisesti:&lt;br /&gt;  memcpy&lt;br /&gt;  (&lt;br /&gt;    (void *)((int *)socketti_ja_osoite)+1,&lt;br /&gt;    (void *)&amp;clientin_osoite,&lt;br /&gt;    sizeof(struct sockaddr)&lt;br /&gt;  );&lt;br /&gt; &lt;br /&gt;*/&lt;br /&gt;&lt;br /&gt;/*&lt;br /&gt;   Ja voila, data on nyt socketti_ja_osoite osoittaman&lt;br /&gt;   muistipaikan perässä, valmiina pthread_create funktiolle&lt;br /&gt;   annettavaksi.&lt;br /&gt;*/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Esimerkki 2: Sekalaisen datan saaminen peräkkäisistä muistipaikoista.&lt;br /&gt;&lt;br /&gt;Nyt rakennamme kasittele_clientin_pyynnot() funktioomme tarvittavat kikkulat, jotta saamme socketin ja clientin tiedot kaivettua argumentin osoittamasta muistialueesta. Vaikeutena on se, että koska tiedot annettiin funktiota kutsuttaessa void * tyyppisenä, ei funktion sisällä ole kääntäjän toimesta mitään tyyppitietoatietoa, eikä näinollen myöskään tietoa siitä, miten pitkä muistialue on kyseessä, onko se varattu käyttöä varten, tai miten monta tietorakennetta se sisältää. Ennen kuin tietoa voidaan käyttää, on tämä kaikki kerrottava kääntäjälle tavalla tai toisella.&lt;br /&gt;&lt;br /&gt;Tähän on ainakin kaksi mahdollista lähestymistapaa. Joko luoda oikean tyyppiset muuttujat joihin tieto kaivetaan argumentista, tai laskea aina oikea muistipaikan osoite ja kertoa kääntäjälle datan tyyppi sen jälkeen.&lt;br /&gt;&lt;br /&gt;Ensimmäinen tapa on mielestäni selkeämpi, helpompi käyttää ja koodia tutkivan työkaverin selkeämpi käsittää.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;void * kasittele_clientin_pyynnot( void *sockettiJaOsoite)&lt;br /&gt;{&lt;br /&gt;  /*&lt;br /&gt;    Luodaan siis muuttujat joihin socketti ja osoite&lt;br /&gt;    talletetaan&lt;br /&gt;  */&lt;br /&gt;  int sock;&lt;br /&gt;  struct sockaddr clientin_tiedot;&lt;br /&gt;&lt;br /&gt;/*&lt;br /&gt;  Minulla on tapana asettaa pointterit jotka eivät enää ole&lt;br /&gt;  käytössä NULL:iksi, eli osoittamaan osoitetta 0. Tämä&lt;br /&gt;  usein auttaa virheiden kiinnisaamisessa. Tarkistetaan&lt;br /&gt;  siis onko argumenttina annettu osoite validi:&lt;br /&gt;*/&lt;br /&gt;  if(sockettiJaOsoite==NULL)&lt;br /&gt;  {&lt;br /&gt;    /*&lt;br /&gt;      Virhekäsittely, socketti ja clientin osoitetieto ei&lt;br /&gt;      ole oikein annettu.&lt;br /&gt;    */&lt;br /&gt;  }&lt;br /&gt;  /*&lt;br /&gt;    socketti on helppo, sillä sen arvo on heti muistin&lt;br /&gt;    osoittmassa paikassa, ja perustyypin ollessa kyseessä&lt;br /&gt;    voimme olla varmoja että sijoitusoperaattori on&lt;br /&gt;    käytössä. Eli valehtelemme siis silmät kirkkaina&lt;br /&gt;    argumentin tietotyypin olevan int *, eli osoite jossa&lt;br /&gt;    on int tyypin tieto talletettuna. Sitten kerromme &lt;br /&gt;    kääntäjälle haluavamme datan tästä osoitteesta, ja&lt;br /&gt;    tallennamme sen int tyypin muuttujaan.&lt;br /&gt;&lt;br /&gt;    Huomaa, kun osoitemuuttuja luodaan, se tapahtuu:&lt;br /&gt;    int *muuttujannimi;&lt;br /&gt;    Näin se myös esitellään funktion prototyypissä. Mutta&lt;br /&gt;    mikäli myöhemmin sijoitamme tähden osoitemuuttujan&lt;br /&gt;    nimen eteen, se tarkoittaa että haluamme datan joka on&lt;br /&gt;    osoitemuuttujan kertomassa muistipaikassa.&lt;br /&gt;&lt;br /&gt;    Eli&lt;br /&gt;    int *foo;  //luo osoitinmuuttujan int tyyppiseen dataan&lt;br /&gt;    int bar;&lt;br /&gt;    *foo=1;  //tallettaa luvun 1, foo:n osoitepaikkaan.&lt;br /&gt;    /* &lt;br /&gt;      tallettaa bar muuttujaan osoitteessa foo olevan DATAN.&lt;br /&gt;      Ei foon osoitetta.&lt;br /&gt;    */&lt;br /&gt;    bar=*foo; &lt;br /&gt;    sock=*((int *)sockettiJaOsoite);&lt;br /&gt;      /*&lt;br /&gt;        (int *)sockettiJaOsoite    =&gt; väitetään&lt;br /&gt;        socettiJaOsoite kertoman muistipaikan sisältävän&lt;br /&gt;        int tyyppisen datan.&lt;br /&gt;        *((int *)sockettiJaOsoite) =&gt; Kerrotaan olevamme&lt;br /&gt;        kiinnostuneita datasta, ei osoitteesta.&lt;br /&gt;      */&lt;br /&gt;    /*&lt;br /&gt;      clientin tiedot saadaan ehkä helpoiten käyttämällä&lt;br /&gt;      jälleen &amp; merkkiä saadaksemme clientin_tiedot muuttujan&lt;br /&gt;      muistipaikan osoitteen, ja  kopioimalla sinne&lt;br /&gt;      sizeof(struct sockaddr) verran dataa sockettiJaOsoite+4&lt;br /&gt;      muistipaikasta alkaen.&lt;br /&gt;    */&lt;br /&gt;    memcpy&lt;br /&gt;    (&lt;br /&gt;        (void *)&amp;clientin_tiedot,&lt;br /&gt;        (void *)(char *)sockettiJaOsoite+sizeof(int),&lt;br /&gt;        sizeof(struct sockaddr)&lt;br /&gt;    );&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Jaha, mutta koska ilta alkaa taas hiipimään nurkkiin, taidan lopettaa tältä päivää ja jättä loput mehevät muistijutut ensi kertaan :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4818234838483192587-7309015905142753572?l=c-ohjelmoijanajatuksia.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-ohjelmoijanajatuksia.blogspot.com/feeds/7309015905142753572/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4818234838483192587&amp;postID=7309015905142753572' title='0 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/7309015905142753572'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/7309015905142753572'/><link rel='alternate' type='text/html' href='http://c-ohjelmoijanajatuksia.blogspot.com/2008/07/c-ja-muisti-osa-1.html' title='C ja muisti, osa 1'/><author><name>Maz</name><uri>http://www.blogger.com/profile/03774313944609044806</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4818234838483192587.post-2863096244818440859</id><published>2008-07-28T21:22:00.043+03:00</published><updated>2008-07-30T11:26:27.271+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C'/><category scheme='http://www.blogger.com/atom/ns#' term='winsock'/><category scheme='http://www.blogger.com/atom/ns#' term='sockets'/><category scheme='http://www.blogger.com/atom/ns#' term='select()'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><category scheme='http://www.blogger.com/atom/ns#' term='Maz'/><title type='text'>Socketit - tapa välittää tietoa.</title><content type='html'>C suomeksi...&lt;br /&gt;&lt;br /&gt;C ja C++ kieli ovat jo suhteellisen ikääntyneitä, mutta edelleen elinvoimaisia ja tehokkaita ohjelmointikieliä. Jostain syystä suomekielistä dokumentaatiota on kuitenkin aika niukasti. Tähän blogiin on tarkoitus kirjoitella simppeleitä pätkiä sieltä täältä C:n laajasta valikoimasta. Tarkoituksena ei ole kirjoittaa  kaikenkattavaa C-kielen opasta, vaan poimia kiinnostavia ja hyödyllisiä palasia ja  antaa niistä riittävä tieto jotta kiinnostunut suomalainen koodarinalku pääsee liikkeelle.&lt;br /&gt;&lt;br /&gt;Ohjelmoija joutuu usein tilanteeseen, jossa hänen on saatava tieto kulkemaan prosessilta toiselle, tai jopa tietokoneelta toiselle. Socketit on yksi mahdollisuus.&lt;br /&gt;&lt;br /&gt;Miksi socketit?&lt;br /&gt;&lt;br /&gt;Samalla koneella pyörivien prosessien väliseen tiedonjakoon löytyy muitakin keinoja, kuten putket, jaettu muisti, tiedostot jne. Kaikissa näissä on omat hyvät ja huonot puolensa. Jaettu muisti on nopea keino, putket ovat yksinkertaisia ja suhteellisen nopeita, tiedostot säilyttävät datansa ohjelman suorituksen päätyttyä...&lt;br /&gt;&lt;br /&gt;Sockettien vahvuus on suunnittelun vapaus. Socketteja käyttämällä voidaan tarvittaessa siirtää kahdesta keskenään keskustelevasta prosessista toinen kokonaan eri koneelle, varsin pienin muutoksin. Lisäksi siinä missä esim putket ovat varsinaisesti Linux/Unix maailmaan kuuluvia, ja jaetun muistin käyttäminenkin tapahtuu eri käyttöjärjestelmissä eri tavoin, socket rajapinta löytyy kaikista POSIX ympäristöistä, ja windowskin tarjoaa oman winsock kirjastonsa joka eroaa POSIX socketeista vain muutamissa kohdissa. Niinpä sockettien avulla, pienellä etukäteistutustumisella on varsin mahdollista tehdä "portattavaa" softaa.&lt;br /&gt;&lt;br /&gt;Ennen esimerkin pariin siirtymistä, mainitsen vielä, että socketteja voi käyttää eri verkkoprotokollien kanssa. Socketit tarjoavat mahdollisuuden lähettää dataa UDP:n, TCP/IP:n ja muutamien muiden protokollien kautta (UNIX maailmassa on mainittava samalla koneella ajettavien prosessien väliseen kommunikointiin tarkoitetut UNIX socketit, jotka ovat erinomainen vaihtoehto harkittaessa miten prosessien välinen kommunikointi tulisi hoitaa). Ja valitusta protokollasta huolimatta, varsinainen datan lähetys/vastaanotto on tehty varsin samanlaiseksi, käyttäjän tarvitsee vain huolehtia oikeantyyppisen protokollan määrittämisestä, ja mahdollisen yhteyden luonnista.&lt;br /&gt;&lt;br /&gt;(TCP/IP on niin kutsuttu 'connected' protokolla, eli protokolla pitää vatsaanottajan ja lähettäjän välillä "yhteyttä auki", Ts. yhteyden muodostus, ylläpito ja lopettaminen tapahtuvat lähettämällä tietynlaisia ennaltasovittuja paketteja.  Mutta sockettien käyttäjän ei tarvitse näistä murehtia, alla olevat kerrokset [IP-pinkka] huolehtivat siitä).&lt;br /&gt;&lt;br /&gt;Koetan käydä läpi yksinkertaisen esimerkin (UNIX) socketien käytöstä. Käytän yksinkertaisuuden vuoksi "blokkaavia" funktioita (Ts. funktioita jotka pysäyttävät ohjelmakoodin ajon siksi aikaa kun odottavat socketin lukemisen/sockettiin kirjoittamisen olevan mahdollista). Ihan lopuksi saatan käydä läpi esimerkin select() funktion käytöstä, tutkittaessa onko uutta dataa saatavilla. HUOM: Windows ja UNIX maailmassa select() funktiossa on pieniä eroja. Windowsissa select():iä voi käyttää vain sockettien kanssa, tutkittaessa socketin tilaa. UNIX maailma ei erittele socketia muista 'file descriptoreista', ja select() funktiota voidaankin UNIX koneissa käyttää myös esim. tiedostojen tilan tutkimiseen.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;HUOM! Esimerkkikoodeja ei taata bugittomiksi, koetan toki parhaani mukaan varmistaa, että koodit kääntyvät, ja toimivat, mutta kaikenkarvaiset pikku (ja isommat) bugit ovat toki mahdollisia. Tarkoitus tosin olikin esitellä vain perusperiaatteet, mutta kunhan saan aikaiseksi, koetan kääntää ja ajaa esimerkit ja korjailla pahimmat mokat :)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Esimerkki 1: TCP/IP Datayhteyden muodostus "kuuntelevaan" koneeseen(Client program)&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;br /&gt;&lt;span style="font-size:80%;"&gt;&lt;br /&gt;#include &amp;lt;sys/socket.h&amp;gt; //otsikkotiedosto jossa esitellään socket ohjelmoinnissa tarpeellisia tietotyyppejä ja funktioita.&lt;br /&gt;#include &amp;lt;unistd.h&amp;gt; //yleishyödyllinen kirjasto&lt;br /&gt;#include &amp;lt;stdio.h&amp;gt; //perus I/O toiminnot&lt;br /&gt;#include &amp;lt;stdlib.h&amp;gt; //malloc&lt;br /&gt;#include &amp;lt;arpa/inet.h&amp;gt; //verkkoliikenne matskua&lt;br /&gt;#include &amp;lt;string.h&amp;gt; //memcpy&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt;    int sock; //socketti kahva; vertaa tiedoston avaaminen =&amp;gt; kahva&lt;br /&gt;    struct sockaddr_in osoite = {0};&lt;br /&gt;    void *lahetettava_data;&lt;br /&gt;    int luettu_koko;&lt;br /&gt;    unsigned short int serverin_portti=12345;&lt;br /&gt;    void *luettu_data;&lt;br /&gt;    int datan_koko;&lt;br /&gt;    char lahteva_sanoma[]="fooooo";&lt;br /&gt;    int luvun_paluuarvo;&lt;br /&gt;&lt;br /&gt;/*&lt;br /&gt;  aseta socketin "perhe" AF_INET:iksi (connections using ethernet), &lt;br /&gt;  tyyppi SOCK_STREAM:iksi (stream soketti, ei paketti data SOCK_DGRAM&lt;br /&gt;  kuten UDP:ssä), protocolla TCP:ksi ja luo socketti.&lt;br /&gt;*/&lt;br /&gt;    if( (sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) ) &amp;lt; 0 )&lt;br /&gt;    {&lt;br /&gt;      //virhekäsittely, socketin luonti epäonnistui&lt;br /&gt;    }&lt;br /&gt;/*&lt;br /&gt;  Täytä vastaanottajan tiedot, kuten portti ja IP sekä sockettisi &lt;br /&gt;  perhe sockaddr_in tyyppiseen structiin.&lt;br /&gt;*/&lt;br /&gt;    osoite.sin_family      = AF_INET;&lt;br /&gt;/*&lt;br /&gt;  inet_addr() funktio muuttaa osoitteen struktiin sopivaan binary &lt;br /&gt;  muotoon, ja kääntää bittijärjestyksen "network byte orderiin"&lt;br /&gt;  (Eniten merkitsevä tavu ensin. Normaalissa PC arkkitehtuurissahan&lt;br /&gt;  käytetään Ns. "Little endian" arkkitehtuuria, missä eniten &lt;br /&gt;  merkitsevät tavut tulevat viimeisenä.&lt;br /&gt;*/&lt;br /&gt;    osoite.sin_addr.s_addr = inet_addr("127.0.0.1"); &lt;br /&gt;/*&lt;br /&gt;  htons() eli host to network short muuttaa short integer tyypin&lt;br /&gt;  luvun bittijärjestyksen "network byte orderiin".&lt;br /&gt;*/&lt;br /&gt;  osoite.sin_port        = htons(serverin_portti);&lt;br /&gt;&lt;br /&gt;/*&lt;br /&gt;  Yhdistä socketti. Tässä vaiheessa IP-pinkka aloittaa neuvottelut&lt;br /&gt;  vastapelurin kanssa, ja mikäli connect kutsu palauttaa jotain muuta&lt;br /&gt;  kuin 0:n, se tarkoittaa, että yhteydenotto syystä tai toisesta &lt;br /&gt;  epäonnistui.&lt;br /&gt;*/&lt;br /&gt;    if( &lt;br /&gt;        connect&lt;br /&gt;        (&lt;br /&gt;           sock,&lt;br /&gt;           (struct sockaddr *)&amp;osoite,&lt;br /&gt;           sizeof(osoite)&lt;br /&gt;        ) &lt;br /&gt;        != 0 &lt;br /&gt;    )&lt;br /&gt;    {&lt;br /&gt;        //virheenkäsittely, sokettia ei voitu yhdistää.&lt;br /&gt;    }&lt;br /&gt;/*&lt;br /&gt;  Kun yhteys on pystyssä, voidaan varsinainen viestien lähettäminen&lt;br /&gt;  ja vastaanotto aloittaa: Tehdään siis viesti joka halutaan lähettää.&lt;br /&gt;  Tässävaiheessa on syytä muistuttaa, että STREAM socketit kuten &lt;br /&gt;  TCP:n tapauksessa, eivät takaa sitä, että koko kerralla lähetetty&lt;br /&gt;  sanoma tulee perille yhdellä kertaa ( yhdellä read() tai recv() &lt;br /&gt;  kutsulla). Siksi kun teet oman ohjelmasi, jossa haluat lähettää ja&lt;br /&gt;  vastaanottaa dataa sockettien kautta, esim. vaihtaessasi tietoa&lt;br /&gt;  prosessien välillä, on hyvä idea toteuttaa jokin oma protokolla.&lt;br /&gt;  Yksinkertaisimmillaan vain päättämällä, että heti jokaisen viestin&lt;br /&gt;  alussa on 32 bittiä (yksi integeri) joihin on talletettu lähetetyn&lt;br /&gt;  datan pituus. UDP-socketteja käytettäessä, pakettien saapumis- &lt;br /&gt;  järjestys taas saattaa olla eri kuin lähetysjärjestys.&lt;br /&gt;*/&lt;br /&gt;    lahetettava_data=malloc(sizeof(int)+7*sizeof(char));&lt;br /&gt;    if(NULL==lahetettava_data)&lt;br /&gt;    {&lt;br /&gt;        /*&lt;br /&gt;            Virhekäsittely, muistin allokointi datan lähettämiseksi &lt;br /&gt;            epäonnistui.&lt;br /&gt;        */&lt;br /&gt;    }&lt;br /&gt; /*&lt;br /&gt;   jätän koon ilmoittavan integerin viemän tilan huomioimatta sekä &lt;br /&gt;   lähetys että vastaanottopäässä &lt;br /&gt; */&lt;br /&gt;    datan_koko=7*sizeof(char);&lt;br /&gt;    //Kopioi lähetetyn datan koko, lähetettävän datan alkuun.&lt;br /&gt;    memcpy(lahetettava_data,(void *)&amp;datan_koko, sizeof(int));&lt;br /&gt;    //kopioi varsinainen viesti heti koon perään.&lt;br /&gt;    memcpy&lt;br /&gt;    (&lt;br /&gt;        (void*)((char *)lahetettava_data)+sizeof(int),&lt;br /&gt;        lahteva_sanoma,7*sizeof(char)&lt;br /&gt;    );&lt;br /&gt;    //Tähän olisi kannattanut käyttää structia :)&lt;br /&gt;&lt;br /&gt;    //lähetä varsinainen viesti&lt;br /&gt;    write(sock, lahetettava_data, datan_koko+sizeof(int));&lt;br /&gt;    /*&lt;br /&gt;    varaa tilaa luettavalle datalle.&lt;br /&gt;    Luetaan ensin ensimmäiset 32 bittiä, ja tulkitaan se integerinä&lt;br /&gt;    joka kertoo viestin koon. Tämän jälkeen luetaan loppu viesti.&lt;br /&gt;    */&lt;br /&gt;&lt;br /&gt;    datan_koko=recv(sock, (void *)&amp;luettu_koko, sizeof(int), 0)&lt;br /&gt;    if(datan_koko&amp;lt;sizeof(int))&lt;br /&gt;    {&lt;br /&gt;    /*&lt;br /&gt;      virhekäsittely, luku palautti vähemmän kuin 4 tavua &lt;br /&gt;     (sizeof(int)). katso palauttiko recv 0, tai negatiivisen luvun,&lt;br /&gt;     ja mikäli palautti tapahtui virhe tai yhteys suljettiin. Mikäli&lt;br /&gt;     luku on positiivinen, mutta pienempi kuin sizeof(int) (ei pitäisi&lt;br /&gt;     olla mahdollista!), lue uudelleen, ja aseta luettavaksi määräksi&lt;br /&gt;     sizeof(int)-datan_koko jotta saat koko koon luettua. &lt;br /&gt;    */&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // Luetaan nyt varsinainen data:&lt;br /&gt;    luettu_data=malloc(luettu_koko);&lt;br /&gt;    if(NULL==luettu_data)&lt;br /&gt;    {&lt;br /&gt;        //virhekäsittely.&lt;br /&gt;    }&lt;br /&gt;    datan_koko=0;&lt;br /&gt;    while(datan_koko&amp;lt;luettu_koko)&lt;br /&gt;    {&lt;br /&gt;        if( &lt;br /&gt;            ( luvun_paluuarvo=recv&lt;br /&gt;                (&lt;br /&gt;                    sock,&lt;br /&gt;                    (void *)(((char *) luettu_data) + datan_koko),&lt;br /&gt;                    luettu_koko-datan_koko, 0&lt;br /&gt;                )&lt;br /&gt;            ) &lt;br /&gt;            &amp;lt;= 0&lt;br /&gt;        )&lt;br /&gt;        {&lt;br /&gt;            /*&lt;br /&gt;              virhekäsittely, recv palautti 0 tai negatiivisen luvun.&lt;br /&gt;              0==yhteys suljettu, -1==virhe. Virhetapauksessa tutki&lt;br /&gt;              errno saadaksesi tarkemman virhenumeron.&lt;br /&gt;            */&lt;br /&gt;&lt;br /&gt;        }&lt;br /&gt;        datan_koko+=luvun_paluuarvo;&lt;br /&gt;    }&lt;br /&gt;    //luetun datan tulisi nyt olla luettu_data muuttujassa.&lt;br /&gt;    /*&lt;br /&gt;      Huomaa, tällainen printti on vaarallinen. Mikäli lähetetty data&lt;br /&gt;      ei pääty NULL characteriin, tapahtuu luettaessa ylivuoto!&lt;br /&gt;    */&lt;br /&gt;    &lt;br /&gt;    printf("Client - vastaanotettu: %s",(char *)luettu_data);&lt;br /&gt;    //Lopuksi on hyvä käytäntä kutsua &lt;br /&gt;    close(sock);&lt;br /&gt;    /*&lt;br /&gt;      vaikkakin käyttöjärjestelmä toki sulkee roikkumaan jääneet &lt;br /&gt;      yhteydet, viimeistään prosessin päättyessä.&lt;br /&gt;    */&lt;br /&gt;    return EXIT_SUCCESS;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Esimerkki 2: Tulevia yhteyksiä kuuntelevan TCP/IP socketin teko (Server program)&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;br /&gt;&lt;span style="font-size:80%;"&gt;&lt;br /&gt;#include &amp;lt;sys/socket.h&amp;gt; //otsikkotiedosto jossa esitellään socket ohjelmoinnissa tarpeellisia tietotyyppejä ja funktioita.&lt;br /&gt;#include &amp;lt;unistd.h&amp;gt; //yleishyödyllinen kirjasto&lt;br /&gt;#include &amp;lt;stdio.h&amp;gt; //perus I/O toiminnot&lt;br /&gt;#include &amp;lt;stdlib.h&amp;gt; //malloc&lt;br /&gt;#include &amp;lt;arpa/inet.h&amp;gt; //verkkoliikenne matskua&lt;br /&gt;#include &amp;lt;string.h&amp;gt; //memcpy&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;/*&lt;br /&gt;  Client koodissa olevasta viestin luonnin hankaluudesta &lt;br /&gt;  viisastuneena, luomme serveri koodiin viesti structin:&lt;br /&gt;*/&lt;br /&gt;#define MAX_DATA 1024&lt;br /&gt;&lt;br /&gt;typedef struct viesti&lt;br /&gt;{&lt;br /&gt;    int viestin_koko;&lt;br /&gt;    void data[MAX_DATA];&lt;br /&gt;}viesti;&lt;br /&gt;&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt;    /*&lt;br /&gt;      socketti johon ulkopuolelta tulevat yhteydet saapuu kahva; &lt;br /&gt;      vertaa tiedoston avaaminen =&amp;gt; kahva&lt;br /&gt;    */&lt;br /&gt;    int kuunteleva_socketti;&lt;br /&gt;    /*&lt;br /&gt;      Socketti joka luodaan varsinaista kommunikointia varten kun&lt;br /&gt;      serveri hyväksyy kuuntelevaan sockettiin tulevan yhteyden. Näin&lt;br /&gt;      kuunteleva socketti vapautuu kuuntelemaan uusia yhteyspyyntöjä.&lt;br /&gt;    */&lt;br /&gt;    int yhdistetty_socketti;&lt;br /&gt;&lt;br /&gt;    struct sockaddr_in oma_osoite = {0};&lt;br /&gt;    struct sockaddr_in clientin_osoite ={0};&lt;br /&gt;    socklen_t osoitteen_pituus;&lt;br /&gt;    unsigned short int kuunneltava_portti=12345;&lt;br /&gt;    int luettu_koko;&lt;br /&gt;    int datan_koko;&lt;br /&gt;    viesti vastaanotettu_sanoma;&lt;br /&gt;    viesti vastaus_sanoma;&lt;br /&gt;    int luvun_paluuarvo;&lt;br /&gt; &lt;br /&gt;//Luodaan socketti jota kuunnellaan, samoin kuin clientin tapauksessa.&lt;br /&gt;    if( (kuunteleva_socketti=socket(AF_INET, SOCK_STREAM, 0)) &lt; 0)&lt;br /&gt;    {&lt;br /&gt; //virhekäsittely, socketin luonti epäonnistui&lt;br /&gt;    }&lt;br /&gt;// Sitten asetetaan oman serverin tiedot sockaddr_in structiin&lt;br /&gt;    oma_osoite.sin_family = AF_INET;&lt;br /&gt;    oma_osoite.sin_addr.s_addr = htonl(INADDR_ANY);&lt;br /&gt;    oma_osoite.sin_port = htons(kuunneltava_portti);&lt;br /&gt;    /*&lt;br /&gt;      Tässävaiheessa client koodissa luotiin yhteys kuuntelevaan&lt;br /&gt;      koneeseen. Nyt kun perustiedot on saatu kasaan, serverikoneessa&lt;br /&gt;      sidotaan luotu socketti porttiin jota tahdotaan kuunnella, ja &lt;br /&gt;      aletaan "kuuntelemaan" yhteydenottoja.&lt;br /&gt;      Huomaa, että 'listen' kutsu ei ole blokkaava! Eli ohjelman &lt;br /&gt;      suoritus jatkuu välittömästi eteenpäin, riippumatta siitä &lt;br /&gt;      tuleeko clientiltä yhteydenottoa vai ei!&lt;br /&gt;    */&lt;br /&gt;    if &lt;br /&gt;    (&lt;br /&gt;        bind&lt;br /&gt;       (&lt;br /&gt;           kuunteleva_socketti,&lt;br /&gt;           (struct sockaddr *) &amp;oma_osoite,&lt;br /&gt;           sizeof(oma_osoite)&lt;br /&gt;       ) &lt;br /&gt;       &amp;lt; 0&lt;br /&gt;    )&lt;br /&gt;    {&lt;br /&gt;        //virhekäsittely, socketin sitominen porttiin epäonnistui.&lt;br /&gt;    }&lt;br /&gt;    /*&lt;br /&gt;      listen funktion toinen parametri, kertoo montako clientin &lt;br /&gt;      yhteyttä serveri pitää 'jonossa' odottamassa hyväksyntää.&lt;br /&gt;    */&lt;br /&gt;    if(-1==listen(kuunteleva_socketti, 1))&lt;br /&gt;    {&lt;br /&gt;        //Virhekäsittely, listen epäonnistui.&lt;br /&gt;    }&lt;br /&gt;    /*&lt;br /&gt;      Nyt tapahtuu varsinainen yhteyksien vastaanotto. Huomaa, accept&lt;br /&gt;      on blokkaava kutsu, ohjelman suoritus pysähtyy kunnes ensimmäinen&lt;br /&gt;      yhteydenottopyyntö saapuu&lt;br /&gt;    */&lt;br /&gt;    osoitteen_pituus = sizeof(clientin_osoite);&lt;br /&gt;    &lt;br /&gt;    yhdistetty_socketti = accept&lt;br /&gt;    (&lt;br /&gt;        kuunteleva_socketti,&lt;br /&gt;        (struct sockaddr *) &amp;clientin_osoite,&lt;br /&gt;        &amp;osoitteen_pituus&lt;br /&gt;    );&lt;br /&gt;    if (yhdistetty_socketti &amp;lt; 0) &lt;br /&gt;    {&lt;br /&gt;        //virhekäsittely, yhteyden hyväksyntä epäonnistui!&lt;br /&gt;    }&lt;br /&gt;    /*&lt;br /&gt;      Huomaa, accept päivittää client_ddress structiin clientin tiedot,&lt;br /&gt;      joten voit jossain määrin valvoa mistä tulevien yhteydenottojen&lt;br /&gt;      kanssa tahdot jatkaa, ja mitkä tahdot lopettaa saman tien.&lt;br /&gt;    */&lt;br /&gt;        //vastaanota ensimmäiset 32 bittiä (viestin koko)&lt;br /&gt;    luettu_koko=recv(&lt;br /&gt;        yhdistetty_socketti,&lt;br /&gt;        (void *)&amp;vastaanotettu_sanoma.viestin_koko,&lt;br /&gt;        sizeof(int),&lt;br /&gt;        0&lt;br /&gt;    );&lt;br /&gt;    if(luettu_koko&amp;lt;(int)sizeof(int))&lt;br /&gt;    {&lt;br /&gt;        //virhekäsittely&lt;br /&gt;    }&lt;br /&gt;    if(vastaanotettu_sanoma.viestin_koko&gt;MAX_DATA-sizeof(int))&lt;br /&gt;    {&lt;br /&gt;        printf("Liian iso sanoma =&gt; lopetan");&lt;br /&gt;        return EXIT_FAILURE;&lt;br /&gt;    }&lt;br /&gt;    //vastaanota loppu viesti:&lt;br /&gt;    &lt;br /&gt;&lt;br /&gt;    datan_koko=0;&lt;br /&gt;    while(datan_koko&amp;lt;vastaanotettu_sanoma.viestin_koko)&lt;br /&gt;    {&lt;br /&gt;        if( &lt;br /&gt;            (luvun_paluuarvo=recv&lt;br /&gt;            (&lt;br /&gt;                yhdistetty_socketti,&lt;br /&gt;                ((char *)vastaanotettu_sanoma.data)+datan_koko,&lt;br /&gt;                vastaanotettu_sanoma.viestin_koko-datan_koko,&lt;br /&gt;                0&lt;br /&gt;            )&lt;br /&gt;            ) &lt;br /&gt;            &amp;lt;= 0&lt;br /&gt;        )&lt;br /&gt;        {&lt;br /&gt;        /*&lt;br /&gt;          virhekäsittely, recv palautti 0 tai negatiivisen luvun. &lt;br /&gt;          0==yhteys suljettu, -1==virhe. Virhetapauksessa tutki errno&lt;br /&gt;          saadaksesi tarkemman virhenumeron.&lt;br /&gt;        */&lt;br /&gt;&lt;br /&gt;        }&lt;br /&gt;        datan_koko+=luvun_paluuarvo;&lt;br /&gt;        luvun_paluuarvo=0;&lt;br /&gt;    }&lt;br /&gt;    //luetun datan tulisi nyt olla luvun_paluuarvo muuttujassa.&lt;br /&gt;    printf("Saatu: %s",(char *)vastaanotettu_sanoma.data);&lt;br /&gt;    // Läheta vastaus&lt;br /&gt;&lt;br /&gt;    &lt;br /&gt;    memset(vastaus_sanoma.data,0,MAX_DATA);&lt;br /&gt;    vastaus_sanoma.viestin_koko=4*sizeof(char);&lt;br /&gt;    strncpy((char *)vastaus_sanoma.data,"bar",4*sizeof(char));&lt;br /&gt;    write&lt;br /&gt;    (&lt;br /&gt;        yhdistetty_socketti,&lt;br /&gt;        (void *)&amp;vastaus_sanoma,&lt;br /&gt;        sizeof(int)+4*sizeof(char)&lt;br /&gt;    );&lt;br /&gt;    //Lopuksi on hyvä käytäntä kutsua &lt;br /&gt;    close(yhdistetty_socketti);&lt;br /&gt;    close(kuunteleva_socketti);&lt;br /&gt;    return EXIT_SUCCESS;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Pari sanaa vielä...&lt;br /&gt;Ehkä yleisin tapa toteuttaa serveriohjelmat on, että listen funktion jälkeen tehdään ikilooppi, jossa kutsutaan acceptia. Ja aina kun uusi yhteys tulee, 'forkataan' uusi prosessi (tai tehdään uusi threadi) jossa yhteyttä käytetään, alkuperäisen prosessin aloittaessa loopissa uuden kierroksen valmiina vastaanottamaan seuraavan sisääntulevan yhteyden.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Esimerkki 3: Select()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Ja vielä lupaamani select() esimerkki:&lt;br /&gt;Allaolevalle funktiolle annetaan tutkittava socketti, ja se hyödyntää select() funktiota tarkistaakseen onko uutta dataa saatavilla vai ei. select():iä voi myös käyttää tutkimaan voiko sockettiin kirjoittaa, tai onko socketissa jokin poikkeus käsiteltäväksi. Tähän select funktio käyttää fd_set tyyppisiä olioita, siten että luettavuuden, kirjoitettavuuden ja poikkeuksen tarkistamiseksi on määritettävä oma fd_setti. Seuraavassa esimerkissä jätän kirjoitus ja poikkeus setit läpikäymättä, sillä kirjoitussetti on samanlainen kuin lukusettikin, ja poikkeukset taas menevät pitkälle tämän blogiartikkelin ulkopuolelle.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;br /&gt;&lt;span style="font-size:80%;"&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;int onko_uutta_dataa_luettavissa(int sock)&lt;br /&gt;{&lt;br /&gt;    //Tee fd_set lukemiselle&lt;br /&gt;    fd_set fdset_luku;&lt;br /&gt;&lt;br /&gt;    int paluuarvo=-1;&lt;br /&gt;    /*&lt;br /&gt;      select() kutsun voi asettaa blockkaamaan haluamakseen aikaa,&lt;br /&gt;      määrittämällä blokkaus ajan timeval tyyppiseen structiin&lt;br /&gt;    */&lt;br /&gt;    struct timeval time;&lt;br /&gt;    //alustetaan tutkittava fd_setti 'ei mitään uutta' tilaan&lt;br /&gt;    FD_ZERO(&amp;fdset_luku);&lt;br /&gt;    //sidotaan tutkittava socketti fdsettiin&lt;br /&gt;    FD_SET(sock,&amp;fdset_luku);&lt;br /&gt;    /*&lt;br /&gt;      koska en halua selectin blokkaavan ohjelman ajoa,&lt;br /&gt;      nollaan timeval structin&lt;br /&gt;    */&lt;br /&gt;    memset(&amp;time,0,sizeof(time));&lt;br /&gt;    //kutsutaan selectiä asettaen muut kuin luku fdsetin nollaksi.&lt;br /&gt;    paluuarvo=select(sock+1,&amp;fdset_luku,0,0,&amp;time);&lt;br /&gt;    if(paluuarvo==-1)&lt;br /&gt;    {&lt;br /&gt;        //virhekäsittely, select epäonnistui!&lt;br /&gt;    }&lt;br /&gt;    //luetaan fdsetistä onko socketissa uutta dataa odottamassa.&lt;br /&gt;    if(FD_ISSET(sock,&amp;fdset_luku))&lt;br /&gt;    {&lt;br /&gt;        //Uutta dataa saatavilla!!!&lt;br /&gt; return 1;&lt;br /&gt;    }&lt;br /&gt;    //Ei uutta luettavaa :(&lt;br /&gt;    return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Loppuun vielä lyhyehkö yhteenveto winsock (windows sockettien) ja UNIX sockettien eroista:&lt;br /&gt;&lt;br /&gt;Windowsissa sockettien esittely tapahtuu kirjastossa winsock.h, ei sys/socket.h&lt;br /&gt;Windowsissa socketin kahva ei ole pelkkä integer, vaan tyyppiä SOCKET&lt;br /&gt;Windowsissa ennen socket kirjaston käyttöönottoa tulee ajaa sokettikirjastolle startti. Se tapahtuu tekemällä seuraavat temput:&lt;br /&gt;&lt;br /&gt;1. luo muuttuja&lt;br /&gt;WSADATA wsadata;&lt;br /&gt;2. aja funktio WSAStartup ja varmista että sen paluuarvo ei ole SOCKET_ERROR&lt;br /&gt;WSAStartup(MAKEWORD(2, 2), &amp;wsadata);&lt;br /&gt;&lt;br /&gt;Windowsissa socketti suljetaan functiolla closesocket() ei close()&lt;br /&gt;Lopuksi windowsissa ajetaan funktio:&lt;br /&gt;WSACleanup()&lt;br /&gt;&lt;br /&gt;Muuta huomioitavaa:&lt;br /&gt;Windowsissa select():iä voi käyttää vain sockettien tutkimiseen.&lt;br /&gt;&lt;br /&gt;Ja vielä viimeiset sanat, windows puolella sockettien käyttöä pääsee helposti kokeilemaan asentamalla ilmaisen DEV-CPP IDE:n (integrated developement environment == kääntäjä, linkkeri ja koodi editori samassa paketissa), ja asentamalla help valikon päivitysten kautta socket lisäosan. Linux maailmassa socketit kuuluvatkin vakiokalustoon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4818234838483192587-2863096244818440859?l=c-ohjelmoijanajatuksia.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://c-ohjelmoijanajatuksia.blogspot.com/feeds/2863096244818440859/comments/default' title='Lähetä kommentteja'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4818234838483192587&amp;postID=2863096244818440859' title='1 kommenttia'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/2863096244818440859'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4818234838483192587/posts/default/2863096244818440859'/><link rel='alternate' type='text/html' href='http://c-ohjelmoijanajatuksia.blogspot.com/2008/07/socketit-tapa-vlitt-tietoa.html' title='Socketit - tapa välittää tietoa.'/><author><name>Maz</name><uri>http://www.blogger.com/profile/03774313944609044806</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry></feed>
