j j j j j k k k k l l l ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s d f l ; ; l k d d s a s s d d d f d s a k j k l ; k j d k l a ; s s k k l ; a a s d f j k f j f j j j s k a ; l k j k s l d k s ; a k d j s s d d d s s s a a a a k k k a l l l l f f f f d d d s s s a a a j j j j j k k k k l l l ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s d f j j j j j k k k k l l l ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s d f d d s a s s d d d f d s a k j k l ; k j d k l a ; s s k k l ; a a s d f j k f j f j j j j s k a ; l k j k s l d k s ; a k d j s s d d d s s s a a a a k k k a l l l l f f f f d d d s s s a a a l l a s j k l ; j k l ; ; l k j a s ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s d f j j j j j k k k k l l l ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s d f j j j j j k k k k l l l ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s g h j j j j j k k k k l l l ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s d f l ; ; l k d d s a s s d d d f d s a k j k l ; k j d k l a ; s s k k l ; a a s d f j k f j f j j j j s k a ; l k j k s l d k s ; a k d j s s d d d s s s a a a a k k k a l l l l f f f f d d d s s s a a a s j j j j j k k k k l l l ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s d f l ; ; l k d d s a s s d d d f d s a k j k l ; k j d k l a ; s s k k l ; a a s d f j k f j f j j j j s k a ; l k j k s l d k s ; a k d j s s d d d s s s a a a a k k k a l l l l f f f f d d d s s s a a a j j j j j k k k k l l l ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s d f j j j j j k k k k l l l ; ; ; f f f d d d s s a a a j kl ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s d f d d s a s s d d d f d s a k j k l ; k j d k l a ; s s k k l ; a a s d f j k f j f j j j j s k a ; l k j k s l d k s ; a k d j s s d d d s s s a a a a k k k a l ll l f f f f d d d s s s a a a l l a s j k l ; j k l ; ; l k j a s ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s d f j j j j j k k k k l l l ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s d f j j j j j k k k k l l l ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s g h j j j j j k k k k l l l ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s d f l ; ; l k d d s a s s d d d f d s a k j k l ; k j d k l a ; s s k k l ; a a s d f j k f j f j j j j s k a ; l k j k s l d k s ; a k d j s s d d d s s s a a a a k k k a l l l l f f f f d d d s s s a a a s j jj jj kk kk ll l; ;; ff fd dd ss sa aa jk l; fd sa fj kl la sj kl ;j kl ;; lk ja sd fl ;; lk dd sa ss dd df ds ak jk l; kj dk la ;s sk kl ;a as df jk fj fj jj js ka ;l kj ks ld ks ;a kd js sd dd ss sa aa ak kk al ll lf ff fd dd ss sa aa lk ja sd fl ;; lk dd sa ss dd df ds ak jk l; kj dk la ;s sk kl as df jk fj fj jj j jj jj kk kk ll l; ;; ff fd dd ss sa aa jk l; fd sa fj kl la sj kl ;j kl ;; lk ja sd fj jj jj kk kk ll l; ;; ff fd dd ss sa aa jk l; fd sa fj kl la sj kl ;j kl ;; lk ja s; ;; ff fd dd ss sa aa jk l; fd sa fj kl la sj kl ;j kl ;; lk ja sd f s ; a k d j s s d d d s s s a a a a k k k a l l l l f f f f d d d s s s a a a s dd sa ss dd df ds ak jk l; kj dk la ;s sk kl ;a as df jk fj fj jj js ka ;l kj ks ld ks ;a kd js sd dd ss sa aa ak kk al ll lf ff fd dd ss sa aa ll as jk l; jk l; ;l kj as ;; ;f ff dd ds ss aa aj kl ;f ds af jk ll as jk l; jk l; ;l kj as df j jj jj kk kk ll l; ;; ff fd dd ss sa aa jk l; fd sa fj kl la sj kl ;j kl ;; lk ja sd fj jj jj kk kk ll l; ;; ff fd dd ss sa aa jk l; fd sa fj kl la sj kl ;j kl ;; lk ja s; ;; ff fd dd ss sa aa jk l; fd sa fj kl la sj kl ;j kl ;; lk ja sg h jj jj jk kk kl ll ;; ;f ff dd ds ss aa aj kl ;f ds af jk ll as jk l; jk l; ;l kj as df l; ;l kd ds as sd dd fd sa kj kl ;k jd kl a; ss kk l; aa sd fj kf jf jj jj sk a; lk jk sl dk s; ak dj ss dd ds ss aa aa kk ka ll ll ff ff dd ds ss aa as jj jj jk kk kl ll ;; ;f ff dd ds ss aa aj kl ;f ds af jk ll as jk l; jk l; ;l kj as df l; ;l kd ds as sd dd fd sa kj kl ;k jd kl a; ss kk l; aa sd fj kf jf jj jj sk a; lk jk sl dk s; ak dj ss dd ds ss aa aa kk ka ll ll ff ff dd ds ss aa a jj jj jk kk kl ll ;; ;f ff dd ds ss aa aj kl ;f ds af jk ll as jk l; jk l; ;l kj as df jj jj jk kk kl ll ;; ;f ff dd ds ss aa aj kl ;f ds af jk ll as jk l; jk l; ;l kj as ;; ;f ff dd ds ss aa aj kl ;f ds af jk ll as jk l; jk l; ;l kj as df d ds as sd dd fd sa kj kl ;k jd kl a; ss kk l; aa sd fj kf jf jj jj sk a; lk jk sl dk s; ak dj ss dd ds ss aa aa kk ka ll ll ff ff dd ds ss aa al la sj kl ;j kl ;; lk ja s; ;; ff fd dd ss sa aa jk l; fd sa fj kl la sj kl ;j kl ;; lk ja sd f jj jj jk kk kl ll ;; ;f ff dd ds ss aa aj kl ;f ds af jk ll as jk l; jk l; ;l kj as df jj jj jk kk kl ll ;; ;f ff dd ds ss aa aj kl ;f ds af jk ll as jk l; jk l; ;l kj as ;; ;f ff dd ds ss aa aj kl ;f ds af jk ll as jk l; jk l; ;l kj as gh j jj jj kk kk ll l; ;; ff fd dd ss sa aa jk l; fd sa fj kl la sj kl ;j kl ;; lk ja sd fl ;; lk dd sa ss dd df ds ak jk l; kj dk la ;s sk kl ;a as df jk fj fj jj js ka ;l kj ks ld ks ;a kd js sd dd ss sa aa ak kk al ll lf ff fd dd ss sa aa saldk slkdk akdf slal la ladk kadd dad kslskf l jfa fklsdk dkfhl a ald slkf la al al dfs asdh laskl la;s klsdkj sa;slkdl llassdfs ss sddfj jfkdsla;sl kdfksla; ;;;;a alskd df fdksl la la alal al fa fal fall fa flk fsdkl lskaf sdkfhsk alhdhgla aldgh alhss j j j k k k k l l l ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s d f l ; ; l k d d s a s s d d d f d s a k j k l ; k j d k l a ; s s k k l ; a a s d f j k f j f j j j j s k a ; l k j k s l d k s ; a k d j s s d d d s s s a a a a k k k a l l l l f f f f d d d s s s a a a ;aksl as as kf j jj jj kk kk ll l; ;; ff fd dd ss sa aa jk l; fd sa fj kl la sj kl ;j kl ;; lk ja sd fj jj jj kk kk ll l; ;; ff fd dd ss sa aa jk l; fd sa fj kl la sj kl ;j kl ;; lk ja s; ;; ff fd dd ss sa aa jk l; fd sa fj kl la sj kl ;j kl ;; lk ja sd f sll si sal sfkdsl dfd lla sdkasd dssdf fddssa ssall;; ;;ldk alskdh kkksldf fdksls slldl dd sa ss dd df ds ak jk l; kj dk la ;s sk kl ;a as df jk fj fj jj js ka ;l kj ks ld ks ;a kd js sd dd ss sa aa ak kk al ll lf ff fd dd ss sa aa ll as jk l; jk l; l l f f f f d d d s s s a a a l l a s j k l ; j k l ; ; l k j a s ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s d f j j j j k k k k l l l ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s d f j j j j j k k k k l l l ; ; ; f f f d d d s s lals j j j j j k k k k l l l ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s d f l ; ; l k d d s a s s d d d f d s a k j k l ; k j d k l a ; s s k k l ; a a s d f j k f j f j j j j s k a ; l k j k s l d k s ; a k d j s s d d d s s s a a a a k k k a l l l l f f f f d d d s s s a a a j j j j j k k k k l l l ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s d f j j j j j k k k k l l l ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s d f d d s a s s d d d f d s a k j k l ; k j d k l a ; s s k k l ; a a s d f j k f j f j j j j s k a ; l k j k s l d k s ; a k d j s s d d d s s s a a a a k k k a l l h s sj jj jj kk kk ll l; ;; ff fd dd ss sa aa jk l; fd sa fj kl la sj kl ;j kl ;; lk ja sd fj jj jj kk kk ll l; ;; ff fd dd ss sa aa jk l; fd sa fj kl la sj kl ;j kl ;; lk ja s; ;; ff fd dd ss sa aa jk l; fd sa fj kl la sj kl ;j kl ;; lk ja sd f s ; a k d j s s d d d s s s a a a a k k k a l l l l f f f f d d d s s s a a a s dd sa ss dd df ds ak jk l; kj dk la ;s sk kl ;a as df jk fj fj jj js ka ;l kj ks ld ks ;a kd js sd dd ss sa aa ak kk al ll lf ff fd dd ss sa aa ll as jk l; jk l; s juj ijoj puio pjuj ijoj puio pjuj ijoj jkll ;iop eiwo qpdu ieqw puio pjuu uior ewqd eaju jijo jpui opju jiwu ewqo ptio wpjo jpui opju jijo jpui opju uuio rewq deaj ujij ojpu iopj ujij ojpu iopj ujij ojpu iopj uuui orew qdea juji jojp uiop juji jojp uiop juji jojp uiop juuw uewq opti owpu iore wqde aiof dsas ssad fdjj kll; iope iwoq pdui eqwu ewqo ptio wpde ajuj ijoj puio pjuj iwue wqop tiow pjoj puio pjuj ijoj puio pjuu uior ewqd eaju jijo jpui oelo llsw lopj ujij ojpu iopj ujij ojpu iopj uuui orew qdea juji joje loll swlo puio pjuj ijoj puio pjuj ijoj puio pjuu wuew elol lswl oqop tiow puio rewq deai ofds asss adfd jjkl l;io peiw oqpd uieq wuew qopt iowp kide kide kide kide kide kide kide kide kide kide kide kide loll swlo elol lswl oelo llsw loas weop qpie ola; ouie ssla lsdk la;s lldf pjuj ijoj puio pjuj ijoj puio pjuu wuew elol lswl oqop tiow puio rewq deai ofds asss adfd jjkl l;io peiw oqpd uieq wuew qopt iowp kide kide kide kide kide ds d ef d deki deki deki deki deki deki delo llsw loel olls wloe loll swlo aswe opqp ieol a;ou iety oeka ;l kj k s ld ks ;a k d js sd dd s s sa aa ak k k al ll lf f f fd dd ss s a aa sal dk s lkdk akd f sl al l a la dk k add dad ksls kf l jfa fkl sdk dkfh l a ald slkf la al a l df s as dh l askl la; s kl sdkj sa; slkd l ll assd fs s s sd dfj jfkd sla; sl k dfks la; ;;;; a al skd df f dksl la la a lal al f a fa l fa ll f a fl k fs dkl lska f sd kfhs k al hdhg la a ldgh alh ssty tiop sawe qopu 'soe ioqp 'sa" ieoh goie opeo iuts uedk oepp qoei owie ladf ieoq eyto uwoe yrow dsof pqpw eopq uyta ytyy tyio ytyt yeyt yyyy ytyy yttt tttt tttt seit iowp tosh ghgh fhsd ioap lgio eiha ldog iioa hgkd ihdl sd;' ahl ahd/ lash ? ah gipe ?sha sie. flsh sdie '" s ahgi ehid soha heoi ehhg hish eyth sgys hdgi shds iths dhgj sdih dila hdgl hley itui weoh laso pdlh goei hghe tyah gl;a da;d kgie h" a sdh lask l la ;s k lsdk j sa ;slk dl l lass dfs ss s ddfj jfk dsla ;sl kdfk sla; ;;; ;a a lskd df fdks l la la alal al fa f al f all fa f lk f sdkl lsk af s dkfh sk a lhdh gla aldg h al hss tyti opsa weqo pu's oeio qp's a"ie ohgo ieop eoiu tsue dkoe ppqo eiow iela dfie oqey touw oeyr owds ofpq pweo pquy tayt yyty ioyt ytye ytyy yyyt yyyt tttt tttt ttse itio wpto shgh ghfh sdio aplg ioei hald joj puio pjuu uior ewqd eaju jijo jpui oelo llsw lopj ujij ojpu iopj ujij ojpu iopj uuui orew qdea juji joje loll swlo puio pjuj ijoj puio pjuj ijoj puio pjuu wuew elol lswl oqop tiow puio rewq deai ofds asss adfd jjkl l;io peiw oqpd uieq wuew qopt iowp kide kide kide kide kide kide kide kide kide kide kide kide loll swlo elol lswl oelo llsw loas weop qpie ola; ouie ssla lsdk la;s lldf pjuj ijoj puio pjuj i tu ieop qooe igho ioai edek idek idek idek idek idek idek idek idek idek idel olls wloe loll swlo elol lswl oasw eopq pieo la;o uies slal sdkl a;sl ldfp juji jojp uide kide kide kide kide kide kide kide kide kide kide loll swlo elol lswl oelo llsw loas weop qpie ola; ouie ssla lsdk la;s lldf pjuj ijoj puis lahs l;al sa;; ;alk d'ls hlh" ashg hl"L :'sa laDS FFou ofeh qipr o'wl opju jijo jpui opju jijo jpui opju uuio rewq deaj ujij ojel olls wlop uiop juji jojp uiop juji jojp uiop juuw uewe loll swlo qopt iowp uior ewqd eaio fdsa sssa dfdj jkll ;iop eiwo qpdu ieqw uewq opti owpk idek idek idek idek idek idek idek idek idek idek idek idel olls wloe loll swlo elol lswl oasw eopq pieo la;o uies slal sdkl a;sl ldfp juji jojp uiop juji jojp uiop tyti opsa weqo pu's oeio qp's a"ie ohgo ieop eoiu tsue dkoe ppqo eiow iela dfie oqey touw oeyr owds ofpq pweo pquy tayt yyty ioyt ytye ytyy yyyt yyyt tttt tttt ttse itio wpto shgh ghfh sdio aplg ioei hald jojp uiop juuu iore wqde ajuj ijoj puio elol lswl opju jijo jpui opju jijo jpui opju uuio rewq deaj ujij ojel olls wlop uiop juji jojp uiop juji jojp uiop juuw uewe loll swlo qopt iowp uior ewqd eaio fdsa sssa dfdj jkll ;iop eiwo qpdu ieqw uewq opti owpk idek idek idek idek idek idek idek idek idek idek idek idel olls wloe loll swlo elol lswl oasw eopq pieo la;o uies slal sdkl a;sl ldfp juji jojp uiop juji sssa dfdj jkll ;iop eiwo qpdu ieqw uewq opti owpk idek idek idek idek idek idek idek idek idek idek idek idel olls wloe loll swlo elol lswl oasw eopq pieo la;o uies slal sdkl a;sl ldfp juji jojp uiop juji tui eopq ooei ghoi oaie deki deki deki deki deki deki deki deki deki deki delo llsw loel olls wloe loll swlo aswe opqp ieol a;ou iess lals dkla ;sll dfpj ujij ojpu idek idek idek idek idek idek idek idek idek idek idel olls wloe loll swlo elol lswl oasw eopq pieo la;o uies slal sdkl a;sl ldfp juji jojp uisl ahsl ;als a;;; alkd 'lsh lh"a shgh l"L: 'sal aDSF Fouo fehq ipro 'wlo pjuj ijoj puio pjuj ijoj puio pjuu uior ewqd eaju jijo jelo llsw lopu iopj ujij ojpu iopj ujij ojpu iopj uuwu ewel olls wloq opti owpu iore wqde aiof dsas ssad fdjj kll; iope iwoq pdui eqwu ewqo ptio wpki deki deki deki deki deki deki deki deki deki deki deki delo llsw loel olls wloe loll swlo aswe opqp ieol a;ou iess lals dkla ;sll dfpj ujij ojpu iopj ujij ojpu iopt ytio psaw eqop u'so eioq p'sa "ieo hgoi eope oiut sued koep pqoe iowi eoqpd uieq wuew qopt iowp kide kide kide kide kide kide kide kide kide kide kide kide loll swlo elol lswl oelo llsw kdgh alldlal hdkds ald aldslsss s op juuu iore wqde ajuj ijoj puio pjuj ijoj puio pjuj ijoj puio pjuu wuew qopt iowp uior ewqd eaio fdsa sssa dfdj jkll ;iop eiwo qpdu ieqw uewq opti owpd eaju jijo jpui opju jiwu ewqo ptio wpjo jpui opju jijo jpui opju uuio rewq deaj ujij ojpu ioel olls wlop juji jojp uiop juji jojp uiop juuu iore wqde ajuj ijoj elol lswl opui opju jijo jpui opju jijo jpui opju uwue welo llsw loqo ptio wpui orew qdea iofd sass sadf djjk ll;i opei woqp duie qwue wqop tiow pkid ekid ekid ekid ekid ekid ekid ekid ekid ekid ekid ekid elol lswl oelo llsw loel olls wloa sweo pqpi eola ;oui essl alsd kla; slld fpju jijo jpui opju jijo jpui opju uwue welo llsw loqo ptio wpui orew qdea iofd sass sadf djjk ll;i opei woqp duie qwue wqop tiow pkid ekid ekid ekid ekid edsd ef n,dl ladc nzdi eytz odig n,dl ladc nzdi eytz odig n,dl ladc nzdi eytz odig tyea eiqt opto n,dl ladc nzdi eytz odig ,,<. adlf .alh /sld h?gh itup oena lhdg llhe n,dl ladc nzdi eytz odig ,,xh glxa hieo pqo oqpd uieq wuew qopt iowp kide kide kide kide kide kide kide kide kide kide kide kide loll swlo elol lswl oelo llsw n,dl ladc nzdi eytz odig lsal glls dlfl laia mint heom tnth eolo ghti sthi ngis eral lyve ryst arsg hall dgho epot ujls ahla hdgj apeo poeo oihg opeo hgoe hahl bclm xbx, cnb, hslg as>s hlas llad ieas oelh /slg ahas liel hghl adfi eytg hbnh lgie thtr yopj uuui orew qdea juji jojp uiop juji jojp uiop juji jojp uiop juuw uewq opti owpu iore wqde aiof dsas ssad fdjj kll; iope iwoq pdui eqwu ewqo ptio wpde ajuj ijoj puio pjuj iwue wqop tiow pjoj puio pjuj ijoj puio pjuu uior ewqd eaju jijo jpui oelo llsw lopj ujij ojpu iopj ujij ojpu iopj uuui orew qdea juji joje loll swlo puio pjuj ijoj puio pjuj ijoj puio pjuu wuew elol lswl oqop tiow puio rewq deai ofds asss adfd jjkl l;io peiw oqpd uieq wuew qopt iowp kide kide kide kide kide kide kide kide kide kide kide kide loll swlo elol lswl oelo llsw loas weop qpie ola; ouie ssla lsdk la;s lldf pjuj ijoj puio pjuj ijoj puio pjuu wuew elol lswl oqop tiow puio rewq deai ofds asss adfd jjkl l;io peiw oqpd uieq wuew qopt iowp kide kide kide kide kide dsde fdille ahdlapeotoa hakd diepoe dishingn sioepgn cn.zpeop sdkghpehslapl ahdogp ahdkg saope ad ad jjk ek iopu uiopead n ,dll adcn zdie ytzo dign ,dll adcn zdie ytzo dign ,dll adcn zdie ytzo digt yeae iqto pton ,dll adcn zdie ytzo dig, ,<.a dlf. alh/ sldh ?ghi tupo enal hdgl lhen ,dll adcn zdie ytzo dig, ,xhg lxah ieop qo paldfhl paldfhl paldfhlpa l dfhl jgdsfnri lsoe paaosti sdhlsgpo akhdk eoepo pdo akd skdlae dhg donc .d>>d <sh lasl ladi easo elh/ slga hasl ielh ghla dfie ytgh bnhl giet htry lanc.dlei hgoei tiehal kdjgi paldfhl lsoe paaosti sdhlsgpo akhdk eoepo pdo j j j k k k k l l l ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s d f l ; ; l k d d s a s s d d d f d s a k j k l ; k j d k l a ; s s k k l ; a a s d f j k f j f j j j j s k a ; l k j k s l d k s ; a k d j s s d d d s s s a a a a k k k a l l l l f f f f d d d s s s a a a j j j j j k k k k l l l ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s d f j j j j j k k k k l l l ; ; ; f f f d d d s s a a a j kl slk n,dl ladc nzdi eytz odig ,,xh glxa hieo pqo oqpd uieq wuew qopt iowp kide kide kide kide kide kide kide kide kide kide kide kide loll swlo elol lswl oelo llsw n,dl ladc nzdi eytz odig lsal glls dlfl laia mint heom tnth eolo ghti sthi ngis eral lyve ryst arsg hall dgho epot ujls ahla hdgj apeo poeo oihg opeo hgoe hahl j j j j j k k k k l l l ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s d f l ; ; l k d d s a s s d d d f d s a k j k l ; k j d k l a ; s s k k l ; a a s d f j k f j f j j j j s k a ; l k j k s l d k s ; a k d j s s d d d s s s a a a a k k k a l l l l f f f f d d d s s s a a a j j j j j k k k k l l l ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s d f j j j j j k k k k l l l ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s ; ; ; f f f d d d s s s a a a j k l ; f d s a f j k l l a s j k l ; j k l ; ; l k j a s d f d d s a s s d d d f d s a k j k l ; k j d k l a ; s s k k l ; a a s d f j k f j f j j j j s k a ; l k j k s l d k s ; a k d j s s d d d s s s a a a a k k k a l l h s sj jj jj kk kk ll l; ;; ff fd dd ss sa aa jk l; fd sa fj kl la sj kl ;j kl ;; lk ja sd fj jj jj kk kk ll l; ;; ff fd dd ss sa aa jk l; fd sa fj kl la sj kl ;j kl ;; lk ja s; ;; ff fd dd ss sa aa jk l; fd sa fj kl la sj kl ;j kl ;; lk ja sd f s ; a k d j s s d d d s s s a a a a k k k a l l l l f f f f d d d s s s a a a s dd sa ss dd df ds ak jk l; kj dk la ;s sk kl ;a as df jk fj fj jj js ka ;l kj ks ld ks ;a kd js sd dd ss sa aa ak kk al ll lf ff fd dd ss sa aa ll as jk l; jk l; sjujijo jpuiopj ujijojp uiopjuj ijojjkl l;iopei woqpdui eqwpuio pjuuuio rewqdea jujijoj puiopju jiwuewq optiowp jojpuio pjujijo jpuiopj uuuiore wqdeaju jijojpu iopjuji jojpuio pjujijo jpuiopj uuuiore wqdeaju jijojpu iopjuji jojpuio pjujijo jpuiopj uuwuewq optiowp uiorewq deaiofd sasssad fdjjkll ;iopeiw oqpduie qwuewqo ptiowpd eajujij ojpuiop jujiwue wqoptio wpjojpu iopjuji jojpuio pjuuuio rewqdea jujijoj puioelo llswlop jujijoj puiopju jijojpu iopjuuu iorewqd eajujij ojeloll swlopui opjujij ojpuiop jujijoj puiopju uwuewel ollswlo qoptiow puiorew qdeaiof dsasssa dfdjjkl l;iopei woqpdui eqwuewq optiowp kidekid ekideki dekidek idekide kidekid ekideki dekidel ollswlo elollsw loeloll swloasw eopqpie ola;oui esslals dkla;sl ldfpjuj ijojpui opjujij ojpuiop juuwuew elollsw loqopti owpuior ewqdeai ofdsass sadfdjj kll;iop eiwoqpd uieqwue wqoptio wpkidek idekide kidekid eds def ddekid ekideki dekidek idekide lollswl oelolls wloelol lswloas weopqpi eola;ou ietyoek a ;l kj ks ld ks ;a k d js sd dd ss sa aa a k kk al ll lf ff fd d d ss sa aa sal dk slkd k akdf slal la ladk k add dad kslskf l jfa fklsdk dkfhl a ald sl kf la a l al df s asdh laskl l a;s kls dkj sa; slkdl l lassdfs ss sdd fj jfkd sla;sl kdfksla ; ;;;;a alskd df fdks l la la alal a l fa fa l fall fa flk fsdkl l skaf sd kfhsk a lhdhgla aldgh alhssty tiopsaw eqopu's oeioqp' sa"ieoh goieope oiutsue dkoeppq oeiowie ladfieo qeytouw oeyrowd sofpqpw eopquyt aytyyty ioytyty eytyyyy ytyyytt ttttttt ttseiti owptosh ghghfhs dioaplg ioeihal dogiioa hgkdihd lsd;' a hlahd/l ash? ah gipe?sh asie.fl shsdie' " sahgi ehidsoh aheoieh hghishe ythsgys hdgishd sithsdh gjsdihd ilahdgl hleyitu iweohla sopdlhg oeihghe tyahgl; ada;dkg ieh" as dh lask l la;s klsdkj sa;slkd l llass dfs ss sddfj j fkdsla; sl kdfk sla; ;; ;;a als kd df f dksl la la ala l al fa fal fa ll fa f lk fsdk l lskaf sdkfhs k alhdh gla ald gh alhs s tytio psaweqo pu'soei oqp'sa" ieohgoi eopeoiu tsuedko eppqoei owielad fieoqey touwoey rowdsof pqpweop quytayt yytyioy tytyeyt yyyyyty yyttttt tttttts eitiowp toshghg hfhsdio aplgioe ihald j ojpuiop juuuior ewqdeaj ujijojp uioelol lswlopj ujijojp uiopjuj ijojpui opjuuui orewqde ajujijo jelolls wlopuio pjujijo jpuiopj ujijojp uiopjuu wuewelo llswloq optiowp uiorewq deaiofd sasssad fdjjkll ;iopeiw oqpduie qwuewqo ptiowpk idekide kidekid ekideki dekidek idekide kidekid ekidelo llswloe lollswl oelolls wloaswe opqpieo la;ouie sslalsd kla;sll dfpjuji jojpuio pjuji t uieopqo oeighoi oaiedek idekide kidekid ekideki dekidek idekide kidelol lswloel ollswlo elollsw loasweo pqpieol a;ouies slalsdk la;slld fpjujij ojpuide kidekid ekideki dekidek idekide kidekid ekidelo llswloe lollswl oelolls wloaswe opqpieo la;ouie sslalsd kla;sll dfpjuji jojpuis lahsl;a lsa;;;a lkd'lsh lh"ashg hl"L:'s alaDSFF ouofehq ipro'wl opjujij ojpuiop jujijoj puiopju uuiorew qdeajuj ijojelo llswlop uiopjuj ijojpui opjujij ojpuiop juuwuew elollsw loqopti owpuior ewqdeai ofdsass sadfdjj kll;iop eiwoqpd uieqwue wqoptio wpkidek idekide kidekid ekideki dekidek idekide kidekid elollsw loeloll swloelo llswloa sweopqp ieola;o uiessla lsdkla; slldfpj ujijojp uiopjuj ijojpui optytio psaweqo pu'soei oqp'sa" ieohgoi eopeoiu tsuedko eppqoei owielad fieoqey touwoey rowdsof pqpweop quytayt yytyioy tytyeyt yyyyyty yyttttt tttttts eitiowp toshghg hfhsdio aplgioe ihaldjo jpuiopj uuuiore wqdeaju jijojpu ioeloll swlopju jijojpu iopjuji jojpuio pjuuuio rewqdea jujijoj elollsw lopuiop jujijoj puiopju jijojpu iopjuuw uewelol lswloqo ptiowpu iorewqd eaiofds asssadf djjkll; iopeiwo qpduieq wuewqop tiowpki dekidek idekide kidekid ekideki dekidek idekide kidelol lswloel ollswlo elollsw loasweo pqpieol a;ouies slalsdk la;slld fpjujij ojpuiop jujisss adfdjjk ll;iope iwoqpdu ieqwuew qoptiow pkideki dekidek idekide kidekid ekideki dekidek idekide lollswl oelolls wloelol lswloas weopqpi eola;ou iesslal sdkla;s lldfpju jijojpu iopjuji tuieop qooeigh oioaied ekideki dekidek idekide kidekid ekideki dekidel ollswlo elollsw loeloll swloasw eopqpie ola;oui esslals dkla;sl ldfpjuj ijojpui dekidek idekide kidekid ekideki dekidek idekide lollswl oelolls wloelol lswloas weopqpi eola;ou iesslal sdkla;s lldfpju jijojpu islahsl ;alsa;; ;alkd'l shlh"as hghl"L: 'salaDS FFouofe hqipro' wlopjuj ijojpui opjujij ojpuiop juuuior ewqdeaj ujijoje lollswl opuiopj ujijojp uiopjuj ijojpui opjuuwu eweloll swloqop tiowpui orewqde aiofdsa sssadfd jjkll;i opeiwoq pduieqw uewqopt iowpkid ekideki dekidek idekide kidekid ekideki dekidek ideloll swloelo llswloe lollswl oasweop qpieola ;ouiess lalsdkl a;slldf pjujijo jpuiopj ujijojp uioptyt iopsawe qopu'so eioqp's a"ieohg oieopeo iutsued koeppqo eiowie opjuuui orewqde ajujijo jpuiopj ujijojp uiopjuj ijojpui opjuuwu ewqopti owpuior ewqdeai ofdsass sadfdjj kll;iop eiwoqpd uieqwue wqoptio wpdeaju jijojpu iopjuji wuewqop tiowpjo jpuiopj ujijojp uiopjuu uiorewq deajuji jojpuio elollsw lopjuji jojpuio pjujijo jpuiopj uuuiore wqdeaju jijojel ollswlo puiopju jijojpu iopjuji jojpuio pjuuwue welolls wloqopt iowpuio rewqdea iofdsas ssadfdj jkll;io peiwoqp duieqwu ewqopti owpkide kidekid ekideki dekidek idekide kidekid ekideki delolls wloelol lswloel ollswlo asweopq pieola; ouiessl alsdkla ;slldfp jujijoj puiopju jijojpu iopjuuw uewelol lswloqo ptiowpu iorewqd eaiofds asssadf djjkll; iopeiwo qpduieq wuewqop tiowpki dekidek idekide kidedsd ef n,d lladcnz dieytzo dign,dl ladcnzd ieytzod ign,dll adcnzdi eytzodi gtyeaei qtopton ,dlladc nzdieyt zodig,, <.adlf. alh/sld h?ghitu poenalh dgllhen ,dlladc nzdieyt zodig,, xhglxah ieopqo n,dllad cnzdiey tzodigl salglls dlfllai aminthe omtnthe ologhti sthingi serally verysta rsghall dghoepo tujlsah lahdgja peopoeo oihgope ohgoeha hlbclmx bx,cnb, hslgas> shlasll adieaso elh/slg ahaslie lhghlad fieytgh bnhlgie thtryop juuuior ewqdeaj ujijojp uiopjuj ijojpui opjujij ojpuiop juuwuew qoptiow puiorew qdeaiof dsasssa dfdjjkl l;iopei woqpdui eqwuewq optiowp deajuji jojpuio pjujiwu ewqopti owpjojp uiopjuj ijojpui opjuuui orewqde ajujijo jpuioel ollswlo pjujijo jpuiopj ujijojp uiopjuu uiorewq deajuji jojelol lswlopu iopjuji jojpuio pjujijo jpuiopj uuwuewe lollswl oqoptio wpuiore wqdeaio fdsasss adfdjjk ll;iope iwoqpdu ieqwuew qoptiow pkideki dekidek idekide kidekid ekideki dekidek idekide lollswl oelolls wloelol lswloas weopqpi eola;ou iesslal sdkla;s lldfpju jijojpu iopjuji jojpuio pjuuwue welolls wloqopt iowpuio rewqdea iofdsas ssadfdj jkll;io peiwoqp duieqwu ewqopti owpkide kidekid ekideki dedsdef n,dll adcnzdi eytzodi gn,dlla dcnzdie ytzodig n,dllad cnzdiey tzodigt yeaeiqt opton,d lladcnz dieytzo dig,,<. adlf.al h/sldh? ghitupo enalhdg llhen,d lladcnz dieytzo dig,,xh glxahie opqo n, dlladcn zdieytz odiglsa lgllsdl fllaiam intheom tntheol oghtist hingise rallyve rystars ghalldg hoepotu jlsahla hdgjape opoeooi hgopeoh goehahl bclmxbx ,cnb,hs lgas>sh lasllad ieasoel h/slgah aslielh ghladfi eytghbn hlgieth try. An Introduction to the Linux Command Shell For Beginners Presented by: Victor Gedris In Co-Operation With: The Ottawa Canada Linux Users Group and ExitCertifiedCopyright and Redistribution This manual was written with the intention of being a helpful guide to Linux users who are trying to become familiar with the Bash shell and basic Linux commands. To make this manual useful to the widest range of people, I decided to release it under a free documentation license, with the hopes that people benefit from it by updating it and re-distributing modified copies. You have permission to modify and distribute this document, as specified under the terms of the GNU Free Documentation License. Comments and suggestions for improvement may be directed to: vic@gedris.org. This document was created using an Open Source office application called Open Office. The file format is non-proprietary, and the document is also published in various other formats online. Updated copies will be available on Vic Gedris' web site [http://vic.dyndns.org/]. For more information on Open Office, please visit http://www.openoffice.org/. Copyright ++ 2003 Victor Gedris. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the license is available from the Free Software Foundation's website: http://www.fsf.org/copyleft/fdl.html Document Version: 1.2, 2003-06-251.0 Introduction The purpose of this document is to provide the reader with a fast and simple introduction to using the Linux command shell and some of its basic utilities. It is assumed that the reader has zero or very limited exposure to the Linux command prompt. This document is designed to accompany an instructor-led tutorial on this subject, and therefore some details have been left out. Explanations, practical examples, and references to DOS commands are made, where appropriate. 1.1 What is a command shell? A program that interprets commands Allows a user to execute commands by typing them manually at a terminal, or automatically in programs called shell scripts. A shell is not an operating system. It is a way to interface with the operating system and run commands. 1.2 What is BASH? BASH = Bourne Again SHell Bash is a shell written as a free replacement to the standard Bourne Shell (/bin/sh) originally written by Steve Bourne for UNIX systems. It has all of the features of the original Bourne Shell, plus additions that make it easier to program with and use from the command line. Since it is Free Software, it has been adopted as the default shell on most Linux systems. 1.3 How is BASH different from the DOS command prompt? Case Sensitivity: In Linux/UNIX, commands and filenames are case sensitive, meaning that typing +,%EXIT"/- instead of the proper #%'exit%$. is a mistake. %/)\(** vs. !$*/#)(: In DOS, the forward-slash &$./,"! is the command argument delimiter, while the backslash '$,\$,# is a directory separator. In Linux/UNIX, the !'.//+) is the directory separator, and the %!(\-/* is an escape character. More about these special characters in a minute! Filenames: The DOS world uses the .(%eight dot three)*- filename convention, meaning that all files followed a format that allowed up to 8 characters in the filename, followed by a period (!')dot&**), followed by an option extension, up to 3 characters long (e.g. FILENAME.TXT). In UNIX/Linux, there is no such thing as a file extension. Periods can be placed at any part of the filename, and &"%extensions),! may be interpreted differently by all programs, or not at all.1.4 Special Characters Before we continue to learn about Linux shell commands, it is important to know that there are many symbols and characters that the shell interprets in special ways. This means that certain typed characters: a) cannot be used in certain situations, b) may be used to perform special operations, or, c) must be #$.escaped(#* if you want to use them in a normal way. Character Description \ Escape character. If you want to reference a special character, you must "'"escape!+! it with a backslash first. Example: / touch /tmp/filename\* Directory separator, used to separate a string of directory names. Example: /usr/src/linux . Current directory. Can also **(hide'#) files when it is the first character in a filename. .. Parent directory ~ User's home directory * Represents 0 or more characters in a filename, or by itself, all files in a directory. Example: ? Represents a single character in a filename. Example: [ ] cd /var/log ; less messages Command separator as above, but only runs the second command if the first one finished without errors. Example: & more < phonenumbers.txt Command separator. Allows you to execute multiple commands on a single line. Example: && echo $#'Mary 555-1234,/" >> phonenumbers.txt Redirect a file as input to a program. Example: ; ls > myfiles.txt Redirect the output of a command onto the end of an existing file. Example: < ls | more Redirect output of a command into a new file. If the file already exists, over-write it. Example: >> hello[0-2].txt represents the names hello0.txt, hello1.txt, and hello2.txt -,*Pipe),&. Redirect the output of one command into another command. Example: > hello?.txt can represent hello1.txt, helloz.txt, but not hello22.txt Can be used to represent a range of values, e.g. [0-9], [A-Z], etc. Example: | pic*2002 can represent the files pic2002, picJanuary2002, picFeb292002, etc. cd /var/logs && less messages Execute a command in the background, and immediately get your shell back. Example: find / -name core > /tmp/corefiles.txt &1.5 Executing Commands The Command PATH: Most common commands are located in your shell's *.)PATH(&$, meaning that you can just type the name of the program to execute it. Example: Typing ).* ls"/& will execute the *!' ls"') command. Your shell's ###PATH)&" variable includes the most common program locations, such as /bin, /usr/bin, /usr/X11R6/bin, and others. To execute commands that are not in your current PATH, you have to give the complete location of the command. Examples: /home/bob/myprogram ./program (Execute a program in the current directory) ~/bin/program (Execute program from a personal bin directory) Command Syntax Commands can be run by themselves, or you can pass in additional arguments to make them do different things. Typical command syntax can look something like this: command [-argument] [-argument] [--argument] [file] Examples: ls List files in current directory ls -l Lists files in *$-long+,) format ls -l --color As above, with colourized output cat filename Show contents of a file cat -n filename Show contents of a file, with line numbers2.0 Getting Help When you're stuck and need help with a Linux command, help is usually only a few keystrokes away! Help on most Linux commands is typically built right into the commands themselves, available through online help programs (!-.man pages"-+ and %-*info pages&/)), and of course online. 2.1 Using a Command's Built-In Help Many commands have simple #)!help)"( screens that can be invoked with special command flags. These flags usually look like "$*-h$%' or ,&*--help!".. Example: 2.2 grep --help Online Manuals: !)#Man Pages.*/ The best source of information for most commands can be found in the online manual pages, known as *.-man pages$%, for short. To read a command's man page, type $',man command$!-. Examples: man ls man man Get help on the #))ls$-- command. A manual about how to use the manual! To search for a particular word within a man page, type +!*/word,"+. To quit from a man page, just type the *"%Q-!. key. Sometimes, you might not remember the name of Linux command and you need to search for it. For example, if you want to know how to change a file's permissions, you can search the man page descriptions for the word $"%permission!,) like this: man -k permission If you look at the output of this command, you will find a line that looks something like: chmod (1) - change file access permissions Now you know that $/'chmod/*. is the command you were looking for. Typing %-(man chmod-!, will show you the chmod command's manual page! 2.3 Info Pages Some programs, particularly those released by the Free Software Foundation, use info pages as their main source of online documentation. Info pages are similar to man page, but instead of being displayed on one long scrolling screen, they are presented in shorter segments with links to other pieces of information. Info pages are accessed with the *#,info$/% command, or on some Linux distributions, ')'pinfo#-. (a nicer info browser). For example: info df Loads the !"/df,)# info page.3.0 Navigating the Linux Filesystem The Linux filesystem is a tree-like hierarchy hierarchy of directories and files. At the base of the filesystem is the %%"/$+# directory, otherwise known as the )/'root!,/ (not to be confused with the root user). Unlike DOS or Windows filesystems that have multiple $-"roots()!, one for each disk drive, the Linux filesystem mounts all disks somewhere underneath the / filesystem. The following table describes many of the most common Linux directories. 3.1 The Linux Directory Layout Directory Description The nameless base of the filesystem. All other directories, files, drives, and devices are attached to this root. Commonly (but incorrectly) referred to as the ,/"slash*"& or ()./.&( directory. The !"$/),& is just a directory separator, not a directory itself. /bin Essential command binaries (programs) are stored here (bash, ls, mount, tar, etc.) /boot Static files of the boot loader. /dev Device files. In Linux, hardware devices are acceessd just like other files, and they are kept under this directory. /etc Host-specific system configuration files. /home Location of users' personal home directories (e.g. /home/susan). /lib Essential shared libraries and kernel modules. /proc Process information pseudo-filesystem. An interface to kernel data structures. /root The root (superuser) home directory. /sbin Essential system binaries (fdisk, fsck, init, etc). /tmp Temporary files. All users have permission to place temporary files here. /usr The base directory for most shareable, read-only data (programs, libraries, documentation, and much more). /usr/bin Most user programs are kept here (cc, find, du, etc.). /usr/include Header files for compiling C programs. /usr/lib Libraries for most binary programs. /usr/local $&&Locally*-) installed files. This directory only really matters in environments where files are stored on the network. Locally-installed files go in /usr/local/bin, /usr/local/lib, etc.). Also often used for software packages installed from source, or software not officially shipped with the distribution. /usr/sbin Non-vital system binaries (lpd, useradd, etc.) /usr/share Architecture-independent data (icons, backgrounds, documentation, terminfo, man pages, etc.). /usr/src Program source code. E.g. The Linux Kernel, source RPMs, etc. /usr/X11R6 The X Window System. /var Variable data: mail and printer spools, log files, lock files, etc.3.2 Commands for Navigating the Linux Filesystems The first thing you usually want to do when learning about the Linux filesystem is take some time to look around and see what's there! These next few commands will: a) Tell you where you are, b) take you somewhere else, and c) show you what's there. The following table describes the basic operation of the pwd, cd, and ls commands, and compares them to certain DOS commands that you might already be familiar with. Linux Command DOS Command Description pwd cd )).Print Working Directory+!'. location in the directory tree. cd cd, chdir #$-Change Directory$&.. When typed all by itself, it returns you to your home directory. cd directory cd directory Change into the specified directory Example: cd /usr/src/linux cd ~ cd .. Shows the current name. *-.~($+ is an alias for your home directory. It can be used as a shortcut to your '+-home",!, or other directories relative to your home. cd.. cd - Move up one directory. For example, if you are in /home/vic and you type (/&cd ..%)+, you will end up in /home. Return to previous directory. An easy way to get back to your previous location! ls dir /w ls directory dir directory List the files in the specified directory. List all files in the current directory, in column format. Example: ls /var/log ls -l dir List files in %#$long#%$ format, one file per line. This also shows you additional info about the file, such as ownership, permissions, date, and size. ls -a dir /a List all files, including )'.hidden&#$ files. Hidden files are those files that begin with a +,)."+,, e.g. The .bash_history file in your home directory. ls -ld directory A ,*.long!+* list of "*)directory.&*, but instead of showing the directory contents, show the directory's detailed information. For example, compare the output of the following two commands: ls -l /usr/bin ls -ld /usr/bin ls /usr/bin/d* dir d*.* List all files whose names begin with the letter )+$d,-( in the /usr/bin directory.4.0 Piping and Re-Direction Before we move on to learning even more commands, let's side-track to the topics of piping and re-direction. The basic UNIX philosophy, therefore by extension the Linux philosophy, is to have many small programs and utilities that do a particular job very well. It is the responsibility of the programmer or user to combine these utilities to make more useful command sequences. 4.1 Piping Commands Together The pipe character, '.&|-+(, is used to chain two or more commands together. The output of the first command is !.+piped!/. into the next program, and if there is a second pipe, the output is sent to the third program, etc. For example: ls -la /usr/bin | less In this example, we run the command $#(ls -la /usr/bin#$*, which gives us a long listing of all of the files in /usr/bin. Because the output of this command is typically very long, we pipe the output to a program called $,%less%"*, which displays the output for us one screen at a time. 4.2 Redirecting Program Output to Files There are times when it is useful to save the output of a command to a file, instead of displaying it to the screen. For example, if we want to create a file that lists all of the MP3 files in a directory, we can do something like this, using the .#,>"&" redirection character: ls -l /home/vic/MP3/*.mp3 > mp3files.txt A similar command can be written so that instead of creating a new file called mp3files.txt, we can append to the end of the original file: ls -l /home/vic/extraMP3s/*.mp3 >> mp3files.txt5.0 Other Linux Commands The following sections describe many other commands that you will find on most Linux systems. I can't possibly cover the details of all of these commands in this document, so don't forget that you can check the !%/man pages-"* for additional information. Not all of the listed commands will be available on all Linux or UNIX distributions. 5.1 Working With Files and Directories These commands can be used to: find out information about files, display files, and manipulate them in other ways (copy, move, delete). Linux Command DOS Command file Description Find out what kind of file it is. For example, -)/file /bin/ls/*. tells us that it is a Linux executable file. cat type head Display the contents of a text file on the screen. For example: cat mp3files.txt would display the file we created in the previous section. Display the first few lines of a text file. Example: head /etc/services tail Display the last few lines of a text file. Example: tail /etc/services tail -f Display the last few lines of a text file, and then output appended data as the file grows (very useful for following log files!). Example: tail -f /var/log/messages cp copy Copies a file from one location to another. Example: cp mp3files.txt /tmp (copies the mp3files.txt file to the /tmp directory) mv rename, ren, move Moves a file to a new location, or renames it. For example: mv mp3files.txt /tmp (copy the file to /tmp, and delete it from the original location) rm del Delete a file. Example: rm /tmp/mp3files.txt mkdir md Make Directory. Example: mkdir /tmp/myfiles/ rmdir rd, rmdir Remove Directory. Example: rmdir /tmp/myfiles/5.2 Finding Things The following commands are used to find files. -%"ls,', is good for finding files if you already know approximately where they are, but sometimes you need more powerful tools such as these: Linux Command Description which Shows the full path of shell commands found in your path. For example, if you want to know exactly where the .#/grep*'! command is located on the filesystem, you can type +%#which grep(-). The output should be something like: /bin/grep whereis Locates the program, source code, and manual page for a command (if all information is available). For example, to find out where )%-ls/"& and its man page are, type: !..whereis ls!&/ The output will look something like: ls: /bin/ls /usr/share/man/man1/ls.1.gz locate A quick way to search for files anywhere on the filesystem. For example, you can find all files and directories that contain the name .#%mozilla''# by typing: locate mozilla find A very powerful command, but sometimes tricky to use. It can be used to search for files matching certain patterns, as well as many other types of searches. A simple example is: find . -name \*mp3 This example starts searching in the current directory *%%.).+ and all sub- directories, looking for files with "!'mp3+($ at the end of their names. 5.3 Informational Commands The following commands are used to find out some information about the user or the system. Linux Command Explanation ps Lists currently running process (programs). w Show who is logged on and what they are doing. id Print your user-id and group id's df Report filesystem disk space usage ($((Disk Free('* is how I remember it) du Disk Usage in a particular directory. %(/du -s$(% provides a summary for the current directory. top Displays CPU processes in a full-screen GUI. A great way to see the activity on your computer in real-time. Type *-/Q&,- to quit. free Displays amount of free and used memory in the system. cat /proc/cpuinfo Displays information about your CPU. cat /proc/meminfo Display lots of information about current memory usage. uname -a Prints system information to the screen (kernel version, machine type, etc.)5.4 Other Utilities Here are some other commands that are useful to know. Linux Command Description clear Clear the screen echo Display text on the screen. Mostly useful when writing shell scripts. For example: echo )&"Hello World%'' more Display a file, or program output one page at a time. Examples: more mp3files.txt ls -la | more less An improved replacement for the !(.more'*' command. Allows you to scroll backwards as well as forwards. grep Search for a pattern in a file or program output. For example, to find out which TCP network port is used by the #&.nfs*-- service, you can do this: grep +*%nfs#%$ /etc/services This looks for any line that contains the string '.(nfs-%+ in the file *%)/etc/services#)/ and displays only those lines. lpr Print a file or program output. Examples: lpr mp3files.txt - Print the mp3files.txt file ls -la | lpr - Print the output of the !!.ls -la//( command. sort Sort a file or program output. Example: sort mp3files.txt su &"-Switch User+##. Allows you to switch to another user's account temporarily. The default account to switch to is the root/superuser account. Examples: su - Switch the root account su - - Switch to root, and log in with root's environment su larry - Switch to Larry's account5.5 Shortcuts to Make it all Easier! When you start using the Bash shell more often, you will appreciate these shortcuts that can save you very much typing time. Shortcut Description Up/Down Arrow Keys Scroll through your most recent commands. You can scroll back to an old command, hit E NTER , and execute the command without having to re-type it. /&,history+(( command Show your complete command history. T AB Completion If you type a partial command or filename that the shell recognizes, you can have it automatically completed for you if you press the T AB key. Try typing the first few characters of your favourite Linux command, then hit T AB a couple of times to see what happens. Complete recent commands with .&&!.$" Try this: Type )--!"(. followed by the first couple of letters of a recent command and press E NTER ! For example, type: find /usr/bin -type f -name m\* ...and now type: !fi Search your command history with CTRL-R Press CTRL-R and then type any portion of a recent command. It will search the commands for you, and once you find the command you want, just press E NTER . Scrolling the screen with Shift- PageUp and Page Down Scroll back and forward through your terminal. 6.0 Further Reading Link Address Description http://www.oclug.on.ca Ottawa Canada Linux Users Group. A group with an active mailing list, monthly meetings, and much more. http://www.exitcertified.com Ottawa's source for Sun training, and the host of OCLUG's technology seminars. http://www.fsf.org The Free Software Foundation. Documentation, source code, and much more for many programs commonly found on Linux systems. http://linux.org.mt/article/terminal ((.A Beginner's Bash-'%. Another very good introduction to Bash. http://www.oreilly.com/catalog/bash2 An excellent book if you want to learn how to customize Bash and use it for shell script programming. Chapter 1. Linux history This chapter briefly tells the history of Unix and where Linux fits in. If you are eager to start working with Linux without this blah, blah, blah over history, distributions, and licensing then jump straight to Part II - Chapter 8. Working with Directories page 73. 3Linux history 1.1. 1969 All modern operating systems have their roots in 1969 when Dennis Ritchie and Ken Thompson developed the C language and the Unix operating system at AT&T Bell Labs. They shared their source code (yes, there was open source back in the Seventies) with the rest of the world, including the hippies in Berkeley California. By 1975, when AT&T started selling Unix commercially, about half of the source code was written by others. The hippies were not happy that a commercial company sold software that they had written; the resulting (legal) battle ended in there being two versions of Unix: the official AT&T Unix, and the free BSD Unix. Development of BSD descendants like FreeBSD, OpenBSD, NetBSD, DragonFly BSD and PC-BSD is still active today. https://en.wikipedia.org/wiki/Dennis_Ritchie https://en.wikipedia.org/wiki/Ken_Thompson https://en.wikipedia.org/wiki/BSD https://en.wikipedia.org/wiki/Comparison_of_BSD_operating_systems 1.2. 1980s In the Eighties many companies started developing their own Unix: IBM created AIX, Sun SunOS (later Solaris), HP HP-UX and about a dozen other companies did the same. The result was a mess of Unix dialects and a dozen different ways to do the same thing. And here is the first real root of Linux, when Richard Stallman aimed to end this era of Unix separation and everybody re-inventing the wheel by starting the GNU project (GNU is Not Unix). His goal was to make an operating system that was freely available to everyone, and where everyone could work together (like in the Seventies). Many of the command line tools that you use today on Linux are GNU tools. https://en.wikipedia.org/wiki/Richard_Stallman https://en.wikipedia.org/wiki/IBM_AIX https://en.wikipedia.org/wiki/HP-UX 1.3. 1990s The Nineties started with Linus Torvalds, a Swedish speaking Finnish student, buying a 386 computer and writing a brand new POSIX compliant kernel. He put the source code online, thinking it would never support anything but 386 hardware. Many people embraced the combination of this kernel with the GNU tools, and the rest, as they say, is history. http://en.wikipedia.org/wiki/Linus_Torvalds https://en.wikipedia.org/wiki/History_of_Linux https://en.wikipedia.org/wiki/Linux https://lwn.net http://www.levenez.com/unix/ (a huge Unix history poster) 4Linux history 1.4. 2015 Today more than 97 percent of the world's supercomputers (including the complete top 10), more than 80 percent of all smartphones, many millions of desktop computers, around 70 percent of all web servers, a large chunk of tablet computers, and several appliances (dvd- players, washing machines, dsl modems, routers, self-driving cars, space station laptops...) run Linux. Linux is by far the most commonly used operating system in the world. Linux kernel version 4.0 was released in April 2015. Its source code grew by several hundred thousand lines (compared to version 3.19 from February 2015) thanks to contributions of thousands of developers paid by hundreds of commercial companies including Red Hat, Intel, Samsung, Broadcom, Texas Instruments, IBM, Novell, Qualcomm, Nokia, Oracle, Google, AMD and even Microsoft (and many more). http://kernelnewbies.org/DevelopmentStatistics http://kernel.org http://www.top500.org 5Chapter 2. distributions This chapter gives a short overview of current Linux distributions. A Linux distribution is a collection of (usually open source) software on top of a Linux kernel. A distribution (or short, distro) can bundle server software, system management tools, documentation and many desktop applications in a central secure software repository. A distro aims to provide a common look and feel, secure and easy software management and often a specific operational purpose. Let's take a look at some popular distributions. 6distributions 2.1. Red Hat Red Hat is a billion dollar commercial Linux company that puts a lot of effort in developing Linux. They have hundreds of Linux specialists and are known for their excellent support. They give their products (Red Hat Enterprise Linux and Fedora) away for free. While Red Hat Enterprise Linux (RHEL) is well tested before release and supported for up to seven years after release, Fedora is a distro with faster updates but without support. 2.2. Ubuntu Canonical started sending out free compact discs with Ubuntu Linux in 2004 and quickly became popular for home users (many switching from Microsoft Windows). Canonical wants Ubuntu to be an easy to use graphical Linux desktop without need to ever see a command line. Of course they also want to make a profit by selling support for Ubuntu. 2.3. Debian There is no company behind Debian. Instead there are thousands of well organised developers that elect a Debian Project Leader every two years. Debian is seen as one of the most stable Linux distributions. It is also the basis of every release of Ubuntu. Debian comes in three versions: stable, testing and unstable. Every Debian release is named after a character in the movie Toy Story. 2.4. Other Distributions like CentOS, Oracle Enterprise Linux and Scientific Linux are based on Red Hat Enterprise Linux and share many of the same principles, directories and system administration techniques. Linux Mint, Edubuntu and many other *buntu named distributions are based on Ubuntu and thus share a lot with Debian. There are hundreds of other Linux distributions. 7distributions 2.5. Which to choose ? Below are some very personal opinions on some of the most popular Linux Distributions. Keep in mind that any of the below Linux distributions can be a stable server and a nice graphical desktop client. Table 2.1. choosing a Linux distro distribution name reason(s) for using Red Hat Enterprise (RHEL) You are a manager and you want a good support contract. CentOS You want Red Hat without the support contract from Red Hat. Fedora You want Red Hat on your laptop/desktop. Linux Mint You want a personal graphical desktop to play movies, music and games. Debian My personal favorite for servers, laptops, and any other device. Ubuntu Very popular, based on Debian, not my favorite. Kali others You want a pointy-clicky hacking interface. Advanced users may prefer Arch, Gentoo, OpenSUSE, Scientific, ... When you are new to Linux in 2015, go for the latest Mint or Fedora. If you only want to practice the Linux command line then install one Debian server and/or one CentOS server (without graphical interface). Here are some links to help you choose: distrowatch.com redhat.com centos.org debian.org www.linuxmint.com ubuntu.com 8Chapter 3. licensing This chapter briefly explains the different licenses used for distributing operating systems software. Many thanks go to Ywein Van den Brande for writing most of this chapter. Ywein is an attorney at law, co-author of The International FOSS Law Book and author of Praktijkboek Informaticarecht (in Dutch). http://ifosslawbook.org http://www.crealaw.eu 9licensing 3.1. about software licenses There are two predominant software paradigms: Free and Open Source Software (FOSS) and proprietary software. The criteria for differentiation between these two approaches is based on control over the software. With proprietary software, control tends to lie more with the vendor, while with Free and Open Source Software it tends to be more weighted towards the end user. But even though the paradigms differ, they use the same copyright laws to reach and enforce their goals. From a legal perspective, Free and Open Source Software can be considered as software to which users generally receive more rights via their license agreement than they would have with a proprietary software license, yet the underlying license mechanisms are the same. Legal theory states that the author of FOSS, contrary to the author of public domain software, has in no way whatsoever given up his rights on his work. FOSS supports on the rights of the author (the copyright) to impose FOSS license conditions. The FOSS license conditions need to be respected by the user in the same way as proprietary license conditions. Always check your license carefully before you use third party software. Examples of proprietary software are AIX from IBM, HP-UX from HP and Oracle Database 11g. You are not authorised to install or use this software without paying a licensing fee. You are not authorised to distribute copies and you are not authorised to modify the closed source code. 3.2. public domain software and freeware Software that is original in the sense that it is an intellectual creation of the author benefits copyright protection. Non-original software does not come into consideration for copyright protection and can, in principle, be used freely. Public domain software is considered as software to which the author has given up all rights and on which nobody is able to enforce any rights. This software can be used, reproduced or executed freely, without permission or the payment of a fee. Public domain software can in certain cases even be presented by third parties as own work, and by modifying the original work, third parties can take certain versions of the public domain software out of the public domain again. Freeware is not public domain software or FOSS. It is proprietary software that you can use without paying a license cost. However, the often strict license terms need to be respected. Examples of freeware are Adobe Reader, Skype and Command and Conquer: Tiberian Sun (this game was sold as proprietary in 1999 and is since 2011 available as freeware). 3.3. Free Software or Open Source Software Both the Free Software (translates to vrije software in Dutch and to Logiciel Libre in French) and the Open Source Software movement largely pursue similar goals and endorse similar software licenses. But historically, there has been some perception of differentiation due to different emphases. Where the Free Software movement focuses on the rights (the 10licensing four freedoms) which Free Software provides to its users, the Open Source Software movement points to its Open Source Definition and the advantages of peer-to-peer software development. Recently, the term free and open source software or FOSS has arisen as a neutral alternative. A lesser-used variant is free/libre/open source software (FLOSS), which uses libre to clarify the meaning of free as in freedom rather than as in at no charge. Examples of free software are gcc, MySQL and gimp. Detailed information about the four freedoms can be found here: http://www.gnu.org/philosophy/free-sw.html The open source definition can be found at: http://www.opensource.org/docs/osd The above definition is based on the Debian Free Software Guidelines available here: http://www.debian.org/social_contract#guidelines 3.4. GNU General Public License More and more software is being released under the GNU GPL (in 2006 Java was released under the GPL). This license (v2 and v3) is the main license endorsed by the Free Software Foundation. It's main characteristic is the copyleft principle. This means that everyone in the chain of consecutive users, in return for the right of use that is assigned, needs to distribute the improvements he makes to the software and his derivative works under the same conditions to other users, if he chooses to distribute such improvements or derivative works. In other words, software which incorporates GNU GPL software, needs to be distributed in turn as GNU GPL software (or compatible, see below). It is not possible to incorporate copyright protected parts of GNU GPL software in a proprietary licensed work. The GPL has been upheld in court. 3.5. using GPLv3 software You can use GPLv3 software almost without any conditions. If you solely run the software you even don't have to accept the terms of the GPLv3. However, any other use - such as modifying or distributing the software - implies acceptance. In case you use the software internally (including over a network), you may modify the software without being obliged to distribute your modification. You may hire third parties to work on the software exclusively for you and under your direction and control. But if you modify the software and use it otherwise than merely internally, this will be considered as distribution. You must distribute your modifications under GPLv3 (the copyleft principle). Several more obligations apply if you distribute GPLv3 software. Check the GPLv3 license carefully. You create output with GPLv3 software: The GPLv3 does not automatically apply to the output. 11licensing 3.6. BSD license There are several versions of the original Berkeley Distribution License. The most common one is the 3-clause license ("New BSD License" or "Modified BSD License"). This is a permissive free software license. The license places minimal restrictions on how the software can be redistributed. This is in contrast to copyleft licenses such as the GPLv. 3 discussed above, which have a copyleft mechanism. This difference is of less importance when you merely use the software, but kicks in when you start redistributing verbatim copies of the software or your own modified versions. 3.7. other licenses FOSS or not, there are many kind of licenses on software. You should read and understand them before using any software. 3.8. combination of software licenses When you use several sources or wishes to redistribute your software under a different license, you need to verify whether all licenses are compatible. Some FOSS licenses (such as BSD) are compatible with proprietary licenses, but most are not. If you detect a license incompatibility, you must contact the author to negotiate different license conditions or refrain from using the incompatible software. 12Part II. installing LinuxTable of Contents Chapter 4. installing Debian 8 This module is a step by step demonstration of an actual installation of Debian 8 (also known as Jessie). We start by downloading an image from the internet and install Debian 8 as a virtual machine in Virtualbox. We will also do some basic configuration of this new machine like setting an ip address and fixing a hostname. This procedure should be very similar for other versions of Debian, and also for distributions like Linux Mint, xubuntu/ubuntu/kubuntu or Mepis. This procedure can also be helpful if you are using another virtualization solution. Go to the next chapter if you want to install CentOS, Fedora, Red Hat Enterprise Linux, .... 15installing Debian 8 4.1. Debian Debian is one of the oldest Linux distributions. I use Debian myself on almost every computer that I own (including raspbian on the Raspberry Pi). Debian comes in releases named after characters in the movie Toy Story. The Jessie release contains about 36000 packages. Table 4.1. Debian releases name number year Woody 3.0 2002 Sarge 3.1 2005 Etch 4.0 2007 Lenny 5.0 2009 Squeeze 6.0 2011 Wheezy 7 2013 Jessie 8 2015 There is never a fixed date for the next Debian release. The next version is released when it is ready. 4.2. Downloading All these screenshots were made in November 2014, which means Debian 8 was still in 'testing' (but in 'freeze', so there will be no major changes when it is released). Download Debian here: 16installing Debian 8 After a couple of clicks on that website, I ended up downloading Debian 8 (testing) here. It should be only one click once Debian 8 is released (somewhere in 2015). You have many other options to download and install Debian. We will discuss them much later. This small screenshot shows the downloading of a netinst .iso file. Most of the software will be downloaded during the installation. This also means that you will have the most recent version of all packages when the install is finished. I already have Debian 8 installed on my laptop (hence the paul@debian8 prompt). Anyway, this is the downloaded file just before starting the installation. paul@debian8:~$ ls -hl debian-testing-amd64-netinst.iso -rw-r--r-- 1 paul paul 231M Nov 10 17:59 debian-testing-amd64-netinst.iso 17installing Debian 8 Create a new virtualbox machine (I already have five, you might have zero for now). Click the New button to start a wizard that will help you create a virtual machine. The machine needs a name, this screenshot shows that I named it server42. 18installing Debian 8 Most of the defaults in Virtualbox are ok. 512MB of RAM is enough to practice all the topics in this book. We do not care about the virtual disk format. 19installing Debian 8 Choosing dynamically allocated will save you some disk space (for a small performance hit). 8GB should be plenty for learning about Linux servers. This finishes the wizard. You virtual machine is almost ready to begin the installation. 20installing Debian 8 First, make sure that you attach the downloaded .iso image to the virtual CD drive. (by opening Settings, Storage followed by a mouse click on the round CD icon) Personally I also disable sound and usb, because I never use these features. I also remove the floppy disk and use a PS/2 mouse pointer. This is probably not very important, but I like the idea that it saves some resources. Now boot the virtual machine and begin the actual installation. After a couple of seconds you should see a screen similar to this. Choose Install to begin the installation of Debian. 21installing Debian 8 First select the language you want to use. Choose your country. This information will be used to suggest a download mirror. 22installing Debian 8 Choose the correct keyboard. On servers this is of no importance since most servers are remotely managed via ssh. Enter a hostname (with fqdn to set a dnsdomainname). 23installing Debian 8 Give the root user a password. Remember this password (or use hunter2). It is adviced to also create a normal user account. I don't give my full name, Debian 8 accepts an identical username and full name paul. 24installing Debian 8 The use entire disk refers to the virtual disk that you created before in Virtualbox.. Again the default is probably what you want. Only change partitioning if you really know what you are doing. 25installing Debian 8 Accept the partition layout (again only change if you really know what you are doing). This is the point of no return, the magical moment where pressing yes will forever erase data on the (virtual) computer. 26installing Debian 8 Software is downloaded from a mirror repository, preferably choose one that is close by (as in the same country). This setup was done in Belgium. 27installing Debian 8 Leave the proxy field empty (unless you are sure that you are behind a proxy server). Choose whether you want to send anonymous statistics to the Debian project (it gathers data about installed packages). You can view the statistics here http://popcon.debian.org/. 28installing Debian 8 Choose what software to install, we do not need any graphical stuff for this training. The latest versions are being downloaded. 29installing Debian 8 Say yes to install the bootloader on the virtual machine. Booting for the first time shows the grub screen 30installing Debian 8 A couple seconds later you should see a lot of text scrolling of the screen (dmesg). After which you are presented with this getty and are allowed your first logon. You should now be able to log on to your virtual machine with the root account. Do you remember the password ? Was it hunter2 ? The screenshots in this book will look like this from now on. You can just type those commands in the terminal (after you logged on). root@server42:~# who am i root tty1 2014-11-10 18:21 root@server42:~# hostname server42 root@server42:~# date Mon Nov 10 18:21:56 CET 2014 31installing Debian 8 4.3. virtualbox networking You can also log on from remote (or from your Windows/Mac/Linux host computer) using ssh or putty. Change the network settings in the virtual machine to bridge. This will enable your virtual machine to receive an ip address from your local dhcp server. The default virtualbox networking is to attach virtual network cards to nat. This screenshiot shows the ip address 10.0.2.15 when on nat: root@server42:~# ifconfig eth0 Link encap:Ethernet HWaddr 08:00:27:f5:74:cf inet addr:10.0.2.15 Bcast:10.0.2.255 Mask:255.255.255.0 inet6 addr: fe80::a00:27ff:fef5:74cf/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:11 errors:0 dropped:0 overruns:0 frame:0 TX packets:19 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:2352 (2.2 KiB) TX bytes:1988 (1.9 KiB) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) By shutting down the network interface and enabling it again, we force Debian to renew an ip address from the bridged network. root@server42:~# # do not run ifdown while connected over ssh! root@server42:~# ifdown eth0 Killed old client process Internet Systems Consortium DHCP Client 4.3.1 Copyright 2004-2014 Internet Systems Consortium. All rights reserved. For info, please visit https://www.isc.org/software/dhcp/ Listening on LPF/eth0/08:00:27:f5:74:cf Sending on LPF/eth0/08:00:27:f5:74:cf 32installing Debian 8 Sending on Socket/fallback DHCPRELEASE on eth0 to 10.0.2.2 port 67 root@server42:~# # now enable bridge in virtualbox settings root@server42:~# ifup eth0 Internet Systems Consortium DHCP Client 4.3.1 Copyright 2004-2014 Internet Systems Consortium. All rights reserved. For info, please visit https://www.isc.org/software/dhcp/ Listening on LPF/eth0/08:00:27:f5:74:cf Sending on LPF/eth0/08:00:27:f5:74:cf Sending on Socket/fallback DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 8 DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 8 DHCPREQUEST on eth0 to 255.255.255.255 port 67 DHCPOFFER from 192.168.1.42 DHCPACK from 192.168.1.42 bound to 192.168.1.111 -- renewal in 2938 seconds. root@server42:~# ifconfig eth0 eth0 Link encap:Ethernet HWaddr 08:00:27:f5:74:cf inet addr:192.168.1.111 Bcast:192.168.1.255 Mask:255.255.255.0 inet6 addr: fe80::a00:27ff:fef5:74cf/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:15 errors:0 dropped:0 overruns:0 frame:0 TX packets:31 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:3156 (3.0 KiB) TX bytes:3722 (3.6 KiB) root@server42:~# Here is an example of ssh to this freshly installed computer. Note that Debian 8 has disabled remote root access, so i need to use the normal user account. paul@debian8:~$ ssh paul@192.168.1.111 paul@192.168.1.111's password: The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. paul@server42:~$ paul@server42:~$ su - Password: root@server42:~# TODO: putty screenshot here... 33installing Debian 8 4.4. setting the hostname The hostname of the server is asked during installation, so there is no need to configure this manually. root@server42:~# hostname server42 root@server42:~# cat /etc/hostname server42 root@server42:~# dnsdomainname paul.local root@server42:~# grep server42 /etc/hosts 127.0.1.1 server42.paul.local server42 root@server42:~# 4.5. adding a static ip address This example shows how to add a static ip address to your server. You can use ifconfig to set a static address that is active until the next reboot (or until the next ifdown). a root@server42:~# ifconfig eth0:0 10.104.33.39 Adding a couple of lines to the /etc/network/interfaces file to enable an extra ip address forever. root@server42:~# vi /etc/network/interfaces root@server42:~# tail -4 /etc/network/interfaces auto eth0:0 iface eth0:0 inet static address 10.104.33.39 netmask 255.255.0.0 root@server42:~# ifconfig eth0 Link encap:Ethernet HWaddr 08:00:27:f5:74:cf inet addr:192.168.1.111 Bcast:192.168.1.255 Mask:255.255.255.0 inet6 addr: fe80::a00:27ff:fef5:74cf/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:528 errors:0 dropped:0 overruns:0 frame:0 TX packets:333 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:45429 (44.3 KiB) TX bytes:48763 (47.6 KiB) eth0:0 Link encap:Ethernet HWaddr 08:00:27:f5:74:cf inet addr:10.104.33.39 Bcast:10.255.255.255 Mask:255.0.0.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) root@server42:~# 34installing Debian 8 4.6. Debian package management To get all information about the newest packages form the online repository: root@server42:~# aptitude update Get: 1 http://ftp.be.debian.org jessie InRelease [191 kB] Get: 2 http://security.debian.org jessie/updates InRelease [84.1 kB] Get: 3 http://ftp.be.debian.org jessie-updates InRelease [117 kB] Get: 4 http://ftp.be.debian.org jessie-backports InRelease [118 kB] Get: 5 http://security.debian.org jessie/updates/main Sources [14 B] Get: 6 http://ftp.be.debian.org jessie/main Sources/DiffIndex [7,876 B] ... (output truncated) To download and apply all updates for all installed packages: root@server42:~# aptitude upgrade Resolving dependencies... The following NEW packages will be installed: firmware-linux-free{a} irqbalance{a} libnuma1{a} linux-image-3.16.0-4-amd64{a} The following packages will be upgraded: busybox file libc-bin libc6 libexpat1 libmagic1 libpaper-utils libpaper1 libsqlite3-0 linux-image-amd64 locales multiarch-support 12 packages upgraded, 4 newly installed, 0 to remove and 0 not upgraded. Need to get 44.9 MB of archives. After unpacking 161 MB will be used. Do you want to continue? [Y/n/?] ... (output truncated) To install new software (vim and tmux in this example): root@server42:~# aptitude install vim tmux The following NEW packages will be installed: tmux vim vim-runtime{a} 0 packages upgraded, 3 newly installed, 0 to remove and 0 not upgraded. Need to get 6,243 kB of archives. After unpacking 29.0 MB will be used. Do you want to continue? [Y/n/?] Get: 1 http://ftp.be.debian.org/debian/ jessie/main tmux amd64 1.9-6 [245 kB] Get: 2 http://ftp.be.debian.org/debian/ jessie/main vim-runtime all 2:7.4.488-1 [5,046 kB] Get: 3 http://ftp.be.debian.org/debian/ jessie/main vim amd64 2:7.4.488-1 [952 kB] Refer to the package management chapter in LinuxAdm.pdf for more information. 35Chapter 5. installing CentOS 7 This module is a step by step demonstration of an actual installation of CentOS 7. We start by downloading an image from the internet and install CentOS 7 as a virtual machine in Virtualbox. We will also do some basic configuration of this new machine like setting an ip address and fixing a hostname. This procedure should be very similar for other versions of CentOS, and also for distributions like RHEL (Red Hat Enterprise Linux) or Fedora. This procedure can also be helpful if you are using another virtualization solution. 36installing CentOS 7 5.1. download a CentOS 7 image This demonstration uses a laptop computer with Virtualbox to install CentOS 7 as a virtual machine. The first task is to download an .iso image of CentOS 7. The CentOS 7 website looks like this today (November 2014). They change the look regularly, so it may look different when you visit it. You can download a full DVD, which allows for an off line installation of a graphical CentOS 7 desktop. You can select this because it should be easy and complete, and should get you started with a working CentOS 7 virtual machine. 37installing CentOS 7 But I clicked instead on 'alternative downloads', selected CentOS 7 and x86_64 and ended up on a mirror list. Each mirror is a server that contains copies of CentOS 7 media. I selected a Belgian mirror because I currently am in Belgium. There is again the option for full DVD's and more. This demonstration will use the minimal .iso file, because it is much smaller in size. The download takes a couple of minutes. Verify the size of the file after download to make sure it is complete. Probably a right click on the file and selecting 'properties' (if you use Windows or Mac OSX). I use Linux on the laptop already: paul@debian8:~$ ls -lh CentOS-7.0-1406-x86_64-Minimal.iso -rw-r--r-- 1 paul paul 566M Nov 1 14:45 CentOS-7.0-1406-x86_64-Minimal.iso Do not worry if you do no understand the above command. Just try to make sure that the size of this file is the same as the size that is mentioned on the CentOS 7 website. 38installing CentOS 7 5.2. Virtualbox This screenshot shows up when I start Virtualbox. I already have four virtual machines, you might have none. Below are the steps for creating a new virtual machine. Start by clicking New and give your machine a name (I chose server33). Click Next. 39installing CentOS 7 A Linux computer without graphical interface will run fine on half a gigabyte of RAM. A Linux virtual machine will need a virtual hard drive. 40installing CentOS 7 Any format will do for our purpose, so I left the default vdi. The default dynamically allocated type will save disk space (until we fill the virtual disk up to 100 percent). It makes the virtual machine a bit slower than fixed size, but the fixed size speed improvement is not worth it for our purpose. 41installing CentOS 7 The name of the virtual disk file on the host computer will be server33.vdi in my case (I left it default and it uses the vm name). Also 16 GB should be enough to practice Linux. The file will stay much smaller than 16GB, unless you copy a lot of files to the virtual machine. You should now be back to the start screen of Virtualbox. If all went well, then you should see the machine you just created in the list. 42installing CentOS 7 After finishing the setup, we go into the Settings of our virtual machine and attach the .iso file we downloaded before. Below is the default screenshot. This is a screenshot with the .iso file properly attached. 43installing CentOS 7 5.3. CentOS 7 installing The screenshots below will show every step from starting the virtual machine for the first time (with the .iso file attached) until the first logon. You should see this when booting, otherwise verify the attachment of the .iso file form the previous steps. Select Test this media and install CentOS 7. 44installing CentOS 7 Carefully select the language in which you want your CentOS. I always install operating systems in English, even though my native language is not English. Also select the right keyboard, mine is a US qwerty, but yours may be different. You should arrive at a summary page (with one or more warnings). 45installing CentOS 7 Start by configuring the network. During this demonstration I had a DHCP server running at 192.168.1.42, yours is probably different. Ask someone (a network administator ?) for help if this step fails. Select your time zone, and activate ntp. 46installing CentOS 7 Choose a mirror that is close to you. If you can't find a local mirror, then you can copy the one from this screenshot (it is a general CentOS mirror). It can take a couple of seconds before the mirror is verified. 47installing CentOS 7 I did not select any software here (because I want to show it all in this training). After configuring network, location, software and all, you should be back on this page. Make sure there are no warnings anymore (and that you made the correct choice everywhere). 48installing CentOS 7 You can enter a root password and create a user account while the installation is downloading from the internet. This is the longest step, it can take several minutes (or up to an hour if you have a slow internet connection). If you see this, then the installation was successful. Time to reboot the computer and start CentOS 7 for the first time. 49installing CentOS 7 This screen will appear briefly when the virtual machines starts. You don't have to do anything. After a couple of seconds, you should see a logon screen. This is called a tty or a getty. Here you can type root as username. The login process will then ask your password (nothing will appear on screen when you type your password). 50installing CentOS 7 And this is what it looks like after logon. You are logged on to your own Linux machine, very good. All subsequent screenshots will be text only, no images anymore. For example this screenshot shows three commands being typed on my new CentOS 7 install. [root@localhost ~]# who am i root pts/0 2014-11-01 22:14 [root@localhost ~]# hostname localhost.localdomain [root@localhost ~]# date Sat Nov 1 22:14:37 CET 2014 When using ssh the same commands will give this screenshot: [root@localhost ~]# who am i root pts/0 2014-11-01 21:00 (192.168.1.35) [root@localhost ~]# hostname localhost.localdomain [root@localhost ~]# date Sat Nov 1 22:10:04 CET 2014 [root@localhost ~]# If the last part is a bit too fast, take a look at the next topic CentOS 7 first logon. 51installing CentOS 7 5.4. CentOS 7 first logon All you have to log on, after finishing the installation, is this screen in Virtualbox. This is workable to learn Linux, and you will be able to practice a lot. But there are more ways to access your virtual machine, the next chapters discuss some of these and will also introduce some basic system configuration. 5.4.1. setting the hostname Setting the hostname is a simple as changing the /etc/hostname file. As you can see here, it is set to localhost.localdomain by default. [root@localhost ~]# cat /etc/hostname localhost.localdomain You could do echo server33.netsec.local > /etc/hostname followed by a reboot. But there is also the new CentOS 7 way of setting a new hostname. [root@localhost ~]# nmtui The above command will give you a menu to choose from with a set system hostname option. Using this nmtui option will edit the /etc/hostname file for you. [root@localhost ~]# cat /etc/hostname server33.netsec.local [root@localhost ~]# hostname server33.netsec.local [root@localhost ~]# dnsdomainname netsec.local For some reason the documentation on the centos.org and docs.redhat.com websites tell you to also execute this command: [root@localhost ~]# systemctl restart systemd-hostnamed 52installing CentOS 7 5.5. Virtualbox network interface By default Virtualbox will connect your virtual machine over a nat interface. This will show up as a 10.0.2.15 (or similar). [root@server33 ~]# ip a 1: lo: mtu 65536 qdisc noqueue state UNKNOWN link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: enp0s3: mtu 1500 qdisc pfifo_fast s\ tate UP qlen 1000 link/ether 08:00:27:1c:f5:ab brd ff:ff:ff:ff:ff:ff inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic enp0s3 valid_lft 86399sec preferred_lft 86399sec inet6 fe80::a00:27ff:fe1c:f5ab/64 scope link valid_lft forever preferred_lft forever You can change this to bridge (over your wi-fi or over the ethernet cable) and thus make it appear as if your virtual machine is directly on your local network (receiving an ip address from your real dhcp server). You can make this change while the vm is running, provided that you execute this command: [root@server33 ~]# systemctl restart network [root@server33 ~]# ip a s dev enp0s3 2: enp0s3: mtu 1500 qdisc pfifo_fast s\ tate UP qlen 1000 link/ether 08:00:27:1c:f5:ab brd ff:ff:ff:ff:ff:ff inet 192.168.1.110/24 brd 192.168.1.255 scope global dynamic enp0s3 valid_lft 7199sec preferred_lft 7199sec inet6 fe80::a00:27ff:fe1c:f5ab/64 scope link valid_lft forever preferred_lft forever [root@server33 ~]# 53installing CentOS 7 5.6. configuring the network The new way of changing network configuration is through the nmtui tool. If you want to manually play with the files in /etc/sysconfig/network-scripts then you will first need to verify (and disable) NetworkManager on that interface. Verify whether an interface is controlled by NetworkManager using the nmcli command (connected means managed bu NM). [root@server33 ~]# nmcli dev DEVICE TYPE STATE enp0s3 ethernet connected lo loopback unmanaged status CONNECTION enp0s3 -- Disable NetworkManager on an interface (enp0s3 in this case): echo 'NM_CONTROLLED=no' >> /etc/sysconfig/network-scripts/ifcfg-enp0s3 You can restart the network without a reboot like this: [root@server33 ~]# systemctl restart network Also, forget ifconfig and instead use ip a. [root@server33 ~]# ip a s dev enp0s3 | grep inet inet 192.168.1.110/24 brd 192.168.1.255 scope global dynamic enp0s3 inet6 fe80::a00:27ff:fe1c:f5ab/64 scope link [root@server33 ~]# 5.7. adding one static ip address This example shows how to add one static ip address to your computer. [root@server33 ~]# nmtui edit enp0s3 In this interface leave the IPv4 configuration to automatic, and add an ip address just below. IPv4 CONFIGURATION Addresses 10.104.33.32/16__________ Execute this command after exiting nmtui. [root@server33 ~]# systemctl restart network And verify with ip (not with ifconfig): [root@server33 ~]# ip a s dev enp0s3 | grep inet 192.168.1.110/24 brd 192.168.1.255 inet 10.104.33.32/16 brd 10.104.255.255 inet6 fe80::a00:27ff:fe1c:f5ab/64 scope [root@server33 ~]# 54 inet scope global dynamic enp0s3 scope global enp0s3 linkinstalling CentOS 7 5.8. package management Even with a network install, CentOS 7 did not install the latest version of some packages. Luckily there is only one command to run (as root). This can take a while. [root@server33 ~]# yum update Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile * base: centos.weepeetelecom.be * extras: centos.weepeetelecom.be * updates: centos.weepeetelecom.be Resolving Dependencies --> Running transaction check ---> Package NetworkManager.x86_64 1:0.9.9.1-13.git20140326.4dba720.el7 \ will be updated ... (output truncated) You can also use yum to install one or more packages. Do not forget to run yum update from time to time. [root@server33 ~]# yum update -y && yum install vim -y Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile * base: centos.weepeetelecom.be ... (output truncated) Refer to the package management chapter for more information on installing and removing packages. 55installing CentOS 7 5.9. logon from Linux and MacOSX You can now open a terminal on Linux or MacOSX and use ssh to log on to your virtual machine. paul@debian8:~$ ssh root@192.168.1.110 root@192.168.1.110's password: Last login: Sun Nov 2 11:53:57 2014 [root@server33 ~]# hostname server33.netsec.local [root@server33 ~]# 5.10. logon from MS Windows There is no ssh installed on MS Windows, but you can download putty.exe from http:// www.chiark.greenend.org.uk/~sgtatham/putty/download.html (just Google it). Use putty.exe as shown in this screenshot (I saved the ip address by giving it a name 'server33' and presing the 'save' button). 56installing CentOS 7 The first time you will get a message about keys, accept this (this is explained in the ssh chapter). Enter your userid (or root) and the correct password (nothing will appear on the screen when typing a password). 57Chapter 6. getting Linux at home This chapter shows a Ubuntu install in Virtualbox. Consider it legacy and use CentOS7 or Debian8 instead (each have their own chapter now). This book assumes you have access to a working Linux computer. Most companies have one or more Linux servers, if you have already logged on to it, then you 're all set (skip this chapter and go to the next). Another option is to insert a Ubuntu Linux CD in a computer with (or without) Microsoft Windows and follow the installation. Ubuntu will resize (or create) partitions and setup a menu at boot time to choose Windows or Linux. If you do not have access to a Linux computer at the moment, and if you are unable or unsure about installing Linux on your computer, then this chapter proposes a third option: installing Linux in a virtual machine. Installation in a virtual machine (provided by Virtualbox) is easy and safe. Even when you make mistakes and crash everything on the virtual Linux machine, then nothing on the real computer is touched. This chapter gives easy steps and screenshots to get a working Ubuntu server in a Virtualbox virtual machine. The steps are very similar to installing Fedora or CentOS or even Debian, and if you like you can also use VMWare instead of Virtualbox. 58getting Linux at home 6.1. download a Linux CD image Start by downloading a Linux CD image (an .ISO file) from the distribution of your choice from the Internet. Take care selecting the correct cpu architecture of your computer; choose i386 if unsure. Choosing the wrong cpu type (like x86_64 when you have an old Pentium) will almost immediately fail to boot the CD. 6.2. download Virtualbox Step two (when the .ISO file has finished downloading) is to download Virtualbox. If you are currently running Microsoft Windows, then download and install Virtualbox for Windows! 59getting Linux at home 6.3. create a virtual machine Now start Virtualbox. Contrary to the screenshot below, your left pane should be empty. Click New to create a new virtual machine. We will walk together through the wizard. The screenshots below are taken on Mac OSX; they will be slightly different if you are running Microsoft Windows. 60getting Linux at home Name your virtual machine (and maybe select 32-bit or 64-bit). Give the virtual machine some memory (512MB if you have 2GB or more, otherwise select 256MB). 61getting Linux at home Select to create a new disk (remember, this will be a virtual disk). If you get the question below, choose vdi. 62getting Linux at home Choose dynamically allocated (fixed size is only useful in production or on really old, slow hardware). Choose between 10GB and 16GB as the disk size. 63getting Linux at home Click create to create the virtual disk. Click create to create the virtual machine. 64getting Linux at home 6.4. attach the CD image Before we start the virtual computer, let us take a look at some settings (click Settings). Do not worry if your screen looks different, just find the button named storage. 65getting Linux at home Remember the .ISO file you downloaded? Connect this .ISO file to this virtual machine by clicking on the CD icon next to Empty. Now click on the other CD icon and attach your ISO file to this virtual CD drive. 66getting Linux at home Verify that your download is accepted. If Virtualbox complains at this point, then you probably did not finish the download of the CD (try downloading it again). It could be useful to set the network adapter to bridge instead of NAT. Bridged usually will connect your virtual computer to the Internet. 67getting Linux at home 6.5. install Linux The virtual machine is now ready to start. When given a choice at boot, select install and follow the instructions on the screen. When the installation is finished, you can log on to the machine and start practising Linux! 68Part III. first steps on the command lineTable of Contents Chapter 7. man pages This chapter will explain the use of man pages (also called manual pages) on your Unix or Linux computer. You will learn the man command together with related commands like whereis, whatis and mandb. Most Unix files and commands have pretty good man pages to explain their use. Man pages also come in handy when you are using multiple flavours of Unix or several Linux distributions since options and parameters sometimes vary. 71man pages 7.1. man $command Type man followed by a command (for which you want help) and start reading. Press q to quit the manpage. Some man pages contain examples (near the end). paul@laika:~$ man whois Reformatting whois(1), please wait... 7.2. man $configfile Most configuration files have their own manual. paul@laika:~$ man syslog.conf Reformatting syslog.conf(5), please wait... 7.3. man $daemon This is also true for most daemons (background programs) on your system.. paul@laika:~$ man syslogd Reformatting syslogd(8), please wait... 7.4. man -k (apropos) man -k (or apropos) shows a list of man pages containing a string. paul@laika:~$ man -k syslog lm-syslog-setup (8) - configure laptop mode to switch syslog.conf ... logger (1) - a shell command interface to the syslog(3) ... syslog-facility (8) - Setup and remove LOCALx facility for sysklogd syslog.conf (5) - syslogd(8) configuration file syslogd (8) - Linux system logging utilities. syslogd-listfiles (8) - list system logfiles 7.5. whatis To see just the description of a manual page, use whatis followed by a string. paul@u810:~$ whatis route route (8) - show / manipulate the IP routing table 7.6. whereis The location of a manpage can be revealed with whereis. paul@laika:~$ whereis -m whois whois: /usr/share/man/man1/whois.1.gz This file is directly readable by man. paul@laika:~$ man /usr/share/man/man1/whois.1.gz 72man pages 7.7. man sections By now you will have noticed the numbers between the round brackets. man man will explain to you that these are section numbers. Executable programs and shell commands reside in section one. 8 Executable programs or shell commands System calls (functions provided by the kernel) Library calls (functions within program libraries) Special files (usually found in /dev) File formats and conventions eg /etc/passwd Games Miscellaneous (including macro packages and conventions), e.g. man(7) System administration commands (usually only for root) Kernel routines [Non standard] 7.8. man $section $file Therefor, when referring to the man page of the passwd command, you will see it written as passwd(1); when referring to the passwd file, you will see it written as passwd(5). The screenshot explains how to open the man page in the correct section. [paul@RHEL52 ~]$ man passwd [paul@RHEL52 ~]$ man 5 passwd # opens the first manual found # opens a page from section 5 7.9. man man If you want to know more about man, then Read The Fantastic Manual (RTFM). Unfortunately, manual pages do not have the answer to everything... paul@laika:~$ man woman No manual entry for woman 7.10. mandb Should you be convinced that a man page exists, but you can't access it, then try running mandb on Debian/Mint. root@laika:~# mandb 0 man subdirectories contained newer manual pages. 0 manual pages were added. 0 stray cats were added. 0 old database entries were purged. Or run makewhatis on CentOS/Redhat. [root@centos65 ~]# apropos scsi scsi: nothing appropriate [root@centos65 ~]# makewhatis [root@centos65 ~]# apropos scsi hpsa (4) - HP Smart Array SCSI driver lsscsi (8) - list SCSI devices (or hosts) and their attributes sd (4) - Driver for SCSI Disk Drives st (4) - SCSI tape device 73Chapter 8. working with directories This module is a brief overview of the most common commands to work with directories: pwd, cd, ls, mkdir and rmdir. These commands are available on any Linux (or Unix) system. This module also discusses absolute and relative paths and path completion in the bash shell. 74working with directories 8.1. pwd The you are here sign can be displayed with the pwd command (Print Working Directory). Go ahead, try it: Open a command line interface (also called a terminal, console or xterm) and type pwd. The tool displays your current directory. paul@debian8:~$ pwd /home/paul 8.2. cd You can change your current directory with the cd command (Change Directory). paul@debian8$ paul@debian8$ /etc paul@debian8$ paul@debian8$ /bin paul@debian8$ paul@debian8$ /home/paul cd /etc pwd cd /bin pwd cd /home/paul/ pwd 8.2.1. cd ~ The cd is also a shortcut to get back into your home directory. Just typing cd without a target directory, will put you in your home directory. Typing cd ~ has the same effect. paul@debian8$ paul@debian8$ /etc paul@debian8$ paul@debian8$ /home/paul paul@debian8$ paul@debian8$ /home/paul cd /etc pwd cd pwd cd ~ pwd 8.2.2. cd .. To go to the parent directory (the one just above your current directory in the directory tree), type cd .. . paul@debian8$ pwd /usr/share/games paul@debian8$ cd .. paul@debian8$ pwd /usr/share To stay in the current directory, type cd . ;-) We will see useful use of the . character representing the current directory later. 75working with directories 8.2.3. cd - Another useful shortcut with cd is to just type cd - to go to the previous directory. paul@debian8$ /home/paul paul@debian8$ paul@debian8$ /etc paul@debian8$ /home/paul paul@debian8$ /etc pwd cd /etc pwd cd - cd - 8.3. absolute and relative paths You should be aware of absolute and relative paths in the file tree. When you type a path starting with a slash (/), then the root of the file tree is assumed. If you don't start your path with a slash, then the current directory is the assumed starting point. The screenshot below first shows the current directory /home/paul. From within this directory, you have to type cd /home instead of cd home to go to the /home directory. paul@debian8$ pwd /home/paul paul@debian8$ cd home bash: cd: home: No such file or directory paul@debian8$ cd /home paul@debian8$ pwd /home When inside /home, you have to type cd paul instead of cd /paul to enter the subdirectory paul of the current directory /home. paul@debian8$ pwd /home paul@debian8$ cd /paul bash: cd: /paul: No such file or directory paul@debian8$ cd paul paul@debian8$ pwd /home/paul In case your current directory is the root directory /, then both cd /home and cd home will get you in the /home directory. paul@debian8$ / paul@debian8$ paul@debian8$ /home paul@debian8$ paul@debian8$ paul@debian8$ /home pwd cd home pwd cd / cd /home pwd This was the last screenshot with pwd statements. From now on, the current directory will often be displayed in the prompt. Later in this book we will explain how the shell variable $PS1 can be configured to show this. 76working with directories 8.4. path completion The tab key can help you in typing a path without errors. Typing cd /et followed by the tab key will expand the command line to cd /etc/. When typing cd /Et followed by the tab key, nothing will happen because you typed the wrong path (upper case E). You will need fewer key strokes when using the tab key, and you will be sure your typed path is correct! 8.5. ls You can list the contents of a directory with ls. paul@debian8:~$ ls allfiles.txt dmesg.txt paul@debian8:~$ services stuff summer.txt 8.5.1. ls -a A frequently used option with ls is -a to show all files. Showing all files means including the hidden files. When a file name on a Linux file system starts with a dot, it is considered a hidden file and it doesn't show up in regular file listings. paul@debian8:~$ ls allfiles.txt dmesg.txt services stuff summer.txt paul@debian8:~$ ls -a . allfiles.txt .bash_profile dmesg.txt .lesshst .. .bash_history .bashrc services .ssh paul@debian8:~$ stuff summer.txt 8.5.2. ls -l Many times you will be using options with ls to display the contents of the directory in different formats or to display different parts of the directory. Typing just ls gives you a list of files in the directory. Typing ls -l (that is a letter L, not the number 1) gives you a long listing. paul@debian8:~$ ls -l total 17296 -rw-r--r-- 1 paul paul 17584442 Sep 17 00:03 allfiles.txt -rw-r--r-- 1 paul paul 96650 Sep 17 00:03 dmesg.txt -rw-r--r-- 1 paul paul 19558 Sep 17 00:04 services drwxr-xr-x 2 paul paul 4096 Sep 17 00:04 stuff -rw-r--r-- 1 paul paul 0 Sep 17 00:04 summer.txt 77working with directories 8.5.3. ls -lh Another frequently used ls option is -h. It shows the numbers (file sizes) in a more human readable format. Also shown below is some variation in the way you can give the options to ls. We will explain the details of the output later in this book. Note that we use the letter L as an option in this screenshot, not the number 1. paul@debian8:~$ ls -l -h total 17M -rw-r--r-- 1 paul paul 17M -rw-r--r-- 1 paul paul 95K -rw-r--r-- 1 paul paul 20K drwxr-xr-x 2 paul paul 4.0K -rw-r--r-- 1 paul paul 0 paul@debian8:~$ ls -lh total 17M -rw-r--r-- 1 paul paul 17M -rw-r--r-- 1 paul paul 95K -rw-r--r-- 1 paul paul 20K drwxr-xr-x 2 paul paul 4.0K -rw-r--r-- 1 paul paul 0 paul@debian8:~$ ls -hl total 17M -rw-r--r-- 1 paul paul 17M -rw-r--r-- 1 paul paul 95K -rw-r--r-- 1 paul paul 20K drwxr-xr-x 2 paul paul 4.0K -rw-r--r-- 1 paul paul 0 paul@debian8:~$ ls -h -l total 17M -rw-r--r-- 1 paul paul 17M -rw-r--r-- 1 paul paul 95K -rw-r--r-- 1 paul paul 20K drwxr-xr-x 2 paul paul 4.0K -rw-r--r-- 1 paul paul 0 paul@debian8:~$ Sep Sep Sep Sep Sep 17 17 17 17 17 00:03 00:03 00:04 00:04 00:04 allfiles.txt dmesg.txt services stuff summer.txt Sep Sep Sep Sep Sep 17 17 17 17 17 00:03 00:03 00:04 00:04 00:04 allfiles.txt dmesg.txt services stuff summer.txt Sep Sep Sep Sep Sep 17 17 17 17 17 00:03 00:03 00:04 00:04 00:04 allfiles.txt dmesg.txt services stuff summer.txt Sep Sep Sep Sep Sep 17 17 17 17 17 00:03 00:03 00:04 00:04 00:04 allfiles.txt dmesg.txt services stuff summer.txt 78working with directories 8.6. mkdir Walking around the Unix file tree is fun, but it is even more fun to create your own directories with mkdir. You have to give at least one parameter to mkdir, the name of the new directory to be created. Think before you type a leading / . paul@debian8:~$ mkdir mydir paul@debian8:~$ cd mydir paul@debian8:~/mydir$ ls -al total 8 drwxr-xr-x 2 paul paul 4096 Sep 17 00:07 . drwxr-xr-x 48 paul paul 4096 Sep 17 00:07 .. paul@debian8:~/mydir$ mkdir stuff paul@debian8:~/mydir$ mkdir otherstuff paul@debian8:~/mydir$ ls -l total 8 drwxr-xr-x 2 paul paul 4096 Sep 17 00:08 otherstuff drwxr-xr-x 2 paul paul 4096 Sep 17 00:08 stuff paul@debian8:~/mydir$ 8.6.1. mkdir -p The following command will fail, because the parent directory of threedirsdeep does not exist. paul@debian8:~$ mkdir mydir2/mysubdir2/threedirsdeep mkdir: cannot create directory -$-mydir2/mysubdir2/threedirsdeep*'!: No such fi\ le or directory When given the option -p, then mkdir will create parent directories as needed. paul@debian8:~$ mkdir -p mydir2/mysubdir2/threedirsdeep paul@debian8:~$ cd mydir2 paul@debian8:~/mydir2$ ls -l total 4 drwxr-xr-x 3 paul paul 4096 Sep 17 00:11 mysubdir2 paul@debian8:~/mydir2$ cd mysubdir2 paul@debian8:~/mydir2/mysubdir2$ ls -l total 4 drwxr-xr-x 2 paul paul 4096 Sep 17 00:11 threedirsdeep paul@debian8:~/mydir2/mysubdir2$ cd threedirsdeep/ paul@debian8:~/mydir2/mysubdir2/threedirsdeep$ pwd /home/paul/mydir2/mysubdir2/threedirsdeep 8.7. rmdir When a directory is empty, you can use rmdir to remove the directory. paul@debian8:~/mydir$ ls -l total 8 drwxr-xr-x 2 paul paul 4096 Sep 17 00:08 otherstuff drwxr-xr-x 2 paul paul 4096 Sep 17 00:08 stuff paul@debian8:~/mydir$ rmdir otherstuff paul@debian8:~/mydir$ cd .. paul@debian8:~$ rmdir mydir rmdir: failed to remove $&-mydir'"%: Directory not empty paul@debian8:~$ rmdir mydir/stuff paul@debian8:~$ rmdir mydir paul@debian8:~$ 79working with directories 8.7.1. rmdir -p And similar to the mkdir -p option, you can also use rmdir to recursively remove directories. paul@debian8:~$ mkdir -p test42/subdir paul@debian8:~$ rmdir -p test42/subdir paul@debian8:~$ 80working with directories 8.8. practice: working with directories 1. Display your current directory. 2. Change to the /etc directory. 3. Now change to your home directory using only three key presses. 4. Change to the /boot/grub directory using only eleven key presses. 5. Go to the parent directory of the current directory. 6. Go to the root directory. 7. List the contents of the root directory. 8. List a long listing of the root directory. 9. Stay where you are, and list the contents of /etc. 10. Stay where you are, and list the contents of /bin and /sbin. 11. Stay where you are, and list the contents of ~. 12. List all the files (including hidden files) in your home directory. 13. List the files in /boot in a human readable format. 14. Create a directory testdir in your home directory. 15. Change to the /etc directory, stay here and create a directory newdir in your home directory. 16. Create in one command the directories ~/dir1/dir2/dir3 (dir3 is a subdirectory from dir2, and dir2 is a subdirectory from dir1 ). 17. Remove the directory testdir. 18. If time permits (or if you are waiting for other students to finish this practice), use and understand pushd and popd. Use the man page of bash to find information about these commands. 81working with directories 8.9. solution: working with directories 1. Display your current directory. pwd 2. Change to the /etc directory. cd /etc 3. Now change to your home directory using only three key presses. cd (and the enter key) 4. Change to the /boot/grub directory using only eleven key presses. cd /boot/grub (use the tab key) 5. Go to the parent directory of the current directory. cd .. (with space between cd and ..) 6. Go to the root directory. cd / 7. List the contents of the root directory. ls 8. List a long listing of the root directory. ls -l 9. Stay where you are, and list the contents of /etc. ls /etc 10. Stay where you are, and list the contents of /bin and /sbin. ls /bin /sbin 11. Stay where you are, and list the contents of ~. ls ~ 12. List all the files (including hidden files) in your home directory. ls -al ~ 13. List the files in /boot in a human readable format. ls -lh /boot 14. Create a directory testdir in your home directory. mkdir ~/testdir 15. Change to the /etc directory, stay here and create a directory newdir in your home directory. 82working with directories cd /etc ; mkdir ~/newdir 16. Create in one command the directories ~/dir1/dir2/dir3 (dir3 is a subdirectory from dir2, and dir2 is a subdirectory from dir1 ). mkdir -p ~/dir1/dir2/dir3 17. Remove the directory testdir. rmdir testdir 18. If time permits (or if you are waiting for other students to finish this practice), use and understand pushd and popd. Use the man page of bash to find information about these commands. man bash /pushd n # opens the manual # searches for pushd # next (do this two/three times) The Bash shell has two built-in commands called pushd and popd. Both commands work with a common stack of previous directories. Pushd adds a directory to the stack and changes to a new current directory, popd removes a directory from the stack and sets the current directory. paul@debian7:/etc$ cd /bin paul@debian7:/bin$ pushd /lib /lib /bin paul@debian7:/lib$ pushd /proc /proc /lib /bin paul@debian7:/proc$ popd /lib /bin paul@debian7:/lib$ popd /bin 83Chapter 9. working with files In this chapter we learn how to recognise, create, remove, copy and move files using commands like file, touch, rm, cp, mv and rename. 84working with files 9.1. all files are case sensitive Files on Linux (or any Unix) are case sensitive. This means that FILE1 is different from file1, and /etc/hosts is different from /etc/Hosts (the latter one does not exist on a typical Linux computer). This screenshot shows the difference between two files, one with upper case W, the other with lower case w. paul@laika:~/Linux$ ls winter.txt Winter.txt paul@laika:~/Linux$ cat winter.txt It is cold. paul@laika:~/Linux$ cat Winter.txt It is very cold! 9.2. everything is a file A directory is a special kind of file, but it is still a (case sensitive!) file. Each terminal window (for example /dev/pts/4), any hard disk or partition (for example /dev/sdb1) and any process are all represented somewhere in the file system as a file. It will become clear throughout this course that everything on Linux is a file. 9.3. file The file utility determines the file type. Linux does not use extensions to determine the file type. The command line does not care whether a file ends in .txt or .pdf. As a system administrator, you should use the file command to determine the file type. Here are some examples on a typical Linux system. paul@laika:~$ file pic33.png pic33.png: PNG image data, 3840 x 1200, 8-bit/color RGBA, non-interlaced paul@laika:~$ file /etc/passwd /etc/passwd: ASCII text paul@laika:~$ file HelloWorld.c HelloWorld.c: ASCII C program text The file command uses a magic file that contains patterns to recognise file types. The magic file is located in /usr/share/file/magic. Type man 5 magic for more information. It is interesting to point out file -s for special files like those in /dev and /proc. root@debian6~# file /dev/sda /dev/sda: block special root@debian6~# file -s /dev/sda /dev/sda: x86 boot sector; partition 1: ID=0x83, active, starthead... root@debian6~# file /proc/cpuinfo /proc/cpuinfo: empty root@debian6~# file -s /proc/cpuinfo /proc/cpuinfo: ASCII C++ program text 85working with files 9.4. touch 9.4.1. create an empty file One easy way to create an empty file is with touch. (We will see many other ways for creating files later in this book.) This screenshot starts with an empty directory, creates two files with touch and the lists those files. paul@debian7:~$ ls -l total 0 paul@debian7:~$ touch file42 paul@debian7:~$ touch file33 paul@debian7:~$ ls -l total 0 -rw-r--r-- 1 paul paul 0 Oct 15 08:57 file33 -rw-r--r-- 1 paul paul 0 Oct 15 08:56 file42 paul@debian7:~$ 9.4.2. touch -t The touch command can set some properties while creating empty files. Can you determine what is set by looking at the next screenshot? If not, check the manual for touch. paul@debian7:~$ touch -t paul@debian7:~$ touch -t paul@debian7:~$ ls -l total 0 -rw-r--r-- 1 paul paul 0 -rw-r--r-- 1 paul paul 0 -rw-r--r-- 1 paul paul 0 -rw-r--r-- 1 paul paul 0 paul@debian7:~$ 200505050000 SinkoDeMayo 130207111630 BigBattle.txt Jul 11 1302 BigBattle.txt Oct 15 08:57 file33 Oct 15 08:56 file42 May 5 2005 SinkoDeMayo 86working with files 9.5. rm 9.5.1. remove forever When you no longer need a file, use rm to remove it. Unlike some graphical user interfaces, the command line in general does not have a waste bin or trash can to recover files. When you use rm to remove a file, the file is gone. Therefore, be careful when removing files! paul@debian7:~$ ls BigBattle.txt file33 file42 SinkoDeMayo paul@debian7:~$ rm BigBattle.txt paul@debian7:~$ ls file33 file42 SinkoDeMayo paul@debian7:~$ 9.5.2. rm -i To prevent yourself from accidentally removing a file, you can type rm -i. paul@debian7:~$ ls file33 file42 SinkoDeMayo paul@debian7:~$ rm -i file33 rm: remove regular empty file `file33'? yes paul@debian7:~$ rm -i SinkoDeMayo rm: remove regular empty file `SinkoDeMayo'? n paul@debian7:~$ ls file42 SinkoDeMayo paul@debian7:~$ 9.5.3. rm -rf By default, rm -r will not remove non-empty directories. However rm accepts several options that will allow you to remove any directory. The rm -rf statement is famous because it will erase anything (providing that you have the permissions to do so). When you are logged on as root, be very careful with rm -rf (the f means force and the r means recursive) since being root implies that permissions don't apply to you. You can literally erase your entire file system by accident. paul@debian7:~$ mkdir test paul@debian7:~$ rm test rm: cannot remove `test': Is a directory paul@debian7:~$ rm -rf test paul@debian7:~$ ls test ls: cannot access test: No such file or directory paul@debian7:~$ 87working with files 9.6. cp 9.6.1. copy one file To copy a file, use cp with a source and a target argument. paul@debian7:~$ ls file42 SinkoDeMayo paul@debian7:~$ cp file42 file42.copy paul@debian7:~$ ls file42 file42.copy SinkoDeMayo 9.6.2. copy to another directory If the target is a directory, then the source files are copied to that target directory. paul@debian7:~$ mkdir dir42 paul@debian7:~$ cp SinkoDeMayo dir42 paul@debian7:~$ ls dir42/ SinkoDeMayo 9.6.3. cp -r To copy complete directories, use cp -r (the -r option forces recursive copying of all files in all subdirectories). paul@debian7:~$ ls dir42 file42 file42.copy SinkoDeMayo paul@debian7:~$ cp -r dir42/ dir33 paul@debian7:~$ ls dir33 dir42 file42 file42.copy SinkoDeMayo paul@debian7:~$ ls dir33/ SinkoDeMayo 9.6.4. copy multiple files to directory You can also use cp to copy multiple files into a directory. In this case, the last argument (a.k.a. the target) must be a directory. paul@debian7:~$ cp file42 file42.copy SinkoDeMayo dir42/ paul@debian7:~$ ls dir42/ file42 file42.copy SinkoDeMayo 9.6.5. cp -i To prevent cp from overwriting existing files, use the -i (for interactive) option. paul@debian7:~$ cp SinkoDeMayo file42 paul@debian7:~$ cp SinkoDeMayo file42 paul@debian7:~$ cp -i SinkoDeMayo file42 cp: overwrite `file42'? n paul@debian7:~$ 88working with files 9.7. mv 9.7.1. rename files with mv Use mv to rename a file or to move the file to another directory. paul@debian7:~$ ls dir33 dir42 file42 file42.copy paul@debian7:~$ mv file42 file33 paul@debian7:~$ ls dir33 dir42 file33 file42.copy paul@debian7:~$ SinkoDeMayo SinkoDeMayo When you need to rename only one file then mv is the preferred command to use. 9.7.2. rename directories with mv The same mv command can be used to rename directories. paul@debian7:~$ ls -l total 8 drwxr-xr-x 2 paul paul 4096 Oct drwxr-xr-x 2 paul paul 4096 Oct -rw-r--r-- 1 paul paul 0 Oct -rw-r--r-- 1 paul paul 0 Oct -rw-r--r-- 1 paul paul 0 May paul@debian7:~$ mv dir33 backup paul@debian7:~$ ls -l total 8 drwxr-xr-x 2 paul paul 4096 Oct drwxr-xr-x 2 paul paul 4096 Oct -rw-r--r-- 1 paul paul 0 Oct -rw-r--r-- 1 paul paul 0 Oct -rw-r--r-- 1 paul paul 0 May paul@debian7:~$ 15 15 15 15 5 09:36 09:36 09:38 09:16 2005 dir33 dir42 file33 file42.copy SinkoDeMayo 15 15 15 15 5 09:36 09:36 09:38 09:16 2005 backup dir42 file33 file42.copy SinkoDeMayo 9.7.3. mv -i The mv also has a -i switch similar to cp and rm. this screenshot shows that mv -i will ask permission to overwrite an existing file. paul@debian7:~$ mv -i file33 SinkoDeMayo mv: overwrite `SinkoDeMayo'? no paul@debian7:~$ 89working with files 9.8. rename 9.8.1. about rename The rename command is one of the rare occasions where the Linux Fundamentals book has to make a distinction between Linux distributions. Almost every command in the Fundamentals part of this book works on almost every Linux computer. But rename is different. Try to use mv whenever you need to rename only a couple of files. 9.8.2. rename on Debian/Ubuntu The rename command on Debian uses regular expressions (regular expression or shor regex are explained in a later chapter) to rename many files at once. Below a rename example that switches all occurrences of txt to png for all file names ending in .txt. paul@debian7:~/test42$ ls abc.txt file33.txt file42.txt paul@debian7:~/test42$ rename 's/\.txt/\.png/' *.txt paul@debian7:~/test42$ ls abc.png file33.png file42.png This second example switches all (first) occurrences of file into document for all file names ending in .png. paul@debian7:~/test42$ ls abc.png file33.png file42.png paul@debian7:~/test42$ rename 's/file/document/' *.png paul@debian7:~/test42$ ls abc.png document33.png document42.png paul@debian7:~/test42$ 9.8.3. rename on CentOS/RHEL/Fedora On Red Hat Enterprise Linux, the syntax of rename is a bit different. The first example below renames all *.conf files replacing any occurrence of .conf with .backup. [paul@centos7 ~]$ touch one.conf two.conf three.conf [paul@centos7 ~]$ rename .conf .backup *.conf [paul@centos7 ~]$ ls one.backup three.backup two.backup [paul@centos7 ~]$ The second example renames all (*) files replacing one with ONE. [paul@centos7 ~]$ ls one.backup three.backup two.backup [paul@centos7 ~]$ rename one ONE * [paul@centos7 ~]$ ls ONE.backup three.backup two.backup [paul@centos7 ~]$ 90working with files 9.9. practice: working with files 1. List the files in the /bin directory 2. Display the type of file of /bin/cat, /etc/passwd and /usr/bin/passwd. 3a. Download wolf.jpg and LinuxFun.pdf from http://linux-training.be (wget http:// linux-training.be/files/studentfiles/wolf.jpg and wget http://linux-training.be/files/books/ LinuxFun.pdf) wget http://linux-training.be/files/studentfiles/wolf.jpg wget http://linux-training.be/files/studentfiles/wolf.png wget http://linux-training.be/files/books/LinuxFun.pdf 3b. Display the type of file of wolf.jpg and LinuxFun.pdf 3c. Rename wolf.jpg to wolf.pdf (use mv). 3d. Display the type of file of wolf.pdf and LinuxFun.pdf. 4. Create a directory ~/touched and enter it. 5. Create the files today.txt and yesterday.txt in touched. 6. Change the date on yesterday.txt to match yesterday's date. 7. Copy yesterday.txt to copy.yesterday.txt 8. Rename copy.yesterday.txt to kim 9. Create a directory called ~/testbackup and copy all files from ~/touched into it. 10. Use one command to remove the directory ~/testbackup and all files into it. 11. Create a directory ~/etcbackup and copy all *.conf files from /etc into it. Did you include all subdirectories of /etc ? 12. Use rename to rename all *.conf files to *.backup . (if you have more than one distro available, try it on all!) 91working with files 9.10. solution: working with files 1. List the files in the /bin directory ls /bin 2. Display the type of file of /bin/cat, /etc/passwd and /usr/bin/passwd. file /bin/cat /etc/passwd /usr/bin/passwd 3a. Download wolf.jpg and LinuxFun.pdf from http://linux-training.be (wget http:// linux-training.be/files/studentfiles/wolf.jpg and wget http://linux-training.be/files/books/ LinuxFun.pdf) wget http://linux-training.be/files/studentfiles/wolf.jpg wget http://linux-training.be/files/studentfiles/wolf.png wget http://linux-training.be/files/books/LinuxFun.pdf 3b. Display the type of file of wolf.jpg and LinuxFun.pdf file wolf.jpg LinuxFun.pdf 3c. Rename wolf.jpg to wolf.pdf (use mv). mv wolf.jpg wolf.pdf 3d. Display the type of file of wolf.pdf and LinuxFun.pdf. file wolf.pdf LinuxFun.pdf 4. Create a directory ~/touched and enter it. mkdir ~/touched ; cd ~/touched 5. Create the files today.txt and yesterday.txt in touched. touch today.txt yesterday.txt 6. Change the date on yesterday.txt to match yesterday's date. touch -t 200810251405 yesterday.txt (substitute 20081025 with yesterday) 7. Copy yesterday.txt to copy.yesterday.txt cp yesterday.txt copy.yesterday.txt 8. Rename copy.yesterday.txt to kim mv copy.yesterday.txt kim 9. Create a directory called ~/testbackup and copy all files from ~/touched into it. mkdir ~/testbackup ; cp -r ~/touched ~/testbackup/ 10. Use one command to remove the directory ~/testbackup and all files into it. rm -rf ~/testbackup 11. Create a directory ~/etcbackup and copy all *.conf files from /etc into it. Did you include all subdirectories of /etc ? 92working with files cp -r /etc/*.conf ~/etcbackup Only *.conf files that are directly in /etc/ are copied. 12. Use rename to rename all *.conf files to *.backup . (if you have more than one distro available, try it on all!) On RHEL: touch 1.conf 2.conf ; rename conf backup *.conf On Debian: touch 1.conf 2.conf ; rename 's/conf/backup/' *.conf 93Chapter 10. working with file contents In this chapter we will look at the contents of text files with head, tail, cat, tac, more, less and strings. We will also get a glimpse of the possibilities of tools like cat on the command line. 94working with file contents 10.1. head You can use head to display the first ten lines of a file. paul@debian7~$ head /etc/passwd root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/bin/sh bin:x:2:2:bin:/bin:/bin/sh sys:x:3:3:sys:/dev:/bin/sh sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/bin/sh man:x:6:12:man:/var/cache/man:/bin/sh lp:x:7:7:lp:/var/spool/lpd:/bin/sh mail:x:8:8:mail:/var/mail:/bin/sh news:x:9:9:news:/var/spool/news:/bin/sh root@debian7~# The head command can also display the first n lines of a file. paul@debian7~$ head -4 /etc/passwd root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/bin/sh bin:x:2:2:bin:/bin:/bin/sh sys:x:3:3:sys:/dev:/bin/sh paul@debian7~$ And head can also display the first n bytes. paul@debian7~$ head -c14 /etc/passwd root:x:0:0:roopaul@debian7~$ 10.2. tail Similar to head, the tail command will display the last ten lines of a file. paul@debian7~$ tail /etc/services vboxd 20012/udp binkp 24554/tcp asp 27374/tcp asp 27374/udp csync2 30865/tcp dircproxy 57000/tcp tfido 60177/tcp fido 60179/tcp # binkp fidonet protocol # Address Search Protocol # # # # cluster synchronization tool Detachable IRC Proxy fidonet EMSI over telnet fidonet EMSI over TCP # Local services paul@debian7~$ You can give tail the number of lines you want to see. paul@debian7~$ tail -3 /etc/services fido 60179/tcp # fidonet EMSI over TCP # Local services paul@debian7~$ The tail command has other useful options, some of which we will use during this course. 95working with file contents 10.3. cat The cat command is one of the most universal tools, yet all it does is copy standard input to standard output. In combination with the shell this can be very powerful and diverse. Some examples will give a glimpse into the possibilities. The first example is simple, you can use cat to display a file on the screen. If the file is longer than the screen, it will scroll to the end. paul@debian8:~$ cat /etc/resolv.conf domain linux-training.be search linux-training.be nameserver 192.168.1.42 10.3.1. concatenate cat is short for concatenate. One of the basic uses of cat is to concatenate files into a bigger (or complete) file. paul@debian8:~$ paul@debian8:~$ paul@debian8:~$ paul@debian8:~$ one paul@debian8:~$ two paul@debian8:~$ three paul@debian8:~$ one two three paul@debian8:~$ paul@debian8:~$ one two three paul@debian8:~$ echo one >part1 echo two >part2 echo three >part3 cat part1 cat part2 cat part3 cat part1 part2 part3 cat part1 part2 part3 >all cat all 10.3.2. create files You can use cat to create flat text files. Type the cat > winter.txt command as shown in the screenshot below. Then type one or more lines, finishing each line with the enter key. After the last line, type and hold the Control (Ctrl) key and press d. paul@debian8:~$ It is very cold paul@debian8:~$ It is very cold paul@debian8:~$ cat > winter.txt today! cat winter.txt today! The Ctrl d key combination will send an EOF (End of File) to the running process ending the cat command. 96working with file contents 10.3.3. custom end marker You can choose an end marker for cat with << as is shown in this screenshot. This construction is called a here directive and will end the cat command. paul@debian8:~$ cat > hot.txt < It is hot today! > Yes it is summer. > stop paul@debian8:~$ cat hot.txt It is hot today! Yes it is summer. paul@debian8:~$ 10.3.4. copy files In the third example you will see that cat can be used to copy files. We will explain in detail what happens here in the bash shell chapter. paul@debian8:~$ It is very cold paul@debian8:~$ paul@debian8:~$ It is very cold paul@debian8:~$ cat winter.txt today! cat winter.txt > cold.txt cat cold.txt today! 10.4. tac Just one example will show you the purpose of tac (cat backwards). paul@debian8:~$ cat count one two three four paul@debian8:~$ tac count four three two one 97working with file contents 10.5. more and less The more command is useful for displaying files that take up more than one screen. More will allow you to see the contents of the file page by page. Use the space bar to see the next page, or q to quit. Some people prefer the less command to more. 10.6. strings With the strings command you can display readable ascii strings found in (binary) files. This example locates the ls binary then displays readable strings in the binary file (output is truncated). paul@laika:~$ which ls /bin/ls paul@laika:~$ strings /bin/ls /lib/ld-linux.so.2 librt.so.1 __gmon_start__ _Jv_RegisterClasses clock_gettime libacl.so.1 ... 98working with file contents 10.7. practice: file contents 1. Display the first 12 lines of /etc/services. 2. Display the last line of /etc/passwd. 3. Use cat to create a file named count.txt that looks like this: One Two Three Four Five 4. Use cp to make a backup of this file to cnt.txt. 5. Use cat to make a backup of this file to catcnt.txt. 6. Display catcnt.txt, but with all lines in reverse order (the last line first). 7. Use more to display /etc/services. 8. Display the readable character strings from the /usr/bin/passwd command. 9. Use ls to find the biggest file in /etc. 10. Open two terminal windows (or tabs) and make sure you are in the same directory in both. Type echo this is the first line > tailing.txt in the first terminal, then issue tail -f tailing.txt in the second terminal. Now go back to the first terminal and type echo This is another line >> tailing.txt (note the double >>), verify that the tail -f in the second terminal shows both lines. Stop the tail -f with Ctrl-C. 11. Use cat to create a file named tailing.txt that contains the contents of tailing.txt followed by the contents of /etc/passwd. 12. Use cat to create a file named tailing.txt that contains the contents of tailing.txt preceded by the contents of /etc/passwd. 99working with file contents 10.8. solution: file contents 1. Display the first 12 lines of /etc/services. head -12 /etc/services 2. Display the last line of /etc/passwd. tail -1 /etc/passwd 3. Use cat to create a file named count.txt that looks like this: cat > count.txt One Two Three Four Five (followed by Ctrl-d) 4. Use cp to make a backup of this file to cnt.txt. cp count.txt cnt.txt 5. Use cat to make a backup of this file to catcnt.txt. cat count.txt > catcnt.txt 6. Display catcnt.txt, but with all lines in reverse order (the last line first). tac catcnt.txt 7. Use more to display /etc/services. more /etc/services 8. Display the readable character strings from the /usr/bin/passwd command. strings /usr/bin/passwd 9. Use ls to find the biggest file in /etc. ls -lrS /etc 10. Open two terminal windows (or tabs) and make sure you are in the same directory in both. Type echo this is the first line > tailing.txt in the first terminal, then issue tail -f tailing.txt in the second terminal. Now go back to the first terminal and type echo This is another line >> tailing.txt (note the double >>), verify that the tail -f in the second terminal shows both lines. Stop the tail -f with Ctrl-C. 11. Use cat to create a file named tailing.txt that contains the contents of tailing.txt followed by the contents of /etc/passwd. cat /etc/passwd >> tailing.txt 12. Use cat to create a file named tailing.txt that contains the contents of tailing.txt preceded by the contents of /etc/passwd. mv tailing.txt tmp.txt ; cat /etc/passwd tmp.txt > tailing.txt 100Chapter 11. the Linux file tree This chapter takes a look at the most common directories in the Linux file tree. It also shows that on Unix everything is a file. 101the Linux file tree 11.1. filesystem hierarchy standard Many Linux distributions partially follow the Filesystem Hierarchy Standard. The FHS may help make more Unix/Linux file system trees conform better in the future. The FHS is available online at http://www.pathname.com/fhs/ where we read: "The filesystem hierarchy standard has been designed to be used by Unix distribution developers, package developers, and system implementers. However, it is primarily intended to be a reference and is not a tutorial on how to manage a Unix filesystem or directory hierarchy." 11.2. man hier There are some differences in the filesystems between Linux distributions. For help about your machine, enter man hier to find information about the file system hierarchy. This manual will explain the directory structure on your computer. 11.3. the root directory / All Linux systems have a directory structure that starts at the root directory. The root directory is represented by a forward slash, like this: /. Everything that exists on your Linux system can be found below this root directory. Let's take a brief look at the contents of the root directory. [paul@RHELv4u3 ~]$ ls / bin dev home media mnt boot etc lib misc opt proc root sbin selinux 102 srv sys tftpboot tmp usr varthe Linux file tree 11.4. binary directories Binaries are files that contain compiled source code (or machine code). Binaries can be executed on the computer. Sometimes binaries are called executables. 11.4.1. /bin The /bin directory contains binaries for use by all users. According to the FHS the /bin directory should contain /bin/cat and /bin/date (among others). In the screenshot below you see common Unix/Linux commands like cat, cp, cpio, date, dd, echo, grep, and so on. Many of these will be covered in this book. paul@laika:~$ ls /bin archdetect egrep autopartition false bash fgconsole bunzip2 fgrep bzcat fuser bzcmp fusermount bzdiff get_mountoptions bzegrep grep bzexe gunzip bzfgrep gzexe bzgrep gzip bzip2 hostname bzip2recover hw-detect bzless ip bzmore kbd_mode cat kill ... mt mt-gnu mv nano nc nc.traditional netcat netstat ntfs-3g ntfs-3g.probe parted_devices parted_server partman partman-commit perform_recipe pidof setupcon sh sh.distrib sleep stralign stty su sync sysfs tailf tar tempfile touch true ulockmgr umount 11.4.2. other /bin directories You can find a /bin subdirectory in many other directories. A user named serena could put her own programs in /home/serena/bin. Some applications, often when installed directly from source will put themselves in /opt. A samba server installation can use /opt/samba/bin to store its binaries. 11.4.3. /sbin /sbin contains binaries to configure the operating system. Many of the system binaries require root privilege to perform certain tasks. Below a screenshot containing system binaries to change the ip address, partition a disk and create an ext4 file system. paul@ubu1010:~$ ls -l /sbin/ifconfig /sbin/fdisk /sbin/mkfs.ext4 -rwxr-xr-x 1 root root 97172 2011-02-02 09:56 /sbin/fdisk -rwxr-xr-x 1 root root 65708 2010-07-02 09:27 /sbin/ifconfig -rwxr-xr-x 5 root root 55140 2010-08-18 18:01 /sbin/mkfs.ext4 103the Linux file tree 11.4.4. /lib Binaries found in /bin and /sbin often use shared libraries located in /lib. Below is a screenshot of the partial contents of /lib. paul@laika:~$ ls /lib/libc* /lib/libc-2.5.so /lib/libcfont.so.0.0.0 /lib/libcap.so.1 /lib/libcidn-2.5.so /lib/libcap.so.1.10 /lib/libcidn.so.1 /lib/libcfont.so.0 /lib/libcom_err.so.2 /lib/libcom_err.so.2.1 /lib/libconsole.so.0 /lib/libconsole.so.0.0.0 /lib/libcrypt-2.5.so /lib/modules Typically, the Linux kernel loads kernel modules from /lib/modules/$kernel-version/. This directory is discussed in detail in the Linux kernel chapter. /lib32 and /lib64 We currently are in a transition between 32-bit and 64-bit systems. Therefore, you may encounter directories named /lib32 and /lib64 which clarify the register size used during compilation time of the libraries. A 64-bit computer may have some 32-bit binaries and libraries for compatibility with legacy applications. This screenshot uses the file utility to demonstrate the difference. paul@laika:~$ file /lib32/libc-2.5.so /lib32/libc-2.5.so: ELF 32-bit LSB shared object, Intel 80386, \ version 1 (SYSV), for GNU/Linux 2.6.0, stripped paul@laika:~$ file /lib64/libcap.so.1.10 /lib64/libcap.so.1.10: ELF 64-bit LSB shared object, AMD x86-64, \ version 1 (SYSV), stripped The ELF (Executable and Linkable Format) is used in almost every Unix-like operating system since System V. 11.4.5. /opt The purpose of /opt is to store optional software. In many cases this is software from outside the distribution repository. You may find an empty /opt directory on many systems. A large package can install all its files in /bin, /lib, /etc subdirectories within /opt/ $packagename/. If for example the package is called wp, then it installs in /opt/wp, putting binaries in /opt/wp/bin and manpages in /opt/wp/man. 104the Linux file tree 11.5. configuration directories 11.5.1. /boot The /boot directory contains all files needed to boot the computer. These files don't change very often. On Linux systems you typically find the /boot/grub directory here. /boot/grub contains /boot/grub/grub.cfg (older systems may still have /boot/grub/grub.conf) which defines the boot menu that is displayed before the kernel starts. 11.5.2. /etc All of the machine-specific configuration files should be located in /etc. Historically /etc stood for etcetera, today people often use the Editable Text Configuration backronym. Many times the name of a configuration files is the same as the application, daemon, or protocol with .conf added as the extension. paul@laika:~$ ls /etc/*.conf /etc/adduser.conf /etc/ld.so.conf /etc/brltty.conf /etc/lftp.conf /etc/ccertificates.conf /etc/libao.conf /etc/cvs-cron.conf /etc/logrotate.conf /etc/ddclient.conf /etc/ltrace.conf /etc/debconf.conf /etc/mke2fs.conf /etc/deluser.conf /etc/netscsid.conf /etc/fdmount.conf /etc/nsswitch.conf /etc/hdparm.conf /etc/pam.conf /etc/host.conf /etc/pnm2ppa.conf /etc/inetd.conf /etc/povray.conf /etc/kernel-img.conf /etc/resolv.conf paul@laika:~$ /etc/scrollkeeper.conf /etc/sysctl.conf /etc/syslog.conf /etc/ucf.conf /etc/uniconf.conf /etc/updatedb.conf /etc/usplash.conf /etc/uswsusp.conf /etc/vnc.conf /etc/wodim.conf /etc/wvdial.conf There is much more to be found in /etc. /etc/init.d/ A lot of Unix/Linux distributions have an /etc/init.d directory that contains scripts to start and stop daemons. This directory could disappear as Linux migrates to systems that replace the old init way of starting all daemons. /etc/X11/ The graphical display (aka X Window System or just X) is driven by software from the X.org foundation. The configuration file for your graphical display is /etc/X11/xorg.conf. /etc/skel/ The skeleton directory /etc/skel is copied to the home directory of a newly created user. It usually contains hidden files like a .bashrc script. /etc/sysconfig/ This directory, which is not mentioned in the FHS, contains a lot of Red Hat Enterprise Linux configuration files. We will discuss some of them in greater detail. The screenshot below is the /etc/sysconfig directory from RHELv4u4 with everything installed. 105the Linux file tree paul@RHELv4u4:~$ ls /etc/sysconfig/ apmd firstboot irda apm-scripts grub irqbalance authconfig hidd keyboard autofs httpd kudzu bluetooth hwconf lm_sensors clock i18n mouse console init mouse.B crond installinfo named desktop ipmi netdump diskdump iptables netdump_id_dsa dund iptables-cfg netdump_id_dsa.p paul@RHELv4u4:~$ network networking ntpd openib.conf pand pcmcia pgsql prelink rawdevices rhn samba saslauthd selinux spamassassin squid syslog sys-config-sec sys-config-users sys-logviewer tux vncservers xinetd The file /etc/sysconfig/firstboot tells the Red Hat Setup Agent not to run at boot time. If you want to run the Red Hat Setup Agent at the next reboot, then simply remove this file, and run chkconfig --level 5 firstboot on. The Red Hat Setup Agent allows you to install the latest updates, create a user account, join the Red Hat Network and more. It will then create the /etc/sysconfig/firstboot file again. paul@RHELv4u4:~$ cat /etc/sysconfig/firstboot RUN_FIRSTBOOT=NO The /etc/sysconfig/harddisks file contains some parameters to tune the hard disks. The file explains itself. You can see hardware detected by kudzu in /etc/sysconfig/hwconf. Kudzu is software from Red Hat for automatic discovery and configuration of hardware. The keyboard type and keymap table are set in the /etc/sysconfig/keyboard file. For more console keyboard information, check the manual pages of keymaps(5), dumpkeys(1), loadkeys(1) and the directory /lib/kbd/keymaps/. root@RHELv4u4:/etc/sysconfig# cat keyboard KEYBOARDTYPE="pc" KEYTABLE="us" We will discuss networking files in this directory in the networking chapter. 106the Linux file tree 11.6. data directories 11.6.1. /home Users can store personal or project data under /home. It is common (but not mandatory by the fhs) practice to name the users home directory after the user name in the format /home/ $USERNAME. For example: paul@ubu606:~$ ls /home geert annik sandra paul tom Besides giving every user (or every project or group) a location to store personal files, the home directory of a user also serves as a location to store the user profile. A typical Unix user profile contains many hidden files (files whose file name starts with a dot). The hidden files of the Unix user profiles contain settings specific for that user. paul@ubu606:~$ ls -d /home/paul/.* /home/paul/. /home/paul/.bash_profile /home/paul/.. /home/paul/.bashrc /home/paul/.bash_history /home/paul/.lesshst /home/paul/.ssh /home/paul/.viminfo 11.6.2. /root On many systems /root is the default location for personal data and profile of the root user. If it does not exist by default, then some administrators create it. 11.6.3. /srv You may use /srv for data that is served by your system. The FHS allows locating cvs, rsync, ftp and www data in this location. The FHS also approves administrative naming in / srv, like /srv/project55/ftp and /srv/sales/www. On Sun Solaris (or Oracle Solaris) /export is used for this purpose. 11.6.4. /media The /media directory serves as a mount point for removable media devices such as CD- ROM's, digital cameras, and various usb-attached devices. Since /media is rather new in the Unix world, you could very well encounter systems running without this directory. Solaris 9 does not have it, Solaris 10 does. Most Linux distributions today mount all removable media in /media. paul@debian5:~$ ls /media/ cdrom cdrom0 usbdisk 11.6.5. /mnt The /mnt directory should be empty and should only be used for temporary mount points (according to the FHS). 107the Linux file tree Unix and Linux administrators used to create many directories here, like /mnt/something/. You likely will encounter many systems with more than one directory created and/or mounted inside /mnt to be used for various local and remote filesystems. 11.6.6. /tmp Applications and users should use /tmp to store temporary data when needed. Data stored in /tmp may use either disk space or RAM. Both of which are managed by the operating system. Never use /tmp to store data that is important or which you wish to archive. 108the Linux file tree 11.7. in memory directories 11.7.1. /dev Device files in /dev appear to be ordinary files, but are not actually located on the hard disk. The /dev directory is populated with files as the kernel is recognising hardware. common physical devices Common hardware such as hard disk devices are represented by device files in /dev. Below a screenshot of SATA device files on a laptop and then IDE attached drives on a desktop. (The detailed meaning of these devices will be discussed later.) # # SATA or SCSI or USB # paul@laika:~$ ls /dev/sd* /dev/sda /dev/sda1 /dev/sda2 /dev/sda3 /dev/sdb /dev/sdb1 /dev/sdb2 # # IDE or ATAPI # paul@barry:~$ ls /dev/hd* /dev/hda /dev/hda1 /dev/hda2 /dev/hdb /dev/hdb1 /dev/hdb2 /dev/hdc Besides representing physical hardware, some device files are special. These special devices can be very useful. /dev/tty and /dev/pts For example, /dev/tty1 represents a terminal or console attached to the system. (Don't break your head on the exact terminology of 'terminal' or 'console', what we mean here is a command line interface.) When typing commands in a terminal that is part of a graphical interface like Gnome or KDE, then your terminal will be represented as /dev/pts/1 (1 can be another number). /dev/null On Linux you will find other special devices such as /dev/null which can be considered a black hole; it has unlimited storage, but nothing can be retrieved from it. Technically speaking, anything written to /dev/null will be discarded. /dev/null can be useful to discard unwanted output from commands. /dev/null is not a good location to store your backups ;-). 11.7.2. /proc conversation with the kernel /proc is another special directory, appearing to be ordinary files, but not taking up disk space. It is actually a view of the kernel, or better, what the kernel manages, and is a means to interact with it directly. /proc is a proc filesystem. paul@RHELv4u4:~$ mount -t proc 109the Linux file tree none on /proc type proc (rw) When listing the /proc directory you will see many numbers (on any Unix) and some interesting files (on Linux) mul@laika:~$ ls /proc 1 2339 4724 5418 10175 2523 4729 5421 10211 2783 4741 5658 10239 2975 4873 5661 141 29775 4874 5665 15045 29792 4878 5927 1519 2997 4879 6 1548 3 4881 6032 1551 30228 4882 6033 1554 3069 5 6145 1557 31422 5073 6298 1606 3149 5147 6414 180 31507 5203 6418 181 3189 5206 6419 182 3193 5228 6420 18898 3246 5272 6421 19799 3248 5291 6422 19803 3253 5294 6423 19804 3372 5356 6424 1987 4 5370 6425 1989 42 5379 6426 2 45 5380 6430 20845 4542 5412 6450 221 46 5414 6551 2338 4704 5416 6568 6587 6596 6599 6638 6652 6719 6736 6737 6755 6762 6774 6816 6991 6993 6996 7157 7163 7164 7171 7175 7188 7189 7191 7192 7199 7201 7204 7206 7214 7216 7218 7223 7224 7227 7260 7267 7275 7282 7298 7319 7330 7345 7513 7525 7529 9964 acpi asound buddyinfo bus cmdline cpuinfo crypto devices diskstats dma driver execdomains fb filesystems fs ide interrupts iomem ioports irq kallsyms kcore key-users kmsg loadavg locks meminfo misc modules mounts mtrr net pagetypeinfo partitions sched_debug scsi self slabinfo stat swaps sys sysrq-trigger sysvipc timer_list timer_stats tty uptime version version_signature vmcore vmnet vmstat zoneinfo Let's investigate the file properties inside /proc. Looking at the date and time will display the current date and time showing the files are constantly updated (a view on the kernel). paul@RHELv4u4:~$ date Mon Jan 29 18:06:32 EST 2007 paul@RHELv4u4:~$ ls -al /proc/cpuinfo -r--r--r-- 1 root root 0 Jan 29 18:06 /proc/cpuinfo paul@RHELv4u4:~$ paul@RHELv4u4:~$ ...time passes... paul@RHELv4u4:~$ paul@RHELv4u4:~$ date Mon Jan 29 18:10:00 EST 2007 paul@RHELv4u4:~$ ls -al /proc/cpuinfo -r--r--r-- 1 root root 0 Jan 29 18:10 /proc/cpuinfo Most files in /proc are 0 bytes, yet they contain data--sometimes a lot of data. You can see this by executing cat on files like /proc/cpuinfo, which contains information about the CPU. paul@RHELv4u4:~$ file /proc/cpuinfo /proc/cpuinfo: empty paul@RHELv4u4:~$ cat /proc/cpuinfo processor : 0 vendor_id : AuthenticAMD cpu family : 15 model : 43 110the Linux file tree model name stepping cpu MHz cache size fdiv_bug hlt_bug f00f_bug coma_bug fpu fpu_exception cpuid level wp flags bogomips : : : : : : : : : : : : : : AMD Athlon(tm) 64 X2 Dual Core Processor 4600+ 1 2398.628 512 KB no no no no yes yes 1 yes fpu vme de pse tsc msr pae mce cx8 apic mtrr pge... 4803.54 Just for fun, here is /proc/cpuinfo on a Sun Sunblade 1000... paul@pasha:~$ cat /proc/cpuinfo cpu : TI UltraSparc III (Cheetah) fpu : UltraSparc III integrated FPU promlib : Version 3 Revision 2 prom : 4.2.2 type : sun4u ncpus probed : 2 ncpus active : 2 Cpu0Bogo : 498.68 Cpu0ClkTck : 000000002cb41780 Cpu1Bogo : 498.68 Cpu1ClkTck : 000000002cb41780 MMU Type : Cheetah State: CPU0: online CPU1: online Most of the files in /proc are read only, some require root privileges, some files are writable, and many files in /proc/sys are writable. Let's discuss some of the files in /proc. 111the Linux file tree /proc/interrupts On the x86 architecture, /proc/interrupts displays the interrupts. paul@RHELv4u4:~$ cat /proc/interrupts CPU0 0: 13876877 IO-APIC-edge timer 1: 15 IO-APIC-edge i8042 8: 1 IO-APIC-edge rtc 9: 0 IO-APIC-level acpi 12: 67 IO-APIC-edge i8042 14: 128 IO-APIC-edge ide0 15: 124320 IO-APIC-edge ide1 169: 111993 IO-APIC-level ioc0 177: 2428 IO-APIC-level eth0 NMI: 0 LOC: 13878037 ERR: 0 MIS: 0 On a machine with two CPU's, the file looks like this. paul@laika:~$ cat /proc/interrupts CPU0 CPU1 0: 860013 0 IO-APIC-edge 1: 4533 0 IO-APIC-edge 7: 0 0 IO-APIC-edge 8: 6588227 0 IO-APIC-edge 10: 2314 0 IO-APIC-fasteoi 12: 133 0 IO-APIC-edge 14: 0 0 IO-APIC-edge 15: 72269 0 IO-APIC-edge 18: 1 0 IO-APIC-fasteoi 19: 115036 0 IO-APIC-fasteoi 20: 126871 0 IO-APIC-fasteoi 21: 30204 0 IO-APIC-fasteoi 22: 1334 0 IO-APIC-fasteoi 24: 234739 0 IO-APIC-fasteoi NMI: 72 42 LOC: 860000 859994 ERR: 0 timer i8042 parport0 rtc acpi i8042 libata libata yenta eth0 libata, ohci1394 ehci_hcd:usb1, uhci_hcd:usb2 saa7133[0], saa7133[0] nvidia /proc/kcore The physical memory is represented in /proc/kcore. Do not try to cat this file, instead use a debugger. The size of /proc/kcore is the same as your physical memory, plus four bytes. paul@laika:~$ ls -lh /proc/kcore -r-------- 1 root root 2.0G 2007-01-30 08:57 /proc/kcore paul@laika:~$ 112the Linux file tree 11.7.3. /sys Linux 2.6 hot plugging The /sys directory was created for the Linux 2.6 kernel. Since 2.6, Linux uses sysfs to support usb and IEEE 1394 (FireWire) hot plug devices. See the manual pages of udev(8) (the successor of devfs) and hotplug(8) for more info (or visit http://linux- hotplug.sourceforge.net/ ). Basically the /sys directory contains kernel information about hardware. 113the Linux file tree 11.8. /usr Unix System Resources Although /usr is pronounced like user, remember that it stands for Unix System Resources. The /usr hierarchy should contain shareable, read only data. Some people choose to mount /usr as read only. This can be done from its own partition or from a read only NFS share (NFS is discussed later). 11.8.1. /usr/bin The /usr/bin directory contains a lot of commands. paul@deb508:~$ ls /usr/bin | wc -l 1395 (On Solaris the /bin directory is a symbolic link to /usr/bin.) 11.8.2. /usr/include The /usr/include directory contains general use include files for C. paul@ubu1010:~$ ls /usr/include/ aalib.h expat_config.h af_vfs.h expat_external.h aio.h expat.h AL fcntl.h aliases.h features.h ... math.h mcheck.h memory.h menu.h mntent.h search.h semaphore.h setjmp.h sgtty.h shadow.h 11.8.3. /usr/lib The /usr/lib directory contains libraries that are not directly executed by users or scripts. paul@deb508:~$ ls /usr/lib | head -7 4Suite ao apt arj aspell avahi bonobo 11.8.4. /usr/local The /usr/local directory can be used by an administrator to install software locally. paul@deb508:~$ ls /usr/local/ bin etc games include lib man paul@deb508:~$ du -sh /usr/local/ 128K /usr/local/ sbin share src 11.8.5. /usr/share The /usr/share directory contains architecture independent data. As you can see, this is a fairly large directory. paul@deb508:~$ ls /usr/share/ | wc -l 114the Linux file tree 263 paul@deb508:~$ du -sh /usr/share/ 1.3G /usr/share/ This directory typically contains /usr/share/man for manual pages. paul@deb508:~$ ls /usr/share/man cs fr hu it.UTF-8 man2 man6 pl.ISO8859-2 sv de fr.ISO8859-1 id ja man3 man7 pl.UTF-8 tr es fr.UTF-8 it ko man4 man8 pt_BR zh_CN fi gl it.ISO8859-1 man1 man5 pl ru zh_TW And it contains /usr/share/games for all static game data (so no high-scores or play logs). paul@ubu1010:~$ ls /usr/share/games/ openttd wesnoth 11.8.6. /usr/src The /usr/src directory is the recommended location for kernel source files. paul@deb508:~$ ls -l /usr/src/ total 12 drwxr-xr-x 4 root root 4096 2011-02-01 14:43 linux-headers-2.6.26-2-686 drwxr-xr-x 18 root root 4096 2011-02-01 14:43 linux-headers-2.6.26-2-common drwxr-xr-x 3 root root 4096 2009-10-28 16:01 linux-kbuild-2.6.26 115the Linux file tree 11.9. /var variable data Files that are unpredictable in size, such as log, cache and spool files, should be located in /var. 11.9.1. /var/log The /var/log directory serves as a central point to contain all log files. [paul@RHEL4b ~]$ ls /var/log acpid cron.2 maillog.2 amanda cron.3 maillog.3 anaconda.log cron.4 maillog.4 anaconda.syslog cups mailman anaconda.xlog dmesg messages audit exim messages.1 boot.log gdm messages.2 boot.log.1 httpd messages.3 boot.log.2 iiim messages.4 boot.log.3 iptraf mysqld.log boot.log.4 lastlog news canna mail pgsql cron maillog ppp cron.1 maillog.1 prelink.log quagga radius rpmpkgs rpmpkgs.1 rpmpkgs.2 rpmpkgs.3 rpmpkgs.4 sa samba scrollkeeper.log secure secure.1 secure.2 secure.3 secure.4 spooler spooler.1 spooler.2 spooler.3 spooler.4 squid uucp vbox vmware-tools-guestd wtmp wtmp.1 Xorg.0.log Xorg.0.log.old 11.9.2. /var/log/messages A typical first file to check when troubleshooting on Red Hat (and derivatives) is the /var/ log/messages file. By default this file will contain information on what just happened to the system. The file is called /var/log/syslog on Debian and Ubuntu. [root@RHEL4b ~]# tail /var/log/messages Jul 30 05:13:56 anacron: anacron startup succeeded Jul 30 05:13:56 atd: atd startup succeeded Jul 30 05:13:57 messagebus: messagebus startup succeeded Jul 30 05:13:57 cups-config-daemon: cups-config-daemon startup succeeded Jul 30 05:13:58 haldaemon: haldaemon startup succeeded Jul 30 05:14:00 fstab-sync[3560]: removed all generated mount points Jul 30 05:14:01 fstab-sync[3628]: added mount point /media/cdrom for... Jul 30 05:14:01 fstab-sync[3646]: added mount point /media/floppy for... Jul 30 05:16:46 sshd(pam_unix)[3662]: session opened for user paul by... Jul 30 06:06:37 su(pam_unix)[3904]: session opened for user root by paul 11.9.3. /var/cache The /var/cache directory can contain cache data for several applications. paul@ubu1010:~$ ls /var/cache/ apt dictionaries-common binfmts flashplugin-installer cups fontconfig debconf fonts gdm hald jockey ldconfig man pm-utils pppconfig samba software-center 11.9.4. /var/spool The /var/spool directory typically contains spool directories for mail and cron, but also serves as a parent directory for other spool files (for example print spool files). 116the Linux file tree 11.9.5. /var/lib The /var/lib directory contains application state information. Red Hat Enterprise Linux for example keeps files pertaining to rpm in /var/lib/rpm/. 11.9.6. /var/... /var also contains Process ID files in /var/run (soon to be replaced with /run) and temporary files that survive a reboot in /var/tmp and information about file locks in /var/lock. There will be more examples of /var usage further in this book. 117the Linux file tree 11.10. practice: file system tree 1. Does the file /bin/cat exist ? What about /bin/dd and /bin/echo. What is the type of these files ? 2. What is the size of the Linux kernel file(s) (vmlinu*) in /boot ? 3. Create a directory ~/test. Then issue the following commands: cd ~/test dd if=/dev/zero of=zeroes.txt count=1 bs=100 od zeroes.txt dd will copy one times (count=1) a block of size 100 bytes (bs=100) from the file /dev/zero to ~/test/zeroes.txt. Can you describe the functionality of /dev/zero ? 4. Now issue the following command: dd if=/dev/random of=random.txt count=1 bs=100 ; od random.txt dd will copy one times (count=1) a block of size 100 bytes (bs=100) from the file /dev/ random to ~/test/random.txt. Can you describe the functionality of /dev/random ? 5. Issue the following two commands, and look at the first character of each output line. ls -l /dev/sd* /dev/hd* ls -l /dev/tty* /dev/input/mou* The first ls will show block(b) devices, the second ls shows character(c) devices. Can you tell the difference between block and character devices ? 6. Use cat to display /etc/hosts and /etc/resolv.conf. What is your idea about the purpose of these files ? 7. Are there any files in /etc/skel/ ? Check also for hidden files. 8. Display /proc/cpuinfo. On what architecture is your Linux running ? 9. Display /proc/interrupts. What is the size of this file ? Where is this file stored ? 10. Can you enter the /root directory ? Are there (hidden) files ? 11. Are ifconfig, fdisk, parted, shutdown and grub-install present in /sbin ? Why are these binaries in /sbin and not in /bin ? 12. Is /var/log a file or a directory ? What about /var/spool ? 13. Open two command prompts (Ctrl-Shift-T in gnome-terminal) or terminals (Ctrl-Alt-F1, Ctrl-Alt-F2, ...) and issue the who am i in both. Then try to echo a word from one terminal to the other. 118the Linux file tree 14. Read the man page of random and explain the difference between /dev/random and / dev/urandom. 119the Linux file tree 11.11. solution: file system tree 1. Does the file /bin/cat exist ? What about /bin/dd and /bin/echo. What is the type of these files ? ls /bin/cat ; file /bin/cat ls /bin/dd ; file /bin/dd ls /bin/echo ; file /bin/echo 2. What is the size of the Linux kernel file(s) (vmlinu*) in /boot ? ls -lh /boot/vm* 3. Create a directory ~/test. Then issue the following commands: cd ~/test dd if=/dev/zero of=zeroes.txt count=1 bs=100 od zeroes.txt dd will copy one times (count=1) a block of size 100 bytes (bs=100) from the file /dev/zero to ~/test/zeroes.txt. Can you describe the functionality of /dev/zero ? /dev/zero is a Linux special device. It can be considered a source of zeroes. You cannot send something to /dev/zero, but you can read zeroes from it. 4. Now issue the following command: dd if=/dev/random of=random.txt count=1 bs=100 ; od random.txt dd will copy one times (count=1) a block of size 100 bytes (bs=100) from the file /dev/ random to ~/test/random.txt. Can you describe the functionality of /dev/random ? /dev/random acts as a random number generator on your Linux machine. 5. Issue the following two commands, and look at the first character of each output line. ls -l /dev/sd* /dev/hd* ls -l /dev/tty* /dev/input/mou* The first ls will show block(b) devices, the second ls shows character(c) devices. Can you tell the difference between block and character devices ? Block devices are always written to (or read from) in blocks. For hard disks, blocks of 512 bytes are common. Character devices act as a stream of characters (or bytes). Mouse and keyboard are typical character devices. 6. Use cat to display /etc/hosts and /etc/resolv.conf. What is your idea about the purpose of these files ? /etc/hosts contains hostnames with their ip address /etc/resolv.conf should contain the ip address of a DNS name server. 120the Linux file tree 7. Are there any files in /etc/skel/ ? Check also for hidden files. Issue "ls -al /etc/skel/". Yes, there should be hidden files there. 8. Display /proc/cpuinfo. On what architecture is your Linux running ? The file should contain at least one line with Intel or other cpu. 9. Display /proc/interrupts. What is the size of this file ? Where is this file stored ? The size is zero, yet the file contains data. It is not stored anywhere because /proc is a virtual file system that allows you to talk with the kernel. (If you answered "stored in RAM- memory, that is also correct...). 10. Can you enter the /root directory ? Are there (hidden) files ? Try "cd /root". The /root directory is not accessible for normal users on most modern Linux sys 11. Are ifconfig, fdisk, parted, shutdown and grub-install present in /sbin ? Why are these binaries in /sbin and not in /bin ? Because those files are only meant for system administrators. 12. Is /var/log a file or a directory ? What about /var/spool ? Both are directories. 13. Open two command prompts (Ctrl-Shift-T in gnome-terminal) or terminals (Ctrl-Alt-F1, Ctrl-Alt-F2, ...) and issue the who am i in both. Then try to echo a word from one terminal to the other. tty-terminal: echo Hello > /dev/tty1 pts-terminal: echo Hello > /dev/pts/1 14. Read the man page of random and explain the difference between /dev/random and / dev/urandom. man 4 random 121Part IV. shell expansionTable of Contents 167Chapter 12. commands and arguments This chapter introduces you to shell expansion by taking a close look at commands and arguments. Knowing shell expansion is important because many commands on your Linux system are processed and most likely changed by the shell before they are executed. The command line interface or shell used on most Linux systems is called bash, which stands for Bourne again shell. The bash shell incorporates features from sh (the original Bourne shell), csh (the C shell), and ksh (the Korn shell). This chapter frequently uses the echo command to demonstrate shell features. The echo command is very simple: it echoes the input that it receives. paul@laika:~$ echo Burtonville Burtonville paul@laika:~$ echo Smurfs are blue Smurfs are blue 125commands and arguments 12.1. arguments One of the primary features of a shell is to perform a command line scan. When you enter a command at the shell's command prompt and press the enter key, then the shell will start scanning that line, cutting it up in arguments. While scanning the line, the shell may make many changes to the arguments you typed. This process is called shell expansion. When the shell has finished scanning and modifying that line, then it will be executed. 12.2. white space removal Parts that are separated by one or more consecutive white spaces (or tabs) are considered separate arguments, any white space is removed. The first argument is the command to be executed, the other arguments are given to the command. The shell effectively cuts your command into one or more arguments. This explains why the following four different command lines are the same after shell expansion. [paul@RHELv4u3 Hello World [paul@RHELv4u3 Hello World [paul@RHELv4u3 Hello World [paul@RHELv4u3 Hello World ~]$ echo Hello World ~]$ echo Hello ~]$ echo ~]$ echo Hello World World Hello World The echo command will display each argument it receives from the shell. The echo command will also add a new white space between the arguments it received. 126commands and arguments 12.3. single quotes You can prevent the removal of white spaces by quoting the spaces. The contents of the quoted string are considered as one argument. In the screenshot below the echo receives only one argument. [paul@RHEL4b ~]$ echo 'A line with A line with single quotes [paul@RHEL4b ~]$ single quotes' 12.4. double quotes You can also prevent the removal of white spaces by double quoting the spaces. Same as above, echo only receives one argument. [paul@RHEL4b ~]$ echo "A line with A line with double quotes [paul@RHEL4b ~]$ double quotes" Later in this book, when discussing variables we will see important differences between single and double quotes. 12.5. echo and quotes Quoted lines can include special escaped characters recognised by the echo command (when using echo -e). The screenshot below shows how to use \n for a newline and \t for a tab (usually eight white spaces). [paul@RHEL4b A line with a newline [paul@RHEL4b A line with a newline [paul@RHEL4b A line with [paul@RHEL4b A line with [paul@RHEL4b ~]$ echo -e "A line with \na newline" ~]$ echo -e 'A line with \na newline' ~]$ echo -e "A line with \ta tab" a tab ~]$ echo -e 'A line with \ta tab' a tab ~]$ The echo command can generate more than white spaces, tabs and newlines. Look in the man page for a list of options. 127commands and arguments 12.6. commands 12.6.1. external or builtin commands ? Not all commands are external to the shell, some are builtin. External commands are programs that have their own binary and reside somewhere in the file system. Many external commands are located in /bin or /sbin. Builtin commands are an integral part of the shell program itself. 12.6.2. type To find out whether a command given to the shell will be executed as an external command or as a builtin command, use the type command. paul@laika:~$ type cd cd is a shell builtin paul@laika:~$ type cat cat is /bin/cat As you can see, the cd command is builtin and the cat command is external. You can also use this command to show you whether the command is aliased or not. paul@laika:~$ type ls ls is aliased to `ls --color=auto' 12.6.3. running external commands Some commands have both builtin and external versions. When one of these commands is executed, the builtin version takes priority. To run the external version, you must enter the full path to the command. paul@laika:~$ type -a echo echo is a shell builtin echo is /bin/echo paul@laika:~$ /bin/echo Running the external echo command... Running the external echo command... 12.6.4. which The which command will search for binaries in the $PATH environment variable (variables will be explained later). In the screenshot below, it is determined that cd is builtin, and ls, cp, rm, mv, mkdir, pwd, and which are external commands. [root@RHEL4b ~]# which cp ls cd mkdir pwd /bin/cp /bin/ls /usr/bin/which: no cd in (/usr/kerberos/sbin:/usr/kerberos/bin:... /bin/mkdir /bin/pwd 128commands and arguments 12.7. aliases 12.7.1. create an alias The shell allows you to create aliases. Aliases are often used to create an easier to remember name for an existing command or to easily supply parameters. [paul@RHELv4u3 ~]$ cat count.txt one two three [paul@RHELv4u3 ~]$ alias dog=tac [paul@RHELv4u3 ~]$ dog count.txt three two one 12.7.2. abbreviate commands An alias can also be useful to abbreviate an existing command. paul@laika:~$ alias ll='ls -lh --color=auto' paul@laika:~$ alias c='clear' paul@laika:~$ 12.7.3. default options Aliases can be used to supply commands with default options. The example below shows how to set the -i option default when typing rm. [paul@RHELv4u3 ~]$ rm: remove regular [paul@RHELv4u3 ~]$ [paul@RHELv4u3 ~]$ ls: winter.txt: No [paul@RHELv4u3 ~]$ [paul@RHELv4u3 ~]$ [paul@RHELv4u3 ~]$ rm: remove regular [paul@RHELv4u3 ~]$ rm -i winter.txt file `winter.txt'? no rm winter.txt ls winter.txt such file or directory touch winter.txt alias rm='rm -i' rm winter.txt empty file `winter.txt'? no Some distributions enable default aliases to protect users from accidentally erasing files ('rm -i', 'mv -i', 'cp -i') 12.7.4. viewing aliases You can provide one or more aliases as arguments to the alias command to get their definitions. Providing no arguments gives a complete list of current aliases. paul@laika:~$ alias c ll alias c='clear' alias ll='ls -lh --color=auto' 129commands and arguments 12.7.5. unalias You can undo an alias with the unalias command. [paul@RHEL4b ~]$ /bin/rm [paul@RHEL4b ~]$ [paul@RHEL4b ~]$ alias rm='rm -i' /bin/rm [paul@RHEL4b ~]$ [paul@RHEL4b ~]$ /bin/rm [paul@RHEL4b ~]$ which rm alias rm='rm -i' which rm unalias rm which rm 12.8. displaying shell expansion You can display shell expansion with set -x, and stop displaying it with set +x. You might want to use this further on in this course, or when in doubt about exactly what the shell is doing with your command. [paul@RHELv4u3 ~]$ set -x ++ echo -ne '\033]0;paul@RHELv4u3:~\007' [paul@RHELv4u3 ~]$ echo $USER + echo paul paul ++ echo -ne '\033]0;paul@RHELv4u3:~\007' [paul@RHELv4u3 ~]$ echo \$USER + echo '$USER' $USER ++ echo -ne '\033]0;paul@RHELv4u3:~\007' [paul@RHELv4u3 ~]$ set +x + set +x [paul@RHELv4u3 ~]$ echo $USER paul 130commands and arguments 12.9. practice: commands and arguments 1. How many arguments are in this line (not counting the command itself). touch '/etc/cron/cron.allow' 'file 42.txt' "file 33.txt" 2. Is tac a shell builtin command ? 3. Is there an existing alias for rm ? 4. Read the man page of rm, make sure you understand the -i option of rm. Create and remove a file to test the -i option. 5. Execute: alias rm='rm -i' . Test your alias with a test file. Does this work as expected ? 6. List all current aliases. 7a. Create an alias called 'city' that echoes your hometown. 7b. Use your alias to test that it works. 8. Execute set -x to display shell expansion for every command. 9. Test the functionality of set -x by executing your city and rm aliases. 10 Execute set +x to stop displaying shell expansion. 11. Remove your city alias. 12. What is the location of the cat and the passwd commands ? 13. Explain the difference between the following commands: echo /bin/echo 14. Explain the difference between the following commands: echo Hello echo -n Hello 15. Display A B C with two spaces between B and C. (optional)16. Complete the following command (do not use spaces) to display exactly the following output: 4+4 10+14 =8 =24 17. Use echo to display the following exactly: ??\\ 131commands and arguments Find two solutions with single quotes, two with double quotes and one without quotes (and say thank you to Ren.' and Darioush from Google for this extra). 18. Use one echo command to display three words on three lines. 132commands and arguments 12.10. solution: commands and arguments 1. How many arguments are in this line (not counting the command itself). touch '/etc/cron/cron.allow' 'file 42.txt' "file 33.txt" answer: three 2. Is tac a shell builtin command ? type tac 3. Is there an existing alias for rm ? alias rm 4. Read the man page of rm, make sure you understand the -i option of rm. Create and remove a file to test the -i option. man rm touch testfile rm -i testfile 5. Execute: alias rm='rm -i' . Test your alias with a test file. Does this work as expected ? touch testfile rm testfile (should ask for confirmation) 6. List all current aliases. alias 7a. Create an alias called 'city' that echoes your hometown. alias city='echo Antwerp' 7b. Use your alias to test that it works. city (it should display Antwerp) 8. Execute set -x to display shell expansion for every command. set -x 9. Test the functionality of set -x by executing your city and rm aliases. shell should display the resolved aliases and then execute the command: paul@deb503:~$ set -x paul@deb503:~$ city + echo antwerp antwerp 10 Execute set +x to stop displaying shell expansion. set +x 11. Remove your city alias. 133commands and arguments unalias city 12. What is the location of the cat and the passwd commands ? which cat (probably /bin/cat) which passwd (probably /usr/bin/passwd) 13. Explain the difference between the following commands: echo /bin/echo The echo command will be interpreted by the shell as the built-in echo command. The /bin/ echo command will make the shell execute the echo binary located in the /bin directory. 14. Explain the difference between the following commands: echo Hello echo -n Hello The -n option of the echo command will prevent echo from echoing a trailing newline. echo Hello will echo six characters in total, echo -n hello only echoes five characters. (The -n option might not work in the Korn shell.) 15. Display A B C with two spaces between B and C. echo "A B C" 16. Complete the following command (do not use spaces) to display exactly the following output: 4+4 10+14 =8 =24 The solution is to use tabs with \t. echo -e "4+4\t=8" ; echo -e "10+14\t=24" 17. Use echo to display the following exactly: ??\\ echo echo echo echo echo '??\\' -e '??\\\\' "??\\\\" -e "??\\\\\\" ??\\\\ Find two solutions with single quotes, two with double quotes and one without quotes (and say thank you to Ren*, and Darioush from Google for this extra). 18. Use one echo command to display three words on three lines. echo -e "one \ntwo \nthree" 134Chapter 13. control operators In this chapter we put more than one command on the command line using control operators. We also briefly discuss related parameters ($?) and similar special characters(&). 135control operators 13.1. ; semicolon You can put two or more commands on the same line separated by a semicolon ; . The shell will scan the line until it reaches the semicolon. All the arguments before this semicolon will be considered a separate command from all the arguments after the semicolon. Both series will be executed sequentially with the shell waiting for each command to finish before starting the next one. [paul@RHELv4u3 Hello [paul@RHELv4u3 World [paul@RHELv4u3 Hello World [paul@RHELv4u3 ~]$ echo Hello ~]$ echo World ~]$ echo Hello ; echo World ~]$ 13.2. & ampersand When a line ends with an ampersand &, the shell will not wait for the command to finish. You will get your shell prompt back, and the command is executed in background. You will get a message when this command has finished executing in background. [paul@RHELv4u3 ~]$ sleep 20 & [1] 7925 [paul@RHELv4u3 ~]$ ...wait 20 seconds... [paul@RHELv4u3 ~]$ [1]+ Done sleep 20 The technical explanation of what happens in this case is explained in the chapter about processes. 13.3. $? dollar question mark The exit code of the previous command is stored in the shell variable $?. Actually $? is a shell parameter and not a variable, since you cannot assign a value to $?. paul@debian5:~/test$ touch file1 paul@debian5:~/test$ echo $? 0 paul@debian5:~/test$ rm file1 paul@debian5:~/test$ echo $? 0 paul@debian5:~/test$ rm file1 rm: cannot remove `file1': No such file or directory paul@debian5:~/test$ echo $? 1 paul@debian5:~/test$ 136control operators 13.4. && double ampersand The shell will interpret && as a logical AND. When using && the second command is executed only if the first one succeeds (returns a zero exit status). paul@barry:~$ echo first && echo second first second paul@barry:~$ zecho first && echo second -bash: zecho: command not found Another example of the same logical AND principle. This example starts with a working cd followed by ls, then a non-working cd which is not followed by ls. [paul@RHELv4u3 ~]$ cd gen && ls file1 file3 File55 fileab FileAB fileabc file2 File4 FileA Fileab fileab2 [paul@RHELv4u3 gen]$ cd gen && ls -bash: cd: gen: No such file or directory 13.5. || double vertical bar The || represents a logical OR. The second command is executed only when the first command fails (returns a non-zero exit status). paul@barry:~$ echo first || echo second ; echo third first third paul@barry:~$ zecho first || echo second ; echo third -bash: zecho: command not found second third paul@barry:~$ Another example of the same logical OR principle. [paul@RHELv4u3 ~]$ cd gen || ls [paul@RHELv4u3 gen]$ cd gen || ls -bash: cd: gen: No such file or directory file1 file3 File55 fileab FileAB fileabc file2 File4 FileA Fileab fileab2 13.6. combining && and || You can use this logical AND and logical OR to write an if-then-else structure on the command line. This example uses echo to display whether the rm command was successful. paul@laika:~/test$ rm file1 && echo It worked! || echo It failed! It worked! paul@laika:~/test$ rm file1 && echo It worked! || echo It failed! rm: cannot remove `file1': No such file or directory It failed! paul@laika:~/test$ 137control operators 13.7. # pound sign Everything written after a pound sign (#) is ignored by the shell. This is useful to write a shell comment, but has no influence on the command execution or shell expansion. paul@debian4:~$ mkdir test paul@debian4:~$ cd test paul@debian4:~/test$ ls paul@debian4:~/test$ # we create a directory #### we enter the directory # is it empty ? 13.8. \ escaping special characters The backslash \ character enables the use of control characters, but without the shell interpreting it, this is called escaping characters. [paul@RHELv4u3 hello ; world [paul@RHELv4u3 hello world [paul@RHELv4u3 escaping \ # & [paul@RHELv4u3 escaping \?*"' ~]$ echo hello \; world ~]$ echo hello\ \ \ world ~]$ echo escaping \\\ \#\ \&\ \"\ \' " ' ~]$ echo escaping \\\?\*\"\' 13.8.1. end of line backslash Lines ending in a backslash are continued on the next line. The shell does not interpret the newline character and will wait on shell expansion and execution of the command line until a newline without backslash is encountered. [paul@RHEL4b ~]$ echo This command line \ > is split in three \ > parts This command line is split in three parts [paul@RHEL4b ~]$ 138control operators 13.9. practice: control operators 0. Each question can be answered by one command line! 1. When you type passwd, which file is executed ? 2. What kind of file is that ? 3. Execute the pwd command twice. (remember 0.) 4. Execute ls after cd /etc, but only if cd /etc did not error. 5. Execute cd /etc after cd etc, but only if cd etc fails. 6. Echo it worked when touch test42 works, and echo it failed when the touch failed. All on one command line as a normal user (not root). Test this line in your home directory and in /bin/ . 7. Execute sleep 6, what is this command doing ? 8. Execute sleep 200 in background (do not wait for it to finish). 9. Write a command line that executes rm file55. Your command line should print 'success' if file55 is removed, and print 'failed' if there was a problem. (optional)10. Use echo to display "Hello World with strange' characters \ * [ } ~ \ \ ." (including all quotes) 139control operators 13.10. solution: control operators 0. Each question can be answered by one command line! 1. When you type passwd, which file is executed ? which passwd 2. What kind of file is that ? file /usr/bin/passwd 3. Execute the pwd command twice. (remember 0.) pwd ; pwd 4. Execute ls after cd /etc, but only if cd /etc did not error. cd /etc && ls 5. Execute cd /etc after cd etc, but only if cd etc fails. cd etc || cd /etc 6. Echo it worked when touch test42 works, and echo it failed when the touch failed. All on one command line as a normal user (not root). Test this line in your home directory and in /bin/ . paul@deb503:~$ cd ; touch test42 && echo it worked || echo it failed it worked paul@deb503:~$ cd /bin; touch test42 && echo it worked || echo it failed touch: cannot touch `test42': Permission denied it failed 7. Execute sleep 6, what is this command doing ? pausing for six seconds 8. Execute sleep 200 in background (do not wait for it to finish). sleep 200 & 9. Write a command line that executes rm file55. Your command line should print 'success' if file55 is removed, and print 'failed' if there was a problem. rm file55 && echo success || echo failed (optional)10. Use echo to display "Hello World with strange' characters \ * [ } ~ \ \ ." (including all quotes) echo \"Hello World with strange\' characters \\ \* \[ \} \~ \\\\ \. \" or echo \""Hello World with strange' characters \ * [ } ~ \\ . "\" 140Chapter 14. shell variables In this chapter we learn to manage environment variables in the shell. These variables are often needed by applications. 141shell variables 14.1. $ dollar sign Another important character interpreted by the shell is the dollar sign $. The shell will look for an environment variable named like the string following the dollar sign and replace it with the value of the variable (or with nothing if the variable does not exist). These are some examples using $HOSTNAME, $USER, $UID, $SHELL, and $HOME. [paul@RHELv4u3 ~]$ echo This is the $SHELL shell This is the /bin/bash shell [paul@RHELv4u3 ~]$ echo This is $SHELL on computer $HOSTNAME This is /bin/bash on computer RHELv4u3.localdomain [paul@RHELv4u3 ~]$ echo The userid of $USER is $UID The userid of paul is 500 [paul@RHELv4u3 ~]$ echo My homedir is $HOME My homedir is /home/paul 14.2. case sensitive This example shows that shell variables are case sensitive! [paul@RHELv4u3 ~]$ echo Hello $USER Hello paul [paul@RHELv4u3 ~]$ echo Hello $user Hello 14.3. creating variables This example creates the variable $MyVar and sets its value. It then uses echo to verify the value. [paul@RHELv4u3 gen]$ MyVar=555 [paul@RHELv4u3 gen]$ echo $MyVar 555 [paul@RHELv4u3 gen]$ 142shell variables 14.4. quotes Notice that double quotes still allow the parsing of variables, whereas single quotes prevent this. [paul@RHELv4u3 [paul@RHELv4u3 555 [paul@RHELv4u3 555 [paul@RHELv4u3 $MyVar ~]$ MyVar=555 ~]$ echo $MyVar ~]$ echo "$MyVar" ~]$ echo '$MyVar' The bash shell will replace variables with their value in double quoted lines, but not in single quoted lines. paul@laika:~$ city=Burtonville paul@laika:~$ echo "We are in $city today." We are in Burtonville today. paul@laika:~$ echo 'We are in $city today.' We are in $city today. 14.5. set You can use the set command to display a list of environment variables. On Ubuntu and Debian systems, the set command will also list shell functions after the shell variables. Use set | more to see the variables then. 14.6. unset Use the unset command to remove a variable from your shell environment. [paul@RHEL4b [paul@RHEL4b 8472 [paul@RHEL4b [paul@RHEL4b ~]$ MyVar=8472 ~]$ echo $MyVar ~]$ unset MyVar ~]$ echo $MyVar [paul@RHEL4b ~]$ 143shell variables 14.7. $PS1 The $PS1 variable determines your shell prompt. You can use backslash escaped special characters like \u for the username or \w for the working directory. The bash manual has a complete reference. In this example we change the value of $PS1 a couple of times. paul@deb503:~$ PS1=prompt prompt promptPS1='prompt ' prompt prompt PS1='> ' > > PS1='\u@\h$ ' paul@deb503$ paul@deb503$ PS1='\u@\h:\W$' paul@deb503:~$ To avoid unrecoverable mistakes, you can set normal user prompts to green and the root prompt to red. Add the following to your .bashrc for a green user prompt: # color prompt by paul RED='\[\033[01;31m\]' WHITE='\[\033[01;00m\]' GREEN='\[\033[01;32m\]' BLUE='\[\033[01;34m\]' export PS1="${debian_chroot:+($debian_chroot)}$GREEN\u$WHITE@$BLUE\h$WHITE\w\$ " 144shell variables 14.8. $PATH The $PATH variable is determines where the shell is looking for commands to execute (unless the command is builtin or aliased). This variable contains a list of directories, separated by colons. [[paul@RHEL4b ~]$ echo $PATH /usr/kerberos/bin:/usr/local/bin:/bin:/usr/bin: The shell will not look in the current directory for commands to execute! (Looking for executables in the current directory provided an easy way to hack PC-DOS computers). If you want the shell to look in the current directory, then add a . at the end of your $PATH. [paul@RHEL4b ~]$ PATH=$PATH:. [paul@RHEL4b ~]$ echo $PATH /usr/kerberos/bin:/usr/local/bin:/bin:/usr/bin:. [paul@RHEL4b ~]$ Your path might be different when using su instead of su - because the latter will take on the environment of the target user. The root user typically has /sbin directories added to the $PATH variable. [paul@RHEL3 ~]$ su Password: [root@RHEL3 paul]# echo $PATH /usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin [root@RHEL3 paul]# exit [paul@RHEL3 ~]$ su - Password: [root@RHEL3 ~]# echo $PATH /usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin: [root@RHEL3 ~]# 145shell variables 14.9. env The env command without options will display a list of exported variables. The difference with set with options is that set lists all variables, including those not exported to child shells. But env can also be used to start a clean shell (a shell without any inherited environment). The env -i command clears the environment for the subshell. Notice in this screenshot that bash will set the $SHELL variable on startup. [paul@RHEL4b ~]$ bash -c 'echo $SHELL $HOME $USER' /bin/bash /home/paul paul [paul@RHEL4b ~]$ env -i bash -c 'echo $SHELL $HOME $USER' /bin/bash [paul@RHEL4b ~]$ You can use the env command to set the $LANG, or any other, variable for just one instance of bash with one command. The example below uses this to show the influence of the $LANG variable on file globbing (see the chapter on file globbing). [paul@RHEL4b test]$ env LANG=C bash -c 'ls File[a-z]' Filea Fileb [paul@RHEL4b test]$ env LANG=en_US.UTF-8 bash -c 'ls File[a-z]' Filea FileA Fileb FileB [paul@RHEL4b test]$ 14.10. export You can export shell variables to other shells with the export command. This will export the variable to child shells. [paul@RHEL4b [paul@RHEL4b [paul@RHEL4b [paul@RHEL4b three four [paul@RHEL4b [paul@RHEL4b four ~]$ ~]$ ~]$ ~]$ var3=three var4=four export var4 echo $var3 $var4 ~]$ bash ~]$ echo $var3 $var4 But it will not export to the parent shell (previous screenshot continued). [paul@RHEL4b [paul@RHEL4b four five [paul@RHEL4b exit [paul@RHEL4b three four [paul@RHEL4b ~]$ export var5=five ~]$ echo $var3 $var4 $var5 ~]$ exit ~]$ echo $var3 $var4 $var5 ~]$ 146shell variables 14.11. delineate variables Until now, we have seen that bash interprets a variable starting from a dollar sign, continuing until the first occurrence of a non-alphanumeric character that is not an underscore. In some situations, this can be a problem. This issue can be resolved with curly braces like in this example. [paul@RHEL4b ~]$ prefix=Super [paul@RHEL4b ~]$ echo Hello $prefixman and $prefixgirl Hello and [paul@RHEL4b ~]$ echo Hello ${prefix}man and ${prefix}girl Hello Superman and Supergirl [paul@RHEL4b ~]$ 14.12. unbound variables The example below tries to display the value of the $MyVar variable, but it fails because the variable does not exist. By default the shell will display nothing when a variable is unbound (does not exist). [paul@RHELv4u3 gen]$ echo $MyVar [paul@RHELv4u3 gen]$ There is, however, the nounset shell option that you can use to generate an error when a variable does not exist. paul@laika:~$ set -u paul@laika:~$ echo $Myvar bash: Myvar: unbound variable paul@laika:~$ set +u paul@laika:~$ echo $Myvar paul@laika:~$ In the bash shell set -u is identical to set -o nounset and likewise set +u is identical to set +o nounset. 147shell variables 14.13. practice: shell variables 1. Use echo to display Hello followed by your username. (use a bash variable!) 2. Create a variable answer with a value of 42. 3. Copy the value of $LANG to $MyLANG. 4. List all current shell variables. 5. List all exported shell variables. 6. Do the env and set commands display your variable ? 6. Destroy your answer variable. 7. Create two variables, and export one of them. 8. Display the exported variable in an interactive child shell. 9. Create a variable, give it the value 'Dumb', create another variable with value 'do'. Use echo and the two variables to echo Dumbledore. 10. Find the list of backslash escaped characters in the manual of bash. Add the time to your PS1 prompt. 148shell variables 14.14. solution: shell variables 1. Use echo to display Hello followed by your username. (use a bash variable!) echo Hello $USER 2. Create a variable answer with a value of 42. answer=42 3. Copy the value of $LANG to $MyLANG. MyLANG=$LANG 4. List all current shell variables. set set|more on Ubuntu/Debian 5. List all exported shell variables. env export declare -x 6. Do the env and set commands display your variable ? env | more set | more 6. Destroy your answer variable. unset answer 7. Create two variables, and export one of them. var1=1; export var2=2 8. Display the exported variable in an interactive child shell. bash echo $var2 9. Create a variable, give it the value 'Dumb', create another variable with value 'do'. Use echo and the two variables to echo Dumbledore. varx=Dumb; vary=do echo ${varx}le${vary}re solution by Yves from Dexia : echo $varx'le'$vary're' solution by Erwin from Telenet : echo "$varx"le"$vary"re 10. Find the list of backslash escaped characters in the manual of bash. Add the time to your PS1 prompt. PS1='\t \u@\h \W$ ' 149Chapter 15. shell embedding and options This chapter takes a brief look at child shells, embedded shells and shell options. 150shell embedding and options 15.1. shell embedding Shells can be embedded on the command line, or in other words, the command line scan can spawn new processes containing a fork of the current shell. You can use variables to prove that new shells are created. In the screenshot below, the variable $var1 only exists in the (temporary) sub shell. [paul@RHELv4u3 gen]$ echo $var1 [paul@RHELv4u3 gen]$ echo $(var1=5;echo $var1) 5 [paul@RHELv4u3 gen]$ echo $var1 [paul@RHELv4u3 gen]$ You can embed a shell in an embedded shell, this is called nested embedding of shells. This screenshot shows an embedded shell inside an embedded shell. paul@deb503:~$ A=shell paul@deb503:~$ echo $C$B$A $(B=sub;echo $C$B$A; echo $(C=sub;echo $C$B$A)) shell subshell subsubshell 15.1.1. backticks Single embedding can be useful to avoid changing your current directory. The screenshot below uses backticks instead of dollar-bracket to embed. [paul@RHELv4u3 ~]$ echo `cd /etc; ls -d * | grep pass` passwd passwd- passwd.OLD [paul@RHELv4u3 ~]$ You can only use the $() notation to nest embedded shells, backticks cannot do this. 15.1.2. backticks or single quotes Placing the embedding between backticks uses one character less than the dollar and parenthesis combo. Be careful however, backticks are often confused with single quotes. The technical difference between ' and ` is significant! [paul@RHELv4u3 gen]$ echo `var1=5;echo $var1` 5 [paul@RHELv4u3 gen]$ echo 'var1=5;echo $var1' var1=5;echo $var1 [paul@RHELv4u3 gen]$ 151shell embedding and options 15.2. shell options Both set and unset are builtin shell commands. They can be used to set options of the bash shell itself. The next example will clarify this. By default, the shell will treat unset variables as a variable having no value. By setting the -u option, the shell will treat any reference to unset variables as an error. See the man page of bash for more information. [paul@RHEL4b ~]$ echo $var123 [paul@RHEL4b ~]$ set -u [paul@RHEL4b ~]$ echo $var123 -bash: var123: unbound variable [paul@RHEL4b ~]$ set +u [paul@RHEL4b ~]$ echo $var123 [paul@RHEL4b ~]$ To list all the set options for your shell, use echo $-. The noclobber (or -C) option will be explained later in this book (in the I/O redirection chapter). [paul@RHEL4b himBH [paul@RHEL4b [paul@RHEL4b himuBCH [paul@RHEL4b [paul@RHEL4b himBH [paul@RHEL4b ~]$ echo $- ~]$ set -C ; set -u ~]$ echo $- ~]$ set +C ; set +u ~]$ echo $- ~]$ When typing set without options, you get a list of all variables without function when the shell is on posix mode. You can set bash in posix mode typing set -o posix. 152shell embedding and options 15.3. practice: shell embedding 1. Find the list of shell options in the man page of bash. What is the difference between set -u and set -o nounset? 2. Activate nounset in your shell. Test that it shows an error message when using non- existing variables. 3. Deactivate nounset. 4. Execute cd /var and ls in an embedded shell. The echo command is only needed to show the result of the ls command. Omitting will result in the shell trying to execute the first file as a command. 5. Create the variable embvar in an embedded shell and echo it. Does the variable exist in your current shell now ? 6. Explain what "set -x" does. Can this be useful ? (optional)7. Given the following screenshot, add exactly four characters to that command line so that the total output is FirstMiddleLast. [paul@RHEL4b ~]$ echo First; echo Middle; echo Last 8. Display a long listing (ls -l) of the passwd command using the which command inside an embedded shell. 153shell embedding and options 15.4. solution: shell embedding 1. Find the list of shell options in the man page of bash. What is the difference between set -u and set -o nounset? read the manual of bash (man bash), search for nounset -- both mean the same thing. 2. Activate nounset in your shell. Test that it shows an error message when using non- existing variables. set -u OR set -o nounset Both these lines have the same effect. 3. Deactivate nounset. set +u OR set +o nounset 4. Execute cd /var and ls in an embedded shell. echo $(cd /var ; ls) The echo command is only needed to show the result of the ls command. Omitting will result in the shell trying to execute the first file as a command. 5. Create the variable embvar in an embedded shell and echo it. Does the variable exist in your current shell now ? echo $(embvar=emb;echo $embvar) ; echo $embvar #the last echo fails $embvar does not exist in your current shell 6. Explain what "set -x" does. Can this be useful ? It displays shell expansion for troubleshooting your command. (optional)7. Given the following screenshot, add exactly four characters to that command line so that the total output is FirstMiddleLast. [paul@RHEL4b ~]$ echo First; echo Middle; echo Last echo -n First; echo -n Middle; echo Last 8. Display a long listing (ls -l) of the passwd command using the which command inside an embedded shell. ls -l $(which passwd) 154Chapter 16. shell history The shell makes it easy for us to repeat commands, this chapter explains how. 155shell history 16.1. repeating the last command To repeat the last command in bash, type !!. This is pronounced as bang bang. paul@debian5:~/test42$ echo this will be repeated > file42.txt paul@debian5:~/test42$ !! echo this will be repeated > file42.txt paul@debian5:~/test42$ 16.2. repeating other commands You can repeat other commands using one bang followed by one or more characters. The shell will repeat the last command that started with those characters. paul@debian5:~/test42$ touch file42 paul@debian5:~/test42$ cat file42 paul@debian5:~/test42$ !to touch file42 paul@debian5:~/test42$ 16.3. history To see older commands, use history to display the shell command history (or use history n to see the last n commands). paul@debian5:~/test$ history 10 38 mkdir test 39 cd test 40 touch file1 41 echo hello > file2 42 echo It is very cold today > winter.txt 43 ls 44 ls -l 45 cp winter.txt summer.txt 46 ls -l 47 history 10 16.4. !n When typing ! followed by the number preceding the command you want repeated, then the shell will echo the command and execute it. paul@debian5:~/test$ !43 ls file1 file2 summer.txt winter.txt 156shell history 16.5. Ctrl-r Another option is to use ctrl-r to search in the history. In the screenshot below i only typed ctrl-r followed by four characters apti and it finds the last command containing these four consecutive characters. paul@debian5:~$ (reverse-i-search)`apti': sudo aptitude install screen 16.6. $HISTSIZE The $HISTSIZE variable determines the number of commands that will be remembered in your current environment. Most distributions default this variable to 500 or 1000. paul@debian5:~$ echo $HISTSIZE 500 You can change it to any value you like. paul@debian5:~$ HISTSIZE=15000 paul@debian5:~$ echo $HISTSIZE 15000 16.7. $HISTFILE The $HISTFILE variable points to the file that contains your history. The bash shell defaults this value to ~/.bash_history. paul@debian5:~$ echo $HISTFILE /home/paul/.bash_history A session history is saved to this file when you exit the session! Closing a gnome-terminal with the mouse, or typing reboot as root will NOT save your terminal's history. 16.8. $HISTFILESIZE The number of commands kept in your history file can be set using $HISTFILESIZE. paul@debian5:~$ echo $HISTFILESIZE 15000 157shell history 16.9. prevent recording a command You can prevent a command from being recorded in history using a space prefix. paul@debian8:~/github$ echo abc abc paul@debian8:~/github$ echo def def paul@debian8:~/github$ echo ghi ghi paul@debian8:~/github$ history 3 9501 echo abc 9502 echo ghi 9503 history 3 16.10. (optional)regular expressions It is possible to use regular expressions when using the bang to repeat commands. The screenshot below switches 1 into 2. paul@debian5:~/test$ cat file1 paul@debian5:~/test$ !c:s/1/2 cat file2 hello paul@debian5:~/test$ 16.11. (optional) Korn shell history Repeating a command in the Korn shell is very similar. The Korn shell also has the history command, but uses the letter r to recall lines from history. This screenshot shows the history command. Note the different meaning of the parameter. $ history 17 17 clear 18 echo hoi 19 history 12 20 echo world 21 history 17 Repeating with r can be combined with the line numbers given by the history command, or with the first few letters of the command. $ r e echo world world $ cd /etc $ r cd /etc $ 158shell history 16.12. practice: shell history 1. Issue the command echo The answer to the meaning of life, the universe and everything is 42. 2. Repeat the previous command using only two characters (there are two solutions!) 3. Display the last 5 commands you typed. 4. Issue the long echo from question 1 again, using the line numbers you received from the command in question 3. 5. How many commands can be kept in memory for your current shell session ? 6. Where are these commands stored when exiting the shell ? 7. How many commands can be written to the history file when exiting your current shell session ? 8. Make sure your current bash shell remembers the next 5000 commands you type. 9. Open more than one console (by press Ctrl-shift-t in gnome-terminal, or by opening an extra putty.exe in MS Windows) with the same user account. When is command history written to the history file ? 159shell history 16.13. solution: shell history 1. Issue the command echo The answer to the meaning of life, the universe and everything is 42. echo The answer to the meaning of life, the universe and everything is 42 2. Repeat the previous command using only two characters (there are two solutions!) !! OR !e 3. Display the last 5 commands you typed. paul@ubu1010:~$ history 5 52 ls -l 53 ls 54 df -h | grep sda 55 echo The answer to the meaning of life, the universe and everything is 42 56 history 5 You will receive different line numbers. 4. Issue the long echo from question 1 again, using the line numbers you received from the command in question 3. paul@ubu1010:~$ !55 echo The answer to the meaning of life, the universe and everything is 42 The answer to the meaning of life, the universe and everything is 42 5. How many commands can be kept in memory for your current shell session ? echo $HISTSIZE 6. Where are these commands stored when exiting the shell ? echo $HISTFILE 7. How many commands can be written to the history file when exiting your current shell session ? echo $HISTFILESIZE 8. Make sure your current bash shell remembers the next 5000 commands you type. HISTSIZE=5000 9. Open more than one console (by press Ctrl-shift-t in gnome-terminal, or by opening an extra putty.exe in MS Windows) with the same user account. When is command history written to the history file ? when you type exit 160Chapter 17. file globbing The shell is also responsible for file globbing (or dynamic filename generation). This chapter will explain file globbing. 161file globbing 17.1. * asterisk The asterisk * is interpreted by the shell as a sign to generate filenames, matching the asterisk to any combination of characters (even none). When no path is given, the shell will use filenames in the current directory. See the man page of glob(7) for more information. (This is part of LPI topic 1.103.3.) [paul@RHELv4u3 gen]$ file1 file2 file3 [paul@RHELv4u3 gen]$ File4 File55 FileA [paul@RHELv4u3 gen]$ file1 file2 file3 [paul@RHELv4u3 gen]$ File55 [paul@RHELv4u3 gen]$ File55 [paul@RHELv4u3 gen]$ File55 [paul@RHELv4u3 gen]$ ls File4 File55 FileA ls File* Fileab FileAB ls file* fileab fileabc ls *ile55 fileab Fileab FileAB fileabc ls F*ile55 ls F*55 17.2. ? question mark Similar to the asterisk, the question mark ? is interpreted by the shell as a sign to generate filenames, matching the question mark with exactly one character. [paul@RHELv4u3 gen]$ ls file1 file2 file3 File4 File55 [paul@RHELv4u3 gen]$ ls File? File4 FileA [paul@RHELv4u3 gen]$ ls Fil?4 File4 [paul@RHELv4u3 gen]$ ls Fil?? File4 FileA [paul@RHELv4u3 gen]$ ls File?? File55 Fileab FileAB [paul@RHELv4u3 gen]$ FileA 162 fileab Fileab FileAB fileabcfile globbing 17.3. [] square brackets The square bracket [ is interpreted by the shell as a sign to generate filenames, matching any of the characters between [ and the first subsequent ]. The order in this list between the brackets is not important. Each pair of brackets is replaced by exactly one character. [paul@RHELv4u3 gen]$ ls file1 file2 file3 File4 File55 FileA fileab Fileab FileAB [paul@RHELv4u3 gen]$ ls File[5A] FileA [paul@RHELv4u3 gen]$ ls File[A5] FileA [paul@RHELv4u3 gen]$ ls File[A5][5b] File55 [paul@RHELv4u3 gen]$ ls File[a5][5b] File55 Fileab [paul@RHELv4u3 gen]$ ls File[a5][5b][abcdefghijklm] ls: File[a5][5b][abcdefghijklm]: No such file or directory [paul@RHELv4u3 gen]$ ls file[a5][5b][abcdefghijklm] fileabc [paul@RHELv4u3 gen]$ fileabc You can also exclude characters from a list between square brackets with the exclamation mark !. And you are allowed to make combinations of these wild cards. [paul@RHELv4u3 gen]$ file1 file2 file3 [paul@RHELv4u3 gen]$ fileab [paul@RHELv4u3 gen]$ file1 file2 file3 [paul@RHELv4u3 gen]$ fileab [paul@RHELv4u3 gen]$ ls File4 File55 FileA ls file[a5][!Z] ls file[!5]* fileab fileabc ls file[!5]? 163 fileab Fileab FileAB fileabcfile globbing 17.4. a-z and 0-9 ranges The bash shell will also understand ranges of characters between brackets. [paul@RHELv4u3 gen]$ ls file1 file3 File55 fileab FileAB fileabc file2 File4 FileA Fileab fileab2 [paul@RHELv4u3 gen]$ ls file[a-z]* fileab fileab2 fileabc [paul@RHELv4u3 gen]$ ls file[0-9] file1 file2 file3 [paul@RHELv4u3 gen]$ ls file[a-z][a-z][0-9]* fileab2 [paul@RHELv4u3 gen]$ 17.5. $LANG and square brackets But, don't forget the influence of the LANG variable. Some languages include lower case letters in an upper case range (and vice versa). paul@RHELv4u4:~/test$ ls [A-Z]ile? file1 file2 file3 File4 paul@RHELv4u4:~/test$ ls [a-z]ile? file1 file2 file3 File4 paul@RHELv4u4:~/test$ echo $LANG en_US.UTF-8 paul@RHELv4u4:~/test$ LANG=C paul@RHELv4u4:~/test$ echo $LANG C paul@RHELv4u4:~/test$ ls [a-z]ile? file1 file2 file3 paul@RHELv4u4:~/test$ ls [A-Z]ile? File4 paul@RHELv4u4:~/test$ If $LC_ALL is set, then this will also need to be reset to prevent file globbing. 164file globbing 17.6. preventing file globbing The screenshot below should be no surprise. The echo * will echo a * when in an empty directory. And it will echo the names of all files when the directory is not empty. paul@ubu1010:~$ mkdir test42 paul@ubu1010:~$ cd test42 paul@ubu1010:~/test42$ echo * * paul@ubu1010:~/test42$ touch file42 file33 paul@ubu1010:~/test42$ echo * file33 file42 Globbing can be prevented using quotes or by escaping the special characters, as shown in this screenshot. paul@ubu1010:~/test42$ file33 file42 paul@ubu1010:~/test42$ * paul@ubu1010:~/test42$ * paul@ubu1010:~/test42$ * echo * echo \* echo '*' echo "*" 165file globbing 17.7. practice: shell globbing 1. Create a test directory and enter it. 2. Create the following files : file1 file10 file11 file2 File2 File3 file33 fileAB filea fileA fileAAA file( file 2 (the last one has 6 characters including a space) 3. List (with ls) all files starting with file 4. List (with ls) all files starting with File 5. List (with ls) all files starting with file and ending in a number. 6. List (with ls) all files starting with file and ending with a letter 7. List (with ls) all files starting with File and having a digit as fifth character. 8. List (with ls) all files starting with File and having a digit as fifth character and nothing else. 9. List (with ls) all files starting with a letter and ending in a number. 10. List (with ls) all files that have exactly five characters. 11. List (with ls) all files that start with f or F and end with 3 or A. 12. List (with ls) all files that start with f have i or R as second character and end in a number. 13. List all files that do not start with the letter F. 14. Copy the value of $LANG to $MyLANG. 15. Show the influence of $LANG in listing A-Z or a-z ranges. 16. You receive information that one of your servers was cracked, the cracker probably replaced the ls command. You know that the echo command is safe to use. Can echo replace ls ? How can you list the files in the current directory with echo ? 17. Is there another command besides cd to change directories ? 166file globbing 17.8. solution: shell globbing 1. Create a test directory and enter it. mkdir testdir; cd testdir 2. Create the following files : file1 file10 file11 file2 File2 File3 file33 fileAB filea fileA fileAAA file( file 2 (the last one has 6 characters including a space) touch touch touch touch file1 file10 file11 file2 File2 File3 file33 fileAB filea fileA fileAAA "file(" "file 2" 3. List (with ls) all files starting with file ls file* 4. List (with ls) all files starting with File ls File* 5. List (with ls) all files starting with file and ending in a number. ls file*[0-9] 6. List (with ls) all files starting with file and ending with a letter ls file*[a-z] 7. List (with ls) all files starting with File and having a digit as fifth character. ls File[0-9]* 8. List (with ls) all files starting with File and having a digit as fifth character and nothing else. ls File[0-9] 9. List (with ls) all files starting with a letter and ending in a number. ls [a-z]*[0-9] 10. List (with ls) all files that have exactly five characters. 167file globbing ls ????? 11. List (with ls) all files that start with f or F and end with 3 or A. ls [fF]*[3A] 12. List (with ls) all files that start with f have i or R as second character and end in a number. ls f[iR]*[0-9] 13. List all files that do not start with the letter F. ls [!F]* 14. Copy the value of $LANG to $MyLANG. MyLANG=$LANG 15. Show the influence of $LANG in listing A-Z or a-z ranges. see example in book 16. You receive information that one of your servers was cracked, the cracker probably replaced the ls command. You know that the echo command is safe to use. Can echo replace ls ? How can you list the files in the current directory with echo ? echo * 17. Is there another command besides cd to change directories ? pushd popd 168Part V. pipes and commandsTable of Contents 18. I/O redirection 171 18.1. stdin, stdout, and stderr 172 18.2. output redirection . 173 18.3. error redirection. 175 18.4. output redirection and pipes ..176 18.5. joining stdout and stderr .. 176 18.6. input redirection .177 18.7. confusing redirection .178 18.8. quick file clear .178 18.9. practice: input/output redirection .179 18.10. solution: input/output redirection 180 19. filters .81 19.1. cat .182 19.2. tee ?182 19.3. grep .182 19.4. cut 19.6. wc185 19.7. sort .186 19.8. uniq 187 19.9. comm .188 19.10. od"189 19.11. sed .190 19.12. pipe examples;191 19.13. practice: filters. 192 19.14. solution: filters ..193 20. basic Unix tools .195 20.1. find. 196 20.2. locate ..197 20.3. date .197 20.4. cal .198 20.5. sleep . 198 20.6. time199 20.7. gzip - gunzip .200 20.8. zcat - zmore . 200 20.9. bzip2 - bunzip2 . 201 20.10. bzcat - bzmore201 20.11. practice: basic Unix tools .202 20.12. solution: basic Unix tools 203 21. regular expressions 205 21.1. regex versions: 206 21.2. grep. 207 21.3. rename .212 21.4. sed . 215 21.5. bash history .219 170Chapter 18. I/O redirection One of the powers of the Unix command line is the use of input/output redirection and pipes. This chapter explains redirection of input, output and error streams. 171I/O redirection 18.1. stdin, stdout, and stderr The bash shell has three basic streams; it takes input from stdin (stream 0), it sends output to stdout (stream 1) and it sends error messages to stderr (stream 2) . The drawing below has a graphical interpretation of these three streams. st dout (1) st din (0) bash st derr (2) The keyboard often serves as stdin, whereas stdout and stderr both go to the display. This can be confusing to new Linux users because there is no obvious way to recognize stdout from stderr. Experienced users know that separating output from errors can be very useful. The next sections will explain how to redirect these streams. 172I/O redirection 18.2. output redirection 18.2.1. > stdout stdout can be redirected with a greater than sign. While scanning the line, the shell will see the > sign and will clear the file. The > notation is in fact the abbreviation of 1> (stdout being referred to as stream 1). [paul@RHELv4u3 ~]$ echo It is cold today! It is cold today! [paul@RHELv4u3 ~]$ echo It is cold today! > winter.txt [paul@RHELv4u3 ~]$ cat winter.txt It is cold today! [paul@RHELv4u3 ~]$ Note that the bash shell effectively removes the redirection from the command line before argument 0 is executed. This means that in the case of this command: echo hello > greetings.txt the shell only counts two arguments (echo = argument 0, hello = argument 1). The redirection is removed before the argument counting takes place. 18.2.2. output file is erased While scanning the line, the shell will see the > sign and will clear the file! Since this happens before resolving argument 0, this means that even when the command fails, the file will have been cleared! [paul@RHELv4u3 ~]$ cat winter.txt It is cold today! [paul@RHELv4u3 ~]$ zcho It is cold today! > winter.txt -bash: zcho: command not found [paul@RHELv4u3 ~]$ cat winter.txt [paul@RHELv4u3 ~]$ 173I/O redirection 18.2.3. noclobber Erasing a file while using > can be prevented by setting the noclobber option. [paul@RHELv4u3 ~]$ It is cold today! [paul@RHELv4u3 ~]$ [paul@RHELv4u3 ~]$ -bash: winter.txt: [paul@RHELv4u3 ~]$ [paul@RHELv4u3 ~]$ cat winter.txt set -o noclobber echo It is cold today! > winter.txt cannot overwrite existing file set +o noclobber 18.2.4. overruling noclobber The noclobber can be overruled with >|. [paul@RHELv4u3 ~]$ set -o noclobber [paul@RHELv4u3 ~]$ echo It is cold today! > winter.txt -bash: winter.txt: cannot overwrite existing file [paul@RHELv4u3 ~]$ echo It is very cold today! >| winter.txt [paul@RHELv4u3 ~]$ cat winter.txt It is very cold today! [paul@RHELv4u3 ~]$ 18.2.5. >> append Use >> to append output to a file. [paul@RHELv4u3 ~]$ echo It is cold today! > winter.txt [paul@RHELv4u3 ~]$ cat winter.txt It is cold today! [paul@RHELv4u3 ~]$ echo Where is the summer ? >> winter.txt [paul@RHELv4u3 ~]$ cat winter.txt It is cold today! Where is the summer ? [paul@RHELv4u3 ~]$ 174I/O redirection 18.3. error redirection 18.3.1. 2> stderr Redirecting stderr is done with 2>. This can be very useful to prevent error messages from cluttering your screen. The screenshot below shows redirection of stdout to a file, and stderr to /dev/null. Writing 1> is the same as >. [paul@RHELv4u3 ~]$ find / > allfiles.txt 2> /dev/null [paul@RHELv4u3 ~]$ 18.3.2. 2>&1 To redirect both stdout and stderr to the same file, use 2>&1. [paul@RHELv4u3 ~]$ find / > allfiles_and_errors.txt 2>&1 [paul@RHELv4u3 ~]$ Note that the order of redirections is significant. For example, the command ls > dirlist 2>&1 directs both standard output (file descriptor 1) and standard error (file descriptor 2) to the file dirlist, while the command ls 2>&1 > dirlist directs only the standard output to file dirlist, because the standard error made a copy of the standard output before the standard output was redirected to dirlist. 175I/O redirection 18.4. output redirection and pipes By default you cannot grep inside stderr when using pipes on the command line, because only stdout is passed. paul@debian7:~$ rm file42 file33 file1201 | grep file42 rm: cannot remove %%%file42&*#: No such file or directory rm: cannot remove ."!file33')&: No such file or directory rm: cannot remove +(+file1201(+/: No such file or directory With 2>&1 you can force stderr to go to stdout. This enables the next command in the pipe to act on both streams. paul@debian7:~$ rm file42 file33 file1201 2>&1 | grep file42 rm: cannot remove *"'file42-/$: No such file or directory You cannot use both 1>&2 and 2>&1 to switch stdout and stderr. paul@debian7:~$ rm file42 file33 file1201 2>&1 1>&2 | grep file42 rm: cannot remove ,!(file42".*: No such file or directory paul@debian7:~$ echo file42 2>&1 1>&2 | sed 's/file42/FILE42/' FILE42 You need a third stream to switch stdout and stderr after a pipe symbol. paul@debian7:~$ echo file42 3>&1 1>&2 2>&3 | sed 's/file42/FILE42/' file42 paul@debian7:~$ rm file42 3>&1 1>&2 2>&3 | sed 's/file42/FILE42/' rm: cannot remove %+.FILE42*!(: No such file or directory 18.5. joining stdout and stderr The &> construction will put both stdout and stderr in one stream (to a file). paul@debian7:~$ rm file42 &> out_and_err paul@debian7:~$ cat out_and_err rm: cannot remove ,&)file42$,*: No such file or directory paul@debian7:~$ echo file42 &> out_and_err paul@debian7:~$ cat out_and_err file42 paul@debian7:~$ 176I/O redirection 18.6. input redirection 18.6.1. < stdin Redirecting stdin is done with < (short for 0<). [paul@RHEL4b ~]$ cat < text.txt one two [paul@RHEL4b ~]$ tr 'onetw' 'ONEZZ' < text.txt ONE ZZO [paul@RHEL4b ~]$ 18.6.2. << here document The here document (sometimes called here-is-document) is a way to append input until a certain sequence (usually EOF) is encountered. The EOF marker can be typed literally or can be called with Ctrl-D. [paul@RHEL4b > one > two > EOF [paul@RHEL4b one two [paul@RHEL4b > brel > brol [paul@RHEL4b brel [paul@RHEL4b ~]$ cat < text.txt ~]$ cat text.txt ~]$ cat < text.txt ~]$ cat text.txt ~]$ 18.6.3. <<< here string The here string can be used to directly pass strings to a command. The result is the same as using echo string | command (but you have one less process running). paul@ubu1110~$ base64 <<< linux-training.be bGludXgtdHJhaW5pbmcuYmUK paul@ubu1110~$ base64 -d <<< bGludXgtdHJhaW5pbmcuYmUK linux-training.be See rfc 3548 for more information about base64. 177I/O redirection 18.7. confusing redirection The shell will scan the whole line before applying redirection. The following command line is very readable and is correct. cat winter.txt > snow.txt 2> errors.txt But this one is also correct, but less readable. 2> errors.txt cat winter.txt > snow.txt Even this will be understood perfectly by the shell. < winter.txt > snow.txt 2> errors.txt cat 18.8. quick file clear So what is the quickest way to clear a file ? >foo And what is the quickest way to clear a file when the noclobber option is set ? >|bar 178I/O redirection 18.9. practice: input/output redirection 1. Activate the noclobber shell option. 2. Verify that noclobber is active by repeating an ls on /etc/ with redirected output to a file. 3. When listing all shell options, which character represents the noclobber option ? 4. Deactivate the noclobber option. 5. Make sure you have two shells open on the same computer. Create an empty tailing.txt file. Then type tail -f tailing.txt. Use the second shell to append a line of text to that file. Verify that the first shell displays this line. 6. Create a file that contains the names of five people. Use cat and output redirection to create the file and use a here document to end the input. 179I/O redirection 18.10. solution: input/output redirection 1. Activate the noclobber shell option. set -o noclobber set -C 2. Verify that noclobber is active by repeating an ls on /etc/ with redirected output to a file. ls /etc > etc.txt ls /etc > etc.txt (should not work) 4. When listing all shell options, which character represents the noclobber option ? echo $- (noclobber is visible as C) 5. Deactivate the noclobber option. set +o noclobber 6. Make sure you have two shells open on the same computer. Create an empty tailing.txt file. Then type tail -f tailing.txt. Use the second shell to append a line of text to that file. Verify that the first shell displays this line. paul@deb503:~$ > tailing.txt paul@deb503:~$ tail -f tailing.txt hello world in the other shell: paul@deb503:~$ echo hello >> tailing.txt paul@deb503:~$ echo world >> tailing.txt 7. Create a file that contains the names of five people. Use cat and output redirection to create the file and use a here document to end the input. paul@deb503:~$ cat > tennis.txt << ace > Justine Henin > Venus Williams > Serena Williams > Martina Hingis > Kim Clijsters > ace paul@deb503:~$ cat tennis.txt Justine Henin Venus Williams Serena Williams Martina Hingis Kim Clijsters paul@deb503:~$ 180Chapter 19. filters Commands that are created to be used with a pipe are often called filters. These filters are very small programs that do one specific thing very efficiently. They can be used as building blocks. This chapter will introduce you to the most common filters. The combination of simple commands and filters in a long pipe allows you to design elegant solutions. 181filters 19.1. cat When between two pipes, the cat command does nothing (except putting stdin on stdout). [paul@RHEL4b pipes]$ tac count.txt | cat | cat | cat | cat | cat five four three two one [paul@RHEL4b pipes]$ 19.2. tee Writing long pipes in Unix is fun, but sometimes you may want intermediate results. This is were tee comes in handy. The tee filter puts stdin on stdout and also into a file. So tee is almost the same as cat, except that it has two identical outputs. [paul@RHEL4b pipes]$ tac count.txt | tee temp.txt | tac one two three four five [paul@RHEL4b pipes]$ cat temp.txt five four three two one [paul@RHEL4b pipes]$ 19.3. grep The grep filter is famous among Unix users. The most common use of grep is to filter lines of text containing (or not containing) a certain string. [paul@RHEL4b pipes]$ cat tennis.txt Amelie Mauresmo, Fra Kim Clijsters, BEL Justine Henin, Bel Serena Williams, usa Venus Williams, USA [paul@RHEL4b pipes]$ cat tennis.txt | grep Williams Serena Williams, usa Venus Williams, USA You can write this without the cat. [paul@RHEL4b pipes]$ grep Williams tennis.txt Serena Williams, usa Venus Williams, USA One of the most useful options of grep is grep -i which filters in a case insensitive way. [paul@RHEL4b pipes]$ grep Bel tennis.txt Justine Henin, Bel [paul@RHEL4b pipes]$ grep -i Bel tennis.txt 182filters Kim Clijsters, BEL Justine Henin, Bel [paul@RHEL4b pipes]$ Another very useful option is grep -v which outputs lines not matching the string. [paul@RHEL4b pipes]$ grep -v Fra tennis.txt Kim Clijsters, BEL Justine Henin, Bel Serena Williams, usa Venus Williams, USA [paul@RHEL4b pipes]$ And of course, both options can be combined to filter all lines not containing a case insensitive string. [paul@RHEL4b pipes]$ grep -vi usa tennis.txt Amelie Mauresmo, Fra Kim Clijsters, BEL Justine Henin, Bel [paul@RHEL4b pipes]$ With grep -A1 one line after the result is also displayed. paul@debian5:~/pipes$ grep -A1 Henin tennis.txt Justine Henin, Bel Serena Williams, usa With grep -B1 one line before the result is also displayed. paul@debian5:~/pipes$ grep -B1 Henin tennis.txt Kim Clijsters, BEL Justine Henin, Bel With grep -C1 (context) one line before and one after are also displayed. All three options (A,B, and C) can display any number of lines (using e.g. A2, B4 or C20). paul@debian5:~/pipes$ grep -C1 Henin tennis.txt Kim Clijsters, BEL Justine Henin, Bel Serena Williams, usa 183filters 19.4. cut The cut filter can select columns from files, depending on a delimiter or a count of bytes. The screenshot below uses cut to filter for the username and userid in the /etc/passwd file. It uses the colon as a delimiter, and selects fields 1 and 3. [[paul@RHEL4b pipes]$ cut -d: -f1,3 /etc/passwd | tail -4 Figo:510 Pfaff:511 Harry:516 Hermione:517 [paul@RHEL4b pipes]$ When using a space as the delimiter for cut, you have to quote the space. [paul@RHEL4b pipes]$ cut -d" " -f1 tennis.txt Amelie Kim Justine Serena Venus [paul@RHEL4b pipes]$ This example uses cut to display the second to the seventh character of /etc/passwd. [paul@RHEL4b pipes]$ cut -c2-7 /etc/passwd | tail -4 igo:x: faff:x arry:x ermion [paul@RHEL4b pipes]$ 19.5. tr You can translate characters with tr. The screenshot shows the translation of all occurrences of e to E. [paul@RHEL4b pipes]$ cat tennis.txt | tr 'e' 'E' AmEliE MaurEsmo, Fra Kim ClijstErs, BEL JustinE HEnin, BEl SErEna Williams, usa VEnus Williams, USA Here we set all letters to uppercase by defining two ranges. [paul@RHEL4b pipes]$ cat tennis.txt | tr 'a-z' 'A-Z' AMELIE MAURESMO, FRA KIM CLIJSTERS, BEL JUSTINE HENIN, BEL SERENA WILLIAMS, USA VENUS WILLIAMS, USA [paul@RHEL4b pipes]$ Here we translate all newlines to spaces. [paul@RHEL4b pipes]$ cat count.txt one two 184filters three four five [paul@RHEL4b pipes]$ cat count.txt | tr '\n' ' ' one two three four five [paul@RHEL4b pipes]$ The tr -s filter can also be used to squeeze multiple occurrences of a character to one. [paul@RHEL4b pipes]$ cat spaces.txt one two three four five six [paul@RHEL4b pipes]$ cat spaces.txt | tr -s ' ' one two three four five six [paul@RHEL4b pipes]$ You can also use tr to 'encrypt' texts with rot13. [paul@RHEL4b pipes]$ cat count.txt | tr 'a-z' 'nopqrstuvwxyzabcdefghijklm' bar gjb guerr sbhe svir [paul@RHEL4b pipes]$ cat count.txt | tr 'a-z' 'n-za-m' bar gjb guerr sbhe svir [paul@RHEL4b pipes]$ This last example uses tr -d to delete characters. paul@debian5:~/pipes$ cat tennis.txt | tr -d e Amli Maursmo, Fra Kim Clijstrs, BEL Justin Hnin, Bl Srna Williams, usa Vnus Williams, USA 19.6. wc Counting words, lines and characters is easy with wc. [paul@RHEL4b pipes]$ wc 5 15 100 tennis.txt [paul@RHEL4b pipes]$ wc 5 tennis.txt [paul@RHEL4b pipes]$ wc 15 tennis.txt [paul@RHEL4b pipes]$ wc 100 tennis.txt [paul@RHEL4b pipes]$ tennis.txt -l tennis.txt -w tennis.txt -c tennis.txt 185filters 19.7. sort The sort filter will default to an alphabetical sort. paul@debian5:~/pipes$ cat music.txt Queen Brel Led Zeppelin Abba paul@debian5:~/pipes$ sort music.txt Abba Brel Led Zeppelin Queen But the sort filter has many options to tweak its usage. This example shows sorting different columns (column 1 or column 2). [paul@RHEL4b pipes]$ sort -k1 country.txt Belgium, Brussels, 10 France, Paris, 60 Germany, Berlin, 100 Iran, Teheran, 70 Italy, Rome, 50 [paul@RHEL4b pipes]$ sort -k2 country.txt Germany, Berlin, 100 Belgium, Brussels, 10 France, Paris, 60 Italy, Rome, 50 Iran, Teheran, 70 The screenshot below shows the difference between an alphabetical sort and a numerical sort (both on the third column). [paul@RHEL4b pipes]$ sort -k3 country.txt Belgium, Brussels, 10 Germany, Berlin, 100 Italy, Rome, 50 France, Paris, 60 Iran, Teheran, 70 [paul@RHEL4b pipes]$ sort -n -k3 country.txt Belgium, Brussels, 10 Italy, Rome, 50 France, Paris, 60 Iran, Teheran, 70 Germany, Berlin, 100 186filters 19.8. uniq With uniq you can remove duplicates from a sorted list. paul@debian5:~/pipes$ cat music.txt Queen Brel Queen Abba paul@debian5:~/pipes$ sort music.txt Abba Brel Queen Queen paul@debian5:~/pipes$ sort music.txt |uniq Abba Brel Queen uniq can also count occurrences with the -c option. paul@debian5:~/pipes$ sort music.txt |uniq -c 1 Abba 1 Brel 2 Queen 187filters 19.9. comm Comparing streams (or files) can be done with the comm. By default comm will output three columns. In this example, Abba, Cure and Queen are in both lists, Bowie and Sweet are only in the first file, Turner is only in the second. paul@debian5:~/pipes$ cat > list1.txt Abba Bowie Cure Queen Sweet paul@debian5:~/pipes$ cat > list2.txt Abba Cure Queen Turner paul@debian5:~/pipes$ comm list1.txt list2.txt Abba Bowie Cure Queen Sweet Turner The output of comm can be easier to read when outputting only a single column. The digits point out which output columns should not be displayed. paul@debian5:~/pipes$ comm -12 list1.txt list2.txt Abba Cure Queen paul@debian5:~/pipes$ comm -13 list1.txt list2.txt Turner paul@debian5:~/pipes$ comm -23 list1.txt list2.txt Bowie Sweet 188filters 19.10. od European humans like to work with ascii characters, but computers store files in bytes. The example below creates a simple file, and then uses od to show the contents of the file in hexadecimal bytes paul@laika:~/test$ cat > text.txt abcdefg 1234567 paul@laika:~/test$ od -t x1 text.txt 0000000 61 62 63 64 65 66 67 0a 31 32 33 34 35 36 37 0a 0000020 The same file can also be displayed in octal bytes. paul@laika:~/test$ od -b text.txt 0000000 141 142 143 144 145 146 147 012 061 062 063 064 065 066 067 012 0000020 And here is the file in ascii (or backslashed) characters. paul@laika:~/test$ od -c text.txt 0000000 a b c d e f g 0000020 \n 189 1 2 3 4 5 6 7 \nfilters 19.11. sed The stream editor sed can perform editing functions in the stream, using regular expressions. paul@debian5:~/pipes$ echo level5 | sed 's/5/42/' level42 paul@debian5:~/pipes$ echo level5 | sed 's/level/jump/' jump5 Add g for global replacements (all occurrences of the string per line). paul@debian5:~/pipes$ echo level5 level7 | sed 's/level/jump/' jump5 level7 paul@debian5:~/pipes$ echo level5 level7 | sed 's/level/jump/g' jump5 jump7 With d you can remove lines from a stream containing a character. paul@debian5:~/test42$ cat tennis.txt Venus Williams, USA Martina Hingis, SUI Justine Henin, BE Serena williams, USA Kim Clijsters, BE Yanina Wickmayer, BE paul@debian5:~/test42$ cat tennis.txt | sed '/BE/d' Venus Williams, USA Martina Hingis, SUI Serena williams, USA 190filters 19.12. pipe examples 19.12.1. who | wc How many users are logged on to this system ? [paul@RHEL4b pipes]$ who root tty1 Jul 25 10:50 paul pts/0 Jul 25 09:29 (laika) Harry pts/1 Jul 25 12:26 (barry) paul pts/2 Jul 25 12:26 (pasha) [paul@RHEL4b pipes]$ who | wc -l 4 19.12.2. who | cut | sort Display a sorted list of logged on users. [paul@RHEL4b pipes]$ who | cut -d' ' -f1 | sort Harry paul paul root Display a sorted list of logged on users, but every user only once . [paul@RHEL4b pipes]$ who | cut -d' ' -f1 | sort | uniq Harry paul root 19.12.3. grep | cut Display a list of all bash user accounts on this computer. Users accounts are explained in detail later. paul@debian5:~$ grep bash /etc/passwd root:x:0:0:root:/root:/bin/bash paul:x:1000:1000:paul,,,:/home/paul:/bin/bash serena:x:1001:1001::/home/serena:/bin/bash paul@debian5:~$ grep bash /etc/passwd | cut -d: -f1 root paul serena 191filters 19.13. practice: filters 1. Put a sorted list of all bash users in bashusers.txt. 2. Put a sorted list of all logged on users in onlineusers.txt. 3. Make a list of all filenames in /etc that contain the string conf in their filename. 4. Make a sorted list of all files in /etc that contain the case insensitive string conf in their filename. 5. Look at the output of /sbin/ifconfig. Write a line that displays only ip address and the subnet mask. 6. Write a line that removes all non-letters from a stream. 7. Write a line that receives a text file, and outputs all words on a separate line. 8. Write a spell checker on the command line. (There may be a dictionary in /usr/share/ dict/ .) 192filters 19.14. solution: filters 1. Put a sorted list of all bash users in bashusers.txt. grep bash /etc/passwd | cut -d: -f1 | sort > bashusers.txt 2. Put a sorted list of all logged on users in onlineusers.txt. who | cut -d' ' -f1 | sort > onlineusers.txt 3. Make a list of all filenames in /etc that contain the string conf in their filename. ls /etc | grep conf 4. Make a sorted list of all files in /etc that contain the case insensitive string conf in their filename. ls /etc | grep -i conf | sort 5. Look at the output of /sbin/ifconfig. Write a line that displays only ip address and the subnet mask. /sbin/ifconfig | head -2 | grep 'inet ' | tr -s ' ' | cut -d' ' -f3,5 6. Write a line that removes all non-letters from a stream. paul@deb503:~$ cat text This is, yes really! , a text with ?&* too many str$ange# characters ;-) paul@deb503:~$ cat text | tr -d ',!$?.*&^%#@;()-' This is yes really a text with too many strange characters 7. Write a line that receives a text file, and outputs all words on a separate line. paul@deb503:~$ cat text2 it is very cold today without the sun paul@deb503:~$ cat text2 | tr ' ' '\n' it is very cold today without the sun 8. Write a spell checker on the command line. (There may be a dictionary in /usr/share/ dict/ .) paul@rhel ~$ echo "The zun is shining today" > text paul@rhel ~$ cat > DICT is shining sun the 193filters today paul@rhel ~$ cat text | tr 'A-Z ' 'a-z\n' | sort | uniq | comm -23 - DICT zun You could also add the solution from question number 6 to remove non-letters, and tr -s ' ' to remove redundant spaces. 194Chapter 20. basic Unix tools This chapter introduces commands to find or locate files and to compress files, together with other common tools that were not discussed before. While the tools discussed here are technically not considered filters, they can be used in pipes. 195basic Unix tools 20.1. find The find command can be very useful at the start of a pipe to search for files. Here are some examples. You might want to add 2>/dev/null to the command lines to avoid cluttering your screen with error messages. Find all files in /etc and put the list in etcfiles.txt find /etc > etcfiles.txt Find all files of the entire system and put the list in allfiles.txt find / > allfiles.txt Find files that end in .conf in the current directory (and all subdirs). find . -name "*.conf" Find files of type file (not directory, pipe or etc.) that end in .conf. find . -type f -name "*.conf" Find files of type directory that end in .bak . find /data -type d -name "*.bak" Find files that are newer than file42.txt find . -newer file42.txt Find can also execute another command on every file found. This example will look for *.odf files and copy them to /backup/. find /data -name "*.odf" -exec cp {} /backup/ \; Find can also execute, after your confirmation, another command on every file found. This example will remove *.odf files if you approve of it for every file found. find /data -name "*.odf" -ok rm {} \; 196basic Unix tools 20.2. locate The locate tool is very different from find in that it uses an index to locate files. This is a lot faster than traversing all the directories, but it also means that it is always outdated. If the index does not exist yet, then you have to create it (as root on Red Hat Enterprise Linux) with the updatedb command. [paul@RHEL4b ~]$ locate Samba warning: locate: could not open database: /var/lib/slocate/slocate.db:... warning: You need to run the 'updatedb' command (as root) to create th... Please have a look at /etc/updatedb.conf to enable the daily cron job. [paul@RHEL4b ~]$ updatedb fatal error: updatedb: You are not authorized to create a default sloc... [paul@RHEL4b ~]$ su - Password: [root@RHEL4b ~]# updatedb [root@RHEL4b ~]# Most Linux distributions will schedule the updatedb to run once every day. 20.3. date The date command can display the date, time, time zone and more. paul@rhel55 ~$ date Sat Apr 17 12:44:30 CEST 2010 A date string can be customised to display the format of your choice. Check the man page for more options. paul@rhel55 ~$ date +'%A %d-%m-%Y' Saturday 17-04-2010 Time on any Unix is calculated in number of seconds since 1969 (the first second being the first second of the first of January 1970). Use date +%s to display Unix time in seconds. paul@rhel55 ~$ date +%s 1271501080 When will this seconds counter reach two thousand million ? paul@rhel55 ~$ date -d '1970-01-01 + 2000000000 seconds' Wed May 18 04:33:20 CEST 2033 197basic Unix tools 20.4. cal The cal command displays the current month, with the current day highlighted. paul@rhel55 ~$ cal April 2010 Su Mo Tu We Th Fr Sa 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 You can select any month in the past or the future. paul@rhel55 February Su Mo Tu We 1 2 3 4 8 9 10 11 15 16 17 18 22 23 24 25 ~$ cal 2 1970 1970 Th Fr Sa 5 6 7 12 13 14 19 20 21 26 27 28 20.5. sleep The sleep command is sometimes used in scripts to wait a number of seconds. This example shows a five second sleep. paul@rhel55 ~$ sleep 5 paul@rhel55 ~$ 198basic Unix tools 20.6. time The time command can display how long it takes to execute a command. The date command takes only a little time. paul@rhel55 ~$ time date Sat Apr 17 13:08:27 CEST 2010 real user sys 0m0.014s 0m0.008s 0m0.006s The sleep 5 command takes five real seconds to execute, but consumes little cpu time. paul@rhel55 ~$ time sleep 5 real user sys 0m5.018s 0m0.005s 0m0.011s This bzip2 command compresses a file and uses a lot of cpu time. paul@rhel55 ~$ time bzip2 text.txt real user sys 0m2.368s 0m0.847s 0m0.539s 199basic Unix tools 20.7. gzip - gunzip Users never have enough disk space, so compression comes in handy. The gzip command can make files take up less space. paul@rhel55 ~$ ls -lh text.txt -rw-rw-r-- 1 paul paul 6.4M Apr 17 13:11 text.txt paul@rhel55 ~$ gzip text.txt paul@rhel55 ~$ ls -lh text.txt.gz -rw-rw-r-- 1 paul paul 760K Apr 17 13:11 text.txt.gz You can get the original back with gunzip. paul@rhel55 ~$ gunzip text.txt.gz paul@rhel55 ~$ ls -lh text.txt -rw-rw-r-- 1 paul paul 6.4M Apr 17 13:11 text.txt 20.8. zcat - zmore Text files that are compressed with gzip can be viewed with zcat and zmore. paul@rhel55 ~$ head -4 text.txt / /opt /opt/VBoxGuestAdditions-3.1.6 /opt/VBoxGuestAdditions-3.1.6/routines.sh paul@rhel55 ~$ gzip text.txt paul@rhel55 ~$ zcat text.txt.gz | head -4 / /opt /opt/VBoxGuestAdditions-3.1.6 /opt/VBoxGuestAdditions-3.1.6/routines.sh 200basic Unix tools 20.9. bzip2 - bunzip2 Files can also be compressed with bzip2 which takes a little more time than gzip, but compresses better. paul@rhel55 ~$ bzip2 text.txt paul@rhel55 ~$ ls -lh text.txt.bz2 -rw-rw-r-- 1 paul paul 569K Apr 17 13:11 text.txt.bz2 Files can be uncompressed again with bunzip2. paul@rhel55 ~$ bunzip2 text.txt.bz2 paul@rhel55 ~$ ls -lh text.txt -rw-rw-r-- 1 paul paul 6.4M Apr 17 13:11 text.txt 20.10. bzcat - bzmore And in the same way bzcat and bzmore can display files compressed with bzip2. paul@rhel55 ~$ bzip2 text.txt paul@rhel55 ~$ bzcat text.txt.bz2 | head -4 / /opt /opt/VBoxGuestAdditions-3.1.6 /opt/VBoxGuestAdditions-3.1.6/routines.sh 201basic Unix tools 20.11. practice: basic Unix tools 1. Explain the difference between these two commands. This question is very important. If you don't know the answer, then look back at the shell chapter. find /data -name "*.txt" find /data -name *.txt 2. Explain the difference between these two statements. Will they both work when there are 200 .odf files in /data ? How about when there are 2 million .odf files ? find /data -name "*.odf" > data_odf.txt find /data/*.odf > data_odf.txt 3. Write a find command that finds all files created after January 30th 2010. 4. Write a find command that finds all *.odf files created in September 2009. 5. Count the number of *.conf files in /etc and all its subdirs. 6. Here are two commands that do the same thing: copy *.odf files to /backup/ . What would be a reason to replace the first command with the second ? Again, this is an important question. cp -r /data/*.odf /backup/ find /data -name "*.odf" -exec cp {} /backup/ \; 7. Create a file called loctest.txt. Can you find this file with locate ? Why not ? How do you make locate find this file ? 8. Use find and -exec to rename all .htm files to .html. 9. Issue the date command. Now display the date in YYYY/MM/DD format. 10. Issue the cal command. Display a calendar of 1582 and 1752. Notice anything special ? 202basic Unix tools 20.12. solution: basic Unix tools 1. Explain the difference between these two commands. This question is very important. If you don't know the answer, then look back at the shell chapter. find /data -name "*.txt" find /data -name *.txt When *.txt is quoted then the shell will not touch it. The find tool will look in the /data for all files ending in .txt. When *.txt is not quoted then the shell might expand this (when one or more files that ends in .txt exist in the current directory). The find might show a different result, or can result in a syntax error. 2. Explain the difference between these two statements. Will they both work when there are 200 .odf files in /data ? How about when there are 2 million .odf files ? find /data -name "*.odf" > data_odf.txt find /data/*.odf > data_odf.txt The first find will output all .odf filenames in /data and all subdirectories. The shell will redirect this to a file. The second find will output all files named .odf in /data and will also output all files that exist in directories named *.odf (in /data). With two million files the command line would be expanded beyond the maximum that the shell can accept. The last part of the command line would be lost. 3. Write a find command that finds all files created after January 30th 2010. touch -t 201001302359 marker_date find . -type f -newer marker_date There is another solution : find . -type f -newerat "20100130 23:59:59" 4. Write a find command that finds all *.odf files created in September 2009. touch -t 200908312359 marker_start touch -t 200910010000 marker_end find . -type f -name "*.odf" -newer marker_start ! -newer marker_end The exclamation mark ! -newer can be read as not newer. 5. Count the number of *.conf files in /etc and all its subdirs. find /etc -type f -name '*.conf' | wc -l 6. Here are two commands that do the same thing: copy *.odf files to /backup/ . What would be a reason to replace the first command with the second ? Again, this is an important question. cp -r /data/*.odf /backup/ 203basic Unix tools find /data -name "*.odf" -exec cp {} /backup/ \; The first might fail when there are too many files to fit on one command line. 7. Create a file called loctest.txt. Can you find this file with locate ? Why not ? How do you make locate find this file ? You cannot locate this with locate because it is not yet in the index. updatedb 8. Use find and -exec to rename all .htm files to .html. paul@rhel55 ~$ find . -name '*.htm' ./one.htm ./two.htm paul@rhel55 ~$ find . -name '*.htm' -exec mv {} {}l \; paul@rhel55 ~$ find . -name '*.htm*' ./one.html ./two.html 9. Issue the date command. Now display the date in YYYY/MM/DD format. date +%Y/%m/%d 10. Issue the cal command. Display a calendar of 1582 and 1752. Notice anything special ? cal 1582 The calendars are different depending on the country. Check http://linux-training.be/files/ studentfiles/dates.txt 204Chapter 21. regular expressions Regular expressions are a very powerful tool in Linux. They can be used with a variety of programs like bash, vi, rename, grep, sed, and more. This chapter introduces you to the basics of regular expressions. 205regular expressions 21.1. regex versions There are three different versions of regular expression syntax: BRE: Basic Regular Expressions ERE: Extended Regular Expressions PRCE: Perl Regular Expressions Depending on the tool being used, one or more of these syntaxes can be used. For example the grep tool has the -E option to force a string to be read as ERE while -G forces BRE and -P forces PRCE. Note that grep also has -F to force the string to be read literally. The sed tool also has options to choose a regex syntax. Read the manual of the tools you use! 206regular expressions 21.2. grep 21.2.1. print lines matching a pattern grep is a popular Linux tool to search for lines that match a certain pattern. Below are some examples of the simplest regular expressions. This is the contents of the test file. This file contains three lines (or three newline characters). paul@rhel65:~$ cat names Tania Laura Valentina When grepping for a single character, only the lines containing that character are returned. paul@rhel65:~$ grep u names Laura paul@rhel65:~$ grep e names Valentina paul@rhel65:~$ grep i names Tania Valentina The pattern matching in this example should be very straightforward; if the given character occurs on a line, then grep will return that line. 21.2.2. concatenating characters Two concatenated characters will have to be concatenated in the same way to have a match. This example demonstrates that ia will match Tania but not Valentina and in will match Valentina but not Tania. paul@rhel65:~$ grep a names Tania Laura Valentina paul@rhel65:~$ grep ia names Tania paul@rhel65:~$ grep in names Valentina paul@rhel65:~$ 207regular expressions 21.2.3. one or the other PRCE and ERE both use the pipe symbol to signify OR. In this example we grep for lines containing the letter i or the letter a. paul@debian7:~$ cat list Tania Laura paul@debian7:~$ grep -E 'i|a' list Tania Laura Note that we use the -E switch of grep to force interpretion of our string as an ERE. We need to escape the pipe symbol in a BRE to get the same logical OR. paul@debian7:~$ grep -G 'i|a' list paul@debian7:~$ grep -G 'i\|a' list Tania Laura 21.2.4. one or more The * signifies zero, one or more occurences of the previous and the + signifies one or more of the previous. paul@debian7:~$ cat list2 ll lol lool loool paul@debian7:~$ grep -E 'o*' list2 ll lol lool loool paul@debian7:~$ grep -E 'o+' list2 lol lool loool paul@debian7:~$ 208regular expressions 21.2.5. match the end of a string For the following examples, we will use this file. paul@debian7:~$ cat names Tania Laura Valentina Fleur Floor The two examples below show how to use the dollar character to match the end of a string. paul@debian7:~$ grep a$ names Tania Laura Valentina paul@debian7:~$ grep r$ names Fleur Floor 21.2.6. match the start of a string The caret character (^) will match a string at the start (or the beginning) of a line. Given the same file as above, here are two examples. paul@debian7:~$ grep ^Val names Valentina paul@debian7:~$ grep ^F names Fleur Floor Both the dollar sign and the little hat are called anchors in a regex. 209regular expressions 21.2.7. separating words Regular expressions use a \b sequence to reference a word separator. Take for example this file: paul@debian7:~$ cat text The governer is governing. The winter is over. Can you get over there? Simply grepping for over will give too many results. paul@debian7:~$ grep over text The governer is governing. The winter is over. Can you get over there? Surrounding the searched word with spaces is not a good solution (because other characters can be word separators). This screenshot below show how to use \b to find only the searched word: paul@debian7:~$ grep '\bover\b' text The winter is over. Can you get over there? paul@debian7:~$ Note that grep also has a -w option to grep for words. paul@debian7:~$ cat text The governer is governing. The winter is over. Can you get over there? paul@debian7:~$ grep -w over text The winter is over. Can you get over there? paul@debian7:~$ 210regular expressions 21.2.8. grep features Sometimes it is easier to combine a simple regex with grep options, than it is to write a more complex regex. These options where discussed before: grep grep grep grep grep grep -i -v -w -A5 -B5 -C5 21.2.9. preventing shell expansion of a regex The dollar sign is a special character, both for the regex and also for the shell (remember variables and embedded shells). Therefore it is advised to always quote the regex, this prevents shell expansion. paul@debian7:~$ grep 'r$' names Fleur Floor 211regular expressions 21.3. rename 21.3.1. the rename command On Debian Linux the /usr/bin/rename command is a link to /usr/bin/prename installed by the perl package. paul@pi ~ $ dpkg -S $(readlink -f $(which rename)) perl: /usr/bin/prename Red Hat derived systems do not install the same rename command, so this section does not describe rename on Red Hat (unless you copy the perl script manually). There is often confusion on the internet about the rename command because solutions that work fine in Debian (and Ubuntu, xubuntu, Mint, ...) cannot be used in Red Hat (and CentOS, Fedora, ...). 21.3.2. perl The rename command is actually a perl script that uses perl regular expressions. The complete manual for these can be found by typing perldoc perlrequick (after installing perldoc). root@pi:~# aptitude install perl-doc The following NEW packages will be installed: perl-doc 0 packages upgraded, 1 newly installed, 0 to remove and 0 not upgraded. Need to get 8,170 kB of archives. After unpacking 13.2 MB will be used. Get: 1 http://mirrordirector.raspbian.org/raspbian/ wheezy/main perl-do... Fetched 8,170 kB in 19s (412 kB/s) Selecting previously unselected package perl-doc. (Reading database ... 67121 files and directories currently installed.) Unpacking perl-doc (from .../perl-doc_5.14.2-21+rpi2_all.deb) ... Adding 'diversion of /usr/bin/perldoc to /usr/bin/perldoc.stub by perl-doc' Processing triggers for man-db ... Setting up perl-doc (5.14.2-21+rpi2) ... root@pi:~# perldoc perlrequick 212regular expressions 21.3.3. well known syntax The most common use of the rename is to search for filenames matching a certain string and replacing this string with an other string. This is often presented as s/string/other string/ as seen in this example: paul@pi ~ abc abc.conf paul@pi ~ paul@pi ~ abc abc.conf $ ls allfiles.TXT bllfiles.TXT Scratch tennis2.TXT backup cllfiles.TXT temp.TXT tennis.TXT $ rename 's/TXT/text/' * $ ls allfiles.text bllfiles.text Scratch tennis2.text backup cllfiles.text temp.text tennis.text And here is another example that uses rename with the well know syntax to change the extensions of the same files once more: paul@pi ~ abc abc.conf paul@pi ~ paul@pi ~ abc abc.conf paul@pi ~ $ ls allfiles.text bllfiles.text Scratch tennis2.text backup cllfiles.text temp.text tennis.text $ rename 's/text/txt/' *.text $ ls allfiles.txt bllfiles.txt Scratch tennis2.txt backup cllfiles.txt temp.txt tennis.txt $ These two examples appear to work because the strings we used only exist at the end of the filename. Remember that file extensions have no meaning in the bash shell. The next example shows what can go wrong with this syntax. paul@pi ~ paul@pi ~ paul@pi ~ abc abc.conf paul@pi ~ $ touch atxt.txt $ rename 's/txt/problem/' atxt.txt $ ls allfiles.txt backup cllfiles.txt aproblem.txt bllfiles.txt Scratch $ temp.txt tennis2.txt Only the first occurrence of the searched string is replaced. 213 tennis.txtregular expressions 21.3.4. a global replace The syntax used in the previous example can be described as s/regex/replacement/. This is simple and straightforward, you enter a regex between the first two slashes and a replacement string between the last two. This example expands this syntax only a little, by adding a modifier. paul@pi ~ $ rename -n 's/TXT/txt/g' aTXT.TXT aTXT.TXT renamed as atxt.txt paul@pi ~ $ The syntax we use now can be described as s/regex/replacement/g where s signifies switch and g stands for global. Note that this example used the -n switch to show what is being done (instead of actually renaming the file). 21.3.5. case insensitive replace Another modifier that can be useful is i. this example shows how to replace a case insensitive string with another string. paul@debian7:~/files$ ls file1.text file2.TEXT file3.txt paul@debian7:~/files$ rename 's/.text/.txt/i' * paul@debian7:~/files$ ls file1.txt file2.txt file3.txt paul@debian7:~/files$ 21.3.6. renaming extensions Command line Linux has no knowledge of MS-DOS like extensions, but many end users and graphical application do use them. Here is an example on how to use rename to only rename the file extension. It uses the dollar sign to mark the ending of the filename. paul@pi ~ $ ls *.txt allfiles.txt bllfiles.txt cllfiles.txt really.txt.txt temp.txt paul@pi ~ $ rename 's/.txt$/.TXT/' *.txt paul@pi ~ $ ls *.TXT allfiles.TXT bllfiles.TXT cllfiles.TXT really.txt.TXT temp.TXT tennis.TXT paul@pi ~ $ tennis.txt Note that the dollar sign in the regex means at the end. Without the dollar sign this command would fail on the really.txt.txt file. 214regular expressions 21.4. sed 21.4.1. stream editor The stream editor or short sed uses regex for stream editing. In this example sed is used to replace a string. echo Sunday | sed 's/Sun/Mon/' Monday The slashes can be replaced by a couple of other characters, which can be handy in some cases to improve readability. echo Sunday | sed 's:Sun:Mon:' Monday echo Sunday | sed 's_Sun_Mon_' Monday echo Sunday | sed 's|Sun|Mon|' Monday 21.4.2. interactive editor While sed is meant to be used in a stream, it can also be used interactively on a file. paul@debian7:~/files$ paul@debian7:~/files$ Sunday paul@debian7:~/files$ paul@debian7:~/files$ Monday echo Sunday > today cat today sed -i 's/Sun/Mon/' today cat today 215regular expressions 21.4.3. simple back referencing The ampersand character can be used to reference the searched (and found) string. In this example the ampersand is used to double the occurence of the found string. echo Sunday | sed 's/Sun/&&/' SunSunday echo Sunday | sed 's/day/&&/' Sundayday 21.4.4. back referencing Parentheses (often called round brackets) are used to group sections of the regex so they can leter be referenced. Consider this simple example: paul@debian7:~$ echo Sunday | sed 's_\(Sun\)_\1ny_' Sunnyday paul@debian7:~$ echo Sunday | sed 's_\(Sun\)_\1ny \1_' Sunny Sunday 21.4.5. a dot for any character In a regex a simple dot can signify any character. paul@debian7:~$ echo 2014-04-01 | sed 's/....-..-../YYYY-MM-DD/' YYYY-MM-DD paul@debian7:~$ echo abcd-ef-gh | sed 's/....-..-../YYYY-MM-DD/' YYYY-MM-DD 21.4.6. multiple back referencing When more than one pair of parentheses is used, each of them can be referenced separately by consecutive numbers. paul@debian7:~$ echo 2014-04-01 | sed 's/\(....\)-\(..\)-\(..\)/\1+\2+\3/' 2014+04+01 paul@debian7:~$ echo 2014-04-01 | sed 's/\(....\)-\(..\)-\(..\)/\3:\2:\1/' 01:04:2014 This feature is called grouping. 216regular expressions 21.4.7. white space The \s can refer to white space such as a space or a tab. This example looks for white spaces (\s) globally and replaces them with 1 space. paul@debian7:~$ echo -e 'today\tis\twarm' today is warm paul@debian7:~$ echo -e 'today\tis\twarm' | sed 's_\s_ _g' today is warm 21.4.8. optional occurrence A question mark signifies that the previous is optional. The example below searches for three consecutive letter o, but the third o is optional. paul@debian7:~$ cat list2 ll lol lool loool paul@debian7:~$ grep -E 'ooo?' list2 lool loool paul@debian7:~$ cat list2 | sed 's/ooo\?/A/' ll lol lAl lAl 217regular expressions 21.4.9. exactly n times You can demand an exact number of times the oprevious has to occur. This example wants exactly three o's. paul@debian7:~$ cat list2 ll lol lool loool paul@debian7:~$ grep -E 'o{3}' list2 loool paul@debian7:~$ cat list2 | sed 's/o\{3\}/A/' ll lol lool lAl paul@debian7:~$ 21.4.10. between n and m times And here we demand exactly from minimum 2 to maximum 3 times. paul@debian7:~$ ll lol lool loool paul@debian7:~$ lool loool paul@debian7:~$ lool loool paul@debian7:~$ ll lol lAl lAl paul@debian7:~$ cat list2 grep -E 'o{2,3}' list2 grep 'o\{2,3\}' list2 cat list2 | sed 's/o\{2,3\}/A/' 218regular expressions 21.5. bash history The bash shell can also interprete some regular expressions. This example shows how to manipulate the exclamation mask history feature of the bash shell. paul@debian7:~$ mkdir hist paul@debian7:~$ cd hist/ paul@debian7:~/hist$ touch file1 file2 file3 paul@debian7:~/hist$ ls -l file1 -rw-r--r-- 1 paul paul 0 Apr 15 22:07 file1 paul@debian7:~/hist$ !l ls -l file1 -rw-r--r-- 1 paul paul 0 Apr 15 22:07 file1 paul@debian7:~/hist$ !l:s/1/3 ls -l file3 -rw-r--r-- 1 paul paul 0 Apr 15 22:07 file3 paul@debian7:~/hist$ This also works with the history numbers in bash. paul@debian7:~/hist$ history 6 2089 mkdir hist 2090 cd hist/ 2091 touch file1 file2 file3 2092 ls -l file1 2093 ls -l file3 2094 history 6 paul@debian7:~/hist$ !2092 ls -l file1 -rw-r--r-- 1 paul paul 0 Apr 15 22:07 file1 paul@debian7:~/hist$ !2092:s/1/2 ls -l file2 -rw-r--r-- 1 paul paul 0 Apr 15 22:07 file2 paul@debian7:~/hist$ 219Part VI. viTable of Contents 221Chapter 22. Introduction to vi The vi editor is installed on almost every Unix. Linux will very often install vim (vi improved) which is similar. Every system administrator should know vi(m), because it is an easy tool to solve problems. The vi editor is not intuitive, but once you get to know it, vi becomes a very powerful application. Most Linux distributions will include the vimtutor which is a 45 minute lesson in vi(m). 222Introduction to vi 22.1. command mode and insert mode The vi editor starts in command mode. In command mode, you can type commands. Some commands will bring you into insert mode. In insert mode, you can type text. The escape key will return you to command mode. Table 22.1. getting to command mode key action Esc set vi(m) in command mode. 22.2. start typing (a A i I o O) The difference between a A i I o and O is the location where you can start typing. a will append after the current character and A will append at the end of the line. i will insert before the current character and I will insert at the beginning of the line. o will put you in a new line after the current line and O will put you in a new line before the current line. Table 22.2. switch to insert mode command action a start typing after the current character A start typing at the end of the current line i start typing before the current character I start typing at the start of the current line o start typing on a new line after the current line O start typing on a new line before the current line 223Introduction to vi 22.3. replace and delete a character (r x X) When in command mode (it doesn't hurt to hit the escape key more than once) you can use the x key to delete the current character. The big X key (or shift x) will delete the character left of the cursor. Also when in command mode, you can use the r key to replace one single character. The r key will bring you in insert mode for just one key press, and will return you immediately to command mode. Table 22.3. replace and delete command action x delete the character below the cursor X delete the character before the cursor r replace the character below the cursor p paste after the cursor (here the last deleted character) xp switch two characters 22.4. undo and repeat (u .) When in command mode, you can undo your mistakes with u. You can do your mistakes twice with . (in other words, the . will repeat your last command). Table 22.4. undo and repeat command action u undo the last action . repeat the last action 22.5. cut, copy and paste a line (dd yy p P) When in command mode, dd will cut the current line. yy will copy the current line. You can paste the last copied or cut line after (p) or before (P) the current line. Table 22.5. cut, copy and paste a line command action dd cut the current line yy (yank yank) copy the current line p paste after the current line P paste before the current line 224Introduction to vi 22.6. cut, copy and paste lines (3dd 2yy) When in command mode, before typing dd or yy, you can type a number to repeat the command a number of times. Thus, 5dd will cut 5 lines and 4yy will copy (yank) 4 lines. That last one will be noted by vi in the bottom left corner as "4 line yanked". Table 22.6. cut, copy and paste lines command action 3dd cut three lines 4yy copy four lines 22.7. start and end of a line (0 or ^ and $) When in command mode, the 0 and the caret ^ will bring you to the start of the current line, whereas the $ will put the cursor at the end of the current line. You can add 0 and $ to the d command, d0 will delete every character between the current character and the start of the line. Likewise d$ will delete everything from the current character till the end of the line. Similarly y0 and y$ will yank till start and end of the current line. Table 22.7. start and end of line command action 0 jump to start of current line ^ jump to start of current line $ jump to end of current line d0 delete until start of line d$ delete until end of line 22.8. join two lines (J) and more When in command mode, pressing J will append the next line to the current line. With yyp you duplicate a line and with ddp you switch two lines. Table 22.8. join two lines command J action join two lines yyp duplicate a line ddp switch two lines 225Introduction to vi 22.9. words (w b) When in command mode, w will jump to the next word and b will move to the previous word. w and b can also be combined with d and y to copy and cut words (dw db yw yb). Table 22.9. words command action w forward one word b back one word 3w forward three words dw delete one word yw yank (copy) one word 5yb yank five words back 7dw delete seven words 22.10. save (or not) and exit (:w :q :q! ) Pressing the colon : will allow you to give instructions to vi (technically speaking, typing the colon will open the ex editor). :w will write (save) the file, :q will quit an unchanged file without saving, and :q! will quit vi discarding any changes. :wq will save and quit and is the same as typing ZZ in command mode. Table 22.10. save and exit vi command :w :w fname :q action save (write) save as fname quit :wq save and quit ZZ save and quit :q! quit (discarding your changes) :w! save (and write to non-writable file!) The last one is a bit special. With :w! vi will try to chmod the file to get write permission (this works when you are the owner) and will chmod it back when the write succeeds. This should always work when you are root (and the file system is writable). 22.11. Searching (/ ?) When in command mode typing / will allow you to search in vi for strings (can be a regular expression). Typing /foo will do a forward search for the string foo and typing ?bar will do a backward search for bar. Table 22.11. searching command /string action forward search for string 226Introduction to vi command action ?string backward search for string n go to next occurrence of search string /^string forward search string at beginning of line /string$ forward search string at end of line /br[aeio]l search for bral brel bril and brol /\ search for the word he (and not for here or the) 22.12. replace all ( :1,$ s/foo/bar/g ) To replace all occurrences of the string foo with bar, first switch to ex mode with : . Then tell vi which lines to use, for example 1,$ will do the replace all from the first to the last line. You can write 1,5 to only process the first five lines. The s/foo/bar/g will replace all occurrences of foo with bar. Table 22.12. replace command action :4,8 s/foo/bar/g replace foo with bar on lines 4 to 8 :1,$ s/foo/bar/g replace foo with bar on all lines 22.13. reading files (:r :r !cmd) When in command mode, :r foo will read the file named foo, :r !foo will execute the command foo. The result will be put at the current location. Thus :r !ls will put a listing of the current directory in your text file. Table 22.13. read files and input command action :r fname (read) file fname and paste contents :r !cmd execute cmd and paste its output 22.14. text buffers There are 36 buffers in vi to store text. You can use them with the " character. Table 22.14. text buffers command action "add delete current line and put text in buffer a "g7yy copy seven lines into buffer g "ap paste from buffer a 22.15. multiple files You can edit multiple files with vi. Here are some tips. 227Introduction to vi Table 22.15. multiple files command vi file1 file2 file3 :args action start editing three files lists files and marks active file :n start editing the next file :e toggle with last edited file :rew rewind file pointer to first file 22.16. abbreviations With :ab you can put abbreviations in vi. Use :una to undo the abbreviation. Table 22.16. abbreviations command :ab str long string :una str action abbreviate str to be 'long string' un-abbreviate str 228Introduction to vi 22.17. key mappings Similarly to their abbreviations, you can use mappings with :map for command mode and :map! for insert mode. This example shows how to set the F6 function key to toggle between set number and set nonumber. The separates the two commands, set number! toggles the state and set number? reports the current state. :map :set number!set number? 22.18. setting options Some options that you can set in vim. :set number ( also try :se nu ) :set nonumber :syntax on :syntax off :set all (list all options) :set tabstop=8 :set tx (CR/LF style endings) :set notx You can set these options (and much more) in ~/.vimrc for vim or in ~/.exrc for standard vi. paul@barry:~$ cat ~/.vimrc set number set tabstop=8 set textwidth=78 map :set number!set number? paul@barry:~$ 229Introduction to vi 22.19. practice: vi(m) 1. Start the vimtutor and do some or all of the exercises. You might need to run aptitude install vim on xubuntu. 2. What 3 key sequence in command mode will duplicate the current line. 3. What 3 key sequence in command mode will switch two lines' place (line five becomes line six and line six becomes line five). 4. What 2 key sequence in command mode will switch a character's place with the next one. 5. vi can understand macro's. A macro can be recorded with q followed by the name of the macro. So qa will record the macro named a. Pressing q again will end the recording. You can recall the macro with @ followed by the name of the macro. Try this example: i 1 'Escape Key' qa yyp 'Ctrl a' q 5@a (Ctrl a will increase the number with one). 6. Copy /etc/passwd to your ~/passwd. Open the last one in vi and press Ctrl v. Use the arrow keys to select a Visual Block, you can copy this with y or delete it with d. Try pasting it. 7. What does dwwP do when you are at the beginning of a word in a sentence ? 230Introduction to vi 22.20. solution: vi(m) 1. Start the vimtutor and do some or all of the exercises. You might need to run aptitude install vim on xubuntu. vimtutor 2. What 3 key sequence in command mode will duplicate the current line. yyp 3. What 3 key sequence in command mode will switch two lines' place (line five becomes line six and line six becomes line five). ddp 4. What 2 key sequence in command mode will switch a character's place with the next one. xp 5. vi can understand macro's. A macro can be recorded with q followed by the name of the macro. So qa will record the macro named a. Pressing q again will end the recording. You can recall the macro with @ followed by the name of the macro. Try this example: i 1 'Escape Key' qa yyp 'Ctrl a' q 5@a (Ctrl a will increase the number with one). 6. Copy /etc/passwd to your ~/passwd. Open the last one in vi and press Ctrl v. Use the arrow keys to select a Visual Block, you can copy this with y or delete it with d. Try pasting it. cp /etc/passwd ~ vi passwd (press Ctrl-V) 7. What does dwwP do when you are at the beginning of a word in a sentence ? dwwP can switch the current word with the next word. 231Part VII. scriptingTable of Content 233Chapter 23. scripting introduction Shells like bash and Korn have support for programming constructs that can be saved as scripts. These scripts in turn then become more shell commands. Many Linux commands are scripts. User profile scripts are run when a user logs on and init scripts are run when a daemon is stopped or started. This means that system administrators also need basic knowledge of scripting to understand how their servers and their applications are started, updated, upgraded, patched, maintained, configured and removed, and also to understand how a user environment is built. The goal of this chapter is to give you enough information to be able to read and understand scripts. Not to become a writer of complex scripts. 234scripting introduction 23.1. prerequisites You should have read and understood part III shell expansion and part IV pipes and commands before starting this chapter. 23.2. hello world Just like in every programming course, we start with a simple hello_world script. The following script will output Hello World. echo Hello World After creating this simple script in vi or with echo, you'll have to chmod +x hello_world to make it executable. And unless you add the scripts directory to your path, you'll have to type the path to the script for the shell to be able to find it. [paul@RHEL4a [paul@RHEL4a [paul@RHEL4a Hello World [paul@RHEL4a ~]$ echo echo Hello World > hello_world ~]$ chmod +x hello_world ~]$ ./hello_world ~]$ 23.3. she-bang Let's expand our example a little further by putting #!/bin/bash on the first line of the script. The #! is called a she-bang (sometimes called sha-bang), where the she-bang is the first two characters of the script. #!/bin/bash echo Hello World You can never be sure which shell a user is running. A script that works flawlessly in bash might not work in ksh, csh, or dash. To instruct a shell to run your script in a certain shell, you can start your script with a she-bang followed by the shell it is supposed to run in. This script will run in a bash shell. #!/bin/bash echo -n hello echo A bash subshell `echo -n hello` This script will run in a Korn shell (unless /bin/ksh is a hard link to /bin/bash). The /etc/ shells file contains a list of shells on your system. #!/bin/ksh echo -n hello echo a Korn subshell `echo -n hello` 235scripting introduction 23.4. comment Let's expand our example a little further by adding comment lines. #!/bin/bash # # Hello World Script # echo Hello World 23.5. variables Here is a simple example of a variable inside a script. #!/bin/bash # # simple variable in script # var1=4 echo var1 = $var1 Scripts can contain variables, but since scripts are run in their own shell, the variables do not survive the end of the script. [paul@RHEL4a ~]$ echo $var1 [paul@RHEL4a ~]$ ./vars var1 = 4 [paul@RHEL4a ~]$ echo $var1 [paul@RHEL4a ~]$ 23.6. sourcing a script Luckily, you can force a script to run in the same shell; this is called sourcing a script. [paul@RHEL4a ~]$ source ./vars var1 = 4 [paul@RHEL4a ~]$ echo $var1 4 [paul@RHEL4a ~]$ The above is identical to the below. [paul@RHEL4a ~]$ . ./vars var1 = 4 [paul@RHEL4a ~]$ echo $var1 4 [paul@RHEL4a ~]$ 236scripting introduction 23.7. troubleshooting a script Another way to run a script in a separate shell is by typing bash with the name of the script as a parameter. paul@debian6~/test$ bash runme 42 Expanding this to bash -x allows you to see the commands that the shell is executing (after shell expansion). paul@debian6~/test$ bash -x runme + var4=42 + echo 42 42 paul@debian6~/test$ cat runme # the runme script var4=42 echo $var4 paul@debian6~/test$ Notice the absence of the commented (#) line, and the replacement of the variable before execution of echo. 23.8. prevent setuid root spoofing Some user may try to perform setuid based script root spoofing. This is a rare but possible attack. To improve script security and to avoid interpreter spoofing, you need to add -- after the #!/bin/bash, which disables further option processing so the shell will not accept any options. #!/bin/bash - or #!/bin/bash -- Any arguments after the -- are treated as filenames and arguments. An argument of - is equivalent to --. 237scripting introduction 23.9. practice: introduction to scripting 0. Give each script a different name, keep them for later! 1. Write a script that outputs the name of a city. 2. Make sure the script runs in the bash shell. 3. Make sure the script runs in the Korn shell. 4. Create a script that defines two variables, and outputs their value. 5. The previous script does not influence your current shell (the variables do not exist outside of the script). Now run the script so that it influences your current shell. 6. Is there a shorter way to source the script ? 7. Comment your scripts so that you know what they are doing. 238scripting introduction 23.10. solution: introduction to scripting 0. Give each script a different name, keep them for later! 1. Write a script that outputs the name of a city. $ echo 'echo Antwerp' > first.bash $ chmod +x first.bash $ ./first.bash Antwerp 2. Make sure the script runs in the bash shell. $ cat first.bash #!/bin/bash echo Antwerp 3. Make sure the script runs in the Korn shell. $ cat first.bash #!/bin/ksh echo Antwerp Note that while first.bash will technically work as a Korn shell script, the name ending in .bash is confusing. 4. Create a script that defines two variables, and outputs their value. $ cat second.bash #!/bin/bash var33=300 var42=400 echo $var33 $var42 5. The previous script does not influence your current shell (the variables do not exist outside of the script). Now run the script so that it influences your current shell. source second.bash 6. Is there a shorter way to source the script ? . ./second.bash 7. Comment your scripts so that you know what they are doing. $ cat second.bash #!/bin/bash # script to test variables and sourcing # define two variables var33=300 var42=400 # output the value of these variables echo $var33 $var42 239Chapter 24. scripting loops 240scripting loops 24.1. test [ ] The test command can test whether something is true or false. Let's start by testing whether 10 is greater than 55. [paul@RHEL4b ~]$ test 10 -gt 55 ; echo $? 1 [paul@RHEL4b ~]$ The test command returns 1 if the test fails. And as you see in the next screenshot, test returns 0 when a test succeeds. [paul@RHEL4b ~]$ test 56 -gt 55 ; echo $? 0 [paul@RHEL4b ~]$ If you prefer true and false, then write the test like this. [paul@RHEL4b ~]$ test 56 -gt 55 && echo true || echo false true [paul@RHEL4b ~]$ test 6 -gt 55 && echo true || echo false false The test command can also be written as square brackets, the screenshot below is identical to the one above. [paul@RHEL4b ~]$ [ 56 -gt 55 ] && echo true || echo false true [paul@RHEL4b ~]$ [ 6 -gt 55 ] && echo true || echo false false Below are some example tests. Take a look at man test to see more options for tests. [ [ [ [ [ [ [ [ [ [ [ -d foo ] -e bar ] '/etc' = $PWD ] $1 != 'secret' ] 55 -lt $bar ] $foo -ge 1000 ] "abc" < $bar ] -f foo ] -r bar ] foo -nt bar ] -o nounset ] Does the directory foo exist ? Does the file bar exist ? Is the string /etc equal to the variable $PWD ? Is the first parameter different from secret ? Is 55 less than the value of $bar ? Is the value of $foo greater or equal to 1000 ? Does abc sort before the value of $bar ? Is foo a regular file ? Is bar a readable file ? Is file foo newer than file bar ? Is the shell option nounset set ? Tests can be combined with logical AND and OR. paul@RHEL4b:~$ [ 66 -gt 55 -a 66 -lt 500 ] && echo true || echo false true paul@RHEL4b:~$ [ 66 -gt 55 -a 660 -lt 500 ] && echo true || echo false false paul@RHEL4b:~$ [ 66 -gt 55 -o 660 -lt 500 ] && echo true || echo false true 241scripting loops 24.2. if then else The if then else construction is about choice. If a certain condition is met, then execute something, else execute something else. The example below tests whether a file exists, and if the file exists then a proper message is echoed. #!/bin/bash if [ -f isit.txt ] then echo isit.txt exists! else echo isit.txt not found! fi If we name the above script 'choice', then it executes like this. [paul@RHEL4a scripts]$ ./choice isit.txt not found! [paul@RHEL4a scripts]$ touch isit.txt [paul@RHEL4a scripts]$ ./choice isit.txt exists! [paul@RHEL4a scripts]$ 24.3. if then elif You can nest a new if inside an else with elif. This is a simple example. #!/bin/bash count=42 if [ $count -eq 42 ] then echo "42 is correct." elif [ $count -gt 42 ] then echo "Too much." else echo "Not enough." fi 24.4. for loop The example below shows the syntax of a classical for loop in bash. for i in 1 2 4 do echo $i done An example of a for loop combined with an embedded shell. #!/bin/ksh for counter in `seq 1 20` do echo counting from 1 to 20, now at $counter sleep 1 done The same example as above can be written without the embedded shell using the bash {from..to} shorthand. 242scripting loops #!/bin/bash for counter in {1..20} do echo counting from 1 to 20, now at $counter sleep 1 done This for loop uses file globbing (from the shell expansion). Putting the instruction on the command line has identical functionality. kahlan@solexp11$ ls count.ksh go.ksh kahlan@solexp11$ for file in *.ksh ; do cp $file $file.backup ; done kahlan@solexp11$ ls count.ksh count.ksh.backup go.ksh go.ksh.backup 24.5. while loop Below a simple example of a while loop. i=100; while [ $i -ge 0 ] ; do echo Counting down, from 100 to 0, now at $i; let i--; done Endless loops can be made with while true or while : , where the colon is the equivalent of no operation in the Korn and bash shells. #!/bin/ksh # endless loop while : do echo hello sleep 1 done 24.6. until loop Below a simple example of an until loop. let i=100; until [ $i -le 0 ] ; do echo Counting down, from 100 to 1, now at $i; let i--; done 243scripting loops 24.7. practice: scripting tests and loops 1. Write a script that uses a for loop to count from 3 to 7. 2. Write a script that uses a for loop to count from 1 to 17000. 3. Write a script that uses a while loop to count from 3 to 7. 4. Write a script that uses an until loop to count down from 8 to 4. 5. Write a script that counts the number of files ending in .txt in the current directory. 6. Wrap an if statement around the script so it is also correct when there are zero files ending in .txt. 244scripting loops 24.8. solution: scripting tests and loops 1. Write a script that uses a for loop to count from 3 to 7. #!/bin/bash for i in 3 4 5 6 7 do echo Counting from 3 to 7, now at $i done 2. Write a script that uses a for loop to count from 1 to 17000. #!/bin/bash for i in `seq 1 17000` do echo Counting from 1 to 17000, now at $i done 3. Write a script that uses a while loop to count from 3 to 7. #!/bin/bash i=3 while [ $i -le 7 ] do echo Counting from 3 to 7, now at $i let i=i+1 done 4. Write a script that uses an until loop to count down from 8 to 4. #!/bin/bash i=8 until [ $i -lt 4 ] do echo Counting down from 8 to 4, now at $i let i=i-1 done 5. Write a script that counts the number of files ending in .txt in the current directory. #!/bin/bash let i=0 for file in *.txt do let i++ done echo "There are $i files ending in .txt" 6. Wrap an if statement around the script so it is also correct when there are zero files ending in .txt. #!/bin/bash ls *.txt > /dev/null 2>&1 if [ $? -ne 0 ] 245scripting loops then echo "There are 0 files ending in .txt" else let i=0 for file in *.txt do let i++ done echo "There are $i files ending in .txt" fi 246Chapter 25. scripting parameters 247scripting parameters 25.1. script parameters A bash shell script can have parameters. The numbering you see in the script below continues if you have more parameters. You also have special parameters containing the number of parameters, a string of all of them, and also the process id, and the last return code. The man page of bash has a full list. #!/bin/bash echo The first argument is $1 echo The second argument is $2 echo The third argument is $3 echo echo echo echo \$ \# \? \* $$ $# $? $* PID of the script count arguments last return code all the arguments Below is the output of the script above in action. [paul@RHEL4a scripts]$ ./pars one two three The first argument is one The second argument is two The third argument is three $ 5610 PID of the script # 3 count arguments ? 0 last return code * one two three all the arguments Once more the same script, but with only two parameters. [paul@RHEL4a scripts]$ ./pars 1 2 The first argument is 1 The second argument is 2 The third argument is $ 5612 PID of the script # 2 count arguments ? 0 last return code * 1 2 all the arguments [paul@RHEL4a scripts]$ Here is another example, where we use $0. The $0 parameter contains the name of the script. paul@debian6~$ cat myname echo this script is called $0 paul@debian6~$ ./myname this script is called ./myname paul@debian6~$ mv myname test42 paul@debian6~$ ./test42 this script is called ./test42 248scripting parameters 25.2. shift through parameters The shift statement can parse all parameters one by one. This is a sample script. kahlan@solexp11$ cat shift.ksh #!/bin/ksh if [ "$#" == "0" ] then echo You have to give at least one parameter. exit 1 fi while (( $# )) do echo You gave me $1 shift done Below is some sample output of the script above. kahlan@solexp11$ ./shift.ksh one You gave me one kahlan@solexp11$ ./shift.ksh one two three 1201 "33 42" You gave me one You gave me two You gave me three You gave me 1201 You gave me 33 42 kahlan@solexp11$ ./shift.ksh You have to give at least one parameter. 25.3. runtime input You can ask the user for input with the read command in a script. #!/bin/bash echo -n Enter a number: read number 249scripting parameters 25.4. sourcing a config file The source (as seen in the shell chapters) can be used to source a configuration file. Below a sample configuration file for an application. [paul@RHEL4a scripts]$ cat myApp.conf # The config file of myApp # Enter the path here myAppPath=/var/myApp # Enter the number of quines here quines=5 And here an application that uses this file. [paul@RHEL4a scripts]$ cat myApp.bash #!/bin/bash # # Welcome to the myApp application # . ./myApp.conf echo There are $quines quines The running application can use the values inside the sourced configuration file. [paul@RHEL4a scripts]$ ./myApp.bash There are 5 quines [paul@RHEL4a scripts]$ 250scripting parameters 25.5. get script options with getopts The getopts function allows you to parse options given to a command. The following script allows for any combination of the options a, f and z. kahlan@solexp11$ cat options.ksh #!/bin/ksh while getopts ":afz" option; do case $option in a) echo received -a ;; f) echo received -f ;; z) echo received -z ;; *) echo "invalid option -$OPTARG" ;; esac done This is sample output from the script above. First we use correct options, then we enter twice an invalid option. kahlan@solexp11$ ./options.ksh kahlan@solexp11$ ./options.ksh -af received -a received -f kahlan@solexp11$ ./options.ksh -zfg received -z received -f invalid option -g kahlan@solexp11$ ./options.ksh -a -b -z received -a invalid option -b received -z 251scripting parameters You can also check for options that need an argument, as this example shows. kahlan@solexp11$ cat argoptions.ksh #!/bin/ksh while getopts ":af:z" option; do case $option in a) echo received -a ;; f) echo received -f with $OPTARG ;; z) echo received -z ;; :) echo "option -$OPTARG needs an argument" ;; *) echo "invalid option -$OPTARG" ;; esac done This is sample output from the script above. kahlan@solexp11$ ./argoptions.ksh -a -f hello -z received -a received -f with hello received -z kahlan@solexp11$ ./argoptions.ksh -zaf 42 received -z received -a received -f with 42 kahlan@solexp11$ ./argoptions.ksh -zf received -z option -f needs an argument 25.6. get shell options with shopt You can toggle the values of variables controlling optional shell behaviour with the shopt built-in shell command. The example below first verifies whether the cdspell option is set; it is not. The next shopt command sets the value, and the third shopt command verifies that the option really is set. You can now use minor spelling mistakes in the cd command. The man page of bash has a complete list of options. paul@laika:~$ 1 paul@laika:~$ paul@laika:~$ 0 paul@laika:~$ /etc shopt -q cdspell ; echo $? shopt -s cdspell shopt -q cdspell ; echo $? cd /Etc 252scripting parameters 25.7. practice: parameters and options 1. Write a script that receives four parameters, and outputs them in reverse order. 2. Write a script that receives two parameters (two filenames) and outputs whether those files exist. 3. Write a script that asks for a filename. Verify existence of the file, then verify that you own the file, and whether it is writable. If not, then make it writable. 4. Make a configuration file for the previous script. Put a logging switch in the config file, logging means writing detailed output of everything the script does to a log file in /tmp. 253scripting parameters 25.8. solution: parameters and options 1. Write a script that receives four parameters, and outputs them in reverse order. echo $4 $3 $2 $1 2. Write a script that receives two parameters (two filenames) and outputs whether those files exist. #!/bin/bash if [ -f $1 ] then echo $1 exists! else echo $1 not found! fi if [ -f $2 ] then echo $2 exists! else echo $2 not found! fi 3. Write a script that asks for a filename. Verify existence of the file, then verify that you own the file, and whether it is writable. If not, then make it writable. 4. Make a configuration file for the previous script. Put a logging switch in the config file, logging means writing detailed output of everything the script does to a log file in /tmp. 254Chapter 26. more scripting 255more scripting 26.1. eval eval reads arguments as input to the shell (the resulting commands are executed). This allows using the value of a variable as a variable. paul@deb503:~/test42$ answer=42 paul@deb503:~/test42$ word=answer paul@deb503:~/test42$ eval x=\$$word ; echo $x 42 Both in bash and Korn the arguments can be quoted. kahlan@solexp11$ answer=42 kahlan@solexp11$ word=answer kahlan@solexp11$ eval "y=\$$word" ; echo $y 42 Sometimes the eval is needed to have correct parsing of arguments. Consider this example where the date command receives one parameter 1 week ago. paul@debian6~$ date --date="1 week ago" Thu Mar 8 21:36:25 CET 2012 When we set this command in a variable, then executing that variable fails unless we use eval. paul@debian6~$ lastweek='date --date="1 week ago"' paul@debian6~$ $lastweek date: extra operand `ago"' Try `date --help' for more information. paul@debian6~$ eval $lastweek Thu Mar 8 21:36:39 CET 2012 26.2. (( )) The (( )) allows for evaluation of numerical expressions. paul@deb503:~/test42$ true paul@deb503:~/test42$ false paul@deb503:~/test42$ paul@deb503:~/test42$ true paul@deb503:~/test42$ true paul@deb503:~/test42$ paul@deb503:~/test42$ false (( 42 > 33 )) && echo true || echo false (( 42 > 1201 )) && echo true || echo false var42=42 (( 42 == var42 )) && echo true || echo false (( 42 == $var42 )) && echo true || echo false var42=33 (( 42 == var42 )) && echo true || echo false 256more scripting 26.3. let The let built-in shell function instructs the shell to perform an evaluation of arithmetic expressions. It will return 0 unless the last arithmetic expression evaluates to 0. [paul@RHEL4b 7 [paul@RHEL4b 20 [paul@RHEL4b 18 [paul@RHEL4b 30 ~]$ let x="3 + 4" ; echo $x ~]$ let x="10 + 100/10" ; echo $x ~]$ let x="10-2+100/10" ; echo $x ~]$ let x="10*2+100/10" ; echo $x The shell can also convert between different bases. [paul@RHEL4b 255 [paul@RHEL4b 192 [paul@RHEL4b 168 [paul@RHEL4b 56 [paul@RHEL4b 63 [paul@RHEL4b 192 ~]$ let x="0xFF" ; echo $x ~]$ let x="0xC0" ; echo $x ~]$ let x="0xA8" ; echo $x ~]$ let x="8#70" ; echo $x ~]$ let x="8#77" ; echo $x ~]$ let x="16#c0" ; echo $x There is a difference between assigning a variable directly, or using let to evaluate the arithmetic expressions (even if it is just assigning a value). kahlan@solexp11$ kahlan@solexp11$ 15 017 0x0f kahlan@solexp11$ kahlan@solexp11$ 15 15 15 dec=15 ; oct=017 ; hex=0x0f echo $dec $oct $hex let dec=15 ; let oct=017 ; let hex=0x0f echo $dec $oct $hex 257more scripting 26.4. case You can sometimes simplify nested if statements with a case construct. [paul@RHEL4b ~]$ ./help What animal did you see ? lion You better start running fast! [paul@RHEL4b ~]$ ./help What animal did you see ? dog Don't worry, give it a cookie. [paul@RHEL4b ~]$ cat help #!/bin/bash # # Wild Animals Helpdesk Advice # echo -n "What animal did you see ? " read animal case $animal in "lion" | "tiger") echo "You better start running fast!" ;; "cat") echo "Let that mouse go..." ;; "dog") echo "Don't worry, give it a cookie." ;; "chicken" | "goose" | "duck" ) echo "Eggs for breakfast!" ;; "liger") echo "Approach and say 'Ah you big fluffy kitty...'." ;; "babelfish") echo "Did it fall out your ear ?" ;; *) echo "You discovered an unknown animal, name it!" ;; esac [paul@RHEL4b ~]$ 258more scripting 26.5. shell functions Shell functions can be used to group commands in a logical way. kahlan@solexp11$ cat funcs.ksh #!/bin/ksh function greetings { echo Hello World! echo and hello to $USER to! } echo We will now call a function greetings echo The end This is sample output from this script with a function. kahlan@solexp11$ ./funcs.ksh We will now call a function Hello World! and hello to kahlan to! The end A shell function can also receive parameters. kahlan@solexp11$ cat addfunc.ksh #!/bin/ksh function plus { let result="$1 + $2" echo $1 + $2 = $result } plus 3 10 plus 20 13 plus 20 22 This script produces the following output. kahlan@solexp11$ ./addfunc.ksh 3 + 10 = 13 20 + 13 = 33 20 + 22 = 42 259more scripting 26.6. practice : more scripting 1. Write a script that asks for two numbers, and outputs the sum and product (as shown here). Enter a number: 5 Enter another number: 2 Sum: Product: 5 + 2 = 7 5 x 2 = 10 2. Improve the previous script to test that the numbers are between 1 and 100, exit with an error if necessary. 3. Improve the previous script to congratulate the user if the sum equals the product. 4. Write a script with a case insensitive case statement, using the shopt nocasematch option. The nocasematch option is reset to the value it had before the scripts started. 5. If time permits (or if you are waiting for other students to finish this practice), take a look at Linux system scripts in /etc/init.d and /etc/rc.d and try to understand them. Where does execution of a script start in /etc/init.d/samba ? There are also some hidden scripts in ~, we will discuss them later. 260more scripting 26.7. solution : more scripting 1. Write a script that asks for two numbers, and outputs the sum and product (as shown here). Enter a number: 5 Enter another number: 2 Sum: Product: 5 + 2 = 7 5 x 2 = 10 #!/bin/bash echo -n "Enter a number : " read n1 echo -n "Enter another number : " read n2 let sum="$n1+$n2" let pro="$n1*$n2" echo -e "Sum\t: $n1 + $n2 = $sum" echo -e "Product\t: $n1 * $n2 = $pro" 2. Improve the previous script to test that the numbers are between 1 and 100, exit with an error if necessary. echo -n "Enter a number between 1 and 100 : " read n1 if [ $n1 -lt 1 -o $n1 -gt 100 ] then echo Wrong number... exit 1 fi 3. Improve the previous script to congratulate the user if the sum equals the product. if [ $sum -eq $pro ] then echo Congratulations $sum == $pro fi 4. Write a script with a case insensitive case statement, using the shopt nocasematch option. The nocasematch option is reset to the value it had before the scripts started. #!/bin/bash # # Wild Animals Case Insensitive Helpdesk Advice # if shopt -q nocasematch; then nocase=yes; else nocase=no; shopt -s nocasematch; fi echo -n "What animal did you see ? " read animal case $animal in 261more scripting "lion" | "tiger") echo "You better start running fast!" ;; "cat") echo "Let that mouse go..." ;; "dog") echo "Don't worry, give it a cookie." ;; "chicken" | "goose" | "duck" ) echo "Eggs for breakfast!" ;; "liger") echo "Approach and say 'Ah you big fluffy kitty.'" ;; "babelfish") echo "Did it fall out your ear ?" ;; *) echo "You discovered an unknown animal, name it!" ;; esac if [ nocase = yes ] ; then shopt -s nocasematch; else shopt -u nocasematch; fi 5. If time permits (or if you are waiting for other students to finish this practice), take a look at Linux system scripts in /etc/init.d and /etc/rc.d and try to understand them. Where does execution of a script start in /etc/init.d/samba ? There are also some hidden scripts in ~, we will discuss them later. 262Part VIII. local user managementTable of Contents 304Chapter 27. introduction to users This little chapter will teach you how to identify your user account on a Unix computer using commands like who am i, id, and more. In a second part you will learn how to become another user with the su command. And you will learn how to run a program as another user with sudo. 266introduction to users 27.1. whoami The whoami command tells you your username. [paul@centos7 ~]$ whoami paul [paul@centos7 ~]$ 27.2. who The who command will give you information about who is logged on the system. [paul@centos7 ~]$ who root pts/0 2014-10-10 23:07 (10.104.33.101) paul pts/1 2014-10-10 23:30 (10.104.33.101) laura pts/2 2014-10-10 23:34 (10.104.33.96) tania pts/3 2014-10-10 23:39 (10.104.33.91) [paul@centos7 ~]$ 27.3. who am i With who am i the who command will display only the line pointing to your current session. [paul@centos7 ~]$ who am i paul pts/1 2014-10-10 23:30 (10.104.33.101) [paul@centos7 ~]$ 27.4. w The w command shows you who is logged on and what they are doing. [paul@centos7 ~]$ w 23:34:07 up 31 min, 2 users, load average: 0.00, 0.01, 0.02 USER TTY LOGIN@ IDLE JCPU PCPU WHAT root pts/0 23:07 15.00s 0.01s 0.01s top paul pts/1 23:30 7.00s 0.00s 0.00s w [paul@centos7 ~]$ 27.5. id The id command will give you your user id, primary group id, and a list of the groups that you belong to. paul@debian7:~$ id uid=1000(paul) gid=1000(paul) groups=1000(paul) On RHEL/CentOS you will also get SELinux context information with this command. [root@centos7 ~]# id uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r\ :unconfined_t:s0-s0:c0.c1023 267introduction to users 27.6. su to another user The su command allows a user to run a shell as another user. laura@debian7:~$ su tania Password: tania@debian7:/home/laura$ 27.7. su to root Yes you can also su to become root, when you know the root password. laura@debian7:~$ su root Password: root@debian7:/home/laura# 27.8. su as root You need to know the password of the user you want to substitute to, unless your are logged in as root. The root user can become any existing user without knowing that user's password. root@debian7:~# id uid=0(root) gid=0(root) groups=0(root) root@debian7:~# su - valentina valentina@debian7:~$ 27.9. su - $username By default, the su command maintains the same shell environment. To become another user and also get the target user's environment, issue the su - command followed by the target username. root@debian7:~# su laura laura@debian7:/root$ exit exit root@debian7:~# su - laura laura@debian7:~$ pwd /home/laura 27.10. su - When no username is provided to su or su -, the command will assume root is the target. tania@debian7:~$ su - Password: root@debian7:~# 268introduction to users 27.11. run a program as another user The sudo program allows a user to start a program with the credentials of another user. Before this works, the system administrator has to set up the /etc/sudoers file. This can be useful to delegate administrative tasks to another user (without giving the root password). The screenshot below shows the usage of sudo. User paul received the right to run useradd with the credentials of root. This allows paul to create new users on the system without becoming root and without knowing the root password. First the command fails for paul. paul@debian7:~$ /usr/sbin/useradd -m valentina useradd: Permission denied. useradd: cannot lock /etc/passwd; try again later. But with sudo it works. paul@debian7:~$ sudo /usr/sbin/useradd -m valentina [sudo] password for paul: paul@debian7:~$ 27.12. visudo Check the man page of visudo before playing with the /etc/sudoers file. Editing the sudoers is out of scope for this fundamentals book. paul@rhel65:~$ apropos visudo visudo (8) - edit the sudoers file paul@rhel65:~$ 269introduction to users 27.13. sudo su - On some Linux systems like Ubuntu and Xubuntu, the root user does not have a password set. This means that it is not possible to login as root (extra security). To perform tasks as root, the first user is given all sudo rights via the /etc/sudoers. In fact all users that are members of the admin group can use sudo to run all commands as root. root@laika:~# grep admin /etc/sudoers # Members of the admin group may gain root privileges %admin ALL=(ALL) ALL The end result of this is that the user can type sudo su - and become root without having to enter the root password. The sudo command does require you to enter your own password. Thus the password prompt in the screenshot below is for sudo, not for su. paul@laika:~$ sudo su - Password: root@laika:~# 27.14. sudo logging Using sudo without authorization will result in a severe warning: paul@rhel65:~$ sudo su - We trust you have received the usual lecture from the local System Administrator. It usually boils down to these three things: #1) Respect the privacy of others. #2) Think before you type. #3) With great power comes great responsibility. [sudo] password for paul: paul is not in the sudoers file. paul@rhel65:~$ This incident will be reported. The root user can see this in the /var/log/secure on Red Hat and in /var/log/auth.log on Debian). root@rhel65:~# tail /var/log/secure | grep sudo | tr -s ' ' Apr 13 16:03:42 rhel65 sudo: paul : user NOT in sudoers ; TTY=pts/0 ; PWD=\ /home/paul ; USER=root ; COMMAND=/bin/su - root@rhel65:~# 270introduction to users 27.15. practice: introduction to users 1. Run a command that displays only your currently logged on user name. 2. Display a list of all logged on users. 3. Display a list of all logged on users including the command they are running at this very moment. 4. Display your user name and your unique user identification (userid). 5. Use su to switch to another user account (unless you are root, you will need the password of the other account). And get back to the previous account. 6. Now use su - to switch to another user and notice the difference. Note that su - gets you into the home directory of Tania. 7. Try to create a new user account (when using your normal user account). this should fail. (Details on adding user accounts are explained in the next chapter.) 8. Now try the same, but with sudo before your command. 271introduction to users 27.16. solution: introduction to users 1. Run a command that displays only your currently logged on user name. laura@debian7:~$ whoami laura laura@debian7:~$ echo $USER laura 2. Display a list of all logged on users. laura@debian7:~$ who laura pts/0 laura@debian7:~$ 2014-10-13 07:22 (10.104.33.101) 3. Display a list of all logged on users including the command they are running at this very moment. laura@debian7:~$ w 07:47:02 up 16 min, 2 users, load average: 0.00, 0.00, USER TTY FROM LOGIN@ IDLE JCPU root pts/0 10.104.33.101 07:30 6.00s 0.04s root pts/1 10.104.33.101 07:46 6.00s 0.01s laura@debian7:~$ 0.00 PCPU WHAT 0.00s w 0.00s sleep 42 4. Display your user name and your unique user identification (userid). laura@debian7:~$ id uid=1005(laura) gid=1007(laura) groups=1007(laura) laura@debian7:~$ 5. Use su to switch to another user account (unless you are root, you will need the password of the other account). And get back to the previous account. laura@debian7:~$ su tania Password: tania@debian7:/home/laura$ id uid=1006(tania) gid=1008(tania) groups=1008(tania) tania@debian7:/home/laura$ exit laura@debian7:~$ 6. Now use su - to switch to another user and notice the difference. laura@debian7:~$ su - tania Password: tania@debian7:~$ pwd /home/tania tania@debian7:~$ logout laura@debian7:~$ Note that su - gets you into the home directory of Tania. 272introduction to users 7. Try to create a new user account (when using your normal user account). this should fail. (Details on adding user accounts are explained in the next chapter.) laura@debian7:~$ useradd valentina -su: useradd: command not found laura@debian7:~$ /usr/sbin/useradd valentina useradd: Permission denied. useradd: cannot lock /etc/passwd; try again later. It is possible that useradd is located in /sbin/useradd on your computer. 8. Now try the same, but with sudo before your command. laura@debian7:~$ sudo /usr/sbin/useradd valentina [sudo] password for laura: laura is not in the sudoers file. This incident will be reported. laura@debian7:~$ Notice that laura has no permission to use the sudo on this system. 273Chapter 28. user management This chapter will teach you how to use useradd, usermod and userdel to create, modify and remove user accounts. You will need root access on a Linux computer to complete this chapter. 274user management 28.1. user management User management on Linux can be done in three complementary ways. You can use the graphical tools provided by your distribution. These tools have a look and feel that depends on the distribution. If you are a novice Linux user on your home system, then use the graphical tool that is provided by your distribution. This will make sure that you do not run into problems. Another option is to use command line tools like useradd, usermod, gpasswd, passwd and others. Server administrators are likely to use these tools, since they are familiar and very similar across many different distributions. This chapter will focus on these command line tools. A third and rather extremist way is to edit the local configuration files directly using vi (or vipw/vigr). Do not attempt this as a novice on production systems! 28.2. /etc/passwd The local user database on Linux (and on most Unixes) is /etc/passwd. [root@RHEL5 ~]# tail /etc/passwd inge:x:518:524:art dealer:/home/inge:/bin/ksh ann:x:519:525:flute player:/home/ann:/bin/bash frederik:x:520:526:rubius poet:/home/frederik:/bin/bash steven:x:521:527:roman emperor:/home/steven:/bin/bash pascale:x:522:528:artist:/home/pascale:/bin/ksh geert:x:524:530:kernel developer:/home/geert:/bin/bash wim:x:525:531:master damuti:/home/wim:/bin/bash sandra:x:526:532:radish stresser:/home/sandra:/bin/bash annelies:x:527:533:sword fighter:/home/annelies:/bin/bash laura:x:528:534:art dealer:/home/laura:/bin/ksh As you can see, this file contains seven columns separated by a colon. The columns contain the username, an x, the user id, the primary group id, a description, the name of the home directory, and the login shell. More information can be found by typing man 5 passwd. [root@RHEL5 ~]# man 5 passwd 28.3. root The root user also called the superuser is the most powerful account on your Linux system. This user can do almost anything, including the creation of other users. The root user always has userid 0 (regardless of the name of the account). [root@RHEL5 ~]# head -1 /etc/passwd root:x:0:0:root:/root:/bin/bash 275user management 28.4. useradd You can add users with the useradd command. The example below shows how to add a user named yanina (last parameter) and at the same time forcing the creation of the home directory (-m), setting the name of the home directory (-d), and setting a description (-c). [root@RHEL5 ~]# useradd -m -d /home/yanina -c "yanina wickmayer" yanina [root@RHEL5 ~]# tail -1 /etc/passwd yanina:x:529:529:yanina wickmayer:/home/yanina:/bin/bash The user named yanina received userid 529 and primary group id 529. 28.5. /etc/default/useradd Both Red Hat Enterprise Linux and Debian/Ubuntu have a file called /etc/default/useradd that contains some default user options. Besides using cat to display this file, you can also use useradd -D. [root@RHEL4 ~]# useradd -D GROUP=100 HOME=/home INACTIVE=-1 EXPIRE= SHELL=/bin/bash SKEL=/etc/skel 28.6. userdel You can delete the user yanina with userdel. The -r option of userdel will also remove the home directory. [root@RHEL5 ~]# userdel -r yanina 28.7. usermod You can modify the properties of a user with the usermod command. This example uses usermod to change the description of the user harry. [root@RHEL4 ~]# tail -1 /etc/passwd harry:x:516:520:harry potter:/home/harry:/bin/bash [root@RHEL4 ~]# usermod -c 'wizard' harry [root@RHEL4 ~]# tail -1 /etc/passwd harry:x:516:520:wizard:/home/harry:/bin/bash 276user management 28.8. creating home directories The easiest way to create a home directory is to supply the -m option with useradd (it is likely set as a default option on Linux). A less easy way is to create a home directory manually with mkdir which also requires setting the owner and the permissions on the directory with chmod and chown (both commands are discussed in detail in another chapter). [root@RHEL5 ~]# mkdir /home/laura [root@RHEL5 ~]# chown laura:laura /home/laura [root@RHEL5 ~]# chmod 700 /home/laura [root@RHEL5 ~]# ls -ld /home/laura/ drwx------ 2 laura laura 4096 Jun 24 15:17 /home/laura/ 28.9. /etc/skel/ When using useradd the -m option, the /etc/skel/ directory is copied to the newly created home directory. The /etc/skel/ directory contains some (usually hidden) files that contain profile settings and default values for applications. In this way /etc/skel/ serves as a default home directory and as a default user profile. [root@RHEL5 ~]# ls total 48 drwxr-xr-x 2 root drwxr-xr-x 97 root -rw-r--r-- 1 root -rw-r--r-- 1 root -rw-r--r-- 1 root -la /etc/skel/ root 4096 Apr 1 00:11 . root 12288 Jun 24 15:36 .. root 24 Jul 12 2006 .bash_logout root 176 Jul 12 2006 .bash_profile root 124 Jul 12 2006 .bashrc 28.10. deleting home directories The -r option of userdel will make sure that the home directory is deleted together with the user account. [root@RHEL5 ~]# ls -ld /home/wim/ drwx------ 2 wim wim 4096 Jun 24 15:19 /home/wim/ [root@RHEL5 ~]# userdel -r wim [root@RHEL5 ~]# ls -ld /home/wim/ ls: /home/wim/: No such file or directory 277user management 28.11. login shell The /etc/passwd file specifies the login shell for the user. In the screenshot below you can see that user annelies will log in with the /bin/bash shell, and user laura with the /bin/ksh shell. [root@RHEL5 ~]# tail -2 /etc/passwd annelies:x:527:533:sword fighter:/home/annelies:/bin/bash laura:x:528:534:art dealer:/home/laura:/bin/ksh You can use the usermod command to change the shell for a user. [root@RHEL5 ~]# usermod -s /bin/bash laura [root@RHEL5 ~]# tail -1 /etc/passwd laura:x:528:534:art dealer:/home/laura:/bin/bash 28.12. chsh Users can change their login shell with the chsh command. First, user harry obtains a list of available shells (he could also have done a cat /etc/shells) and then changes his login shell to the Korn shell (/bin/ksh). At the next login, harry will default into ksh instead of bash. [laura@centos7 ~]$ chsh -l /bin/sh /bin/bash /sbin/nologin /usr/bin/sh /usr/bin/bash /usr/sbin/nologin /bin/ksh /bin/tcsh /bin/csh [laura@centos7 ~]$ Note that the -l option does not exist on Debian and that the above screenshot assumes that ksh and csh shells are installed. The screenshot below shows how laura can change her default shell (active on next login). [laura@centos7 ~]$ chsh -s /bin/ksh Changing shell for laura. Password: Shell changed. 278user management 28.13. practice: user management 1. Create a user account named serena, including a home directory and a description (or comment) that reads Serena Williams. Do all this in one single command. 2. Create a user named venus, including home directory, bash shell, a description that reads Venus Williams all in one single command. 3. Verify that both users have correct entries in /etc/passwd, /etc/shadow and /etc/group. 4. Verify that their home directory was created. 5. Create a user named einstime with /bin/date as his default logon shell. 7. What happens when you log on with the einstime user ? Can you think of a useful real world example for changing a user's login shell to an application ? 8. Create a file named welcome.txt and make sure every new user will see this file in their home directory. 9. Verify this setup by creating (and deleting) a test user account. 10. Change the default login shell for the serena user to /bin/bash. Verify before and after you make this change. 279user management 28.14. solution: user management 1. Create a user account named serena, including a home directory and a description (or comment) that reads Serena Williams. Do all this in one single command. root@debian7:~# useradd -m -c 'Serena Williams' serena 2. Create a user named venus, including home directory, bash shell, a description that reads Venus Williams all in one single command. root@debian7:~# useradd -m -c "Venus Williams" -s /bin/bash venus 3. Verify that both users have correct entries in /etc/passwd, /etc/shadow and /etc/group. root@debian7:~# tail -2 /etc/passwd serena:x:1008:1010:Serena Williams:/home/serena:/bin/sh venus:x:1009:1011:Venus Williams:/home/venus:/bin/bash root@debian7:~# tail -2 /etc/shadow serena:!:16358:0:99999:7::: venus:!:16358:0:99999:7::: root@debian7:~# tail -2 /etc/group serena:x:1010: venus:x:1011: 4. Verify that their home directory was created. root@debian7:~# ls -lrt /home | tail -2 drwxr-xr-x 2 serena serena 4096 Oct 15 10:50 serena drwxr-xr-x 2 venus venus 4096 Oct 15 10:59 venus root@debian7:~# 5. Create a user named einstime with /bin/date as his default logon shell. root@debian7:~# useradd -s /bin/date einstime Or even better: root@debian7:~# useradd -s $(which date) einstime 7. What happens when you log on with the einstime user ? Can you think of a useful real world example for changing a user's login shell to an application ? root@debian7:~# su - einstime Wed Oct 15 11:05:56 UTC 2014 # You get the output of the date command root@debian7:~# It can be useful when users need to access only one application on the server. Just logging in opens the application for them, and closing the application automatically logs them out. 280user management 8. Create a file named welcome.txt and make sure every new user will see this file in their home directory. root@debian7:~# echo Hello > /etc/skel/welcome.txt 9. Verify this setup by creating (and deleting) a test user account. root@debian7:~# useradd -m test root@debian7:~# ls -l /home/test total 4 -rw-r--r-- 1 test test 6 Oct 15 11:16 welcome.txt root@debian7:~# userdel -r test root@debian7:~# 10. Change the default login shell for the serena user to /bin/bash. Verify before and after you make this change. root@debian7:~# grep serena /etc/passwd serena:x:1008:1010:Serena Williams:/home/serena:/bin/sh root@debian7:~# usermod -s /bin/bash serena root@debian7:~# grep serena /etc/passwd serena:x:1008:1010:Serena Williams:/home/serena:/bin/bash root@debian7:~# 281Chapter 29. user passwords This chapter will tell you more about passwords for local users. Three methods for setting passwords are explained; using the passwd command, using openssel passwd, and using the crypt function in a C program. The chapter will also discuss password settings and disabling, suspending or locking accounts. 282user passwords 29.1. passwd Passwords of users can be set with the passwd command. Users will have to provide their old password before twice entering the new one. [tania@centos7 ~]$ passwd Changing password for user tania. Changing password for tania. (current) UNIX password: New password: BAD PASSWORD: The password is shorter than 8 characters New password: BAD PASSWORD: The password is a palindrome New password: BAD PASSWORD: The password is too similar to the old one passwd: Have exhausted maximum number of retries for service As you can see, the passwd tool will do some basic verification to prevent users from using too simple passwords. The root user does not have to follow these rules (there will be a warning though). The root user also does not have to provide the old password before entering the new password twice. root@debian7:~# passwd tania Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully 29.2. shadow file User passwords are encrypted and kept in /etc/shadow. The /etc/shadow file is read only and can only be read by root. We will see in the file permissions section how it is possible for users to change their password. For now, you will have to know that users can change their password with the /usr/bin/passwd command. [root@centos7 ~]# tail -4 /etc/shadow paul:$6$ikp2Xta5BT.Tml.p$2TZjNnOYNNQKpwLJqoGJbVsZG5/Fti8ovBRd.VzRbiDSl7TEq\ IaSMH.TeBKnTS/SjlMruW8qffC0JNORW.BTW1:16338:0:99999:7::: tania:$6$8Z/zovxj$9qvoqT8i9KIrmN.k4EQwAF5ryz5yzNwEvYjAa9L5XVXQu.z4DlpvMREH\ eQpQzvRnqFdKkVj17H5ST.c79HDZw0:16356:0:99999:7::: laura:$6$glDuTY5e$/NYYWLxfHgZFWeoujaXSMcR.Mz.lGOxtcxFocFVJNb98nbTPhWFXfKWG\ SyYh1WCv6763Wq54.w24Yr3uAZBOm/:16356:0:99999:7::: valentina:$6$jrZa6PVI$1uQgqR6En9mZB6mKJ3LXRB4CnFko6LRhbh.v4iqUk9MVreui1lv7\ GxHOUDSKA0N55ZRNhGHa6T2ouFnVno/0o1:16356:0:99999:7::: [root@centos7 ~]# The /etc/shadow file contains nine colon separated columns. The nine fields contain (from left to right) the user name, the encrypted password (note that only inge and laura have an encrypted password), the day the password was last changed (day 1 is January 1, 1970), number of days the password must be left unchanged, password expiry day, warning number of days before password expiry, number of days after expiry before disabling the account, and the day the account was disabled (again, since 1970). The last field has no meaning yet. All the passwords in the screenshot above are hashes of hunter2. 283user passwords 29.3. encryption with passwd Passwords are stored in an encrypted format. This encryption is done by the crypt function. The easiest (and recommended) way to add a user with a password to the system is to add the user with the useradd -m user command, and then set the user's password with passwd. [root@RHEL4 ~]# useradd -m xavier [root@RHEL4 ~]# passwd xavier Changing password for user xavier. New UNIX password: Retype new UNIX password: passwd: all authentication tokens updated successfully. [root@RHEL4 ~]# 29.4. encryption with openssl Another way to create users with a password is to use the -p option of useradd, but that option requires an encrypted password. You can generate this encrypted password with the openssl passwd command. The openssl passwd command will generate several distinct hashes for the same password, for this it uses a salt. paul@rhel65:~$ openssl passwd hunter2 86jcUNlnGDFpY paul@rhel65:~$ openssl passwd hunter2 Yj7mDO9OAnvq6 paul@rhel65:~$ openssl passwd hunter2 YqDcJeGoDbzKA paul@rhel65:~$ This salt can be chosen and is visible as the first two characters of the hash. paul@rhel65:~$ openssl passwd -salt 42 hunter2 42ZrbtP1Ze8G. paul@rhel65:~$ openssl passwd -salt 42 hunter2 42ZrbtP1Ze8G. paul@rhel65:~$ openssl passwd -salt 42 hunter2 42ZrbtP1Ze8G. paul@rhel65:~$ This example shows how to create a user with password. root@rhel65:~# useradd -m -p $(openssl passwd hunter2) mohamed Note that this command puts the password in your command history! 284user passwords 29.5. encryption with crypt A third option is to create your own C program using the crypt function, and compile this into a command. paul@rhel65:~$ cat MyCrypt.c #include #define __USE_XOPEN #include int main(int argc, char** argv) { if(argc==3) { printf("%s\n", crypt(argv[1],argv[2])); } else { printf("Usage: MyCrypt $password $salt\n" ); } return 0; } This little program can be compiled with gcc like this. paul@rhel65:~$ gcc MyCrypt.c -o MyCrypt -lcrypt To use it, we need to give two parameters to MyCrypt. The first is the unencrypted password, the second is the salt. The salt is used to perturb the encryption algorithm in one of 4096 different ways. This variation prevents two users with the same password from having the same entry in /etc/shadow. paul@rhel65:~$ ./MyCrypt hunter2 42 42ZrbtP1Ze8G. paul@rhel65:~$ ./MyCrypt hunter2 33 33d6taYSiEUXI Did you notice that the first two characters of the password are the salt? The standard output of the crypt function is using the DES algorithm which is old and can be cracked in minutes. A better method is to use md5 passwords which can be recognized by a salt starting with $1$. paul@rhel65:~$ ./MyCrypt hunter2 '$1$42' $1$42$7l6Y3xT5282XmZrtDOF9f0 paul@rhel65:~$ ./MyCrypt hunter2 '$6$42' $6$42$OqFFAVnI3gTSYG0yI9TZWX9cpyQzwIop7HwpG1LLEsNBiMr4w6OvLX1KDa./UpwXfrFk1i... The md5 salt can be up to eight characters long. The salt is displayed in /etc/shadow between the second and third $, so never use the password as the salt! paul@rhel65:~$ ./MyCrypt hunter2 '$1$hunter2' $1$hunter2$YVxrxDmidq7Xf8Gdt6qM2. 285user passwords 29.6. /etc/login.defs The /etc/login.defs file contains some default settings for user passwords like password aging and length settings. (You will also find the numerical limits of user ids and group ids and whether or not a home directory should be created by default). root@rhel65:~# grep ^PASS /etc/login.defs PASS_MAX_DAYS 99999 PASS_MIN_DAYS 0 PASS_MIN_LEN 5 PASS_WARN_AGE 7 Debian also has this file. root@debian7:~# grep PASS /etc/login.defs # PASS_MAX_DAYS Maximum number of days a password may be used. # PASS_MIN_DAYS Minimum number of days allowed between password changes. # PASS_WARN_AGE Number of days warning given before a password expires. PASS_MAX_DAYS 99999 PASS_MIN_DAYS 0 PASS_WARN_AGE 7 #PASS_CHANGE_TRIES #PASS_ALWAYS_WARN #PASS_MIN_LEN #PASS_MAX_LEN # NO_PASSWORD_CONSOLE root@debian7:~# 29.7. chage The chage command can be used to set an expiration date for a user account (-E), set a minimum (-m) and maximum (-M) password age, a password expiration date, and set the number of warning days before the password expiration date. Much of this functionality is also available from the passwd command. The -l option of chage will list these settings for a user. root@rhel65:~# chage -l paul Last password change Password expires Password inactive Account expires Minimum number of days between password change Maximum number of days between password change Number of days of warning before password expires root@rhel65:~# 286 : : : : : : : Mar 27, 2014 never never never 0 99999 7user passwords 29.8. disabling a password Passwords in /etc/shadow cannot begin with an exclamation mark. When the second field in /etc/passwd starts with an exclamation mark, then the password can not be used. Using this feature is often called locking, disabling, or suspending a user account. Besides vi (or vipw) you can also accomplish this with usermod. The first command in the next screenshot will show the hashed password of laura in /etc/ shadow. The next command disables the password of laura, making it impossible for Laura to authenticate using this password. root@debian7:~# grep laura /etc/shadow | cut -c1-70 laura:$6$JYj4JZqp$stwwWACp3OtE1R2aZuE87j.nbW.puDkNUYVk7mCHfCVMa3CoDUJV root@debian7:~# usermod -L laura As you can see below, the password hash is simply preceded with an exclamation mark. root@debian7:~# grep laura /etc/shadow | cut -c1-70 laura:!$6$JYj4JZqp$stwwWACp3OtE1R2aZuE87j.nbW.puDkNUYVk7mCHfCVMa3CoDUJ root@debian7:~# The root user (and users with sudo rights on su) still will be able to su into the laura account (because the password is not needed here). Also note that laura will still be able to login if she has set up passwordless ssh! root@debian7:~# su - laura laura@debian7:~$ You can unlock the account again with usermod -U. root@debian7:~# usermod -U laura root@debian7:~# grep laura /etc/shadow | cut -c1-70 laura:$6$JYj4JZqp$stwwWACp3OtE1R2aZuE87j.nbW.puDkNUYVk7mCHfCVMa3CoDUJV Watch out for tiny differences in the command line options of passwd, usermod, and useradd on different Linux distributions. Verify the local files when using features like "disabling, suspending, or locking" on user accounts and their passwords. 29.9. editing local files If you still want to manually edit the /etc/passwd or /etc/shadow, after knowing these commands for password management, then use vipw instead of vi(m) directly. The vipw tool will do proper locking of the file. [root@RHEL5 ~]# vipw /etc/passwd vipw: the password file is busy (/etc/ptmp present) 287user passwords 29.10. practice: user passwords 1. Set the password for serena to hunter2. 2. Also set a password for venus and then lock the venus user account with usermod. Verify the locking in /etc/shadow before and after you lock it. 3. Use passwd -d to disable the serena password. Verify the serena line in /etc/shadow before and after disabling. 4. What is the difference between locking a user account and disabling a user account's password like we just did with usermod -L and passwd -d? 5. Try changing the password of serena to serena as serena. 6. Make sure serena has to change her password in 10 days. 7. Make sure every new user needs to change their password every 10 days. 8. Take a backup as root of /etc/shadow. Use vi to copy an encrypted hunter2 hash from venus to serena. Can serena now log on with hunter2 as a password ? 9. Why use vipw instead of vi ? What could be the problem when using vi or vim ? 10. Use chsh to list all shells (only works on RHEL/CentOS/Fedora), and compare to cat / etc/shells. 11. Which useradd option allows you to name a home directory ? 12. How can you see whether the password of user serena is locked or unlocked ? Give a solution with grep and a solution with passwd. 288user passwords 29.11. solution: user passwords 1. Set the password for serena to hunter2. root@debian7:~# passwd serena Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully 2. Also set a password for venus and then lock the venus user account with usermod. Verify the locking in /etc/shadow before and after you lock it. root@debian7:~# passwd venus Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully root@debian7:~# grep venus /etc/shadow | cut -c1-70 venus:$6$gswzXICW$uSnKFV1kFKZmTPaMVS4AvNA/KO27OxN0v5LHdV9ed0gTyXrjUeM/ root@debian7:~# usermod -L venus root@debian7:~# grep venus /etc/shadow | cut -c1-70 venus:!$6$gswzXICW$uSnKFV1kFKZmTPaMVS4AvNA/KO27OxN0v5LHdV9ed0gTyXrjUeM Note that usermod -L precedes the password hash with an exclamation mark (!). 3. Use passwd -d to disable the serena password. Verify the serena line in /etc/shadow before and after disabling. root@debian7:~# grep serena /etc/shadow | cut -c1-70 serena:$6$Es/omrPE$F2Ypu8kpLrfKdW0v/UIwA5jrYyBD2nwZ/dt.i/IypRgiPZSdB/B root@debian7:~# passwd -d serena passwd: password expiry information changed. root@debian7:~# grep serena /etc/shadow serena::16358:0:99999:7::: root@debian7:~# 4. What is the difference between locking a user account and disabling a user account's password like we just did with usermod -L and passwd -d? Locking will prevent the user from logging on to the system with his password by putting a ! in front of the password in /etc/shadow. Disabling with passwd will erase the password from /etc/shadow. 5. Try changing the password of serena to serena as serena. log on as serena, then execute: passwd serena... it should fail! 6. Make sure serena has to change her password in 10 days. chage -M 10 serena 7. Make sure every new user needs to change their password every 10 days. vi /etc/login.defs (and change PASS_MAX_DAYS to 10) 289user passwords 8. Take a backup as root of /etc/shadow. Use vi to copy an encrypted hunter2 hash from venus to serena. Can serena now log on with hunter2 as a password ? Yes. 9. Why use vipw instead of vi ? What could be the problem when using vi or vim ? vipw will give a warning when someone else is already using that file (with vipw). 10. Use chsh to list all shells (only works on RHEL/CentOS/Fedora), and compare to cat / etc/shells. chsh -l cat /etc/shells 11. Which useradd option allows you to name a home directory ? -d 12. How can you see whether the password of user serena is locked or unlocked ? Give a solution with grep and a solution with passwd. grep serena /etc/shadow passwd -S serena 290Chapter 30. user profiles Logged on users have a number of preset (and customized) aliases, variables, and functions, but where do they come from ? The shell uses a number of startup files that are executed (or rather sourced) whenever the shell is invoked. What follows is an overview of startup scripts. 291user profiles 30.1. system profile Both the bash and the ksh shell will verify the existence of /etc/profile and source it if it exists. When reading this script, you will notice (both on Debian and on Red Hat Enterprise Linux) that it builds the PATH environment variable (among others). The script might also change the PS1 variable, set the HOSTNAME and execute even more scripts like /etc/inputrc This screenshot uses grep to show PATH manipulation in /etc/profile on Debian. root@debian7:~# grep PATH /etc/profile PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" PATH="/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games" export PATH root@debian7:~# This screenshot uses grep to show PATH manipulation in /etc/profile on RHEL7/CentOS7. [root@centos7 ~]# grep PATH /etc/profile case ":${PATH}:" in PATH=$PATH:$1 PATH=$1:$PATH export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE HISTCONTROL [root@centos7 ~]# The root user can use this script to set aliases, functions, and variables for every user on the system. 30.2. ~/.bash_profile When this file exists in the home directory, then bash will source it. On Debian Linux 5/6/7 this file does not exist by default. RHEL7/CentOS7 uses a small ~/.bash_profile where it checks for the existence of ~/.bashrc and then sources it. It also adds $HOME/bin to the $PATH variable. [root@rhel7 ~]# cat /home/paul/.bash_profile # .bash_profile # Get the aliases and functions if [ -f ~/.bashrc ]; then . ~/.bashrc fi # User specific environment and startup programs PATH=$PATH:$HOME/.local/bin:$HOME/bin export PATH [root@rhel7 ~]# 292user profiles 30.3. ~/.bash_login When .bash_profile does not exist, then bash will check for ~/.bash_login and source it. Neither Debian nor Red Hat have this file by default. 30.4. ~/.profile When neither ~/.bash_profile and ~/.bash_login exist, then bash will verify the existence of ~/.profile and execute it. This file does not exist by default on Red Hat. On Debian this script can execute ~/.bashrc and will add $HOME/bin to the $PATH variable. root@debian7:~# tail -11 /home/paul/.profile if [ -n "$BASH_VERSION" ]; then # include .bashrc if it exists if [ -f "$HOME/.bashrc" ]; then . "$HOME/.bashrc" fi fi # set PATH so it includes user's private bin if it exists if [ -d "$HOME/bin" ] ; then PATH="$HOME/bin:$PATH" fi RHEL/CentOS does not have this file by default. 30.5. ~/.bashrc The ~/.bashrc script is often sourced by other scripts. Let us take a look at what it does by default. Red Hat uses a very simple ~/.bashrc, checking for /etc/bashrc and sourcing it. It also leaves room for custom aliases and functions. [root@rhel7 ~]# cat /home/paul/.bashrc # .bashrc # Source global definitions if [ -f /etc/bashrc ]; then . /etc/bashrc fi # Uncomment the following line if you don't like systemctl's auto-paging feature: # export SYSTEMD_PAGER= # User specific aliases and functions On Debian this script is quite a bit longer and configures $PS1, some history variables and a number af active and inactive aliases. root@debian7:~# wc -l /home/paul/.bashrc 110 /home/paul/.bashrc 293user profiles 30.6. ~/.bash_logout When exiting bash, it can execute ~/.bash_logout. Debian use this opportunity to clear the console screen. serena@deb503:~$ cat .bash_logout # ~/.bash_logout: executed by bash(1) when login shell exits. # when leaving the console clear the screen to increase privacy if [ "$SHLVL" = 1 ]; then [ -x /usr/bin/clear_console ] && /usr/bin/clear_console -q fi Red Hat Enterprise Linux 5 will simple call the /usr/bin/clear command in this script. [serena@rhel53 ~]$ cat .bash_logout # ~/.bash_logout /usr/bin/clear Red Hat Enterprise Linux 6 and 7 create this file, but leave it empty (except for a comment). paul@rhel65:~$ cat .bash_logout # ~/.bash_logout 294user profiles 30.7. Debian overview Below is a table overview of when Debian is running any of these bash startup scripts. Table 30.1. Debian User Environment script su su - ssh gdm ~./bashrc no yes yes yes ~/.profile no yes yes yes /etc/profile no yes yes yes /etc/bash.bashrc yes no no yes 30.8. RHEL5 overview Below is a table overview of when Red Hat Enterprise Linux 5 is running any of these bash startup scripts. Table 30.2. Red Hat User Environment script su su - ssh gdm ~./bashrc yes yes yes yes ~/.bash_profile no yes yes yes /etc/profile no yes yes yes /etc/bashrc yes yes yes yes 295user profiles 30.9. practice: user profiles 1. Make a list of all the profile files on your system. 2. Read the contents of each of these, often they source extra scripts. 3. Put a unique variable, alias and function in each of those files. 4. Try several different ways to obtain a shell (su, su -, ssh, tmux, gnome-terminal, Ctrl- alt-F1, ...) and verify which of your custom variables, aliases and function are present in your environment. 5. Do you also know the order in which they are executed? 6. When an application depends on a setting in $HOME/.profile, does it matter whether $HOME/.bash_profile exists or not ? 296user profiles 30.10. solution: user profiles 1. Make a list of all the profile files on your system. ls -a ~ ; ls -l /etc/pro* /etc/bash* 2. Read the contents of each of these, often they source extra scripts. 3. Put a unique variable, alias and function in each of those files. 4. Try several different ways to obtain a shell (su, su -, ssh, tmux, gnome-terminal, Ctrl- alt-F1, ...) and verify which of your custom variables, aliases and function are present in your environment. 5. Do you also know the order in which they are executed? same name aliases, functions and variables will overwrite each other 6. When an application depends on a setting in $HOME/.profile, does it matter whether $HOME/.bash_profile exists or not ? Yes it does matter. (man bash /INVOCATION) 297Chapter 31. groups Users can be listed in groups. Groups allow you to set permissions on the group level instead of having to set permissions for every individual user. Every Unix or Linux distribution will have a graphical tool to manage groups. Novice users are advised to use this graphical tool. More experienced users can use command line tools to manage users, but be careful: Some distributions do not allow the mixed use of GUI and CLI tools to manage groups (YaST in Novell Suse). Senior administrators can edit the relevant files directly with vi or vigr. 298groups 31.1. groupadd Groups can be created with the groupadd command. The example below shows the creation of five (empty) groups. root@laika:~# root@laika:~# root@laika:~# root@laika:~# root@laika:~# groupadd groupadd groupadd groupadd groupadd tennis football snooker formula1 salsa 31.2. group file Users can be a member of several groups. Group membership is defined by the /etc/group file. root@laika:~# tail -5 /etc/group tennis:x:1006: football:x:1007: snooker:x:1008: formula1:x:1009: salsa:x:1010: root@laika:~# The first field is the group's name. The second field is the group's (encrypted) password (can be empty). The third field is the group identification or GID. The fourth field is the list of members, these groups have no members. 31.3. groups A user can type the groups command to see a list of groups where the user belongs to. [harry@RHEL4b ~]$ groups harry sports [harry@RHEL4b ~]$ 299groups 31.4. usermod Group membership can be modified with the useradd or usermod command. root@laika:~# usermod -a -G tennis inge root@laika:~# usermod -a -G tennis katrien root@laika:~# usermod -a -G salsa katrien root@laika:~# usermod -a -G snooker sandra root@laika:~# usermod -a -G formula1 annelies root@laika:~# tail -5 /etc/group tennis:x:1006:inge,katrien football:x:1007: snooker:x:1008:sandra formula1:x:1009:annelies salsa:x:1010:katrien root@laika:~# Be careful when using usermod to add users to groups. By default, the usermod command will remove the user from every group of which he is a member if the group is not listed in the command! Using the -a (append) switch prevents this behaviour. 31.5. groupmod You can change the group name with the groupmod command. root@laika:~# groupmod -n darts snooker root@laika:~# tail -5 /etc/group tennis:x:1006:inge,katrien football:x:1007: formula1:x:1009:annelies salsa:x:1010:katrien darts:x:1008:sandra 31.6. groupdel You can permanently remove a group with the groupdel command. root@laika:~# groupdel tennis root@laika:~# 300groups 31.7. gpasswd You can delegate control of group membership to another user with the gpasswd command. In the example below we delegate permissions to add and remove group members to serena for the sports group. Then we su to serena and add harry to the sports group. [root@RHEL4b ~]# gpasswd -A serena sports [root@RHEL4b ~]# su - serena [serena@RHEL4b ~]$ id harry uid=516(harry) gid=520(harry) groups=520(harry) [serena@RHEL4b ~]$ gpasswd -a harry sports Adding user harry to group sports [serena@RHEL4b ~]$ id harry uid=516(harry) gid=520(harry) groups=520(harry),522(sports) [serena@RHEL4b ~]$ tail -1 /etc/group sports:x:522:serena,venus,harry [serena@RHEL4b ~]$ Group administrators do not have to be a member of the group. They can remove themselves from a group, but this does not influence their ability to add or remove members. [serena@RHEL4b ~]$ gpasswd -d serena sports Removing user serena from group sports [serena@RHEL4b ~]$ exit Information about group administrators is kept in the /etc/gshadow file. [root@RHEL4b ~]# tail -1 /etc/gshadow sports:!:serena:venus,harry [root@RHEL4b ~]# To remove all group administrators from a group, use the gpasswd command to set an empty administrators list. [root@RHEL4b ~]# gpasswd -A "" sports 301groups 31.8. newgrp You can start a child shell with a new temporary primary group using the newgrp command. root@rhel65:~# mkdir prigroup root@rhel65:~# cd prigroup/ root@rhel65:~/prigroup# touch standard.txt root@rhel65:~/prigroup# ls -l total 0 -rw-r--r--. 1 root root 0 Apr 13 17:49 standard.txt root@rhel65:~/prigroup# echo $SHLVL 1 root@rhel65:~/prigroup# newgrp tennis root@rhel65:~/prigroup# echo $SHLVL 2 root@rhel65:~/prigroup# touch newgrp.txt root@rhel65:~/prigroup# ls -l total 0 -rw-r--r--. 1 root tennis 0 Apr 13 17:49 newgrp.txt -rw-r--r--. 1 root root 0 Apr 13 17:49 standard.txt root@rhel65:~/prigroup# exit exit root@rhel65:~/prigroup# 31.9. vigr Similar to vipw, the vigr command can be used to manually edit the /etc/group file, since it will do proper locking of the file. Only experienced senior administrators should use vi or vigr to manage groups. 302groups 31.10. practice: groups 1. Create the groups tennis, football and sports. 2. In one command, make venus a member of tennis and sports. 3. Rename the football group to foot. 4. Use vi to add serena to the tennis group. 5. Use the id command to verify that serena is a member of tennis. 6. Make someone responsible for managing group membership of foot and sports. Test that it works. 303groups 31.11. solution: groups 1. Create the groups tennis, football and sports. groupadd tennis ; groupadd football ; groupadd sports 2. In one command, make venus a member of tennis and sports. usermod -a -G tennis,sports venus 3. Rename the football group to foot. groupmod -n foot football 4. Use vi to add serena to the tennis group. vi /etc/group 5. Use the id command to verify that serena is a member of tennis. id (and after logoff logon serena should be member) 6. Make someone responsible for managing group membership of foot and sports. Test that it works. gpasswd -A (to make manager) gpasswd -a (to add member) 304Part IX. file securityTable of Contents 306Chapter 32. standard file permissions This chapter contains details about basic file security through file ownership and file permissions. 307standard file permissions 32.1. file ownership 32.1.1. user owner and group owner The users and groups of a system can be locally managed in /etc/passwd and /etc/group, or they can be in a NIS, LDAP, or Samba domain. These users and groups can own files. Actually, every file has a user owner and a group owner, as can be seen in the following screenshot. paul@rhel65:~/owners$ ls -lh total 636K -rw-r--r--. 1 paul snooker 1.1K -rw-r--r--. 1 paul paul 626K -rw-r--r--. 1 root tennis 185 -rw-rw-r--. 1 root root 0 paul@rhel65:~/owners$ Apr Apr Apr Apr 8 8 8 8 18:47 18:46 18:46 18:47 data.odt file1 file2 stuff.txt User paul owns three files; file1 has paul as user owner and has the group paul as group owner, data.odt is group owned by the group snooker, file2 by the group tennis. The last file is called stuff.txt and is owned by the root user and the root group. 32.1.2. listing user accounts You can use the following command to list all local user accounts. paul@debian7~$ cut -d: -f1 /etc/passwd | column root ntp sam bert daemon mysql tom rino bin paul wouter antonio sys maarten robrecht simon sync kevin bilal sven games yuri dimitri wouter2 man william ahmed tarik lp yves dylan jan mail kris robin ian news hamid matthias ivan uucp vladimir ben azeddine proxy abiy mike eric www-data david kevin2 kamel backup chahid kenzo ischa list stef aaron bart irc joeri lorenzo omer gnats glenn jens kurt nobody yannick ruben steve libuuid christof jelle constantin Debian-exim george stefaan sam2 statd joost marc bjorn sshd arno thomas ronald 308 naomi matthias2 bram fabrice chimene messagebus roger frank toon rinus eddy bram2 keith jesse frederick hans dries steve2 tomas johan tom2standard file permissions 32.1.3. chgrp You can change the group owner of a file using the chgrp command. root@rhel65:/home/paul/owners# ls -l file2 -rw-r--r--. 1 root tennis 185 Apr 8 18:46 file2 root@rhel65:/home/paul/owners# chgrp snooker file2 root@rhel65:/home/paul/owners# ls -l file2 -rw-r--r--. 1 root snooker 185 Apr 8 18:46 file2 root@rhel65:/home/paul/owners# 32.1.4. chown The user owner of a file can be changed with chown command. root@laika:/home/paul# -rw-r--r-- 1 root paul root@laika:/home/paul# root@laika:/home/paul# -rw-r--r-- 1 paul paul ls -l FileForPaul 0 2008-08-06 14:11 FileForPaul chown paul FileForPaul ls -l FileForPaul 0 2008-08-06 14:11 FileForPaul You can also use chown to change both the user owner and the group owner. root@laika:/home/paul# ls -l FileForPaul -rw-r--r-- 1 paul paul 0 2008-08-06 14:11 FileForPaul root@laika:/home/paul# chown root:project42 FileForPaul root@laika:/home/paul# ls -l FileForPaul -rw-r--r-- 1 root project42 0 2008-08-06 14:11 FileForPaul 309standard file permissions 32.2. list of special files When you use ls -l, for each file you can see ten characters before the user and group owner. The first character tells us the type of file. Regular files get a -, directories get a d, symbolic links are shown with an l, pipes get a p, character devices a c, block devices a b, and sockets an s. Table 32.1. Unix special files first character file type - normal file d directory l symbolic link p named pipe b block device c character device s socket Below a screenshot of a character device (the console) and a block device (the hard disk). paul@debian6lt~$ ls -ld /dev/console /dev/sda crw------- 1 root root 5, 1 Mar 15 12:45 /dev/console brw-rw---- 1 root disk 8, 0 Mar 15 12:45 /dev/sda And here you can see a directory, a regular file and a symbolic link. paul@debian6lt~$ ls drwxr-xr-x 128 root -rw-r--r-- 1 root lrwxrwxrwx 1 root -ld /etc /etc/hosts /etc/motd root 12288 Mar 15 18:34 /etc root 372 Dec 10 17:36 /etc/hosts root 13 Dec 5 10:36 /etc/motd -> /var/run/motd 310standard file permissions 32.3. permissions 32.3.1. rwx The nine characters following the file type denote the permissions in three triplets. A permission can be r for read access, w for write access, and x for execute. You need the r permission to list (ls) the contents of a directory. You need the x permission to enter (cd) a directory. You need the w permission to create files in or remove files from a directory. Table 32.2. standard Unix file permissions permission on a file on a directory r (read) read file contents (cat) read directory contents (ls) w (write) change file contents (vi) create files in (touch) x (execute) execute the file enter the directory (cd) 32.3.2. three sets of rwx We already know that the output of ls -l starts with ten characters for each file. This screenshot shows a regular file (because the first character is a - ). paul@RHELv4u4:~/test$ ls -l proc42.bash -rwxr-xr-- 1 paul proj 984 Feb 6 12:01 proc42.bash Below is a table describing the function of all ten characters. Table 32.3. Unix file permissions position position characters function 1 - this is a regular file 2-4 rwx permissions for the user owner 5-7 r-x permissions for the group owner 8-10 r-- permissions for others When you are the user owner of a file, then the user owner permissions apply to you. The rest of the permissions have no influence on your access to the file. When you belong to the group that is the group owner of a file, then the group owner permissions apply to you. The rest of the permissions have no influence on your access to the file. When you are not the user owner of a file and you do not belong to the group owner, then the others permissions apply to you. The rest of the permissions have no influence on your access to the file. 311standard file permissions 32.3.3. permission examples Some example combinations on files and directories are seen in this screenshot. The name of the file explains the permissions. paul@laika:~/perms$ ls total 12K drwxr-xr-x 2 paul paul -rwxrwxrwx 1 paul paul -r--r----- 1 paul paul -rwxrwx--- 1 paul paul dr-xr-x--- 2 paul paul dr-x------ 2 paul paul paul@laika:~/perms$ -lh 4.0K 0 0 0 4.0K 4.0K 2007-02-07 2007-02-07 2007-02-07 2007-02-07 2007-02-07 2007-02-07 22:26 22:21 22:21 22:21 22:25 22:25 AllEnter_UserCreateDelete EveryoneFullControl.txt OnlyOwnersRead.txt OwnersAll_RestNothing.txt UserAndGroupEnter OnlyUserEnter To summarise, the first rwx triplet represents the permissions for the user owner. The second triplet corresponds to the group owner; it specifies permissions for all members of that group. The third triplet defines permissions for all other users that are not the user owner and are not a member of the group owner. 312standard file permissions 32.3.4. setting permissions (chmod) Permissions can be changed with chmod. The first example gives the user owner execute permissions. paul@laika:~/perms$ ls -l permissions.txt -rw-r--r-- 1 paul paul 0 2007-02-07 22:34 permissions.txt paul@laika:~/perms$ chmod u+x permissions.txt paul@laika:~/perms$ ls -l permissions.txt -rwxr--r-- 1 paul paul 0 2007-02-07 22:34 permissions.txt This example removes the group owners read permission. paul@laika:~/perms$ chmod g-r permissions.txt paul@laika:~/perms$ ls -l permissions.txt -rwx---r-- 1 paul paul 0 2007-02-07 22:34 permissions.txt This example removes the others read permission. paul@laika:~/perms$ chmod o-r permissions.txt paul@laika:~/perms$ ls -l permissions.txt -rwx------ 1 paul paul 0 2007-02-07 22:34 permissions.txt This example gives all of them the write permission. paul@laika:~/perms$ chmod a+w permissions.txt paul@laika:~/perms$ ls -l permissions.txt -rwx-w--w- 1 paul paul 0 2007-02-07 22:34 permissions.txt You don't even have to type the a. paul@laika:~/perms$ chmod +x permissions.txt paul@laika:~/perms$ ls -l permissions.txt -rwx-wx-wx 1 paul paul 0 2007-02-07 22:34 permissions.txt You can also set explicit permissions. paul@laika:~/perms$ chmod u=rw permissions.txt paul@laika:~/perms$ ls -l permissions.txt -rw--wx-wx 1 paul paul 0 2007-02-07 22:34 permissions.txt Feel free to make any kind of combination. paul@laika:~/perms$ chmod u=rw,g=rw,o=r permissions.txt paul@laika:~/perms$ ls -l permissions.txt -rw-rw-r-- 1 paul paul 0 2007-02-07 22:34 permissions.txt Even fishy combinations are accepted by chmod. paul@laika:~/perms$ chmod u=rwx,ug+rw,o=r permissions.txt paul@laika:~/perms$ ls -l permissions.txt -rwxrw-r-- 1 paul paul 0 2007-02-07 22:34 permissions.txt 313standard file permissions 32.3.5. setting octal permissions Most Unix administrators will use the old school octal system to talk about and set permissions. Look at the triplet bitwise, equating r to 4, w to 2, and x to 1. Table 32.4. Octal permissions binary octal permission 000 0 --- 001 1 --x 010 2 -w- 011 3 -wx 100 4 r-- 101 5 r-x 110 6 rw- 111 7 rwx This makes 777 equal to rwxrwxrwx and by the same logic, 654 mean rw-r-xr-- . The chmod command will accept these numbers. paul@laika:~/perms$ chmod 777 permissions.txt paul@laika:~/perms$ ls -l permissions.txt -rwxrwxrwx 1 paul paul 0 2007-02-07 22:34 permissions.txt paul@laika:~/perms$ chmod 664 permissions.txt paul@laika:~/perms$ ls -l permissions.txt -rw-rw-r-- 1 paul paul 0 2007-02-07 22:34 permissions.txt paul@laika:~/perms$ chmod 750 permissions.txt paul@laika:~/perms$ ls -l permissions.txt -rwxr-x--- 1 paul paul 0 2007-02-07 22:34 permissions.txt 314standard file permissions 32.3.6. umask When creating a file or directory, a set of default permissions are applied. These default permissions are determined by the umask. The umask specifies permissions that you do not want set on by default. You can display the umask with the umask command. [Harry@RHEL4b 0002 [Harry@RHEL4b [Harry@RHEL4b -rw-rw-r-- 1 [Harry@RHEL4b ~]$ umask ~]$ touch test ~]$ ls -l test Harry Harry 0 Jul 24 06:03 test ~]$ As you can also see, the file is also not executable by default. This is a general security feature among Unixes; newly created files are never executable by default. You have to explicitly do a chmod +x to make a file executable. This also means that the 1 bit in the umask has no meaning--a umask of 0022 is the same as 0033. 32.3.7. mkdir -m When creating directories with mkdir you can use the -m option to set the mode. This screenshot explains. paul@debian5~$ mkdir -m 700 MyDir paul@debian5~$ mkdir -m 777 Public paul@debian5~$ ls -dl MyDir/ Public/ drwx------ 2 paul paul 4096 2011-10-16 19:16 MyDir/ drwxrwxrwx 2 paul paul 4096 2011-10-16 19:16 Public/ 32.3.8. cp -p To preserve permissions and time stamps from source files, use cp -p. paul@laika:~/perms$ cp file* cp paul@laika:~/perms$ cp -p file* cpp paul@laika:~/perms$ ll * -rwx------ 1 paul paul 0 2008-08-25 13:26 file33 -rwxr-x--- 1 paul paul 0 2008-08-25 13:26 file42 cp: total 0 -rwx------ 1 paul paul 0 2008-08-25 13:34 file33 -rwxr-x--- 1 paul paul 0 2008-08-25 13:34 file42 cpp: total 0 -rwx------ 1 paul paul 0 2008-08-25 13:26 file33 -rwxr-x--- 1 paul paul 0 2008-08-25 13:26 file42 315standard file permissions 32.4. practice: standard file permissions 1. As normal user, create a directory ~/permissions. Create a file owned by yourself in there. 2. Copy a file owned by root from /etc/ to your permissions dir, who owns this file now ? 3. As root, create a file in the users ~/permissions directory. 4. As normal user, look at who owns this file created by root. 5. Change the ownership of all files in ~/permissions to yourself. 6. Make sure you have all rights to these files, and others can only read. 7. With chmod, is 770 the same as rwxrwx--- ? 8. With chmod, is 664 the same as r-xr-xr-- ? 9. With chmod, is 400 the same as r-------- ? 10. With chmod, is 734 the same as rwxr-xr-- ? 11a. Display the umask in octal and in symbolic form. 11b. Set the umask to 077, but use the symbolic format to set it. Verify that this works. 12. Create a file as root, give only read to others. Can a normal user read this file ? Test writing to this file with vi. 13a. Create a file as normal user, give only read to others. Can another normal user read this file ? Test writing to this file with vi. 13b. Can root read this file ? Can root write to this file with vi ? 14. Create a directory that belongs to a group, where every member of that group can read and write to files, and create files. Make sure that people can only delete their own files. 316standard file permissions 32.5. solution: standard file permissions 1. As normal user, create a directory ~/permissions. Create a file owned by yourself in there. mkdir ~/permissions ; touch ~/permissions/myfile.txt 2. Copy a file owned by root from /etc/ to your permissions dir, who owns this file now ? cp /etc/hosts ~/permissions/ The copy is owned by you. 3. As root, create a file in the users ~/permissions directory. (become root)# touch /home/username/permissions/rootfile 4. As normal user, look at who owns this file created by root. ls -l ~/permissions The file created by root is owned by root. 5. Change the ownership of all files in ~/permissions to yourself. chown user ~/permissions/* You cannot become owner of the file that belongs to root. 6. Make sure you have all rights to these files, and others can only read. chmod 644 (on files) chmod 755 (on directories) 7. With chmod, is 770 the same as rwxrwx--- ? yes 8. With chmod, is 664 the same as r-xr-xr-- ? No 9. With chmod, is 400 the same as r-------- ? yes 10. With chmod, is 734 the same as rwxr-xr-- ? no 11a. Display the umask in octal and in symbolic form. umask ; umask -S 11b. Set the umask to 077, but use the symbolic format to set it. Verify that this works. umask -S u=rwx,go= 317standard file permissions 12. Create a file as root, give only read to others. Can a normal user read this file ? Test writing to this file with vi. (become root) # echo hello > /home/username/root.txt # chmod 744 /home/username/root.txt (become user) vi ~/root.txt 13a. Create a file as normal user, give only read to others. Can another normal user read this file ? Test writing to this file with vi. echo hello > file ; chmod 744 file Yes, others can read this file 13b. Can root read this file ? Can root write to this file with vi ? Yes, root can read and write to this file. Permissions do not apply to root. 14. Create a directory that belongs to a group, where every member of that group can read and write to files, and create files. Make sure that people can only delete their own files. mkdir /home/project42 ; groupadd project42 chgrp project42 /home/project42 ; chmod 775 /home/project42 You can not yet do the last part of this exercise... 318Chapter 33. advanced file permissions 319advanced file permissions 33.1. sticky bit on directory You can set the sticky bit on a directory to prevent users from removing files that they do not own as a user owner. The sticky bit is displayed at the same location as the x permission for others. The sticky bit is represented by a t (meaning x is also there) or a T (when there is no x for others). root@RHELv4u4:~# mkdir /project55 root@RHELv4u4:~# ls -ld /project55 drwxr-xr-x 2 root root 4096 Feb 7 17:38 /project55 root@RHELv4u4:~# chmod +t /project55/ root@RHELv4u4:~# ls -ld /project55 drwxr-xr-t 2 root root 4096 Feb 7 17:38 /project55 root@RHELv4u4:~# The sticky bit can also be set with octal permissions, it is binary 1 in the first of four triplets. root@RHELv4u4:~# chmod 1775 /project55/ root@RHELv4u4:~# ls -ld /project55 drwxrwxr-t 2 root root 4096 Feb 7 17:38 /project55 root@RHELv4u4:~# You will typically find the sticky bit on the /tmp directory. root@barry:~# ls -ld /tmp drwxrwxrwt 6 root root 4096 2009-06-04 19:02 /tmp 33.2. setgid bit on directory setgid can be used on directories to make sure that all files inside the directory are owned by the group owner of the directory. The setgid bit is displayed at the same location as the x permission for group owner. The setgid bit is represented by an s (meaning x is also there) or a S (when there is no x for the group owner). As this example shows, even though root does not belong to the group proj55, the files created by root in /project55 will belong to proj55 since the setgid is set. root@RHELv4u4:~# groupadd proj55 root@RHELv4u4:~# chown root:proj55 /project55/ root@RHELv4u4:~# chmod 2775 /project55/ root@RHELv4u4:~# touch /project55/fromroot.txt root@RHELv4u4:~# ls -ld /project55/ drwxrwsr-x 2 root proj55 4096 Feb 7 17:45 /project55/ root@RHELv4u4:~# ls -l /project55/ total 4 -rw-r--r-- 1 root proj55 0 Feb 7 17:45 fromroot.txt root@RHELv4u4:~# You can use the find command to find all setgid directories. paul@laika:~$ find / -type d -perm -2000 2> /dev/null /var/log/mysql /var/log/news /var/local ... 320advanced file permissions 33.3. setgid and setuid on regular files These two permissions cause an executable file to be executed with the permissions of the file owner instead of the executing owner. This means that if any user executes a program that belongs to the root user, and the setuid bit is set on that program, then the program runs as root. This can be dangerous, but sometimes this is good for security. Take the example of passwords; they are stored in /etc/shadow which is only readable by root. (The root user never needs permissions anyway.) root@RHELv4u4:~# ls -l /etc/shadow -r-------- 1 root root 1260 Jan 21 07:49 /etc/shadow Changing your password requires an update of this file, so how can normal non-root users do this? Let's take a look at the permissions on the /usr/bin/passwd. root@RHELv4u4:~# ls -l /usr/bin/passwd -r-s--x--x 1 root root 21200 Jun 17 2005 /usr/bin/passwd When running the passwd program, you are executing it with root credentials. You can use the find command to find all setuid programs. paul@laika:~$ find /usr/bin -type f -perm -04000 /usr/bin/arping /usr/bin/kgrantpty /usr/bin/newgrp /usr/bin/chfn /usr/bin/sudo /usr/bin/fping6 /usr/bin/passwd /usr/bin/gpasswd ... In most cases, setting the setuid bit on executables is sufficient. Setting the setgid bit will result in these programs to run with the credentials of their group owner. 33.4. setuid on sudo The sudo binary has the setuid bit set, so any user can run it with the effective userid of root. paul@rhel65:~$ ls -l $(which sudo) ---s--x--x. 1 root root 123832 Oct paul@rhel65:~$ 7 2013 /usr/bin/sudo 321advanced file permissions 33.5. practice: sticky, setuid and setgid bits 1a. Set up a directory, owned by the group sports. 1b. Members of the sports group should be able to create files in this directory. 1c. All files created in this directory should be group-owned by the sports group. 1d. Users should be able to delete only their own user-owned files. 1e. Test that this works! 2. Verify the permissions on /usr/bin/passwd. Remove the setuid, then try changing your password as a normal user. Reset the permissions back and try again. 3. If time permits (or if you are waiting for other students to finish this practice), read about file attributes in the man page of chattr and lsattr. Try setting the i attribute on a file and test that it works. 322advanced file permissions 33.6. solution: sticky, setuid and setgid bits 1a. Set up a directory, owned by the group sports. groupadd sports mkdir /home/sports chown root:sports /home/sports 1b. Members of the sports group should be able to create files in this directory. chmod 770 /home/sports 1c. All files created in this directory should be group-owned by the sports group. chmod 2770 /home/sports 1d. Users should be able to delete only their own user-owned files. chmod +t /home/sports 1e. Test that this works! Log in with different users (group members and others and root), create files and watch the permissions. Try changing and deleting files... 2. Verify the permissions on /usr/bin/passwd. Remove the setuid, then try changing your password as a normal user. Reset the permissions back and try again. root@deb503:~# ls -l /usr/bin/passwd -rwsr-xr-x 1 root root 31704 2009-11-14 15:41 /usr/bin/passwd root@deb503:~# chmod 755 /usr/bin/passwd root@deb503:~# ls -l /usr/bin/passwd -rwxr-xr-x 1 root root 31704 2009-11-14 15:41 /usr/bin/passwd A normal user cannot change password now. root@deb503:~# chmod 4755 /usr/bin/passwd root@deb503:~# ls -l /usr/bin/passwd -rwsr-xr-x 1 root root 31704 2009-11-14 15:41 /usr/bin/passwd 3. If time permits (or if you are waiting for other students to finish this practice), read about file attributes in the man page of chattr and lsattr. Try setting the i attribute on a file and test that it works. paul@laika:~$ sudo su - [sudo] password for paul: root@laika:~# mkdir attr root@laika:~# cd attr/ root@laika:~/attr# touch file42 root@laika:~/attr# lsattr ------------------ ./file42 root@laika:~/attr# chattr +i file42 323advanced file permissions root@laika:~/attr# lsattr ----i------------- ./file42 root@laika:~/attr# rm -rf file42 rm: cannot remove `file42': Operation not permitted root@laika:~/attr# chattr -i file42 root@laika:~/attr# rm -rf file42 root@laika:~/attr# 324Chapter 34. access control lists Standard Unix permissions might not be enough for some organisations. This chapter introduces access control lists or acl's to further protect files and directories. 325access control lists 34.1. acl in /etc/fstab File systems that support access control lists, or acls, have to be mounted with the acl option listed in /etc/fstab. In the example below, you can see that the root file system has acl support, whereas /home/data does not. root@laika:~# tail -4 /etc/fstab /dev/sda1 / ext3 /dev/sdb2 /home/data auto pasha:/home/r /home/pasha nfs wolf:/srv/data /home/wolf nfs acl,relatime noacl,defaults defaults defaults 0 0 0 0 1 0 0 0 34.2. getfacl Reading acls can be done with /usr/bin/getfacl. This screenshot shows how to read the acl of file33 with getfacl. paul@laika:~/test$ getfacl file33 # file: file33 # owner: paul # group: paul user::rw- group::r-- mask::rwx other::r-- 34.3. setfacl Writing or changing acls can be done with /usr/bin/setfacl. These screenshots show how to change the acl of file33 with setfacl. First we add user sandra with octal permission 7 to the acl. paul@laika:~/test$ setfacl -m u:sandra:7 file33 Then we add the group tennis with octal permission 6 to the acl of the same file. paul@laika:~/test$ setfacl -m g:tennis:6 file33 The result is visible with getfacl. paul@laika:~/test$ getfacl file33 # file: file33 # owner: paul # group: paul user::rw- user:sandra:rwx group::r-- group:tennis:rw- mask::rwx other::r-- 326access control lists 34.4. remove an acl entry The -x option of the setfacl command will remove an acl entry from the targeted file. paul@laika:~/test$ paul@laika:~/test$ user:sandra:rwx paul@laika:~/test$ paul@laika:~/test$ setfacl -m u:sandra:7 file33 getfacl file33 | grep sandra setfacl -x sandra file33 getfacl file33 | grep sandra Note that omitting the u or g when defining the acl for an account will default it to a user account. 34.5. remove the complete acl The -b option of the setfacl command will remove the acl from the targeted file. paul@laika:~/test$ setfacl -b file33 paul@laika:~/test$ getfacl file33 # file: file33 # owner: paul # group: paul user::rw- group::r-- other::r-- 34.6. the acl mask The acl mask defines the maximum effective permissions for any entry in the acl. This mask is calculated every time you execute the setfacl or chmod commands. You can prevent the calculation by using the --no-mask switch. paul@laika:~/test$ setfacl --no-mask -m u:sandra:7 file33 paul@laika:~/test$ getfacl file33 # file: file33 # owner: paul # group: paul user::rw- user:sandra:rwx #effective:rw- group::r-- mask::rw- other::r-- 327access control lists 34.7. eiciel Desktop users might want to use eiciel to manage acls with a graphical tool. You will need to install eiciel and nautilus-actions to have an extra tab in nautilus to manage acls. paul@laika:~$ sudo aptitude install eiciel nautilus-actions 328Chapter 35. file links An average computer using Linux has a file system with many hard links and symbolic links. To understand links in a file system, you first have to understand what an inode is. 329file links 35.1. inodes 35.1.1. inode contents An inode is a data structure that contains metadata about a file. When the file system stores a new file on the hard disk, it stores not only the contents (data) of the file, but also extra properties like the name of the file, the creation date, its permissions, the owner of the file, and more. All this information (except the name of the file and the contents of the file) is stored in the inode of the file. The ls -l command will display some of the inode contents, as seen in this screenshot. root@rhel53 ~# ls -ld /home/project42/ drwxr-xr-x 4 root pro42 4.0K Mar 27 14:29 /home/project42/ 35.1.2. inode table The inode table contains all of the inodes and is created when you create the file system (with mkfs). You can use the df -i command to see how many inodes are used and free on mounted file systems. root@rhel53 ~# df -i Filesystem Inodes IUsed IFree IUse% Mounted on /dev/mapper/VolGroup00-LogVol00 4947968 115326 4832642 3% / /dev/hda1 26104 45 26059 1% /boot tmpfs 64417 1 64416 1% /dev/shm /dev/sda1 262144 2207 259937 1% /home/project42 /dev/sdb1 74400 5519 68881 8% /home/project33 /dev/sdb5 0 0 0 - /home/sales /dev/sdb6 100744 11 100733 1% /home/research In the df -i screenshot above you can see the inode usage for several mounted file systems. You don't see numbers for /dev/sdb5 because it is a fat file system. 35.1.3. inode number Each inode has a unique number (the inode number). You can see the inode numbers with the ls -li command. paul@RHELv4u4:~/test$ touch file1 paul@RHELv4u4:~/test$ touch file2 paul@RHELv4u4:~/test$ touch file3 paul@RHELv4u4:~/test$ ls -li total 12 817266 -rw-rw-r-- 1 paul paul 0 Feb 817267 -rw-rw-r-- 1 paul paul 0 Feb 817268 -rw-rw-r-- 1 paul paul 0 Feb paul@RHELv4u4:~/test$ 5 15:38 file1 5 15:38 file2 5 15:38 file3 These three files were created one after the other and got three different inodes (the first column). All the information you see with this ls command resides in the inode, except for the filename (which is contained in the directory). 330file links 35.1.4. inode and file contents Let's put some data in one of the files. paul@RHELv4u4:~/test$ ls -li total 16 817266 -rw-rw-r-- 1 paul paul 0 Feb 817270 -rw-rw-r-- 1 paul paul 92 Feb 817268 -rw-rw-r-- 1 paul paul 0 Feb paul@RHELv4u4:~/test$ cat file2 It is winter now and it is very cold. We do not like the cold, we prefer hot paul@RHELv4u4:~/test$ 5 15:38 file1 5 15:42 file2 5 15:38 file3 summer nights. The data that is displayed by the cat command is not in the inode, but somewhere else on the disk. The inode contains a pointer to that data. 35.2. about directories 35.2.1. a directory is a table A directory is a special kind of file that contains a table which maps filenames to inodes. Listing our current directory with ls -ali will display the contents of the directory file. paul@RHELv4u4:~/test$ total 32 817262 drwxrwxr-x 2 800768 drwx------ 16 817266 -rw-rw-r-- 1 817270 -rw-rw-r-- 1 817268 -rw-rw-r-- 1 paul@RHELv4u4:~/test$ ls -ali paul paul paul paul paul paul 4096 Feb paul 4096 Feb paul 0 Feb paul 92 Feb paul 0 Feb 5 5 5 5 5 15:42 15:42 15:38 15:42 15:38 . .. file1 file2 file3 35.2.2. . and .. You can see five names, and the mapping to their five inodes. The dot . is a mapping to itself, and the dotdot .. is a mapping to the parent directory. The three other names are mappings to different inodes. 331file links 35.3. hard links 35.3.1. creating hard links When we create a hard link to a file with ln, an extra entry is added in the directory. A new file name is mapped to an existing inode. paul@RHELv4u4:~/test$ ln file2 hardlink_to_file2 paul@RHELv4u4:~/test$ ls -li total 24 817266 -rw-rw-r-- 1 paul paul 0 Feb 5 15:38 file1 817270 -rw-rw-r-- 2 paul paul 92 Feb 5 15:42 file2 817268 -rw-rw-r-- 1 paul paul 0 Feb 5 15:38 file3 817270 -rw-rw-r-- 2 paul paul 92 Feb 5 15:42 hardlink_to_file2 paul@RHELv4u4:~/test$ Both files have the same inode, so they will always have the same permissions and the same owner. Both files will have the same content. Actually, both files are equal now, meaning you can safely remove the original file, the hardlinked file will remain. The inode contains a counter, counting the number of hard links to itself. When the counter drops to zero, then the inode is emptied. 35.3.2. finding hard links You can use the find command to look for files with a certain inode. The screenshot below shows how to search for all filenames that point to inode 817270. Remember that an inode number is unique to its partition. paul@RHELv4u4:~/test$ find / -inum 817270 2> /dev/null /home/paul/test/file2 /home/paul/test/hardlink_to_file2 332file links 35.4. symbolic links Symbolic links (sometimes called soft links) do not link to inodes, but create a name to name mapping. Symbolic links are created with ln -s. As you can see below, the symbolic link gets an inode of its own. paul@RHELv4u4:~/test$ ln -s file2 symlink_to_file2 paul@RHELv4u4:~/test$ ls -li total 32 817273 -rw-rw-r-- 1 paul paul 13 Feb 5 17:06 file1 817270 -rw-rw-r-- 2 paul paul 106 Feb 5 17:04 file2 817268 -rw-rw-r-- 1 paul paul 0 Feb 5 15:38 file3 817270 -rw-rw-r-- 2 paul paul 106 Feb 5 17:04 hardlink_to_file2 817267 lrwxrwxrwx 1 paul paul 5 Feb 5 16:55 symlink_to_file2 -> file2 paul@RHELv4u4:~/test$ Permissions on a symbolic link have no meaning, since the permissions of the target apply. Hard links are limited to their own partition (because they point to an inode), symbolic links can link anywhere (other file systems, even networked). 35.5. removing links Links can be removed with rm. paul@laika:~$ paul@laika:~$ paul@laika:~$ paul@laika:~$ paul@laika:~$ touch data.txt ln -s data.txt sl_data.txt ln data.txt hl_data.txt rm sl_data.txt rm hl_data.txt 333file links 35.6. practice : links 1. Create two files named winter.txt and summer.txt, put some text in them. 2. Create a hard link to winter.txt named hlwinter.txt. 3. Display the inode numbers of these three files, the hard links should have the same inode. 4. Use the find command to list the two hardlinked files 5. Everything about a file is in the inode, except two things : name them! 6. Create a symbolic link to summer.txt called slsummer.txt. 7. Find all files with inode number 2. What does this information tell you ? 8. Look at the directories /etc/init.d/ /etc/rc2.d/ /etc/rc3.d/ ... do you see the links ? 9. Look in /lib with ls -l... 10. Use find to look in your home directory for regular files that do not(!) have one hard link. 334file links 35.7. solution : links 1. Create two files named winter.txt and summer.txt, put some text in them. echo cold > winter.txt ; echo hot > summer.txt 2. Create a hard link to winter.txt named hlwinter.txt. ln winter.txt hlwinter.txt 3. Display the inode numbers of these three files, the hard links should have the same inode. ls -li winter.txt summer.txt hlwinter.txt 4. Use the find command to list the two hardlinked files find . -inum xyz #replace xyz with the inode number 5. Everything about a file is in the inode, except two things : name them! The name of the file is in a directory, and the contents is somewhere on the disk. 6. Create a symbolic link to summer.txt called slsummer.txt. ln -s summer.txt slsummer.txt 7. Find all files with inode number 2. What does this information tell you ? It tells you there is more than one inode table (one for every formatted partition + virtual file systems) 8. Look at the directories /etc/init.d/ /etc/rc.d/ /etc/rc3.d/ ... do you see the links ? ls -l /etc/init.d ls -l /etc/rc2.d ls -l /etc/rc3.d 9. Look in /lib with ls -l... ls -l /lib 10. Use find to look in your home directory for regular files that do not(!) have one hard link. find ~ ! -links 1 -type f 335Part X. AppendicesTable of Contents 344Appendix A. keyboard settings A.1. about keyboard layout Many people (like US-Americans) prefer the default US-qwerty keyboard layout. So when you are not from the USA and want a local keyboard layout on your system, then the best practice is to select this keyboard at installation time. Then the keyboard layout will always be correct. Also, whenever you use ssh to remotely manage a Linux system, your local keyboard layout will be used, independent of the server keyboard configuration. So you will not find much information on changing keyboard layout on the fly on linux, because not many people need it. Below are some tips to help you. A.2. X Keyboard Layout This is the relevant portion in /etc/X11/xorg.conf, first for Belgian azerty, then for US- qwerty. [paul@RHEL5 ~]$ grep -i xkb /etc/X11/xorg.conf Option "XkbModel" "pc105" Option "XkbLayout" "be" [paul@RHEL5 ~]$ grep -i xkb /etc/X11/xorg.conf Option "XkbModel" "pc105" Option "XkbLayout" "us" When in Gnome or KDE or any other graphical environment, look in the graphical menu in preferences, there will be a keyboard section to choose your layout. Use the graphical menu instead of editing xorg.conf. A.3. shell keyboard layout When in bash, take a look in the /etc/sysconfig/keyboard file. Below a sample US-qwerty configuration, followed by a Belgian azerty configuration. [paul@RHEL5 ~]$ cat /etc/sysconfig/keyboard KEYBOARDTYPE="pc" KEYTABLE="us" [paul@RHEL5 ~]$ cat /etc/sysconfig/keyboard KEYBOARDTYPE="pc" KEYTABLE="be-latin1" The keymaps themselves can be found in /usr/share/keymaps or /lib/kbd/keymaps. [paul@RHEL5 ~]$ ls -l /lib/kbd/keymaps/ total 52 drwxr-xr-x 2 root root 4096 Apr 1 00:14 amiga 338keyboard settings drwxr-xr-x drwxr-xr-x drwxr-xr-x drwxr-xr-x lrwxrwxrwx drwxr-xr-x 2 8 2 4 1 2 root root root root root root root root root root root root 4096 4096 4096 4096 3 4096 Apr Apr Apr Apr Apr Apr 1 1 1 1 1 1 00:14 00:14 00:14 00:14 00:14 00:14 339 atari i386 include mac ppc -> mac sunAppendix B. hardware B.1. buses B.1.1. about buses Hardware components communicate with the Central Processing Unit or cpu over a bus. The most common buses today are usb, pci, agp, pci-express and pcmcia aka pc-card. These are all Plag and Play buses. Older x86 computers often had isa buses, which can be configured using jumpers or dip switches. B.1.2. /proc/bus To list the buses recognised by the Linux kernel on your computer, look at the contents of the /proc/bus/ directory (screenshot from Ubuntu 7.04 and RHEL4u4 below). root@laika:~# ls /proc/bus/ input pccard pci usb [root@RHEL4b ~]# ls /proc/bus/ input pci usb Can you guess which of these two screenshots was taken on a laptop ? B.1.3. /usr/sbin/lsusb To list all the usb devices connected to your system, you could read the contents of /proc/ bus/usb/devices (if it exists) or you could use the more readable output of lsusb, which is executed here on a SPARC system with Ubuntu. root@shaka:~# lsusb Bus 001 Device 002: ID 0430:0100 Sun Microsystems, Inc. 3-button Mouse Bus 001 Device 003: ID 0430:0005 Sun Microsystems, Inc. Type 6 Keyboard Bus 001 Device 001: ID 04b0:0136 Nikon Corp. Coolpix 7900 (storage) root@shaka:~# B.1.4. /var/lib/usbutils/usb.ids The /var/lib/usbutils/usb.ids file contains a gzipped list of all known usb devices. paul@barry:~$ zmore /var/lib/usbutils/usb.ids | head ------> /var/lib/usbutils/usb.ids <------ # # List of USB ID's # # Maintained by Vojtech Pavlik 340hardware # If you have any new entries, send them to the maintainer. # The latest version can be obtained from # http://www.linux-usb.org/usb.ids # # $Id: usb.ids,v 1.225 2006/07/13 04:18:02 dbrownell Exp $ B.1.5. /usr/sbin/lspci To get a list of all pci devices connected, you could take a look at /proc/bus/pci or run lspci (partial output below). paul@laika:~$ lspci ... 00:06.0 FireWire (IEEE 1394): Texas Instruments TSB43AB22/A IEEE-139... 00:08.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL-816... 00:09.0 Multimedia controller: Philips Semiconductors SAA7133/SAA713... 00:0a.0 Network controller: RaLink RT2500 802.11g Cardbus/mini-PCI 00:0f.0 RAID bus controller: VIA Technologies, Inc. VIA VT6420 SATA ... 00:0f.1 IDE interface: VIA Technologies, Inc. VT82C586A/B/VT82C686/A... 00:10.0 USB Controller: VIA Technologies, Inc. VT82xxxxx UHCI USB 1.... 00:10.1 USB Controller: VIA Technologies, Inc. VT82xxxxx UHCI USB 1.... ... B.2. interrupts B.2.1. about interrupts An interrupt request or IRQ is a request from a device to the CPU. A device raises an interrupt when it requires the attention of the CPU (could be because the device has data ready to be read by the CPU). Since the introduction of pci, irq's can be shared among devices. Interrupt 0 is always reserved for the timer, interrupt 1 for the keyboard. IRQ 2 is used as a channel for IRQ's 8 to 15, and thus is the same as IRQ 9. B.2.2. /proc/interrupts You can see a listing of interrupts on your system in /proc/interrupts. paul@laika:~$ cat /proc/interrupts CPU0 CPU1 0: 1320048 555 IO-APIC-edge 1: 10224 7 IO-APIC-edge 7: 0 0 IO-APIC-edge 8: 2 1 IO-APIC-edge 10: 3062 21 IO-APIC-fasteoi 12: 131 2 IO-APIC-edge 15: 47073 0 IO-APIC-edge 18: 0 1 IO-APIC-fasteoi 19: 31056 1 IO-APIC-fasteoi 20: 19042 1 IO-APIC-fasteoi 21: 44052 1 IO-APIC-fasteoi 22: 188352 1 IO-APIC-fasteoi timer i8042 parport0 rtc acpi i8042 ide1 yenta libata, ohci1394 eth0 uhci_hcd:usb1, uhci_hcd:usb2,... ra0 341hardware 23: 24: 632444 1585 1 1 IO-APIC-fasteoi IO-APIC-fasteoi nvidia VIA82XX-MODEM, VIA8237 B.2.3. dmesg You can also use dmesg to find irq's allocated at boot time. paul@laika:~$ dmesg | grep "irq 1[45]" [ 28.930069] ata3: PATA max UDMA/133 cmd 0x1f0 ctl 0x3f6 bmdma 0x2090 irq 14 [ 28.930071] ata4: PATA max UDMA/133 cmd 0x170 ctl 0x376 bmdma 0x2098 irq 15 B.3. io ports B.3.1. about io ports Communication in the other direction, from CPU to device, happens through IO ports. The CPU writes data or control codes to the IO port of the device. But this is not only a one way communication, the CPU can also use a device's IO port to read status information about the device. Unlike interrupts, ports cannot be shared! B.3.2. /proc/ioports You can see a listing of your system's IO ports via /proc/ioports. [root@RHEL4b ~]# cat /proc/ioports 0000-001f : dma1 0020-0021 : pic1 0040-0043 : timer0 0050-0053 : timer1 0060-006f : keyboard 0070-0077 : rtc 0080-008f : dma page reg 00a0-00a1 : pic2 00c0-00df : dma2 00f0-00ff : fpu 0170-0177 : ide1 02f8-02ff : serial ... B.4. dma B.4.1. about dma A device that needs a lot of data, interrupts and ports can pose a heavy load on the cpu. With dma or Direct Memory Access a device can gain (temporary) access to a specific range of the ram memory. B.4.2. /proc/dma Looking at /proc/dma might not give you the information that you want, since it only contains currently assigned dma channels for isa devices. 342hardware root@laika:~# cat /proc/dma 1: parport0 4: cascade pci devices that are using dma are not listed in /proc/dma, in this case dmesg can be useful. The screenshot below shows that during boot the parallel port received dma channel 1, and the Infrared port received dma channel 3. root@laika:~# dmesg | egrep -C 1 'dma 1|dma 3' [ 20.576000] parport: PnPBIOS parport detected. [ 20.580000] parport0: PC-style at 0x378 (0x778), irq 7, dma 1... [ 20.764000] irda_init() -- [ 21.204000] pnp: Device 00:0b activated. [ 21.204000] nsc_ircc_pnp_probe() : From PnP, found firbase 0x2F8... [ 21.204000] nsc-ircc, chip->init 343Appendix C. License GNU Free Documentation License Version 1.3, 3 November 2008 Copyright )/ 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. 0. PREAMBLE The purpose of this License is to make a manual, textbook, or other functional and useful document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others. This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software. We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference. 1. APPLICABILITY AND DEFINITIONS This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you". You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law. A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language. A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them. The "Invariant Sections" are certain Secondary Sections whose titles 344License are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none. The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words. A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not "Transparent" is called "Opaque". Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only. The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text. The "publisher" means any person or entity that distributes copies of the Document to the public. A section "Entitled XYZ" means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as "Acknowledgements", "Dedications", "Endorsements", or "History".) To "Preserve the Title" of such a section when you modify the Document means that it remains a section "Entitled XYZ" according to this definition. The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License. 2. VERBATIM COPYING You may copy and distribute the Document in any medium, either 345License commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3. You may also lend copies, under the same conditions stated above, and you may publicly display copies. 3. COPYING IN QUANTITY If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects. If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages. If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public. It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document. 4. MODIFICATIONS You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version: * A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission. 346License * B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement. * C. State on the Title page the name of the publisher of the Modified Version, as the publisher. * D. Preserve all the copyright notices of the Document. * E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices. * F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below. * G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice. * H. Include an unaltered copy of this License. * I. Preserve the section Entitled "History", Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence. * J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission. * K. For any section Entitled "Acknowledgements" or "Dedications", Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein. * L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles. * M. Delete any section Entitled "Endorsements". Such a section may not be included in the Modified Version. * N. Do not retitle any existing section to be Entitled "Endorsements" or to conflict in title with any Invariant Section. * O. Preserve any Warranty Disclaimers. If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles. You may add a section Entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties"+'for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard. You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, 347License you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one. The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version. 5. COMBINING DOCUMENTS You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers. The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work. In the combination, you must combine any sections Entitled "History" in the various original documents, forming one section Entitled "History"; likewise combine any sections Entitled "Acknowledgements", and any sections Entitled "Dedications". You must delete all sections Entitled "Endorsements". 6. COLLECTIONS OF DOCUMENTS You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects. You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document. 7. AGGREGATION WITH INDEPENDENT WORKS A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an "aggregate" if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document. If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate. 348License 8. TRANSLATION Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail. If a section in the Document is Entitled "Acknowledgements", "Dedications", or "History", the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title. 9. TERMINATION You may not copy, modify, sublicense, or distribute the Document except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, or distribute it is void, and will automatically terminate your rights under this License. However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, receipt of a copy of some or all of the same material does not give you any rights to use it. 10. FUTURE REVISIONS OF THIS LICENSE The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/. Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation. If the Document specifies 349License that a proxy can decide which future versions of this License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Document. 11. RELICENSING "Massive Multiauthor Collaboration Site" (or "MMC Site") means any World Wide Web server that publishes copyrightable works and also provides prominent facilities for anybody to edit those works. A public wiki that anybody can edit is an example of such a server. A "Massive Multiauthor Collaboration" (or "MMC") contained in the site means any set of copyrightable works thus published on the MMC site. "CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0 license published by Creative Commons Corporation, a not-for-profit corporation with a principal place of business in San Francisco, California, as well as future copyleft versions of that license published by that same organization. "Incorporate" means to publish or republish a Document, in whole or in part, as part of another Document. An MMC is "eligible for relicensing" if it is licensed under this License, and if all works that were first published under this License somewhere other than this MMC, and subsequently incorporated in whole or in part into the MMC, (1) had no cover texts or invariant sections, and (2) were thus incorporated prior to November 1, 2008. The operator of an MMC Site may republish an MMC contained in the site under CC-BY-SA on the same site at any time before August 1, 2009, provided the MMC is eligible for relicensing. 350Index Symbols ; (shell), 136 !! (shell), 156 ! (bash history), 156 ! (file globbing), 163 ? (file globbing), 162 /, 76, 102 /bin, 103, 128 /bin/bash, 125, 292 /bin/cat, 103 /bin/csh, 125 /bin/date, 103 /bin/ksh, 125, 292 /bin/rm, 129 /bin/sh, 125 /boot, 105 /boot/grub, 105 /boot/grub/grub.cfg, 105 /boot/grub/grub.conf, 105 /dev, 85, 109 /dev/null, 109, 175 /dev/pts/1, 109 /dev/random, 120 /dev/tty1, 109 /dev/urandom, 119, 121 /dev/zero, 120 /etc, 105 /etc/bashrc, 293 /etc/default/useradd, 276 /etc/fstab, 326 /etc/group, 299, 308 /etc/gshadow, 301 /etc/hosts, 120 /etc/init.d/, 105 /etc/inputrc, 292 /etc/login.defs, 286 /etc/passwd, 191, 275, 278, 287, 287, 308 /etc/profile, 292 /etc/resolv.conf, 120 /etc/shadow, 283, 285, 321 /etc/shells, 235, 278 /etc/skel, 105, 277 /etc/sudoers, 269, 270 /etc/sysconfig, 105 /etc/sysconfig/firstboot, 106 /etc/sysconfig/harddisks, 106 /etc/sysconfig/hwconf, 106 /etc/sysconfig/keyboard, 106 /etc/X11/xorg.conf, 105 /export, 107 /home, 107 /lib, 104 /lib/kbd/keymaps/, 106 /lib/modules, 104 /lib32, 104 /lib64, 104 /media, 107 /opt, 104 /proc, 85, 109 /proc/bus, 340 /proc/bus/pci, 341 /proc/bus/usb/devices, 340 /proc/cpuinfo, 110 /proc/dma, 342 /proc/interrupts, 112, 341 /proc/ioports, 342 /proc/kcore, 112 /proc/sys, 111 /root, 107 /run, 117 /sbin, 103, 128 /srv, 107 /sys, 113 /tmp, 108, 320 /usr, 114 /usr/bin, 114 /usr/bin/getfacl, 326 /usr/bin/passwd, 321 /usr/bin/setfacl, 326 /usr/include, 114 /usr/lib, 114 /usr/local, 114 /usr/share, 114 /usr/share/games, 115 /usr/share/man, 115 /usr/src, 115 /var, 116 /var/cache, 116 /var/lib, 117 /var/lib/rpm, 117 /var/lib/usbutils/usb.ids, 340 /var/lock, 117 /var/log, 116 /var/log/messages, 116 /var/log/syslog, 116 /var/run, 117 /var/spool, 116 /var/tmp, 117 ., 75 .., 75 .. (directory), 331 . (directory), 331 . (shell), 236 .bash_history, 157 .bash_login, 293 .bash_logout, 294 .bash_profile, 292 .bashrc, 292, 293 .exrc, 229 .vimrc, 229 `(backtick), 151 ~, 75 351Index '(single quote), 151 " (double quotes), 127 (( (shell), 256 -- (shell), 237 [ (file globbing), 163 [ (shell), 241 $? (shell variables), 136 $() embedded shell, 151 $ (shell variables), 142 $HISTFILE, 157 $HISTFILESIZE, 157 $HISTSIZE, 157 $LANG, 164 $PATH, 128, 145 $PS1, 76 * (file globbing), 162 \ (backslash), 138 &, 136 &&, 137 #!/bin/bash, 235 #! (shell), 235 # (pound sign), 138 >, 173 >>, 174 >|, 174 ||, 137 1>, 175 2>, 175 2>&1, 175 777, 314 A access control list, 326 acl, 328 acls, 326 agp, 340 AIX, 4 alias(bash), 129 alias(shell), 129 apropos, 72 arguments(shell), 126 B backticks, 151 base64, 177 bash, 219, 248 bash history, 156 bash -x, 237 binaries, 103 Bourne again shell, 125 BSD, 4 bunzip2, 201 bus, 340 bzcat, 201 bzip2, 199, 201, 201 bzmore, 201 C cal, 198 case, 258 case sensitive, 85 cat, 96, 182 cd, 75 cd -, 76 CentOS, 7 chage, 286 chgrp(1), 309 chkconfig, 106 chmod, 277, 314 chmod(1), 226, 313 chmod +x, 235, 315 chown, 277 chown(1), 309 chsh(1), 278 comm(1), 188 command line scan, 126 command mode(vi), 223 copyleft, 11 copyright, 10, 10 cp, 88 cp(1), 88 cpu, 340 crypt, 284 csh, 235 Ctrl d, 96 ctrl-r, 157 current directory, 75 cut, 191 cut(1), 184 D daemon, 72 date, 197 Debian, 7 Dennis Ritchie, 4 devfs, 113 df -i, 330 directory, 331 distribution, 6 distributions, 102 dma, 342 dmesg(1), 342, 343 dumpkeys(1), 106 E echo, 126 echo(1), 125, 127 echo $-, 152 echo *, 165 Edubuntu, 7 eiciel, 328 ELF, 104 elif, 242 embedding(shell), 151 352Index env(1), 146, 146 environment variable, 142 EOF, 96, 177 escaping (shell), 165 eval, 256 executables, 103 exit (bash), 157 export, 146 F Fedora, 7 FHS, 102 file, 85 file(1), 104 file globbing, 161 file ownership, 308 Filesystem Hierarchy Standard, 102 filters, 181 find(1), 196, 320, 321, 332 FireWire, 113 for (bash), 242 FOSS, 10 four freedoms, 11 Free Software, 10 free software, 10 freeware, 10 function (shell), 259 G gcc(1), 285 getfacl, 326 getopts, 251 GID, 299 glob(7), 162 GNU, 4 gpasswd, 301 GPL, 11 GPLv3, 11 grep, 206, 207, 210 grep(1), 182 grep -i, 182 grep -v, 183 groupadd(1), 299 groupdel(1), 300 groupmod(1), 300 groups, 298 groups(1), 299 gunzip(1), 200 gzip, 200 gzip(1), 200 H hard link, 332 head(1), 95 here directive, 97 here document, 177 here string, 177 hidden files, 77 HP, 4 HP-UX, 4 http://www.pathname.com/fhs/, 102 I IBM, 4 id, 267 IEEE 1394, 113 if then else (bash), 242 inode, 329, 332 inode table, 330 insert mode(vi), 223 interrupt, 341 IO Ports, 342 IRQ, 341 isa, 340 K Ken Thompson, 4 kernel, 104 keymaps(5), 106 Korn shell, 158 Korn Shell, 278 ksh, 158, 235 kudzu, 106 L less(1), 98 let, 257 Linus Torvalds, 4 Linux Mint, 7 ln, 333 ln(1), 332 loadkeys(1), 106 locate(1), 197 logical AND, 137 logical OR, 137 Logiciel Libre, 10 ls, 77, 311, 330 ls(1), 77, 330, 331 ls -l, 310 lspci, 341 lsusb, 340 M magic, 85 makewhatis, 73 man(1), 72, 72, 73 mandb(1), 73 man hier, 102 man -k, 72 md5, 285 mkdir, 277 mkdir(1), 79, 315 mkdir -p, 79 mkfs, 330 353Index more(1), 98 mv, 89 N noclobber, 174 nounset(shell), 147 O octal permissions, 314 od(1), 189 OEL, 7 open source, 10 open source definition, 11 open source software, 10 openssl, 284 Oracle Enterprise Linux, 7 owner, 311 P parent directory, 75 passwd, 283, 283, 284, 286 passwd(1), 73, 321 passwd(5), 73 path, 76, 77 pc-card, 340 pci, 340 pci-express, 340 pcmcia, 340 perl, 212 perldoc, 212 popd, 83 prename, 212 primary group, 276 proprietary, 10 public domain, 10 pushd, 83 pwd, 75 pwd(1), 76 R random number generator, 120 read, 249 reboot, 157 Red Hat, 7 regular expressions, 158 rename, 90, 212, 213, 214 repository, 6 Richard Stallman, 4 rm, 87 rm(1), 333 rmdir(1), 79 rmdir -p, 80 rm -rf, 87 root, 103, 268, 269, 270, 275 root directory, 102 rpm, 117 S salt (encryption), 285 Scientific, 7 sed, 190, 215, 216 set, 152 set(shell), 143 set +x, 130 setfacl, 326 setgid, 320, 320 setuid, 237, 321, 321, 321 set -x, 130 she-bang (shell), 235 shell, 291 shell comment, 138 shell embedding, 151 shell escaping, 138 shell expansion, 126, 126 shell functions, 259 shift, 249 shopt, 252 skeleton, 105 sleep, 198 soft link, 333 Solaris, 4 sort, 191 sort(1), 186 source, 236, 250 standard input, 96 standard output, 96 stderr, 172 stdin, 172, 182 stdout, 172, 182 sticky bit, 320 strings(1), 98 su, 268, 268, 287, 301 su -, 145 sudo, 269, 270, 287 sudo su -, 270 Sun, 4 SunOS, 4 superuser, 275 symbolic link, 333 sysfs, 113 System V, 104 T tab key(bash), 77 tac, 97 tail(1), 95 tee(1), 182 test, 241 time, 199 touch(1), 86 tr, 185 tr(1), 184 type(shell), 128 354Index U Ubuntu, 7 umask(1), 315 unalias(bash), 130 uniq, 191 uniq(1), 187 Unix, 4 unset, 152 unset(shell), 143 until (bash), 243 updatedb(1), 197 usb, 113, 340 useradd, 276, 277, 284 useradd(1), 277 useradd -D, 276 userdel(1), 276 usermod, 287, 287, 300 usermod(1), 276 V vi, 302 vi(1), 222 vigr(1), 302 vim(1), 222 vimtutor(1), 222 vipw, 287 visudo, 269 vrije software, 10 W w, 267 wc(1), 185 whatis(1), 72 whereis(1), 72 which(1), 128 while (bash), 243 white space(shell), 126 who, 191, 267 whoami, 267 who am i, 267 wild cards, 163 X X, 105 X Window System, 105 Z zcat, 200 zmore, 200 355 An A-Z Index of the Windows CMD command line a: ADDUSERS Add or list users to/from a CSV file, ADmodcmd Active Directory Bulk Modify, ARP Address Resolution Protocol, ASSOC Change file extension associations, ASSOCIAT One step file association, AT Schedule a command to run at a specific time, ATTRIB Change file attributes. b: BCDBOOT Create or repair a system partition, BCDEDIT Manage Boot Configuration Data, BITSADMIN Background Intelligent Transfer Service, BOOTCFG Edit Windows boot settings, BROWSTAT Get domain, browser and PDC info. c: CACLS Change file permissions, CALL Call one batch program from another, CERTREQ Request certificate from a certification authority, CERTUTIL Utility for certification authority (CA) files and services, CD Change Directory - move to a specific Folder, CHANGE Change Terminal Server Session properties, CHKDSK Check Disk - check and repair disk problems, CHKNTFS Check the NTFS file system, CHOICE Accept keyboard input to a batch file, CIPHER Encrypt or Decrypt files/folders, CleanMgr Automated cleanup of Temp files, recycle bin, CLIP Copy STDIN to the Windows clipboard, CLS Clear the screen, CMD Start a new CMD shell, CMDKEY Manage stored usernames/passwords, COLOR Change colors of the CMD window, COMP Compare the contents of two files or sets of files, COMPACT Compress files or folders on an NTFS partition, COMPRESS Compress one or more files, CONVERT Convert a FAT drive to NTFS, COPY Copy one or more files to another location, Coreinfo Show the mapping between logical & physical processors, CSCcmd Client-side caching (Offline Files), CSVDE Import or Export Active Directory data. d: DATE Display or set the date, DEFRAG Defragment hard drive, DEL Delete one or more files, DELPROF Delete user profiles, DELTREE Delete a folder and all subfolders, DevCon Device Manager Command Line Utility, DIR Display a list of files and folders, DIRQUOTA File Server Resource Manager Disk quotas, DIRUSE Display disk usage, DISKPART Disk Administration, DISKSHADOW Volume Shadow Copy Service, DISKUSE Show the space used in folders, DOSKEY Edit command line, recall commands, and create macros, DriverQuery Display installed device drivers, DSACLs Active Directory ACLs, DSAdd Add items to active directory (user group computer), DSGet View items in active directory (user group computer), DSQuery Search for items in active directory (user group computer), DSMod Modify items in active directory (user group computer), DSMove Move an Active directory Object, DSRM Remove items from Active Directory, Dsmgmt Directory Service Management. e: ECHO Display message on screen, ENDLOCAL End localisation of environment changes in a batch file, ERASE Delete one or more files, EVENTCREATE Add a message to the Windows event log, EXIT Quit the current script/routine and set an errorlevel, EXPAND Uncompress CAB files, EXPLORER Open Windows Explorer, EXTRACT Uncompress CAB files. f: FC Compare two files, FIND Search for a text string in a file, FINDSTR Search for strings in files, FOR /F Loop command: against a set of files, FOR /F Loop command: against the results of another command, FOR Loop command: all options Files, Directory, List, FORFILES Batch process multiple files, FORMAT Format a disk, FREEDISK Check free disk space, FSUTIL File and Volume utilities, FTP File Transfer Protocol, FTYPE File extension file type associations. g: GETMAC Display the Media Access Control (MAC) address, GOTO Direct a batch program to jump to a labelled line, GPRESULT Display Resultant Set of Policy information, GPUPDATE Update Group Policy settings. h: HELP Online Help, HOSTNAME Display the host name of the computer. i iCACLS Change file and folder permissions, IEXPRESS Create a self extracting ZIP file archive, IF Conditionally perform a command, IFMEMBER Is the current user a member of a group, IPCONFIG Configure IP, INUSE Replace files that are in use by the OS. l: LABEL Edit a disk label, LODCTR Load PerfMon performance counters, LOGMAN Manage Performance Monitor logs, LOGOFF Log a user off, LOGTIME Log the date and time in a file. m: MAKECAB Create .CAB files, MAPISEND Send email from the command line, MBSAcli Baseline Security Analyzer, MEM Display memory usage, MD Create new folders, MKLINK Create a symbolic link (linkd), MODE Configure a system device COM/LPT/CON, MORE Display output, one screen at a time, MOUNTVOL Manage a volume mount point, MOVE Move files from one folder to another, MOVEUSER Move a user from one domain to another, MSG Send a message, MSIEXEC Microsoft Windows Installer, MSINFO32 System Information, MSTSC Terminal Server Connection (Remote Desktop Protocol). n: NET Manage network resources, NETDOM Domain Manager, NETSH Configure Network Interfaces, Windows Firewall & Remote access, NBTSTAT Display networking statistics (NetBIOS over TCP/IP), NETSTAT Display networking statistics (TCP/IP), NLSINFO Display locale information (reskit), NLTEST Network Location Test (AD), NOW Display the current Date and Time, NSLOOKUP Name server lookup, NTBACKUP Backup folders to tape, NTDSUtil Active Directory Domain Services management, NTRIGHTS Edit user account rights, NVSPBIND Modify network bindings. o: OPENFILES Query or display open files. p: PATH Display or set a search path for executable files, PATHPING Trace route plus network latency and packet loss, PAUSE Suspend processing of a batch file and display a message, PERMS Show permissions for a user, PERFMON Performance Monitor, PING Test a network connection, POPD Return to a previous directory saved by PUSHD, PORTQRY Display the status of ports and services, POWERCFG Configure power settings, PRINT Print a text file, PRINTBRM Print queue Backup/Recovery, PRNCNFG Configure or rename a printer, PRNMNGR Add, delete, list printers and printer connections, ProcDump Monitor an application for CPU spikes, PROMPT Change the command prompt, PsExec Execute process remotely, PsFile Show files opened remotely, PsGetSid Display the SID of a computer or a user, PsInfo List information about a system, PsKill Kill processes by name or process ID, PsList List detailed information about processes, PsLoggedOn Who's logged on (locally or via resource sharing), PsLogList Event log records, PsPasswd Change account password, PsPing Measure network performance, PsService View and control services, PsShutdown Shutdown or reboot a computer, PsSuspend Suspend processes, PUSHD Save and then change the current directory. q: QGREP Search file(s) for lines that match a given pattern, Query Process / QPROCESS Display processes, Query Session / QWinsta Display all sessions (TS/Remote Desktop), Query TermServer /QAppSrv List all servers (TS/Remote Desktop), Query User / QUSER Display user sessions (TS/Remote Desktop). r: RASDIAL Manage RAS connections, RASPHONE Manage RAS connections, RECOVER Recover a damaged file from a defective disk, REG Registry: Read, Set, Export, Delete keys and values, REGEDIT Import or export registry settings, REGSVR32 Register or unregister a DLL, REGINI Change Registry Permissions, REM Record comments (remarks) in a batch file, REN Rename a file or files, REPLACE Replace or update one file with another, Reset Session Delete a Remote Desktop Session, RD Delete folder(s), RMTSHARE Share a folder or a printer, ROBOCOPY Robust File and Folder Copy, ROUTE Manipulate network routing tables, RUN Start | RUN commands, RUNAS Execute a program under a different user account, RUNDLL32 Run a DLL command (add/remove print connections). s: SC Service Control, SCHTASKS Schedule a command to run at a specific time, SET Display, set, or remove session environment variables, SETLOCAL Control the visibility of environment variables, SetSPN Edit Service Principal Names, SETX Set environment variables, SFC System File Checker, SHARE List or edit a file share or print share, ShellRunAs Run a command under a different user account, SHIFT Shift the position of batch file parameters, SHORTCUT Create a windows shortcut (.LNK file), SHUTDOWN Shutdown the computer, SIGCHECK Display file version no. VirusTotal status & timestamp, SLEEP Wait for x seconds, SLMGR Software Licensing Management (Vista/2008), SORT Sort input, START Start a program, command or batch file, STRINGS Search for ANSI and UNICODE strings in binary files, SUBINACL Edit file and folder Permissions, Ownership and Domain, SUBST Associate a path with a drive letter, SYSMON Monitor and log system activity to the Windows event log, SYSTEMINFO List system configuration. t: TAKEOWN Take ownership of a file, TASKLIST List running applications and services, TASKKILL End a running process, TELNET Communicate with another host using the TELNET protocol, TIME Display or set the system time, TIMEOUT Delay processing of a batch file, TITLE Set the window title for a CMD.EXE session, TLIST Task list with full path, TOUCH Change file timestamps, TRACERT Trace route to a remote host, TREE Graphical display of folder structure, TSDISCON Disconnect a Remote Desktop Session, TSKILL End a running process, TSSHUTDN Remotely shut down or reboot a terminal server, TYPE Display the contents of a text file, TypePerf Write performance data to a log file, TZUTIL Time Zone Utility, v: VER Display version information, VERIFY Verify that files have been saved, VMConnect Connect to a Hyper-V Virtual Machine, VOL Display a disk label. w: W32TM Time Service, WAITFOR Wait for or send a signal, WBADMIN Windows Backup Admin, WECUTIL Windows Event Collector Utility, WEVTUTIL Clear event logs, enable/disable/query logs, WHERE Locate and display files in a directory tree, WHOAMI Output the current UserName and domain, WINDIFF Compare the contents of two files or sets of files, WINRM Windows Remote Management, WINRS Windows Remote Shell, WMIC WMI Commands, WPEUTIL Run WinPE commands, WPR Windows Performance Recorder, WUAUCLT Windows Update. x: XCACLS Change file and folder permissions, XCOPY Copy files and folders. 899808777 681518024 652248906 278630346 25330502 502361846 822729579 675954608 789538338 33889732 619506394 383558985 486772355 146986346 884227437 904379993 367617151 220754727 737730488 190670952 334644495 768998847 992627153 356143787 467602349 494551055 217522293 410063359 938810813 365743779 296123661 610890368 497138511 54684389 207456273 75073720 263537589 184555442 398725457 483672365 515167361 164853474 585839132 641491080 263937067 955443037 90080293 784307105 937887352 551235296 280317389 416736568 435648278 202741405 147870794 855336914 837412147 824712989 217579273 492877462 47234304 619917089 278834640 206172375 422371855 949064443 576652468 691765407 618545502 847456754 979027479 627404535 915825002 745586135 893234351 830083468 765983306 630036734 416507199 737618398 703430191 619409376 777641911 770993809 327899726 205460552 140877088 915003209 219631187 558741026 289157662 288779921 235857334 438127640 485050456 832110677 487100509 436216768 227086591 307983935 718182802 922386094 208874870 901294276 107644878 89110086 657361449 179734498 142153318 708895908 272930710 693079013 684682826 213073240 819748060 733776441 233659076 555147264 925977743 142781014 950384611 11579725 129373034 383045530 108375865 767308097 553969980 129218623 566385204 72157333 176280213 684035418 176317071 468396792 623061105 336003639 818296937 293497088 771915880 471584465 142873087 268496959 338457832 297296586 532939941 719531256 435726603 337042169 745200745 285940999 482573586 281829497 665549173 27210904 246244058 81572673 718527461 909451730 857748413 640222324 982159599 202008680 839527107 244924692 95941831 604362476 178244079 538083761 807001013 11548236 34332748 389973877 178338969 19082998 182274630 341901196 645831163 464529600 303163214 616624283 704745544 221858001 346991967 394344275 521924550 558117101 554928362 641994562 599562021 171621578 336367201 802854438 611295224 851193409 203579461 64689882 558954551 561833496 374767586 841780322 839774837 604685104 629294463 205643770 969320119 737728910 276098048 894853084 393749957 754161452 324630641 275887902 138371084 340287014 371525160 414317154 107471262 383423776 754816272 118302600 63343011 64330674 694833644 208286156 325341793 84543872 563798311 937421567 215245680 91817560 498318753 895100612 946267369 894578216 981263335 122367886 422019225 33754053 455619466 947793364 520464248 572681629 625972373 598935487 362954693 912639235 217947724 511934305 257828094 539487745 94843749 784547494 474819848 293001189 389315769 704947295 681635080 474040352 366618933 707065114 864110869 301668552 800386970 730775087 350557409 868449398 831572249 228863840 776655808 593857411 929081002 296770320 651234179 219566139 780001656 303054627 162460669 328091490 768926461 123097224 171191571 401081582 243371131 159906073 934896621 150991220 116592298 808224773 98200438 583525979 702515953 814044160 786276637 301591457 11051511 101487161 8229059 901328141 226200874 784322385 25374982 262941978 494776193 67656180 60076805 974142498 651763536 374916189 637730391 276564600 319938767 394259371 174277357 723133146 994081196 788195962 764791431 550414743 304586768 278314521 980545250 366121328 939866734 391383225 64340274 91098559 499547901 319983342 298742617 810179829 432745486 613889930 924173891 326656722 900920140 134648395 90659639 561285373 682527616 152414377 589136734 898075582 443127913 533378545 422970181 366682126 863422906 606636110 167716370 130057881 844313034 258611233 331659748 352425904 640337416 486929984 281990632 587748772 422379124 15590274 687145795 964458922 997511738 190582823 232037146 383316568 743877379 435095749 960708526 710675022 929163434 144627113 411329527 321109122 133393547 277311304 450730511 473792072 450489652 958238043 354358587 86818623 936471971 460688770 307735337 349231609 936133756 785048849 733413817 898622203 174546200 368458326 787101851 879374675 983878100 782658231 652338873 514690900 764282842 570573054 396246825 22040635 905983969 628637089 85345599 288280225 960155917 935770468 490888432 943804641 83341777 985724941 403737827 299351692 196275443 887768408 952374694 675330820 700525203 495308978 464933410 113148513 770871775 173722893 31900702 632087067 134977725 135152351 39496205 951039006 709453666 773448221 729627106 512579209 937362314 487169893 565646284 818771764 920337752 980164352 454874870 940764004 392292585 920940442 684674898 774653586 890985260 880338059 909882632 891122599 822360787 341870182 795107853 519182854 623452663 648945059 20345046 654236574 535649030 531990452 203209132 289687484 779704993 253131318 410583765 691602606 32983922 240247057 512163378 847152490 513802026 824833678 466792656 247736882 167537386 444761997 850884680 577103010 514846600 991240234 159510052 932078416 601774748 126038861 57997349 598439620 526330516 555680904 657067701 885352926 609610830 348246661 14361834 20521262 282820836 945197614 786521800 108214916 9629144 382961456 520781662 611979154 813192715 462009911 730584805 674143785 962193156 274495235 39227478 646904072 551642462 85034514 391557957 396181053 103598692 275527058 861695143 456234794 814956419 128597231 251484155 366952580 880565449 169136812 422025106 670265811 431006871 642309311 771972305 35414247 318877301 519704033 366828154 693137855 858887545 929703185 18113220 539868836 814052388 113026376 676885985 207914332 357343019 341083775 486557441 987461163 893524719 572156119 102746708 160615082 31002166 968928076 932867913 787928998 106387754 812461127 310474495 350040414 272271198 563917118 73445324 155203675 107824480 358497697 653528494 469854208 402270419 167922719 321614350 414868905 263203781 458150555 798001192 737033075 247300650 452393047 424296013 910174957 379369933 298387371 508628790 409945084 180333943 623203337 707954143 570367403 196635776 193874696 433254924 538584227 505111891 174805453 254136380 148433375 78727607 317370508 209647722 114262912 596477477 914056131 88304514 702562898 540592882 96393732 472017934 88852287 877410832 786021274 355471706 712953568 121163480 406781141 109182984 238878652 374691055 509578695 773129477 399176082 741769316 159646932 560637497 738084000 480062829 405658653 780678666 7912201 19451227 597759816 300224827 975308014 300518587 624887096 955081275 554155449 154382571 345515993 84949012 18375756 970143835 2579168 688092000 553790616 436006519 493374030 320660257 878863013 377021477 937937454 916436714 126547194 594512947 408603011 428328500 508400059 204835766 487955986 354244158 340551309 528492422 628999006 843037537 812689489 414691042 910215615 848438794 949128931 526314621 2176350 431231868 18567488 437396930 43242739 987203707 559200470 527811260 380812092 602506306 105497931 226516170 783127746 439655510 858247321 143161520 195165122 422352994 933760181 191558521 374141507 595383563 160264112 240734566 769342968 982886107 554208131 332697413 528705809 30281245 786150240 247986459 758583820 779711984 696292222 594543927 396142146 986440264 381181130 937564563 602408362 130178117 926878964 726151687 26606059 379215134 305104051 347139042 331595415 123544344 847184892 340740662 522483807 347059124 423288201 509395989 366539697 92989819 57074803 702761822 854012607 578560110 714309735 105212153 750753217 122049434 909334652 644393134 546536474 283648916 439132111 466434791 894049231 884798239 514193196 76733116 468248543 422053440 545908885 357098920 198626630 769649796 635570329 428556707 214222158 515046135 605019916 215640611 154772940 465161898 707179346 828893124 65014423 630528490 68862200 340786666 277050203 685684045 974224191 620404994 493511424 553136277 933235688 972292734 709248863 178941635 902376239 437462570 626811018 429660072 850508285 370731873 169153277 415231198 521718169 350139219 885597085 132587471 700042712 467943104 681133850 979155782 424707756 488020008 201263722 291694455 568507189 719925511 266370955 622895396 838788027 427086375 537403104 289986069 683058253 799517459 49492863 820973865 34203906 197494564 393757731 720413078 578299425 770931790 508475637 159256107 421225357 254537775 941246710 329441064 827105243 197426967 48281698 544540029 436165734 183167212 122508374 987281744 146841859 678085417 750128869 823576605 167792672 499487384 390581424 446319 68009158 41318143 814567065 209040175 184542555 28834511 772341206 831034614 681712907 241595574 714898938 903548176 163764221 728106807 175928536 43272610 406454441 351093355 913925581 65307680 593549746 77626745 714623972 781185621 206797836 377789265 181976698 897247015 767386940 365716268 390969054 356014493 600787464 278013165 201874167 375310157 422161747 336018938 761687135 276548699 77136904 565286749 751012967 741653902 124478572 279668102 96802432 479992420 728045326 496451542 916501109 291732331 899019877 756434741 561027324 278740618 970035489 958636321 656741347 872909363 491152905 164247896 351424364 462001413 810456410 588483031 902425114 736100359 298204201 931196858 349577754 815375549 745820166 600748984 846722082 204255060 505222723 676682751 788619840 465005683 907418081 851760855 21638112 118455150 710345376 408986898 595761237 919938902 882254837 857316162 984977944 360620169 987961231 405538492 931499117 950121055 982958132 867856253 332964054 453346026 54155823 245240667 221711038 775455950 563686772 778799388 600049325 964923715 786203942 59210641 840538340 991150410 511947159 211844012 728275833 643950275 398785960 318887235 825522237 518763633 256442330 481185115 93777306 380424580 237683228 717688358 119508168 109325041 98417865 379182632 141947788 338677859 800994085 637238911 442521043 272729127 770918030 294956568 316755597 116075152 285843088 716522137 9706625 993961867 911286248 641140036 352108062 136445091 298214363 542115638 594771829 479940760 181277051 290684644 231928342 347482272 931908661 152117149 682438309 655160852 400250710 318588532 222116745 722076086 330043215 835821135 662081075 611195435 347084882 635554728 290332153 141328111 470400193 190831691 433458037 988240280 97898229 919648469 743899788 305201263 206522978 658390624 714352070 140607742 922661728 448433988 975146134 598090538 209372692 622642107 731603675 545434399 239792951 842646715 415047067 665766541 370316296 181642723 70283137 918738819 27321551 922960528 106498333 73492023 239776359 868682207 955100038 626509759 845600074 994251185 604190723 928595749 187482599 32931473 758578461 5969502 890253761 570811975 20076686 534599602 999961626 967323167 811799571 730925375 561519738 255517907 716335482 411976193 250562214 540939551 495577941 98057751 431186082 427817105 3781976 594481150 89767329 359365658 331793585 861071958 867485685 874222980 902695452 202955535 343776790 600114362 15314059 243837679 597691965 82328641 133871578 612030107 542917049 635327159 258525399 105959508 617058541 734593977 337163887 588426917 948560775 607963031 177820401 29123006 218810848 658164061 574150648 99948337 269593251 702264124 25121361 873056662 597049909 859347097 540512551 624418292 928655782 27588250 427028630 405287350 644489893 502129871 797541592 150520940 645751958 650754894 35037472 581498005 187644366 403923511 195082238 795256205 258618857 30561788 691094444 442827854 137634316 155118000 382807995 895918213 327957405 125895635 451403605 196305616 864324004 351238858 740502375 728779748 269781390 217031000 554499544 310100546 65003787 931904332 200242897 384682421 281584949 926468284 891583484 614312402 76001331 528822638 83665368 814925589 759368828 115151797 798925000 968786881 837267072 504253543 405357953 709766268 283515272 531242507 711784074 483655267 96912104 914927595 206609040 209786120 250120975 499619735 427488937 959415260 477243176 980910282 553586423 728918993 675394249 278913606 491978028 303604209 604972625 866988390 303642074 83900974 475745482 21597780 202237936 922028208 488396422 278661117 969723610 809269066 973413476 192523229 169418114 804225193 471175052 783143505 583527201 661088029 21123941 2689958 522326785 537967722 420359673 735833383 833927574 822694791 468033680 528534636 860063146 217795876 187209373 236552840 31030586 693667578 872699708 44263800 422720291 92251004 938847495 346624275 540017798 366517562 943822198 231868455 799164096 194093817 972614254 846524632 455664193 422703272 650372365 164799381 378508657 557721313 426990094 392308862 438261406 790498449 467485956 117846264 959924031 737136621 935293183 347877353 46665181 954294530 956612559 233587660 976798245 435521361 994532008 595649562 430076495 517990006 963999780 337026056 664874009 159391146 309058005 48674548 193390556 832445699 201298415 722337655 25167988 745527399 796583162 162960732 327391201 853871740 968105768 657903300 485502476 963392263 420214197 509421359 17798845 333573820 957178331 512820546 616304056 568600887 31881539 442107657 676429582 75581307 799059723 938286145 856759915 560644374 737496758 261499661 739690672 308303817 593989424 991422855 971155027 207319763 374625653 454150748 832814113 235405526 731840237 292728920 688549144 116779574 529365077 351621567 952603568 81064879 152862200 535163785 431417661 49596832 957084377 539358435 721172717 634024325 156803937 59893183 159439098 549672395 434778946 625075824 309228779 747443825 407737210 914879306 234115662 689664024 66946037 341866584 333250868 738969811 512443589 850128445 688799095 565158609 276017481 180851192 494027706 67099671 938190087 274464190 960654650 182926332 799672249 980710739 966454696 20039370 478910361 887743397 870533500 375341738 227496805 639670626 929016010 424742215 762325952 390962397 219091501 978731709 967796676 275392491 232025703 797995830 239166450 711017223 305239102 417750480 787348943 325392530 694034803 721207519 935898843 929153712 512955041 304093249 522222874 287975732 728543191 832989353 857619866 620049532 625609014 539836074 86328502 257848083 263234040 498911127 66284872 702132341 935656333 274034086 644060779 885566447 586630765 474192286 393140017 97791898 184453768 179577474 543731133 392766155 992277796 288372241 239751322 264300240 632470641 526017218 685875189 917293046 311395124 233157133 799907539 604858962 779884898 895384767 377789458 399449936 97014046 628279922 207237370 596072030 84501689 764323567 159038064 177776957 919780452 434592576 728172894 32286866 392491072 233108304 585603897 389490856 334967623 27294861 491932903 604057910 898345915 252377536 117339455 171225356 745912340 98401450 124942445 101431185 166972583 926776899 180542607 632708280 876531873 748846188 95209511 479503433 138362209 82638275 458709968 346323458 59067323 844192870 398126782 611459909 714460247 615226885 87220992 455852498 71251141 719145452 815277133 419469379 758442341 650928684 383531060 31872704 289986702 363629642 265507684 297772400 915450666 546597366 424463325 663186262 675997818 850442619 851595841 733406330 699330385 296409898 984650638 258710959 547761145 476020201 443244171 832185628 606556163 788608739 787120221 415916348 680405671 433612590 175489883 835465418 240707347 495327974 650631919 870198328 355951261 141350686 443168063 816165335 816393942 65172827 508086673 146389260 713461609 622415060 177121374 770438130 362352477 36634073 824102839 681956070 770686443 487226272 189341381 502292134 694939120 998273437 454563436 1807665 82120640 372300241 147111612 69983532 287250856 752284217 151416668 760748335 681075220 478041032 837864680 172398154 697539244 7181081 21629482 778580439 966844842 29557135 679986078 302736984 578433813 479982363 450728928 965593722 871306261 133586415 97131485 162090229 985753401 471950768 563278762 107694163 341672824 96707307 397593592 614957996 189610135 132355499 88350453 373240580 972925588 745402333 901190534 129155949 361437002 359416955 320043777 910829361 881130790 242162610 60073030 103537868 142741209 487634469 390994616 249874838 292851824 102467469 901025763 184412863 96804293 248364318 386549359 974864389 545019314 292217282 868055695 345237513 461278416 834658665 194989657 894108217 977137734 738505421 338027989 175398834 545080430 433239222 952985023 519674180 792072846 277717885 386242438 643529116 90183951 951900024 925967014 690118223 847116816 904243663 958703554 184403015 543649131 941931749 835014994 125160417 817703743 881841435 641382411 887522152 388804427 803624787 368688761 243738494 882201957 53824808 523445243 263620332 295546685 652113496 629264519 477814460 268815090 61166737 264100335 862741447 652032011 608323857 764263691 280200235 938059596 599503153 902099721 289595304 318777329 439504997 956269227 219928408 947982720 432271841 928726011 516307837 167831461 573830630 735412991 3081836 956904112 771506979 307897053 435665736 829991191 469641588 897497409 223253928 440538013 913297798 282432140 737990889 962881207 984205626 19235245 780888829 701111038 126897993 20056354 403888746 259064376 489418281 734415313 106881526 662112314 784358209 466761426 194874182 591955565 47048753 266912976 702954327 772165452 297486918 803778292 868965209 43535972 661808330 829006793 759748468 902026450 256025736 33372975 224511900 757214 450501203 536814403 73207356 880841179 733577152 465554707 568730537 25217304 880721323 28959872 218928544 198945738 264289099 270602130 789870177 788356921 295337338 428019566 353791517 998266375 311082515 146453949 25176047 181966403 173125329 429452224 765381252 9107732 985342657 168180008 982186711 722722081 294891544 867984726 12155240 502716806 949801450 658376943 801737123 191948094 813173178 927745074 95258848 417280716 194199360 315481987 327622274 608451436 423457596 334262084 192911424 386859970 645325845 897160053 730677419 847077173 433962347 370688790 938572146 503092216 758650680 397195935 733677309 336356746 959651066 745365542 638359872 247258810 400291057 927738849 803240351 409585817 921357920 693180795 220161897 492065109 411618940 228247736 862384301 42066971 311353611 564604169 832879702 470124590 247295416 783479422 871520610 848914297 705274953 131099326 573898925 326930221 389842769 405736580 647050302 373424039 128010614 604880939 150080129 588828518 803494780 96281487 545747844 847734640 16134249 639803171 514967260 878752308 878738051 349935812 285675872 136203268 756836808 309412183 880012616 59057637 303734580 865283919 959677303 449807138 702212035 2408944 805818495 381066982 45586814 122087122 912107048 788303329 348667422 169814866 146755240 130652154 65132041 729192745 823056972 241330847 624105207 278720369 714566346 306890585 815807094 327428134 168235789 86341041 91357772 116004527 490534640 871980930 89562831 386707033 651977660 810544572 155937579 36512288 88478416 311325780 977255441 614337742 939833161 827601653 10825495 328882151 833538504 77373717 999422130 143739263 627855923 409442557 13623125 494482542 476707346 157502656 697201108 583181303 685559686 263145549 120568396 977841922 867779156 584401606 513324395 934150735 541811584 181490675 643165395 837022700 566267231 278396583 884130989 395466976 798179504 957416108 195255784 678325585 742801688 612715813 678654798 409320699 897059199 372045285 507700113 55714041 311285330 508224598 591018356 9121597 119637853 640779125 565085407 15301497 494086779 223313805 931173426 876764630 588011718 2924819 613630384 264506623 403528189 775396396 150793595 788344854 496373577 665666403 557372594 383165072 585768236 929534502 985005911 603010303 343893453 803976417 80607004 284879928 713148138 342981345 712549099 413802467 818535527 99639578 920057550 618998685 327230014 346552548 329969619 201972251 655767582 463909683 744912358 814037658 798009727 795308582 842134716 681095138 821855602 762846405 406068781 63633056 455741853 647670979 943798353 216199918 411675306 987632907 825577934 880810431 639974345 956612778 360617895 3467044 431691348 127952205 183238656 172629420 246426044 473618817 743041032 687555862 876278122 329758578 636038618 613958914 624249515 848364596 679255554 920281731 889335573 668431084 349956906 593448875 802018856 184853511 223564087 435013370 590357880 766988223 42323311 818742494 866062190 252915510 667772030 407807 533782061 473562992 396390165 234971202 620443610 819446002 435682069 833493276 613936928 660928119 759016751 199430377 441368608 988215811 351084195 425480206 638548384 806604255 342216530 428872052 655039493 775269854 575080245 900583718 100140124 596450667 196944739 315261339 284706201 621137354 630486058 468155182 262071045 483751328 978203023 264953046 62536456 541849889 789522245 555488751 135704303 908036919 754325373 583940806 948006064 980817964 833304294 22970069 261971549 397541733 759989668 116314540 765741086 613721265 141491190 704425368 692019844 821641086 592370503 769443859 228087329 612766548 950261478 937662627 817360382 153065301 689294494 864893775 614919725 88152434 901214066 548379952 168779284 70469141 647380375 578317276 638421983 109063343 243473343 492086008 802133942 182987011 52954885 664833517 400148299 162787 396636785 68778479 185595730 797096200 451664448 229873045 768448982 779173476 252618343 129175744 148648476 568514593 257042006 609673805 496828598 330420663 425566827 569287168 988626838 812997243 826192363 179345638 120467299 182341695 94121271 911840194 373746766 510310610 989037948 814507215 423468515 896140231 348909379 317077021 984277325 878697678 485641080 253866835 610444807 962075221 546530072 721276776 693764772 311371227 273935545 118993021 180123593 363724860 447907425 849124597 591998902 89817661 212147240 914112010 724258028 15590069 602133223 544478944 513542291 401674146 853965704 250729344 875557110 451828593 20351639 550313967 480947258 827378989 301852278 465865299 726493800 382776072 212983151 930171856 621277406 619985695 620492086 41433189 544805422 839434979 674907353 818645953 427088685 662620895 672607939 835736017 13814009 379156553 556584178 864822121 825726371 40628559 304054245 321434120 896181660 366373431 78385240 798441229 760225221 436317591 189886987 904952515 222953286 350800824 839079917 75404915 732098123 849942881 163389256 767603920 632211210 219762347 580769842 920967815 886756855 955799529 212240056 804529370 961989732 648164106 417659273 49410873 477734168 989733268 818815363 791961950 862531429 174489179 362127033 6481280 945702401 927719858 704844600 64395730 440511289 833799750 886857510 300740583 401660927 428087770 94416821 157767715 230414489 585375318 159667973 508996334 317276578 584070461 602964977 107906866 896823005 305169442 280354907 787826083 668802499 51389887 369715113 925965558 469246506 385199829 903392969 842928056 393184443 579350641 217332110 658104375 54338963 29312898 717236321 85529720 209338160 449750685 569544615 45358536 999183913 970924542 857059549 766754137 246438248 91694536 668337990 85898080 826280652 299231966 957506374 119569583 782317374 690539775 682293837 626534410 397550522 343993309 398094007 51164166 211356710 24701697 296858919 382728686 221383088 995315381 836682224 160791146 627483586 578140175 113437725 349174591 120622741 760176822 718935458 318859972 90530792 575713966 286445832 494681258 410139737 189540978 289855740 137696152 149818903 19510956 880728582 601997665 113693537 48186038 390805430 644852466 805374217 730781048 39269897 558863466 289048212 254254322 832799102 152780481 618318868 164561375 196250054 896155789 964687759 444375803 909201868 127812446 150747155 519836946 570075631 669468314 88477611 160910951 249476454 187012616 638183291 977491072 6808128 608592871 694089376 69604384 69024240 865610413 472739520 175916729 219469759 503752358 509331054 938083391 891047339 47461107 926256482 143244216 737839293 15983696 669969165 345686228 257529249 177542209 957119835 771633880 219710566 744161466 201564630 295991774 891246074 628851966 792510350 38403208 636408269 182933558 67734981 104434340 778127087 410984966 524190231 914091846 998675507 898513174 295334870 577187851 724310587 524723779 391030602 701663812 363396031 239321232 522143558 297733769 647588141 217490345 645334299 680927568 563418194 568466460 635495280 523966431 61325134 539344733 2719691 908046279 438531266 876294882 287413765 188171199 372348485 592645166 744846664 180611410 111412131 370576827 580726673 431845098 309866220 150871443 87000519 324537189 973311508 400998024 88346330 379291438 517771485 298879874 750708058 281678624 905938278 176462489 354074670 318564380 501079598 500325306 682572724 313634356 733216197 90130011 652966187 788526867 240000741 190084969 58323556 593119427 103551405 371603943 389293600 963027997 35518778 711479376 777585626 493591580 714363375 508820316 475110565 854195085 109614566 45894557 251379541 279214630 987742461 530596379 110279752 546554926 47600903 577498317 836654837 263878136 949401498 933578445 972720334 419668675 778542587 409497193 982046969 121958946 80267302 241212886 221164721 199219738 812348845 326472353 534252829 546848786 576757888 84608346 783217221 946812328 917035636 448664951 10779924 199335829 224343794 397182587 75207408 641902370 398020098 167142421 376613645 459597751 934109744 355979315 880311707 587805563 187303291 40529287 970275275 756831499 351696537 860354683 903345139 654577368 7241455 418910200 831070724 246273608 882068949 383225744 105874470 306364094 754360247 677977163 61259970 986298143 64190426 569059895 321424887 888422033 437430258 503253943 110291754 586784091 766956026 462190626 393586275 974818665 551932418 64888240 946310637 865087994 975801329 170675105 396781396 685905694 185827590 490067025 487677470 825903692 465109951 194546094 44991836 778226376 609509729 844477072 419720325 698116279 614463429 99095669 637102499 499636502 739231821 3408115 300633352 122578190 987702651 494526219 132671193 284830562 519587718 300948921 464660117 687649221 277162944 344629476 895624182 699686529 1860501 288852624 322796856 469876238 675900737 12841803 655872694 737104939 147534613 792271550 55287269 803938631 16800078 112874630 860097546 751206053 234367079 409340712 681665296 15034467 412595036 469941812 48111342 727172140 994776034 180669289 138805305 621670849 707305586 16993187 876442806 671826999 53793016 36051844 493890099 673608605 357691025 84896934 435205322 378952330 593488424 665329265 807239992 2529342 339567857 50550533 108085878 674933749 502337247 166746497 912660290 730704287 409324244 349742741 570081871 472907446 627601833 229513368 331713894 780901269 121593193 953835084 202101483 616338866 545575156 708095570 38882602 492599211 598902825 248456565 637440113 859552149 957552715 637474770 51009076 445095746 104660818 899931214 279180521 689807478 581349086 427626093 385022349 535937713 721569318 733892123 294966686 896500206 486742403 391937255 189654622 790793487 329243494 988948318 20189830 201779295 738045859 697001817 66517809 357274555 481039666 941178356 407787977 123126624 228182277 97143559 937337803 602397350 60742505 739085433 610728353 854082643 997318193 639461874 9006349 412675837 874061275 627391487 88407839 713612868 363459600 248723047 795650397 692191715 973998362 262216273 836225198 904505318 623182147 402283929 31692418 566369223 246602988 283625260 801755653 637733727 100587849 195760359 121147213 746726874 142898108 432536841 435020185 7777889 782287081 557646290 994625594 311771894 475774958 965157791 866815439 290627041 498037581 38376701 502131954 381706411 2053508 167271068 238599368 93877898 296412003 103812477 892708065 380491989 510411946 22247634 799938816 592780072 506118616 243501209 536586812 994019802 18846268 496700967 526328741 698451247 652995458 160188810 935123774 901838967 729735089 396035214 324885083 157946864 981467603 676917537 706409332 957917928 275864530 316824312 442322787 75495868 101358251 269863833 498663574 322813538 574315512 491993647 174602006 596521788 715465723 635128861 418877134 768640467 405757740 920783413 885368545 74138787 348477107 60376442 613423015 860363926 963477839 340738752 279496214 614332081 415095944 795017463 529400055 118806359 52575514 842885578 665693477 534329683 41441272 281352286 834805398 473128402 397570056 200277203 186345751 33141460 302614804 48683235 764631744 143363986 222536777 348041924 899781764 845839411 440894280 341564049 684988275 916343197 125621802 65629921 741777070 491013786 691088054 895042462 116420451 105753718 461944524 841594883 785111940 507427598 401452428 123117354 923008223 25548457 966315555 941278320 448033037 603212830 606469132 27608695 610681769 100534021 499334865 900553851 77013665 427372180 734573911 79119227 568676549 314660052 590604036 264581736 400918002 603923375 286797760 100964191 765539809 677586432 540611787 828725004 447276622 830383210 638882125 565300406 608234281 571191596 861151618 180875031 484833167 526312302 710186750 298382183 831152748 756307061 326217899 33914110 775330068 624350995 832252531 459208823 375538134 613201401 316149600 902725846 530704195 357804513 396645112 299000049 169711280 571672926 120003605 375071908 566710494 19373898 134983825 76929052 320371836 478099999 400414343 257259115 938421726 805718856 857418460 700029299 644758072 823394424 974855452 971614667 742938298 203677679 851389237 917614631 580200673 233938462 611097784 296905172 10931486 578719595 521735064 143784733 693897285 780612527 392770318 137554065 971624757 747029357 60568518 749278050 132867273 294641592 636220641 276710934 460732624 610033915 528280136 529001402 69983880 197951328 462792209 348388398 64475198 11191094 792476212 598488181 591997214 452998474 419349642 73302173 399418989 263419739 4040468 812211309 788864551 134833662 889524018 39121957 845464316 604322984 25344019 801322444 808874161 647932807 93703273 744882448 656368611 738342210 104041773 730109409 941506951 586789858 390951048 409248738 602741685 500401984 182149284 259918481 140297058 140293322 17613053 182958308 166834594 493516916 335199277 269994576 135635026 37214959 831003156 559175915 995975164 868138337 140506428 347277586 542564353 722787845 864698191 232645683 701470629 905253292 310423594 385957979 179397812 981068483 691537383 648676940 1682357 769045293 147601487 872691482 710020584 574957784 215148142 111297104 196406456 814561660 343672631 878433410 329176268 914090195 784886959 728537393 845487593 727699755 865414498 121839848 245240052 854143105 393233742 234242338 625117008 480782310 395466335 369978598 290671305 509655728 595483817 40695445 758077475 485206013 936925535 520127948 481075866 491973579 111172405 117618988 792953722 995542775 259100756 607440430 641848878 310211294 526731748 402158726 23032161 771651974 545844181 938703917 774150398 66958104 22587836 782774253 258217719 616928139 399154934 620252850 936205636 283004263 449130767 480986183 985787022 59483249 221836311 570761059 217971780 406543782 853225941 810725513 872950116 530997634 261876774 728102811 808524913 134976453 877710277 184901580 730653350 677124414 489675343 531793979 536394597 741891651 189987308 690891467 450333466 179413839 578736459 1871892 120080133 803551358 905788619 492963377 978717341 13803480 959637702 979218794 534309554 928721633 99953375 955068623 35353254 741240418 125418783 576728620 453175778 446110830 903501055 596341809 208633515 400832612 431042795 375292111 900462149 860141390 965216697 101646235 634706564 374100093 205869778 799908763 949164920 141075100 510024416 646248978 159831409 927250049 839003894 848522154 793344296 769307700 172782506 189219260 681313623 680647174 971645248 88864202 207603221 71177919 909964460 556555814 613136153 609406419 342602740 301583783 33917472 611077881 955044943 655399204 730829938 76995007 205293919 671118127 361908684 15181467 476030215 92617889 486263300 302866765 641300724 775731181 563045934 606519011 674752433 172436076 158840884 671221828 338369783 985908970 243347709 883505331 290147242 351704022 537208485 355213211 161969636 657645542 785617254 36567277 58530501 352587372 833640520 226073449 870722422 93248768 192186872 732832812 125490638 958576827 90118113 382964666 755397262 942332064 693288504 475770242 385137600 86857545 345796159 914256307 453637716 807338507 391374743 788831608 587042173 943541010 567661602 830793071 925359676 572589906 297865613 864538920 465673757 569252355 366088396 673398018 360791871 683628668 875498833 41937201 709231421 405300052 70926215 438949118 145565883 857673988 364140939 387143274 185611254 25449781 735816632 993548543 974324296 727552833 590082193 391367272 891877709 127365923 746414003 325266575 62042952 565731332 509782200 572465331 899251043 85315387 687297434 764271554 827517096 464236894 168365503 31739335 656850465 299533729 706149934 434440950 629747068 610406011 949877746 77110313 187625508 296636554 941873207 380602832 566021390 215648804 463337605 36538385 104797369 761105118 612158145 347765611 976242224 256649050 452344183 956214990 538701826 919798119 708824994 956937134 256839184 830265470 872225704 594329858 278415467 994607725 510134591 96148542 649359612 696111856 277693796 30862583 358058709 475458427 963606097 107036068 969572886 252184587 922534809 186371030 994425618 289981027 1718677 547479963 146562939 486666974 281613841 320632168 888691579 245814911 567023311 279188835 364252932 862125056 437020900 428530145 976692993 130744188 802536246 64033865 405154620 353705823 358205014 488608268 698454456 476314016 962017059 797805831 833258918 414621970 930598772 86701709 597105190 287167295 814269445 170231038 674476239 90744391 507243709 290835232 430492153 381873707 789689006 492014656 213025526 807236713 497825824 46754509 891836969 604672735 472314870 286084171 837292977 516375944 264106888 127524294 976915244 171165000 405216247 901957318 255849201 490616512 579116985 695709376 248269192 816916912 496308215 930299904 495239067 323772757 288794464 715957841 993418047 623147814 57291396 86068734 963980283 2110304 318366352 775962101 991896139 342719453 210985488 225801272 489850026 913200728 752484179 398079972 969768430 673609377 426182522 767529496 760116337 430601708 690525468 689342754 526915573 641359509 426170548 278781887 257692200 688976701 212650786 920847553 788568012 778685198 663195058 753167226 204651332 825944953 925342173 554383720 863694361 832078335 268140334 448063249 21916457 646998963 60995910 103199844 261342171 62682284 782502563 296113356 286757006 573821408 184491151 543034395 414834063 522372773 813676182 248665504 904827913 978393892 500303414 675727981 971583031 460890321 684476907 454190369 183983834 146531258 719177779 234275911 989327519 906295356 172993785 685930227 466955936 57907017 319005531 501610898 830076083 529576406 823011791 417562141 894381937 645827917 280426568 906627654 200230362 288645919 101977804 260036000 840343459 517692745 530268198 511283679 904967233 779112684 505416034 811931947 320223675 276456063 826774482 955496417 32154156 942331536 171335845 782433150 314842824 424855004 77580479 607584846 575162230 988846305 897876695 949169390 422783270 662684130 315195518 762839734 848386206 583537189 998852465 673658370 529888001 849692463 44706460 302013513 839513534 566519640 775580649 46520396 302632603 995021831 976419457 120392883 317710312 959685375 190147987 699294360 458145975 532549774 153232989 887682458 328918280 608188717 478415183 105401200 936314163 478265448 19730397 205951407 239063216 936108109 84192520 117869341 993551270 185996024 556839056 644593528 529635786 487612217 66912147 838805647 566991835 83655131 268884377 96915762 453917888 948463392 230692678 139832893 358775661 604164565 118008088 66767393 595490994 46524821 968574070 426415208 148548835 700494782 609777758 533504760 346719778 584967068 534339117 588689402 670822054 703694517 280533155 3514088 67435958 647542971 527821392 778680082 4898359 260004566 917104429 36396437 558479816 86259936 279768631 976681389 327532365 173957541 217517026 418032119 259983277 301562468 706912293 727100622 14213279 193668996 803974987 962060606 176961585 15095397 453163965 48781079 450576226 991775803 217284198 288904025 39797076 334183619 237577577 25987443 564072836 69371958 717143276 802794598 896699265 284997875 256764200 516741788 982842267 154255444 650564118 924648142 819601067 94354204 221098824 211552989 99743195 977785350 317598608 356681800 268977313 589440260 519119742 707063822 205663504 248986551 24004482 199395439 797526665 5175719 635002219 339868587 377218532 941014779 304349364 220868513 425444737 626107398 315186379 164705160 809010231 622833010 356139898 718240307 357236820 525505196 451578200 71087206 115898200 837010233 900026485 258205776 482208535 185009643 327785856 517301129 162269797 34346679 329338715 100952776 393622619 563511967 560185792 701627 419514960 596532077 869662268 527720830 746334474 543092272 450194856 78221151 546246715 865617428 8775919 862994557 100914210 426137261 564892037 828281727 881946859 315571241 964489079 92728132 941262015 783821905 902025709 318054369 475228437 545927869 127065333 230715826 306999889 952603213 422985137 527752650 662953111 276473880 320397715 42367478 13282728 897550419 473853970 952184619 367288778 899749955 641068720 536229257 711791269 214399179 948977044 445642250 949215785 647966132 828175331 49153046 926972297 263752281 637827498 676993100 881865531 476680392 351131267 557195624 769864769 148972276 617369993 448149701 940438906 210609105 408209988 926359590 597355965 245104123 617855680 716731328 567302402 698487366 538573259 335294482 988195456 628862124 588664988 420972234 647392137 364077118 605505924 18889581 481882427 180951255 538663105 126174935 697829593 636994941 787593087 801439050 441250857 608967204 724784045 998798115 25969382 166702003 958025053 48808602 703815594 490344455 691373726 788376826 295902202 666043775 199066882 921442774 639087977 519046867 393934290 449123906 719872307 689181655 878603851 939350310 384216501 172298933 388977989 300156444 779320996 272971997 652791444 500268233 323132342 577388836 810728248 751291658 963503147 68764435 251366000 793675279 122074907 273829871 612030169 769846124 517318297 425478610 685866123 624143688 895017996 995106191 979479657 259335783 880994684 662161997 853394523 457745814 475551508 831144827 525852711 501379143 711502868 363600118 62036171 835229205 915945943 720094435 260381687 478500137 315855760 883078227 161113117 393988157 198329664 12249003 48701902 108078595 117361495 968335834 218353673 29372385 427992027 210753445 138737214 963126168 909323321 833682434 885998881 155893999 960927843 216066565 244360914 807557317 540157566 966295899 509390881 29444698 160337408 103117718 533146716 503181235 162498349 702346195 551089992 803867946 193357351 790179684 496761232 80078317 943896720 896692808 97177435 799467290 839882278 468492712 802095126 102075155 460728163 30343927 605295389 306521761 447636239 221037105 831556495 225792016 916965903 845839641 402891949 491548477 471942438 427781905 585341398 3027980 886291329 353973768 993648292 468413684 518209233 267637633 173343378 726801493 228948180 820954432 193933828 328044011 745083719 803627764 813883063 337067745 495199707 980622248 229776790 38808815 267303673 305996193 775970075 897335093 93319735 791159246 640109579 124780588 412185328 843153800 857221311 990649297 671313090 318459862 42591851 657633200 166341316 810976404 680055350 270475239 341524486 829474557 514688883 838470818 103585844 284879910 209509941 811875569 346219748 896643363 799642963 594488594 624429982 251845227 285140914 144160499 590774054 832374786 160775844 415282885 590065609 509997136 824208225 674513112 71991447 546160123 490364019 783281934 823388520 74554213 747730029 608637058 973955109 634749926 777696883 324200142 333687814 931126357 323186008 330493566 771222334 410477700 725899970 734917155 189790475 639849480 342182648 177805432 48892324 804713353 97166521 206674457 705163865 490120337 527053309 473117323 337507985 562977563 912437119 176371964 831736288 970396353 332961652 9642914 882849289 496125003 995756863 323889080 335238948 596384529 396265565 832766440 923654289 882901245 191287037 483413158 331271197 332936542 955622141 751313691 264491581 432202571 845498848 522680178 267949129 629076826 647923124 794821970 765557040 915149414 43181307 333086388 225789746 284542908 821677877 498429978 73588584 712956483 422256071 109314732 276505265 604387494 479838855 145933485 352703629 289634288 462986648 678707332 715022563 784902938 987130187 135878011 447473049 675696320 223813681 980995422 479517052 421540203 327929968 410182748 103931608 401861730 775441273 857829908 457556545 813968853 850847977 932964843 290195996 4427414 166188828 804931157 584380294 717718969 365947925 732520601 76073140 542410752 204953783 462012631 323955865 223174596 443334738 7005218 209237300 740822601 343720509 510071401 549062084 944815638 971914301 41937019 136065612 168400045 981357449 20842720 142760126 545660742 745318127 247062502 893239259 364280417 469274236 221174486 331801736 171037196 96920954 935802793 941057528 70956913 328342797 983465062 657656291 582755513 42882161 893718516 483368303 386152796 183996843 234655409 684837675 696687816 558813627 872930459 836265145 405004330 76184045 226798684 135466586 165842735 239726222 754056045 718072776 408783387 713833552 656710769 540204530 93257419 445546168 393920368 845144240 888151193 280745063 606054133 46813867 977685795 734094176 872259515 370825281 610946317 593737123 126110254 541569687 842411857 744724110 496792786 135568603 712196520 446776180 195399168 17847905 740101888 618068930 506118144 910372313 124878271 747682115 761110194 437200994 743304196 574725929 425966216 377187980 97601321 924406873 495018442 652161493 569638945 941622895 434258977 130141125 839837624 584366007 349338837 141519657 478427752 255830259 394499991 436080253 250402972 13131766 396756113 609710441 716075153 224322425 239174970 656900369 491202620 150573415 461182896 612079603 685647179 215265678 988099867 415748059 283737135 392792779 716073498 227418353 638256556 693234533 138340517 192122002 454758865 984283098 377053976 74992597 692756012 129282773 570038515 945977468 100688898 53018994 404705461 920907193 984631663 621579461 346497158 157070099 732134209 119184421 241984552 723489647 966617606 259682841 596119164 796522370 183619626 344426802 238100668 306711190 274662313 901854075 89092867 369145193 912687108 977090913 433791965 681016362 998926166 997803542 958318396 236985472 407834736 923137813 745449066 86595509 329001895 9355909 742219960 647573084 203533927 321268225 432019302 175013001 181160732 557932519 760152681 12683547 433336238 327766417 557909962 257043335 727723069 257483636 971546888 501973465 535194901 413250458 594088109 274147818 486441290 541371854 638743889 333469348 728470233 796679011 297265747 678297952 688227198 636344697 768705891 351483087 418650347 210498597 432784411 14027644 800985755 324895999 44428241 888829035 748069296 500974654 676344041 151656256 502909545 473228798 937609306 319319029 565362450 208061513 624159867 942016443 22255460 466737231 735557634 130156709 239624966 725821160 392806178 474131591 4161552 337078902 951288549 807375968 620825595 316385715 139893939 558672305 20526401 473355733 528542602 247828662 532019825 788771013 29414877 458063310 803008403 903266811 813014288 346106491 946204577 292890874 642605136 126569467 151031308 696985607 981597183 311512806 342934743 19080881 499311661 453581886 249243346 657070838 631686610 533751212 733586978 25140722 614184878 918867338 477997586 656841782 798673166 368675350 725468468 369634370 243541314 709749864 136824286 820126379 417361874 791669556 510538876 160724199 153476093 282791568 909705488 244708223 88442466 313113548 57667677 48019523 597323788 722560730 205703717 213803716 853453654 208787989 83658833 292589677 503987520 117921139 390013718 103188576 389662193 782345459 777013202 370248266 687673809 883015686 409465904 998520034 628786893 978939857 616719267 91664472 430203607 159671758 108831985 679884384 347415986 80251445 244977165 261481843 607891638 663719795 205241224 836005976 416792861 980459972 603589476 440625 873628537 630983038 165285544 888098231 845917652 16664634 514944050 331823571 226468017 473739524 773570669 470404967 516337189 161072898 870932900 40261413 845226119 386598175 939129223 127824901 26441045 877023075 547134103 915507954 846401020 276659005 807295132 453737857 50976959 670282388 351669575 707804039 694141544 854095061 461031197 15635327 649312936 750803712 912615213 183922529 484577496 293511601 528797415 467965414 660347032 594677393 301082166 806163602 832216382 487970279 652869283 521349876 371554073 99208153 187197859 169609540 442596832 212121513 850899652 341470091 608053838 69801572 53821276 875712856 494084996 736173501 377595401 50572953 870979588 411492172 370601697 431840028 338077169 194455676 449560188 813250327 553191268 444016229 690183536 400801585 835143181 857691326 798315938 141366497 878703273 354990838 204779839 929675672 419308751 201633573 516490131 314750316 431840350 108415350 34030223 49960359 848682557 417022513 430468223 380368973 746931934 641593375 467949215 628792002 624959358 616568520 161675706 174697619 127013291 860907269 941813158 460067195 182080326 979083646 314997313 242364605 944232573 44459797 59056137 335344203 993710591 613509772 267894928 628435132 827645054 189402269 288683931 988296146 364179962 149808095 952874606 423396430 12531056 895334279 94820280 960691022 23640739 705781929 801601195 61740136 939375701 96386835 639131868 892880686 433004157 573458839 17636720 104987861 614189280 498438959 776474332 113935900 255475922 544137949 835784011 351867068 79111398 855406680 272654853 146511213 179186388 135737087 804924326 85297958 639584039 181196540 385917204 459201890 671969663 878200117 862067143 445312938 303451943 541742351 767001058 497788388 503131757 295471253 930291977 964874561 259123798 829772685 603409935 60334062 173590807 473143012 512879347 783052667 606519324 707779037 898097605 40216504 942559905 421717092 59236255 255891235 164982718 654120312 322176840 623585018 76150276 212041333 896793388 883127249 788889054 963352926 199734214 293336902 106759750 582037630 958525774 903057643 12274191 542682742 139518055 372453268 970200455 272509297 597330691 717529056 101833576 436109604 506737898 555238876 384174105 547455438 150344543 466328831 869548113 635678180 26663095 313892934 539288785 522779104 761678181 270060135 195124615 795695433 673909615 935452068 927275392 683910190 611893905 430570856 433049336 733712021 420138195 208317184 173542025 151445766 989828653 685061017 261139397 712497037 479718833 424211577 172762032 377232039 246612695 683555324 718320521 685230118 327846860 246118618 615238472 10814179 10616040 91583811 165643604 375050692 446033607 934190733 523915527 664728814 192240632 948721074 562540444 424018109 252649513 627963838 145954507 1084572 727678407 801059785 234012895 955251969 152763984 18150622 792534109 446583366 562802334 343097221 936175991 626387941 31575841 564737231 320132559 997693146 119123696 404887953 235106199 520849864 543684520 742561561 519165781 930367368 197478141 429627701 507658289 708168827 282476715 766767291 917900052 898143666 680492218 881794306 653520397 6173673 925561385 439533108 885758139 761979569 561387586 653544446 773694520 411789768 591366413 616867214 18027031 136260370 927852633 621721508 678201226 174367045 218689318 321592037 332935507 553749804 753839486 665381875 974958959 24892809 568240587 558776011 582800826 986932544 582950580 401658307 655274053 347725620 63694988 661183060 994089671 846725920 276406503 267929934 805938574 730777706 941445874 505567944 162987578 839690295 584866188 456503368 545377430 887056048 811179255 328221948 321076152 738420691 953191618 559784023 396478288 35787856 569794187 304949137 777317401 208143884 394369156 765068179 630677024 824414667 491273331 202274014 357658013 505040701 290691509 703408309 280481407 884581978 290865517 991559977 491178262 453922457 858622028 952557059 646386227 456956858 46745747 676480996 885136166 47990123 1099248 624959576 710854047 765660257 606538944 592690500 547203774 671387622 9852138 906843922 574348559 493819277 14971625 858081101 738223054 851562640 59150917 897671669 190302999 46213017 606253791 941893473 811526899 239235086 468382306 826626940 921023635 702928481 17106377 795549697 219550977 984759733 840815929 980208427 936492896 701534633 313241733 693862791 240022273 570545495 587660788 511149564 912502211 254916357 505703949 880404773 192367980 778421012 188026780 647986988 519355365 62135807 834844240 838186384 101087400 257592075 87542501 756613970 505315512 759260616 998272049 565925079 671769413 742880774 300033427 907986267 788718261 764506716 302414361 126482366 307338037 205300534 708307628 185097313 571529583 682960983 735700232 72575600 199319803 380489094 743185115 976467456 620429068 176438381 445899914 911643139 978296796 107674992 509151668 70718113 306771507 713457620 45670779 60159122 749983706 897844178 77554057 292988910 622797097 450225258 232013475 607638394 468739616 564533486 207815992 746225140 648002685 724198397 551138051 349514170 990241284 286112129 249757280 455115508 362166368 557589440 357878713 44243984 423382783 799430260 66875301 331108013 437556389 661977363 256370622 984328305 82631097 526240565 564062690 361876093 607991408 682541146 730309321 924113878 718986557 707921873 122719640 574717397 348297708 881006706 589181844 619028669 748069422 775406656 824815594 530010721 393232640 530910549 597757662 540001993 137380067 584341116 915396202 636485308 640412523 760305800 510810217 444201694 395481610 281087031 122815724 930090406 774122132 555901329 903459906 49580568 145270731 211858087 788471853 128226219 809982913 197882847 32544497 211410153 155813357 552402268 499867932 847927314 489502918 773417048 618242220 400104180 451730573 169905318 41038930 849673886 305376775 413850375 167430219 262835628 571711132 1434162 847175008 673031119 933157797 521192644 559463112 885022753 587596545 638922750 757765016 627909501 446364842 277544523 157819380 923579271 980844453 591416320 959945732 57379791 644676362 272989446 841235189 889530541 780035214 863540371 191431709 993550501 228438731 21791412 783134291 149953317 16398683 280066578 151115142 640966211 300936525 811182220 853645313 384064832 559714093 721172951 43035556 549273985 910194355 252479323 681940367 491078607 391318747 967533452 135289000 547666235 298096040 120804188 211773095 70079035 387012058 25772674 210028989 45330040 939677859 955233822 720777772 105896405 194472219 529487071 73788760 79956201 29956225 668668103 663812725 585094105 153601679 683743659 881768628 664836291 651722170 3736878 544220957 807096363 602707587 782650639 497919178 299123806 92365409 135969656 241963190 960171422 294012204 513653595 337237181 848338611 900853163 105976880 135104684 523830531 223759219 674590326 815977663 436453816 856514308 383553396 298202669 236571499 612559924 587476121 800601833 334823753 474863114 961706762 968791234 895021968 306301926 609877137 577784560 758330218 498762535 793499989 443112336 405887125 141986656 755490771 686725986 633807369 98392335 781232361 531175136 760832063 226446399 526146376 100561537 372166889 701030891 471027771 429537041 755384378 969879316 770461167 720339320 660052735 624250490 581569921 529265791 38766002 254660649 865117121 498399836 206570228 571822338 824198957 948511504 37236533 22074178 417655866 19411237 269156720 521948069 39910875 774328669 49555849 956331885 374757430 599181091 392170668 76042526 676158667 608382848 136275028 755580941 293533477 919459046 200402707 52467427 99456146 684173507 105710411 346022353 534544980 316611286 107474453 767066384 145628856 208991888 955928693 848031951 247665309 266724256 212890723 177196820 777845130 946117952 306112949 995712785 159989830 876485841 759053934 234653584 626693032 256799535 984522528 101119441 544591895 230615572 740727420 374204701 671145015 849415613 730198242 806796995 139304319 606118747 286695292 120439190 173964576 10696585 213891303 620173030 853092522 796589329 639755723 969058128 304795191 601679625 677727409 600774043 496461802 839410182 52556164 446593646 460473899 46667594 677477417 841357050 622220438 294451380 77267226 482893502 927365267 283765557 139042392 867237584 879804772 125459205 907178194 831027578 664160018 371745440 651880203 15006297 843395382 639470822 556382703 838095154 931687874 816972022 530859824 621653124 136839198 367093736 603376540 179257964 330943783 244662941 763494047 19689316 12759437 74124876 983917687 369309960 852031081 958510331 849738089 447015154 822548862 634105402 784439802 809540326 28311039 803843682 106505358 855552880 452207615 891851956 279540565 862850343 874453018 203627767 157365262 783950456 110224862 251310965 482475954 157300326 847772418 482362008 122588393 604037740 571299870 27814456 743645949 654332753 293104038 611946156 191243244 788146832 994261469 536566846 887542449 275805662 197027232 816036840 904545571 158834338 347502670 198083579 542978843 858174364 390894521 193731887 399397798 299285858 461781973 412182389 750472664 866480908 141263023 831480750 312328099 38519441 718934780 129732496 962015650 770174054 108392761 68762512 595969076 774511249 144680028 883129048 540681172 3009820 55161882 763625066 803206564 829788317 31255083 101111402 195253899 190786054 584268089 820866224 151946766 361845268 200057226 882650134 815624107 959672852 40349904 456050748 947057250 412008178 122770454 846901247 316513051 45997926 751736597 133485739 265974926 669539996 7564611 762443332 206273276 687457764 196893364 285792627 333812008 618097626 930175378 746054116 702544132 53058197 55894779 675241593 10930490 312427446 197876769 257552485 973384230 46995911 320884608 871339394 838977869 592759503 224506822 23751069 79363440 694815800 341526841 26123456 469263516 13876722 860746063 736605453 961673693 686879242 466805797 20176824 704358991 210714420 977609290 108887955 235361239 244035406 333814124 680387999 254813038 858832367 282619676 354978413 694530207 70880646 967721239 14509002 875770751 776860798 438599697 338400769 75448215 392613669 814879442 593236851 868987692 662882399 873498702 869065838 296758524 188708887 92980830 777965983 400708813 322207778 761216528 231839110 901098004 72331332 212944184 443736568 951836653 124776089 849407969 52502520 694993173 697633238 850054816 902314205 675516515 709058868 291754595 691248884 229354376 643113688 620737047 57299687 644036574 198262723 669367214 717878321 198332727 123925511 428756244 774090268 410845867 434845793 963778113 431001652 287583161 80082770 438436109 389939189 808163619 414824451 822575614 707385322 11966358 163841012 228318873 76494581 364080754 336943786 761928863 94504122 653973419 591881212 600229943 476028836 50130044 936331418 489848908 272955315 323970739 281288639 863379603 711407947 800021521 976657914 234277995 407528956 923825114 673673922 506251546 375347558 861215902 268500611 326763877 444497536 917185516 820367595 645888146 876048060 772351790 114817104 754428424 474675198 450168143 161081086 809186843 808236401 246357977 603905773 861094643 204468784 715360183 908413036 848974107 824097022 976147516 263149957 917541913 12912116 653951501 975497260 142944044 296919322 702919702 139729756 134296136 135649183 638651639 200960518 822092459 632899019 677936144 391732546 395876905 737502065 694436632 724753622 718425331 30434908 901324429 359904450 895556440 889962016 30508691 639213696 943871847 165981366 167715589 365362594 884142832 949070085 426717118 402704064 158959544 31550945 139011162 616190549 57039591 346723834 522025757 472903499 516400248 976270493 205670191 761632152 470313260 292456496 763536227 604148044 814682473 338671910 427160780 203952174 975340207 935721652 289237252 493505083 284510123 592808619 222239225 8300852 68906187 461871556 392754858 485133780 507146010 880356196 444315554 469825443 5963407 68086527 248134023 498496792 209640805 630594914 892507429 786522183 541297119 467224945 638562301 19342716 587976122 24774055 253678238 912030415 615961442 94249991 589612088 461077812 550084908 175663314 339182337 611134858 673784518 823162938 850545432 610176280 969671667 735710866 262654469 966355633 66119524 496136811 667279842 896105502 238277005 113611754 249965422 665607255 352053472 39779572 253261724 494005508 909564687 973213183 413832516 395294074 722870080 360869185 879417825 136768905 667630400 179928953 168110488 437558672 273689753 171254339 565630492 757288309 731618431 704287401 204847243 423881176 999319252 550787079 27402891 652103685 766434311 369095508 596626004 704586063 891290474 955810141 595863201 248438969 325076208 937338159 985826298 682404337 819722633 652855903 746668209 346358886 267543906 604063645 267410902 541349226 8427163 48322380 837361894 285830344 900314700 831169495 537816355 723879497 578348801 566524597 840633659 55236863 963903702 519237516 92038628 529642831 402536747 716569080 270060819 416772841 933477548 406224370 176070794 776976757 213535551 636990532 401138121 645545479 833941239 215826689 57084075 354226837 333423207 583726180 850950910 862853755 113297468 287652170 974640294 358937852 253892302 451824411 190972675 887890463 543042682 612338616 267401855 74733115 388667772 557182360 743599769 329922042 496606535 503369132 452739118 707829338 279017911 413982189 260948001 32012814 230636143 270774689 213178801 659285389 595962049 494747749 915937314 444732424 581311653 231855738 544227255 390839240 952564260 364342945 209135870 986761594 914226800 697942819 37788988 621952495 588265651 778032575 103108127 47538912 538428277 247820045 457981748 953807325 841214961 188822961 930724679 404624660 997248383 936760006 175229266 654875203 96955938 828273384 515060449 979235381 295327996 574415830 12438882 238381556 879357423 211879187 73917192 540494322 313780224 788568338 621634262 665733113 268500242 480285782 938124514 184358392 821462454 351468284 81413659 73541059 761491360 113439619 857410834 230327563 349011 135083739 331834986 847077346 128388242 502972504 735088878 551565446 236815497 876198922 508887280 312073028 760134099 161527242 383509381 825825 753237251 251177391 490780318 598985975 410341880 339631802 257592104 335079266 268458228 319203333 455110483 523017006 638490520 718763051 512714155 387519661 816538296 22507387 514727224 889052291 467802076 227675768 57381330 53687924 385698555 264733134 859121531 81092823 470158769 301790168 687389374 104748525 424967137 631636882 470367212 764858029 706544172 621836177 83185849 881306815 83319122 122581582 557219126 451286590 94666912 45410549 274749596 539712627 682559820 801030677 894473815 497013990 394868869 431016675 620162068 265199629 125160284 115702203 686806032 398009309 282967211 930714005 640731261 783527762 254891904 725092696 727651269 140548542 667955098 154548815 77534171 578258765 761348175 589413401 292184521 670816818 856863847 588514019 666349551 487183111 496546112 871052771 192297257 66366718 706458305 27945086 552999312 147603707 958333411 234939912 350993849 476766176 192369143 33606937 192235023 237296496 290293983 650345603 929015162 743329488 853978638 99344826 318533468 24173105 377687462 149291578 908279382 292652570 333039534 978538456 726590387 288001958 636708839 70782687 579450308 404438356 347914551 798718107 726018966 782625352 546940564 478300393 921124043 935596600 497325035 443539128 682560773 614373632 201579556 149962154 844091236 409412472 58116132 914029465 287212920 744469397 851750804 913717440 415810557 936688376 757734646 201545161 105029004 317071845 59966964 453123373 256769046 944451612 825527959 655914540 273652553 609563556 333864017 385483719 949278530 879001830 198503940 127352134 696197740 688122613 932267307 507172291 811514346 765923932 684117331 301953397 653507835 736794595 24529072 234855526 964300199 670403145 876004369 260216325 642298369 678988901 768697909 869220163 930955332 992442051 765710423 979609619 796421060 771185097 969264025 914083468 735566830 969379795 501189605 425834402 797489914 227606950 630378470 863325822 110458905 135993742 563904632 121691425 411150647 694831231 56457472 159291316 960111488 319115091 134897518 470123595 937066196 924080716 908883421 848873011 695681438 39271897 482971461 22849303 243433506 378166372 390865319 589736690 128727581 635262415 772055048 258452470 393582041 958529342 286178326 966736104 941457978 690196248 659175092 789466101 640475635 127175051 709048732 794512663 209142143 700429075 939341817 171265998 512456382 27098105 747494377 324394998 631469265 847887479 829342198 33916794 749672011 19258503 588614028 604793112 418488432 220022108 209087898 259079578 653609779 415361049 866450981 470541320 978462834 865512032 970231709 16497380 585887754 417452647 172020975 493969822 829372985 288501035 316918816 97711221 234732659 250084257 241747162 704293330 636687844 777172354 881632498 399459287 75948603 297740207 283397797 616941872 316592500 172221334 978192742 518809116 3787298 190867488 536866384 872972563 949633335 698693457 48748291 945474229 712252064 415505138 73274203 383343651 875028054 116780445 827818084 342513067 935253614 867105127 204789932 363282496 695298189 754513527 101504778 683645534 432830389 264970975 374750756 727727900 791284087 12145992 235565533 296660721 966342201 162421613 806048211 287082228 948296756 388792441 200095548 688828248 625488597 779429960 928702084 612527995 425024774 557690564 902513132 749768844 963472913 377973693 845932276 579919760 498299849 384033198 27491506 608244636 114958942 955018199 321979836 696086166 999534190 323498404 825735426 790703055 777803373 941506222 424238161 879807397 72779620 810914283 536344078 655037458 463722452 266910317 434801269 157612277 375036660 867441878 253905865 517422022 539155660 500593118 474361427 545536637 133426928 577915130 211391677 153905073 16687179 497445317 294921741 64417052 621966440 900129600 438669597 301200536 103583131 624272890 235135397 57553422 513977690 296271885 894256596 301791127 450024110 619750283 560449726 124055132 498240846 500411689 872393893 866283403 537636852 605155920 17814805 598550758 985803230 740814841 402449764 297192886 704479388 783052443 516310412 721609258 628579995 965733145 237551327 998034222 771790411 636908911 94785043 466147240 813156478 759849550 8846580 241703033 731078944 499360347 83823772 656626991 316100366 101182050 843403181 256392963 344014402 402007468 60247852 733385999 97020190 12807426 582808909 966707820 984571517 95404092 596167972 26921520 81879868 109624038 145994817 108063733 686062939 782981917 151362398 504378810 537469302 911797587 650897570 274363744 572156776 406830937 745490046 598741170 583838861 958753810 975176887 122106101 763303362 852033724 900408496 92411621 149906902 953576130 539487223 973925781 532196239 906039882 1408001 685353475 16943362 450784909 250434074 898981413 61819508 362434876 899955950 375632418 664108571 638299844 436437610 996219229 529413114 870463606 255646356 359004634 30503149 445789573 453113369 53107009 141919524 7679895 619019977 238876494 667736670 133937846 647790270 607892455 245448015 528674527 774113 123721407 831457130 92497183 73431858 591057379 704667582 693653361 122186700 542633200 748950784 9501072 719963815 507886987 573358597 845880358 770269844 652769593 169371707 250965570 234990554 714877587 859149546 561483940 951242215 78283045 186279928 854202663 422847935 314029491 450585041 676104931 454791607 626359055 643827703 681289159 267102118 428261659 127201127 756512976 20950019 569800058 51886364 174273446 493770863 694509462 33197507 922512935 984583009 639874426 456798738 52074149 646643188 455516060 531321707 215351748 166290807 82127025 275655779 347066063 365083565 426541852 980105666 192760854 508054180 58617425 189614001 277205852 566765803 529750064 869449210 716681137 503025556 669152721 84176541 293132610 215571869 482506045 187593299 777180090 466008389 565863333 240001086 478048357 201680936 98220 480092780 532268140 180303884 409847106 825260089 186247626 731723404 737125467 679888458 458827230 57156976 824347262 172830015 649963958 735901088 759022803 780151855 725678404 50371833 806361961 324449882 119007801 669252335 331910534 174731416 230961642 402273313 497753507 980218413 494776359 530058425 54133326 930482394 617404440 618257012 422817202 516878272 353600654 665789582 578109699 224710701 818972874 626313082 397218184 830103098 796568520 510651515 645835164 468680380 854947498 383734945 838921953 478910284 298329937 205763176 789184978 969054617 758110547 470408485 885965258 808597713 147991959 592105082 853197283 558691491 273580240 255297693 105833546 92209835 122838960 124832685 888061506 287306376 36565032 197544800 944180121 728129951 711164601 746862950 972445991 713346146 958837307 60122291 674651157 945963572 171913632 135562535 897916564 560567304 705776490 759563105 615024072 462625418 943631129 74067770 349518199 791595660 845989353 756252758 838211220 139057987 642985040 443525743 247383538 394675024 670856458 343653275 558917107 601232639 547999370 134888928 491084031 3092874 74269345 42940203 494619664 930652983 961388234 91645043 161211492 491567640 334932106 75742614 513979474 285358137 63698029 236755603 895356402 848909938 324565717 167224567 25387229 580131453 50159936 118731103 284764555 920268389 913901027 834390143 383144639 939732011 502321783 103668113 124542197 106646025 573984043 278607236 553005760 384890680 501995484 65583601 211897732 271464620 38153027 597890190 771184160 729174282 831561444 387363181 562057664 305582149 452392955 2379710 391347472 944379208 29370269 104625354 394078994 437241622 225629672 725127169 580949695 294953149 917611709 150288315 366919576 957923359 98132322 695114515 175467377 667423829 568668758 25646551 95039717 994808357 968114216 565180470 310405652 215359459 18390751 692212099 503982369 858726604 33201819 318258210 252216957 74001955 133661815 455522366 651987700 512565865 123799934 744632632 734342970 266046486 730793726 291848041 905454445 425758768 875268646 787754046 460122173 793246269 814354511 20352219 414114943 297622821 875085052 402118380 72888369 129551840 99148282 694701031 640943807 516997145 771074030 675106358 924954170 588066871 658843069 535138512 626433354 383803245 647297107 250480102 429380048 552779798 278965845 186345628 530708783 35192482 494169972 527121184 847630548 335087452 170587917 610462448 612345819 176817390 599457997 691497431 234405539 164298302 872060300 665099557 790567568 712307451 353404428 677057595 751593497 812406360 342590677 997572603 786877435 326403186 901887097 946607660 361487218 560839086 741886814 429721223 382405578 785224895 906106233 218223600 322808894 256612530 168390684 648910279 792670141 721073619 37529014 374947662 408344816 261422425 246283159 258621543 152214415 137743807 176368796 358008320 958937647 60545646 194095515 828785143 814144949 686098836 922660769 922283520 509092110 697795399 783831281 183734202 789075067 925091004 551058626 915277335 669846407 528400098 919879813 743404754 158996358 605661414 316192779 638595141 594661684 400863540 29045035 589497088 975410004 328729753 514504428 220323916 668193600 545063354 688869660 961955570 800978367 35014876 643845851 539492944 353265615 995156662 878288461 995217263 473777417 993430158 485741126 298029112 771198897 978857369 948997648 211119342 497277489 887068734 461898466 529643166 896528714 772623544 425356013 752269980 471091498 673135521 673105139 83383396 316742556 125913254 411773637 404946169 318456855 486688076 460052981 463676040 881662379 875517226 649722855 587774662 276936055 453427727 175830160 52463786 656795585 735329749 415015655 652321120 122365335 60061854 988078915 596622946 790445058 208480950 200802475 18242602 577483501 577313658 217525185 609498287 883595697 850959001 782877523 452162849 3504139 666580442 580424489 936517950 831920062 994177040 877653252 653817512 890964310 919890121 369536902 315850144 220122451 913473211 642969230 421452032 605193340 225717900 367925147 131526080 173582975 228386775 583164721 481765008 653078292 373523806 434606035 401301580 491196229 424850322 304678544 183199930 519362523 910425210 32097877 861293311 325312699 367159851 569847337 136751553 739967277 901623619 454784370 646121327 955827880 841399792 28864958 209646901 990380712 101459558 544211352 831334093 376773864 720074479 590489348 947342988 528094346 546184165 479769460 6716658 586613326 992504131 164418685 699077404 674239530 265376301 450510985 449694181 58744235 530389398 310762880 530281965 170984627 229149320 64990325 901301117 201394162 41336432 545522874 614493217 532283218 127650708 327947696 958245944 902974919 542605625 27746567 651331467 688106832 311779773 773444516 722456135 114554491 814843964 475299510 255693269 16139533 353710954 359698571 477674951 880007676 746877226 438186539 25913324 477370153 924575159 407729692 972412698 148197249 993580282 220227346 723380181 632500166 810385969 86643480 596535535 824470947 974364091 688233963 673700871 434018424 215069650 336202631 116447175 676472747 1493909 643429489 336510423 965023905 597130061 381868440 792298825 243228421 643862579 21654096 160103735 855251032 164300891 457119953 567659493 144915391 362367981 632179912 324931214 162158952 979984559 930761337 765618460 838291794 459754096 877470628 272001404 564103370 380744756 788308976 919416086 117762465 310781059 801279184 906658281 70472393 997661374 659255286 858087630 996685716 334977460 873346570 192919605 417783407 37024139 456957480 664697134 854977959 867857114 92788603 332740715 956323880 191005288 816062978 293644237 136615515 231417465 90395083 275440573 850059618 524084629 834467088 828317680 300270561 784626665 461988640 173344755 247395381 364880565 547236 603697841 116036794 992323785 691103566 449875574 590033801 600935690 772351771 236259781 706809725 155199974 181866887 690608212 493878654 239249083 301931413 901880168 835651917 112126112 657774501 543308863 315792881 813433278 189552304 344368877 120114178 256195659 880908408 574433577 510015098 37958019 755945508 769032537 526089586 256840939 920258892 842365799 37584042 662326745 898363187 669319233 399277739 497710199 657126674 945439872 308891652 524760558 448342789 446145738 847941626 764580952 507386531 887753932 699050857 479043250 261079761 743512094 223451344 976644610 961373712 166603676 434590249 199242011 32760434 722587232 831309823 144848784 550530216 618020936 904912339 652627087 161156787 937061956 932772938 473237393 79506672 595078698 953067018 428999230 96572515 288624224 288899951 593331936 730228236 102673148 928306484 32781058 30955133 697003198 61248811 900835516 140243860 201643902 824123620 838461207 17059950 816886980 68676403 719646257 97383097 991810343 261010816 445137769 733605382 527908514 739781696 118638339 721094506 79693374 254170386 712447063 192323247 854022402 894279161 132031536 193694728 421874489 358208175 643747988 633139218 513575886 213932084 482806554 647860988 274148502 253543865 169717368 326739398 626919439 577339074 366389609 607090525 172751746 997108854 153872654 595001929 65623413 653526252 709414325 603626360 333656639 906943780 314741188 307490747 449604320 682709395 889494418 714924467 852644555 230204173 317159661 652044822 385606743 154248197 83030492 271616727 435093834 923427834 791886573 938928678 110799328 799442038 552508437 870845795 609424779 747189960 563684966 898281821 57429086 172803705 889234151 257806242 152170123 397292620 763961236 303955333 442868269 19780634 248679758 207768310 961612879 796061535 333602063 629755049 624277512 755063266 795226879 797274446 555489224 95134906 175969214 648073126 219006754 731241201 209635721 272384538 712217402 315436947 240598893 532813195 513734628 984343460 51784206 404490758 962454463 81167505 322263842 626899512 263315042 873621586 213603070 369607206 965257809 700822656 869582596 710002550 370827139 622651212 764602864 213013480 191951865 641452542 468519886 17484295 973001448 320304302 127256490 594969842 738023304 853902131 876390722 771478363 729799003 508682027 27387365 398435155 731204153 521011105 792614047 327583075 133898887 803398864 933294002 249323525 778827122 836024960 379679654 108793633 14580940 809119859 578905207 607236074 127775412 883256165 97756159 963178119 435874987 650085650 628610619 796401467 99591667 535174813 47811271 853591707 819049472 236057807 488667809 849048245 929055949 172848190 96044575 513788285 91907961 104618420 434653233 285733539 687005640 376424196 681190924 314062250 521429665 113913696 271674284 753977325 541828530 682144391 241724774 646953753 770926366 689451749 314916945 39652948 973068323 966948187 140787640 416545673 97546864 68973466 574188643 115364384 91853062 555477984 252067362 763480336 795828746 222902644 9357329 918793774 792991776 676438120 92678796 112384251 680058020 268439041 933567332 552290521 256367934 565491739 339872203 311461781 669120334 284018851 930801268 502360861 611698334 463770681 204166397 202999871 135939170 337646676 604844131 880987161 101205471 986358996 845369342 950400408 83176058 789147717 53504374 566779754 432750402 47241253 104291020 403953882 804940984 644414413 211708434 540182111 275993951 850441565 870733703 98820695 564461874 270675427 280306427 949023370 897131968 980115034 742388206 331043774 231725846 968567598 394285915 575273316 152524885 426600758 735492827 821040741 104151331 226332557 810356763 748095844 225305954 264341047 192289662 126388157 344326001 267285972 768726260 253603709 721583425 54489283 824957730 513234299 682744115 191594150 268113227 352125411 826107233 506406344 933242214 236385884 161132464 471589968 250909678 908281709 981469823 31703089 432402918 739814661 732026758 300827758 398032414 216318693 923944053 988818716 572380677 932810829 585717350 318064172 972931153 55498294 156875459 423585431 358109232 569748587 264794529 131344820 359424800 562400096 448205829 315334405 866465812 336985508 546104787 909263409 379214909 905536001 246510471 798091374 253285732 271093787 736569070 926834250 836971915 750225697 96317152 890129621 231351703 948869395 485945361 933977691 681261462 5764697 941488479 267444469 995040023 919126881 361840062 555582825 633676374 550762278 627223638 468070339 43650844 985941239 120489931 329271629 300273590 823385993 969455193 885202428 329250575 118155995 791075448 726835148 578744769 987393656 405120746 725093775 701294699 664187799 636564248 941990421 489380662 168001794 212304648 228858878 502275566 880012425 799053955 189812023 755694082 408258787 352302117 757952175 45817678 586703678 72836188 391267440 659971682 414441340 350277368 880500548 5598546 190770720 192539277 591230347 880608737 411661053 30216002 534327041 443365189 955171939 714942900 853442021 909834578 627961441 337852696 76310862 77820022 354119365 934503895 53830596 410808580 590875281 289299419 748126487 542791144 878515330 461667929 820674272 705118331 967437772 802130545 79723351 881372386 612734159 183872713 282174943 241974141 942366466 862503760 807691575 885602541 79419485 510889946 491978458 480971714 553173786 738074413 106260925 177053716 319266400 415497804 647990512 324060212 78415714 837208503 452509249 625963096 967384706 871693134 559551925 796505870 872759612 570090265 545902413 123920639 971517407 254221656 602959161 160621865 577288897 523275052 700022712 519037614 686945536 203390297 227344765 495713185 131016233 795052709 723886574 877455524 874437517 304806667 979656754 961997633 490889447 501953158 872002916 846867160 631845462 388237597 901993021 518301163 732037638 497161464 982371884 856130669 455712238 49768765 107994515 156034736 902914356 467213195 903473333 280280469 749834424 487030396 678341319 834119794 837542222 982587959 724703703 32002713 957381700 691569191 340311448 999240018 299013303 870738853 449749894 785037897 774514638 299851922 197578406 133229656 733157889 700496542 860341661 496837674 678663332 685076044 712135318 141162334 474974865 801568489 390847419 499688150 330250207 841781789 46937858 782477890 12093482 9485556 595524872 163815139 112343135 2447168 388182285 547782261 266463330 518799031 786987242 47244817 785194789 942548423 3110697 803887860 143229241 726903358 619256675 576317788 463828885 882717036 970692182 896284485 645415859 954061195 694755782 117196396 581675906 893326525 152508962 463123759 237537877 441653286 946003989 354550619 782362805 341453988 928314761 810316224 499613709 544425525 346628205 238900718 284722792 494987925 333036680 513309065 26986441 988359000 390483173 433198042 771111236 901022634 234723712 683765782 619110043 56591406 835793072 409337754 58454217 604896198 726614281 468440661 371344438 80952088 636366889 982250905 975625113 259176972 164207134 483935544 31074721 964350596 591442760 503032303 434501372 254686645 627123346 683620985 247316512 85641785 572817439 345500450 434546960 488828987 955089262 715517845 197111399 144102065 209576637 796614193 831085655 983982207 415475731 464854887 128328914 966856192 946804071 512845166 369608237 846039013 555279228 333352695 79063326 524507104 197222355 189070017 961140811 512395010 50429734 666545940 343288320 260215708 321006184 862596879 717879063 211809047 418939205 231087362 550111070 52489927 382209601 879851682 979213541 315968830 819130481 598186851 258390011 686330659 908903885 38913701 96669293 275671172 857649608 481702849 255826470 892092120 604894026 823623402 82955204 95103608 643114726 415789550 166826472 342643212 801179040 932164901 541307003 728959575 485914397 637914104 544467320 505943986 75517557 826964288 928789913 859545592 470531760 137252965 175468635 358398943 784146209 695954017 115572202 108668615 142371003 649261792 241930458 274448575 753713504 972203122 901653814 896729 715290693 47530305 165538104 346578115 480019852 121621625 271381660 548473952 659666368 132667789 910878073 717251760 901389589 886400409 905068500 703358179 165895684 539931329 495297811 677032385 122415897 657757144 500837777 2360479 715062257 79488548 356606219 510627402 376428282 7210067 315219471 749631709 350849871 303340085 227837192 353729985 17923340 436799214 559331182 492702108 893062862 757866682 81611508 773132980 629508357 347865635 866066397 802614277 417405808 103116376 732967216 729204759 807753236 712909208 175778295 359073708 896628271 997493875 63628096 718055346 820396914 418044638 488958073 411460677 541729986 960435317 903588230 491956348 119633159 418253849 652319427 576047074 827030012 778291228 56508469 265167349 292830188 955232715 943867501 172360799 635882804 534154833 452062441 428166935 584700723 483200912 746438448 232691071 11141350 628708154 234270382 367153141 777503222 192980764 159364895 998708804 209120695 876967341 896322783 763796162 970189510 798882516 301249600 623830181 271242678 303563382 524743529 50917188 794001872 942196453 845995324 477031977 400386213 715371974 328025953 422855796 214552709 291074398 639022579 363019798 70757691 925001980 387096918 469035041 911235953 409481177 55731898 720451708 681921963 883912212 360602741 650996135 317418981 290678479 497014696 392760206 349902277 301689587 925252996 784147473 560940704 22698844 765042083 576572868 451366957 964127186 978714950 981276810 939770940 453500367 363418949 719676059 749550901 690627086 122112816 771720734 164730052 59677563 673566480 298762104 797150047 669863484 140471699 535085813 319833879 918625987 623951808 903412238 903972490 621024072 616777577 483846864 551460665 276668965 308519952 535003475 408043088 809039026 883981334 312018870 284867640 902066350 586851616 624038392 121399330 43923688 430935431 45564300 244929397 147375363 328924085 215794065 95206007 817834303 858409573 663715612 205385474 640260807 665605701 940434067 960957812 270540538 898188768 144452569 801344161 641588878 835474580 349443120 764340661 654733382 451977266 242212080 777824950 352946729 215917738 621577658 145117054 339287708 296108327 536004220 429909333 613612789 823009178 850091529 397779582 653654132 280583097 911583856 339617629 301870650 442434918 704483699 283948667 174231638 942430893 685972092 143555790 433656945 45837596 235870534 63232343 251471533 352284525 281137081 589148297 630024107 174246090 310986014 498324769 426531288 891925924 621777732 532670146 567917002 117901555 666344277 377312961 539974290 681216402 482028350 420598190 314014278 188263769 70937227 83933961 599112141 594395188 147844085 319343488 333508363 415189605 881509650 369668277 582548454 975872024 16660998 172111339 743036831 4588761 545633701 855618224 42758213 259977339 836288189 545818822 502322656 817976498 848554749 845239815 782202201 389643829 244277260 504867543 69329061 532765628 137891112 612570760 306920 397818666 19278928 851692923 953348639 244347956 276943768 245793489 754186886 778023530 345461478 986370850 760104128 353166528 20224529 231383418 45705636 37575323 399977176 944898639 817812038 212617947 226677753 942534319 543972845 609599047 146605902 979596559 387479889 870908933 339151585 142054313 366799312 164548737 473477094 161421342 26684084 600753209 145021661 355662824 98338707 675819748 691471109 954175297 80596408 658898737 229135134 787095285 293319147 205825714 325085836 380149937 654105577 52038779 621528587 359875117 810451541 164401837 631167876 906370238 143200423 867938002 7996448 476063647 540231334 807936610 927141776 643339237 830297386 921319555 516872038 2168774 564939960 557044006 361683572 506019356 402374344 54037222 890270639 22345999 722933025 449670831 965282759 30344079 273707098 859989332 20945815 793738582 992610054 392603657 790485185 587180782 586369206 856284693 595298620 283757180 458871572 417157031 204538934 600574905 588770340 499841547 336046941 676588819 268910131 224755110 777906406 846845348 109645328 47076940 110377459 49470110 679178911 241501666 459991660 34553095 786720823 675536010 291284539 273079878 668899456 470758237 424021820 273111761 43463130 269552407 131867576 444899625 704886053 815576248 932238513 538102615 304510251 875043471 696109340 723801145 603469925 507066295 288983880 404169479 577540383 976826021 518634560 899675701 420101622 298683166 703981197 401482072 143574716 599992036 443289963 848967597 167309789 948828151 458949473 935867569 265521598 131989971 514745214 266202710 452418322 124928015 473031134 270752717 552495252 816065256 423674743 952914192 106065853 764394969 272742166 272013493 372279910 793916889 656885445 297388478 194693261 32027820 383405649 558492476 856589793 607976253 726841602 696387647 686145948 985688142 940254672 638708737 574468480 596772881 887239141 557898312 945936608 125650401 532509551 458482235 56770079 352090494 101295533 884098103 293658566 454641070 561188660 886132184 385474870 32393194 73141452 90413779 111890711 770473319 833337956 659101497 261620518 684204285 317846006 189549049 695954191 205597237 479838431 658382313 672434886 158413433 127261031 775790682 701957459 896703804 272229138 161038888 430587225 304017207 551000189 177507051 163199803 45925948 803652909 487240874 459671644 278180097 81789188 99704877 700197143 726082516 182003736 858290127 671780632 121021231 808129837 891730673 379466246 428402899 627165317 209389896 584401696 670380493 825062845 893442096 287273445 811061824 954847292 416203441 81675819 161181499 548238391 106790709 505193357 1736374 477445166 233778600 597733496 408464116 835430859 833597860 47738039 510629289 779027877 705552511 409224102 137152191 372527902 273208813 420909488 768380552 150608362 102091118 464097797 988047432 10896066 432213926 727505183 623686431 97373482 799530598 609827513 3250521 352960701 256546294 935432000 19656592 735900050 404277561 621041520 232191138 720483652 73854813 296751132 161225617 791633348 133103981 258881449 525256657 264241443 53800039 920580147 315327831 539939147 793201172 854650069 381912120 873621486 438180012 528540517 734554527 539770343 789339010 65140137 766145133 642815104 930949295 500510800 860328397 263720746 216703791 42827043 675915206 856640326 525868422 755945090 192251343 538730017 125707967 399972346 606934232 728936155 729301483 793249739 315955241 915404929 371371290 604339500 275816644 824955936 121617798 575287062 433672773 252412177 934033783 524002086 286266882 112039581 928444454 689875072 568218680 960372911 655218954 562360766 827437375 276584658 235497049 98462177 168989956 620134648 715540822 690084360 633357757 506261445 924056843 858704931 307209437 521898121 750990405 977579098 117138815 982351094 225575530 500431713 579290638 412613883 860961435 559405456 677996847 143536111 232354926 930862255 318320722 952032757 898151850 606900893 766777548 993524663 3020885 79980059 81867042 848213939 254081710 667764754 344269474 426154671 736631644 44333435 288074399 933880509 419578576 1754453 394344061 728911405 68096337 444980875 925106881 606824858 644001364 30430578 385205716 415777463 282081765 537058201 605338466 792611069 42240277 810429039 153796680 828994534 723972510 206254103 944970591 50823459 210039048 728476170 880938235 165481276 689179576 76669790 67936066 186306669 73620957 497680448 87095135 268617079 245660777 114126856 702589835 757776524 351684486 700998943 671555795 328799769 221196709 511537675 778112373 297735837 208610015 79028964 513866096 480085037 201708828 960475202 101537631 108480236 395954762 232864934 998778504 100805803 530474850 51570162 843692505 673863804 793694979 262631177 425184887 168333800 788168687 372100576 118015430 623606861 830709914 726533077 232353117 694907351 671941866 837093661 264642957 332451918 96983781 940107399 995037302 591209529 458077721 891688833 239712678 180341792 634526685 302818320 232970057 248589602 530647016 498487992 171285512 779560963 654663437 981286517 397995044 765268754 463289529 178945768 956739008 681437686 238030674 931666512 711787938 879129305 183932784 183412677 967244379 728956675 327657993 827411230 939923001 651273205 842091506 68887832 775889160 747990669 956712088 654760835 401968769 717310562 872756773 287721611 976289514 10879275 15596998 41658615 868539597 815061931 232929367 460197748 859667849 907382851 392533696 255848516 669328606 997456747 25877200 392997140 27347836 813916728 10369658 809882650 827785533 593014922 93061097 219265631 950653517 201915299 953329891 492739831 789949134 383762716 223687756 790746816 454779051 597882083 204019537 787207201 164710948 7167036 269168782 716627789 61004553 858079763 6911648 424717867 448505640 197007606 747486637 976258309 871089313 617040335 461053617 344200516 71648998 400108997 186763646 367702897 175582021 260532887 845467004 336169004 282080649 945948384 676263820 913664082 336285878 372260720 389410042 218898658 178476372 928908634 565146975 360410492 968946238 479228569 211356669 349333374 885439426 776547546 144586741 631561279 3163284 373604210 832830488 230620423 904032303 68207650 564723452 275459833 511697758 680312444 31174998 988856317 127469936 146294964 687643036 923026178 358366665 152167780 267366837 265931537 899942085 93971936 60122468 607110947 739860562 875591336 907744201 490200015 346932790 593098403 379933221 798431866 568447624 944212353 595333289 451251155 423441968 893094299 582539892 719809488 722922080 372103355 968130662 413709191 820464014 73791639 151340338 953699136 941705574 691153707 771666567 555253074 573058168 631687359 511914452 259402353 585214944 552162272 236467101 520961600 472581465 292848355 525640327 125704270 209255294 774918813 190132264 751751444 524201498 450587918 48744319 948183659 783233228 866037925 576458313 47594817 775302974 23823196 512339712 992692704 483620468 574879000 575758938 560076910 961357268 803383999 50630261 939954349 867459567 490778189 891328614 304200595 815330695 54523191 894859225 386796549 719274643 214039006 490413166 958164501 779077408 980349632 191269623 296294926 998611846 718918670 888450826 338178375 649195237 133452127 887960814 921580625 726556761 172106987 65471181 847394851 712769382 134684260 486522849 803152438 905820791 190482182 832643084 962396098 556130320 979892975 184086304 263739706 175791862 482267740 276692100 198006387 986460994 870266319 476794985 538408421 785718328 434994403 468012046 316013416 495633490 504307704 948717855 840574763 563987176 906941107 943984364 459804641 592573170 40243940 460659747 60000344 685175795 998692133 154966077 971867988 136100483 556965009 957355428 272912135 703651801 474960921 644159683 146435630 615817991 628396251 775279645 853566840 109648921 881516596 483030237 523683180 722967506 158745453 495772924 962647001 672227862 793825424 139633273 571836501 655742872 830636639 204933141 655391407 181391841 62230335 953312740 850807641 76098690 562006580 551299956 112164625 395100413 480774909 581078217 895102587 191044286 708834114 887932543 716244783 715948996 709414868 457798757 439198770 671572482 544413730 510796384 591043118 759123018 141785263 741890734 714545630 449428310 425343437 969106886 123142629 865070136 507104402 345435952 784395608 259965314 701058388 478878403 768317590 530080403 865676980 26366594 264403069 256897769 781757751 335600844 661885085 376509867 933664707 742302727 47693246 473893790 365452899 632108026 405898910 218417500 414161030 517375024 576489840 833103871 610489093 79885280 909536365 158883916 282098943 226707253 522231015 939059876 253140530 629379659 478287545 705478540 133841145 261527707 232945868 627616523 841324745 223830588 889118596 584032711 88276527 964910486 768099919 490474487 599205285 913847600 568727075 495562329 425365697 567406075 305732237 373796914 939151651 403927024 142268694 342205221 271039388 485762276 143753412 337897363 238461782 376523455 954635939 998066783 358289926 355266567 232598471 843374351 136198751 642935410 952123445 430934676 833423913 466922020 128776538 469660506 452397743 106355532 20292613 30498947 316680938 205188066 644075179 199808568 975701730 36871749 655483679 271591411 976966316 840762566 327932878 645899019 736884500 760444415 356271887 832619896 677190581 890711086 655098679 936512583 272002441 353453864 978728386 667277289 429815582 58707139 76149123 193993643 972703874 159568611 675404713 986691526 932848861 653901657 982206601 640739850 565318388 356871847 158124333 834873827 713827502 650747266 162172013 867612402 161921029 763682780 646503643 370155187 554021891 264807380 182689129 245679333 577128311 708443699 294054577 72306912 982164362 560767656 618616394 755181977 699683974 515461526 278729285 579233434 561275913 760598021 70934383 463743362 600051513 369077600 628692791 448590489 408320184 250547759 882937229 455716806 375555505 527850505 307511773 437875886 782702979 245950652 994316021 151255957 511404892 422516235 715859975 344519874 510835 513366715 324626472 51125915 456909643 664016273 587101542 646487800 992963922 615207169 512327839 177911384 255880218 713354403 188099720 141601880 138368934 593544730 713283371 426824697 119911608 630239756 287307096 954664586 32314303 92046276 106423456 898738269 424516470 556914493 375234978 195502762 925410291 699756340 70838430 626246612 881478240 597560322 231623974 277754828 771174103 620586825 100193271 95069418 98950115 102695048 732739808 112844841 381838748 391547855 563236297 629045870 580554001 160968740 517199457 707503326 684528830 286273756 615057212 18874769 561046032 567701350 276614325 565078392 148399514 691166530 828303973 178608561 193687526 192813124 667656457 150842505 286813330 993154468 120267936 600678313 910126864 486173424 71639060 323261541 275236938 118747871 623886416 913211893 443669904 638694454 104926496 868951550 515975587 721209743 763442372 353981514 811235117 288626631 615055089 916206345 268373508 218134467 222833912 417652792 790378158 926106613 620103899 361802457 275274687 950644779 115381575 770229359 339059199 731769364 170103101 597715150 616895114 174045544 890144991 612822059 128183469 489593259 483304655 58069079 778364043 917587137 172805143 817582263 279043824 342001964 304488507 682451475 659244413 994495315 362764829 255051816 947592483 382037172 324991314 2629620 929889145 318655556 910814608 125971115 907403427 76148748 566457384 482182713 997483998 755816140 12547916 920514837 404779159 614388164 508206673 429428239 475909421 893892236 96584333 503474630 293939551 104305922 361118777 744385907 481545827 191765538 575809504 242255750 497746856 750403062 513810371 633307807 83680083 472741992 842152334 624975853 715392511 700755734 266005549 900868669 422435615 394939984 120349705 517085689 891784047 463918558 779280794 257726993 379878820 444417793 553267829 445835344 952588130 556535111 194350427 513071706 795064884 822272158 914440970 887596443 922683886 649961309 545348183 537258362 113354217 754309311 541889942 924270837 925339192 252196591 67628755 345290786 412783214 78318228 106907248 374375779 280313704 759489333 238840620 684529578 715000936 236348075 355371072 497617445 531593066 346149634 443082986 765379190 862678325 89300852 37567028 348717645 238695614 758919866 584537465 583142010 464072625 650411697 154215192 732838696 310543708 144810321 150973236 18373388 599521936 242812430 128985802 81129706 349645414 600319196 604127558 439123603 429523029 82164714 992184822 302313394 602481380 990032690 259792174 105404443 668745948 107604147 47236331 943155489 715726168 459874236 509406616 387367415 678642633 507962546 810031811 134897454 173806170 397585920 157744193 647761809 539208096 273595781 69389618 50357552 648403318 358495430 486779168 503821924 401190222 526096827 610167984 17170978 458610946 798452053 731563911 148666532 536255539 647874342 895596128 168954556 867148218 97948837 704598600 748179365 87583894 750925668 703158728 590012937 73768951 471709454 215174218 7412265 626127035 283262916 894759608 166960698 882625460 554690650 670061399 546395290 298045692 667092004 827981455 355477028 184219616 513582358 155340160 571468181 126349254 985819798 463450210 583686746 752410988 853553275 203029133 666113623 759848863 530549191 511315627 794314178 153777642 643083477 844254909 434434633 104910892 612931898 916917484 660554459 87246908 97455320 909177950 406853114 885595342 260895434 660639931 496986462 421046221 522853834 463424591 279935300 154166306 384318566 997077178 811915029 342443636 430976965 546185275 301458222 670951000 116342403 388791587 454257493 916970166 516147848 244006400 762901186 932327547 169225246 60138483 631305140 921863918 644215365 703724706 42989862 662017968 587586265 139115296 250411957 435545154 438585136 322703509 728994729 676182556 966319539 700882030 546490002 252090135 302102741 625254668 862390300 680758917 492592401 90219152 154532097 46077530 714087876 818652022 970783856 83311059 891202607 499099190 278022977 741683326 195040448 617939952 818893306 291437176 527258502 79066881 432995868 155012767 604289285 801628774 124736574 22418254 614720048 403337822 338140668 262665337 417261980 739800814 642562957 882622808 481924893 855010177 366857801 801344276 652324004 846830439 553415846 337199816 873031574 474433017 17112705 898390709 43572752 640334074 322485921 97477929 745572544 22980020 514802250 125105821 827106198 888052138 106010980 433169945 915361890 310951867 717933006 90479524 566227228 213889037 897995535 459608701 95709930 349994238 290285648 473437750 680766925 880426266 74679108 3389035 192157109 910097742 373560868 774750159 211330654 827688399 636980866 324279608 411546161 632714553 553477974 531352639 272875733 740722419 922685378 175057736 179750577 798759433 486351453 950767499 650925340 285788333 154196303 2493925 492468939 923472543 791102624 846310679 859295927 542635994 5671104 817740776 535410233 918021643 72275181 327450678 412691524 714936431 680928657 495515346 230836156 732677482 534963799 603162879 224321986 635883844 613257875 148584753 207674767 317686950 264386034 118467772 818957721 376417649 18483469 485729464 897841425 470549761 822434542 859849511 26578477 687133671 359435704 687745734 985346186 964903684 86065526 363587331 350209781 784828578 369657742 61816993 798739382 193339088 855221708 87663765 387070595 317618673 431228182 585896662 293310844 812950767 958762695 948223455 354633004 393095283 779700760 656302091 903601965 90568927 896169012 75209469 542785822 717231117 677181199 646770959 333633303 636051788 282231352 534263441 82731776 187487931 553563380 613092349 118668875 366866837 68593229 526166976 742464608 718948566 430061406 147715955 212135336 321025097 69393970 187833078 944513093 815182216 965223441 204616150 389735665 38864705 863132136 45417363 386965502 794242486 371822723 453080074 154403843 812406814 727667074 105977819 280989295 297552454 59040309 871351898 814037140 435279467 32293084 417771025 168983349 864502057 626340092 631902005 497288556 651512016 893460636 150729966 34088264 477108505 275203655 13149888 312479287 554147772 996353051 128424766 207514896 106417306 482613338 313324291 979868133 825738606 577683360 295244149 964581014 129710399 390218777 580857004 740445837 995813133 387449902 403919895 484005594 755946155 567849635 849250943 232584424 633646414 469527376 445934317 992383029 414308071 144893760 734607906 299642552 333364810 11080369 952868569 79722887 33247180 962768864 498555217 932298507 281140994 73193313 187732479 222393129 217357267 771462355 211035597 731117616 630921451 821193614 805575449 279023961 224866637 835691993 785534414 709481865 582062783 632452160 540406626 657663643 333630562 182422985 702890671 487903068 340141033 434194464 373626212 39158838 742753658 557179071 35761447 289355009 485136908 447627082 331438221 247846675 598014141 463312741 600779643 106081522 266398993 963381739 919271797 720215935 546712310 924762447 77470185 203109940 135547692 522324690 904534410 494760303 992332560 250597219 391377356 773819127 603846982 528092757 304884484 142598262 185748263 269619928 691879482 281621019 319227541 996083526 978048430 230080652 677621297 339929707 102829322 706165608 719448421 477025419 916912793 191037599 264801965 481331001 339821180 28555923 841408589 375678137 461654982 698478268 908995843 668520487 56648700 175185687 851070544 362591042 319946813 821305096 712296230 388621202 634244279 290282482 362997347 84187273 176742503 449432624 567613451 632408596 811576984 487081325 356709027 178462955 891868888 437301504 54134377 458792762 360091518 477288522 532892911 615294295 313286039 311563742 720192190 357706959 914633574 199627910 800323253 813664622 970667207 597407493 865521916 424897683 641565574 146729610 625113916 789044790 441044122 219648234 14902265 857983553 622005470 467812842 658970281 526206979 250465862 648980904 207825341 208716072 556708743 658171917 551699094 107142886 862661862 834709117 694810364 239592909 259465474 10322059 CORE C Programming for primary beginners by noslac haon Introduction: Programming does not simply mean writing codes; it is primarily concerned with structuring the solution to a problem. Suppose you have a problem and need to solve it, you need to come out with the steps which will be used to solve the problem. These steps are called algorithms. This algorithm is then translated to a program code for execution by a computer, Algorithm: It is the step by step procedures used to solve a problem. Writing the algorithm is the most important and time consuming task of programming. Never make the mistake of trying to write the code without actually developing the algorithm because starting with the code causes you to focus on the syntax of the programming language rather than the problem you are solving. Make it a habit to always develop the algorithm before thinking of the computer that solves the language. Note here that developing the steps to solve the problem is much more important than translating the code. Program: These are series of instructions which the computer follows in order to solve a problem. To better understand what programming is all about let's take this example: $#!Suppose Mr. Tabita has a farm and needs to clear it but does not have the strength to clear. He then finds a farmer who can clear but this farmer does not know how Mr. Tabita needs the work to be done. What Mr. Tabita needs do, is to come out with the steps which if the farmer follows he will be able clear as Mr. Tabita desires. The following steps are the relationship between the problem above and programming: The land to be cleared here is the problem. Mr. Tabita is the programmer The farmer is the computer The steps he develops is the algorithm The instructions the farmer follows is the code The paper, phone etc is the programming languag eg: c++, pascal, fortran, python, virtual basic c++ etc Here is what programming is all about, Tabita needs to come out with the steps(algorithm) which he tells the farmer. If it is done through paper there is a special way to do it, same with phone. This is known as the syntax of the programming language. Supposing the farmer does not understand the language Tabita speaks he needs someone to tell him what Tabita wants him to do. This is the job of the compiler. Sequence, selection, iteration statements Every program is built from this three basic logic principles. a. Sequence: Consecutive steps or group of steps executed one after the order in the order that they appear. From above, the first instruction may be to check the boundary, next to mark the area to be cleared with sticks. The farmer has to start executing from the first instruction. These steps must therefore be executed in the order in which they appear. b. Selection: The program selects one of several operations and performs in expense of others depending on certain conditions. From above Tabita may decide that if it rains then clearing should be stopped for that day. c. Iteration: The program repeats certain instructions a certain number of times or until some conditions are fulfilled. From above example; the farmer may have to clear a particular a portion with tough grass over and over until it is leveled as others. As I said earlier it is the combination of these statements that forms a program. For instance, a sequence of instructions can be repeated a specific number of times if a condition is respected or another sequence of instructions is repeated a specific number of times. Whenever you are given a problem to solve do not border about the programming language to use. The most essential thing is to come out with the proper algorithm that solves the problem. Techniques of problem solving When given a problem, think first of what is to carry out the instructions and what your device (usually computer) is capable of doing. Think then of how you are going to explain your solution to it (this is the input) so that it carries it out. Finally, you need to have an expectation of what your result should be, this helps in confirming if the problem has been solved or not (output). Then think of the perfect instructions it can follow and solve the problem efficiently (algorithm) Whenever you are given a problem, solve it manually first before approaching the computer. Because if you can't solve the problem, the computer wouldn't be able either. Example: 1) Write a program that displays the word -)&Hello world,-) First, ask yourself what the problem is(understand your problem) What is to carry out your instruction(in this case; the computer) What instructions can the computer possibly follow in order to display ,%*hello world"&/. From above you then understand that the computer is only one of the many tools which a computer scientist uses to solve problems. If you have understood this problem well, it is clear that the computer does not need any input in order to display hello world, all you need do is give it the proper code(instructions) it can use to output the word ,%!hello world$,$. 2) Write a program that takes two numbers and displays the sum of these numbers. Here you want your computer to be able to calculate the sum of two numbers. The computer knows nothing but only follows whatever you tell it to do. So in this case you have to tell it to calculate the sum of two numbers a user enters. So how then are you going to do it? What code do I write so that if a computer follows it will ask for two numbers and display their sum. One thing you need to know is that what you write in dev c, pascal, python IDl etc are the instructions(source code) that when the computer follows it will solve the problem. Detail explanation of Pseudocode solution: Get num1,num2; Sum=num1+num2; Output sum Get(input) num1,num2; This step means the computer should request for two numbers from the user. Sum=num1+num2; This second step is the actual operation, where the computer adds the two numbers together. Output sum Finally, it displays the result of its operation stored in a variable called sum. C++ proper: To continue from here you would need a dev c++ or any other c programming application. Download dev c++ from software.informer.com. Open the application and install it. Lets start with a program. After installing and setting up the proper development environment, open my computer-my documents-right click and creat a new folder. Name it cprogramming. Open it, creat another folder and name it helloworld. Open back your dev c++, click on file-save and then choose the location you folder you just created to save it there i.e documents-cprogramming-helloworld. Give your file the name hello.c Then beneath choose all files as file type. Note that all your programs must end with the extension *-'.c-'' Type the following, compile and run(F9) Note that c is case sensitive. If you typed correctly, something like this will flash on your screen and immediately disappear. It may even be too quick for you to read a thing but do not border we will fix it in a while. BRAVO BRAVO BRAVO!!! You have just written your first c program. The above structure is the exact platform of how all your programs would look like. Detail understanding of the c basic structure(lexical structure): 0 Comments The first line contains a comment: /* this is my first C program */ You notice that this line starts with a combination of a slash and an asterisk, /*, and ends with */. In C, /* is called the opening comment mark, and */ is the closing comment mark. The C compiler ignores everything between the opening comment mark and closing comment mark. That means the comment in the first line hello.c is ,/*This is my first C program$#,, is ignored by the compiler. The only purpose of including comments in your C program is to help you document(explain) what the program or some specific sections in the programs do. Remember, comments are written for programmers like you. For example, when you read someone's code, the comments in the code help you to understand what the code does, or at least what the code intends to do. Another way to put comments into a C program. C++ started using two slashes (//) to mark the beginning of a comment line; many C compilers now use this convention as well. The comment ends at the end of the line. For instance, if I write a C program in Borland C++ or Visual C++, the following two comments are identical: /* This comment does not increase the size of the executable file (binary code), nor does it affect the performance speed. */ // This comment does not increase the size of // the executable file (binary code), nor does // it affect the performance speed 1,2 The #include Directive #include You see that this line starts with a pound sign, #, which is followed by include. In C, #include forms a preprocessor directive that tells the C preprocessor to look for a file and place the contents of the file in the location where the #include directive indicates. The preprocessor is a program that does some preparations for the C compiler before your code is compiled. Also in this line, you see that follows #include. You may guess that the file the #include directive asks for is something called stdio.h. You are exactly right! Here, the #include directive does ask the C preprocessor to look for and place stdio.h where the directive is in the C program. The name of the stdio.h file stands for standard input-output header file. The stdio.h and stdlib.h (standard libary) files contain numerous functions which we will be calling in the main function. Remember The C programming language distinguishes between lowercase and uppercase characters. In other words, C is a case-sensitive language. For instance, stdio.h and STDIO.H are different filenames in C. Likewise, main() and Main() are two different function names. Header Files The files that are required by the #include directive, like stdio.h and stdlib.h, are called header files because the #include directives are most often placed at the head of C programs. Actually, the extension name of .h does mean "header." Besides stdio.h and stdlib.h, there are more header files, such as string.h, math.h, and so on. Angle Brackets (< >) and Double Quotes (" ") Next, there are two angle brackets, < and >, that are used to surround stdio.h. You may be wondering what the angle brackets do. In C, the angle brackets ask the C preprocessor to look for a header file in a directory other than the current one. For instance, the current directory containing the hello.c file is called C:\code on my computer. Therefore, the angle brackets around tell the C preprocessor to look for stdio.h in a directory other than C:\code. If you want to let the C preprocessor look into the current directory first for a header file before it starts to look elsewhere, you can use double quotes to surround the name of the header file. For instance, when the C preprocessor sees "stdio.h", it looks in the current directory, which is C:\code on my machine, first before it goes elsewhere for the stdio.h header file. Normally, the header files are saved in a subdirectory called include. For instance, I might install a Microsoft C compiler in the directory MSVC on my hard drive, which is labeled as the C drive. Then the path to access the header files becomes C:\MSVC\include. Semicolons ; In C program, the semicolon is a statement terminator. That is, each individual statement must be ended with a semicolon. It indicates the end of one logical entity. For example, the following are two different statements: $/%printf("Hello, World! \n"); return 0; 3 The main() Function : int main () This is a very special function in C. Every C program must have a main() function, and every C program can only have one main() function. You can put the main() function wherever you want in your C program. However, the execution of your program always starts with the main() function. In hello.c the main() function body starts in line 3 and ends in line 7. Because this is a very simple program, the main() function is the only function defined in the program. Within the main() function body, a C library function, printf(), is called in order to print out a greeting message. One more important thing about main() is that the execution of every C program ends with main(). A program ends when all the statements within the main() function have been executed The Basics of the C Program !As a building is made of bricks, a C program is made of basic elements, such as expressions, statements, statement blocks, and function blocks. These elements are discussed in the following sections. But first, you need to learn two smaller but important elements, constant and variable, which make up expressions. Constants and Variables +As its name implies, a constant is a value that never changes. A variable, on the other hand, can be used to present different values. A variable is the name of a memory location which can be assigned a value. You can think of a constant as a music CD-ROM; the music saved in the CD-ROM is never changed. A variable is more like an audio cassette: You can always update the contents of the cassette by simply overwriting the old songs with new ones. You can see many examples in which constants and variables are in the same statement. For instance, consider the following: i = 1; where the symbol 1 is a constant because it always has the same value (1), and the symbol i is assigned the constant 1. In other words, i contains the value of 1 after the statement is executed. Later, if there is another statement, i = 10; after it is executed, i is assigned the value of 10. Because i can contain different values, it's called a variable in the C language. Expressions An expression is a combination of constants, variables, and operators that are used to denote computations. For instance, the following: (2 + 3) * 10 is an expression that adds 2 and 3 first, and then multiplies the result of the addition by 10. (The final result of the expression is 50.) Similarly, the expression 10 * (4 + 5) yields 90. The 80/4 expression results in 20. Here are some other examples of expressions: Expression Description 6 An expression of a constant. i An expression of a variable. 6 + I An expression of a constant plus a variable. exit(0) An expression of a function call. Arithmetic Operators As you've seen, an expression can contain symbols such as +, *, and /. In the C language, these symbols are called arithmetic operators. Table 1.0 lists all the arithmetic operators and their meanings. Table 1.0 C arithmetic operators and meaning + Addition - Subtraction * Multiplication / Division % Remainder(or Modulus) You may already be familiar with all the arithmetic operators, except the remainder (%) operator. % is used to obtain the remainder of the first operand divided by the second operand. For instance, the expression 6 % 4 yields a value of 2 because 4 goes into 6 once with a remainder of 2. The remainder operator, %, is also called the modulus operator. Among the arithmetic operators, the multiplication, division, and remainder operators have a higher precedence than the addition and subtraction operators. For example, the expression 2 + 3 * 10 yields 32, not 50. Because of the higher precedence of the multiplication operator, 3 * 10 is calculated first, and then 2 is added into the result of the multiplication. As you might know, you can put parentheses around an addition (or subtraction) to force the addition (or subtraction) to be performed before a multiplication, division, or modulus computation. For instance, the expression (2 + 3) * 10 performs the addition of 2 and 3 first before it does the multiplication of 10. You'll learn more operators of the C language in Hours 6, "Manipulating Data with Operators," and 8, "More Operators." Statements In the C language, a statement is a complete instruction, ending with a semicolon. In many cases, you can turn an expression into a statement by simply adding a semicolon at the end of the expression. For instance, the following i = 1; is a statement. You may have already figured out that the statement consists of an expression of i = 1 and a semicolon (;). Here are some other examples of statements: i = (2 + 3) * 10; i = 2 + 3 * 10; j = 6 % 4; k = i + j; Also, in the first lesson of this book you learned statements such as return 0; exit(0); printf (Hello World! This is my first C program.\n"); 4) Statement Blocks A group of statements can form a statement block that starts with an opening brace ({) and ends with a closing brace (}). A statement block is treated as a single statement by the C compiler. For instance, the following for(. . .) { s3 = s1 + s2; mul = s3 * c; remainder = sum % c; } is a statement block that starts with { and ends with }. Here for is a keyword in C that determines the statement block. The for keyword, "Doing the Same Thing Over and Over" will be discussed later. A statement block provides a way to group one or more statements together as a single statement. Many C keywords can only control one statement. If you want to put more than one statement under the control of a C keyword, you can add those statements into a statement block so that the block is considered one statement by the C keyword. Integer Types The "integral" types in C form a family of integer types. They all behave like integers and can be mixed together and used in similar ways. The differences are due to the differentnumber of bits ("widths") used to implement each type -- the wider types can store a greater ranges of values. char ASCII character -- at least 8 bits. Pronounced "car". As a practical matter char is basically always a byte which is 8 bits which is enough to store a single ASCII character. 8 bits provides a signed range of -128..127 or an unsigned range is 0..255. charis also required to be the "smallest addressable unit" for the machine --each byte in memory has its own address. short Small integer -- at least 16 bits which provides a signed range of -32768..32767. Typical size is 16 bits. Not used so much. int Default integer-- at least 16 bits, with 32 bits being typical. Defined to be the "most comfortable" size for the computer. If you do not really care about the range for an integer variable, declare it intsince that is likely to be an appropriate size (16 or 32 bit) which works well for that machine. long Large integer -- at least 32 bits. Typical size is 32 bits which gives a signed range of about -2 billion ..+2 billion. Some compilers support "long long" for 64 bit ints. The integer types can be preceded by the qualifier unsigned which disallows representing negative numbers, but doubles the largest positive number representable. For example, a 16 bit implementation of short can store numbers in the range -32768..32767, while unsigned shortcan store 0..65535. You can think of pointers as being a form of unsigned long on a machine with 4 byte pointers. In my opinion, it's best to avoid using unsigned unless you really need to. It tends to cause more misunderstandings and problems than it is worth. vAHbsCK dGh8w3u BDrTqjB loQPcTq ZjGlXww pYbHSlj SQzNBIp MyGjZdo 4leOX10 e4thsri l4Ybv8K DaVTdrN eV0ZQfj dDSEm5O 6EEiNLs rjJz12z 2ITKf9g t4dahvK p2J9MQ5 9QKBV5A EtpfEpB 2I0g4PB bMJ8yu6 LLvL1dM 1J2XJjc Td5J0iv zGmbocy fa4HUoY LlcHg5z AkprYmN uiy7o0X 51wMXzI udq869z i0WNi2r UeDlNC0 8jpdpDV yOu0AcF AQpSKAQ QPbfuaO JyiOyHz gmuTCAO A8vQKnC UFp7b4U oRPdv4r fF0osuk 1cR9y79 LIDbQDE hbltjQW 0lXQNDD VPJzcET nINkCp3 HxpCoG5 c5HMgFP ROE7PRH Zy9nCxP sQ79fu5 0g2mrgE 0fVVlvZ nYbDwLS r3WjFFY a9A8BoK 7Q6yosz ltA4Vds thQKbgz LGqR1Px KgyqmSX JNLtpuY AR6D1Qm owhORst d39W0cO xP98zP5 GNDrHW9 RPOD8El tpzZ2xq yubtQjY G52CPCq bkjYSbp QJrahY5 SHJDGJr v9SXaly ixBOslj ulGOeHj nLjRNMf ItP2DDY UltdW9I XTLDatC A8tJqc3 a6iK1xU 0qbe0iQ EcIYqq9 pE8dMyr rXp2mGX xSUkWS7 DEfQ9fA oz06ASY K4M03xf ggO7Mlh EWkVeW4 yjeZXRm SjfI1HV 9jHZk1s wgr1SRN ehiJTie WS3ppi7 j9iIN1D QtkwNFJ m7czk6v CEEnMBQ krBGH18 sPjp4Ve ft9w5Wx N9JOhc4 jLY027K SVKT3za 2p6Se4a T2p2kLH 4d570L9 CmfY6dF JARxoPr LqHaMPJ GWPIcwN 7SrJRiU O9Yqjow Lo647OP fQBWd49 kVw36VJ vQjOq7R Mp6pEQ9 QWvNmjN kMg1zpt VVLYWGP 9leyqEE 0qmGgCa ZFCWsDD 0NJV5T7 zRrs01K Dx7O6l9 O1zhEIQ Vr48SaI 1y1FCnP Tk4GxDD SZ94Rht 6re3obi 5mm37ga vnRxfQx 0tDTk2V 3dsVLid WZ3sJiG MK4auMo LhaaV8L PAJA1qA AX5OcYT KWy8YtY BS2CBHj Y96odLc FLDOwry hmwut5n QHrzjWU pWs3yAG RDOALXx rnS58X6 Tgkb38z wm7erTj Af1HoII wDjtpba OXvaUUe T0nFmr8 vZoA5i0 o1XKikX 9H776Zx qsSS3l8 WNzuvcN UB8yENm jTiJ9YB BdYdgJF ES5vS9w SNfkhEI HB6PMLc nkNBPNA LeYyCXr ioCrUPK vpElxg5 AqCEMGj 6bjLypS SP7Sda6 HhKHK0K Yyu3yET gFRYTJf rpJBueC tyF0R0L 6hzF43C esa3EQb VQxYmKk ZS9rTJ9 tsZO44K wellWiM 9SKoBnN 0co1TKV Xzuo225 2nJOVUB HqbqKq1 AOc5QBg jNubVeB Q6cnpsd P9t9RnI LycGefL Vux94qN iHhu9dQ hACkXcm 6KW8ID7 XKLm9cs sYEEBoQ fxugUXP BDOXaZT hQDZL1O XnW8PTp R3QXKey kyn8jgA JcyjJqh Sd1PEfm kEVChOc 1MJSIFT qg4sLgA OXPqjxe 9xK59wX ErrpPkf A0tpEvH OiqZ8JD gVEHduL RhS6AOa H9cpaA2 yEFd6tw Y8KZ2vO lXU1Drt 0ax6CiO AtUJrk3 hvMIUBM QOtZRUL FVXf7Hf 7BESRsC kQwGVqe GaDhVJ2 m0B2rNP K01RuZT qt3fgyu vp69tPE N8J29m2 4iCGHRu JXZg8FQ NVrWtjA 2KmVBna Z8Tb8vT KqKgfHq p4wmr6L gTsixqn WAf5C5P MegyRQj GvJYNXr nPvwHAt p8K0EL8 9KGSOiJ XNhEW4B m9O9Bba vHVMVjl 3drimNR UjuCHXA 0Nu5fSm quPATes rgy2fuz LUCyMAR lTOpLxQ bf2RnGk pDU6Q6S GlcVBn1 ObGZrE1 WfuQSMB 1O1iaVB kytDrtR Ll6et0x 7y5zYy5 SSEZ8JS cEFQ0pg EWrm0K4 FyAmK1c fbkd3QF s13yt1k 6cr91cd BCY6CLW H5HJPHg kJIjxhR 3ILLQLa wkNfQBg m9CfWdF BMOccBR Z43Tmvu JnEcp5Z sWQkFTz azGOZvs j5CZLm5 qoRxMrv Cstjfmb 3MNeJ3j NKtSX6m 1hAvy6t 3HtGzSG vf1T937 0zZ3Rup zFOvtzV cLSK8fe lI1RYEf K9oPyvB dJp4icZ 7ss7r5q 0hbdnEg cKRY8WC GVup5Dx 9AbN9My 1AQAAfM XCiNoxI oxhcPtY NoXjJk2 UO69clS 7dahS9k I7kq1TX 7j0DPdX on7Gmqp aIQe6Yv FsnnYUw W0kP0iT 8qh7Cs9 Lai9rrM JzpzvmZ XIhgjDW S7x2Dr1 Wk5QIji dnVwydL lziwFfv Pqhzo5G 1D0sInz k0x7x2j a8pZMET gIVgAiW DVNquNL Gc8Kz5L hUvI7BX 033QNaK bMYZvIh bv2mCCP OCH5dw0 qixhVeK 9CgUKoU 6DCyj5N zQsl0ze ihoMH6R CdZmrvj dJTZZFN 3n8Hx04 zSqgpcm KtPiVyc uOeorKi 7km4x8t Nqohbn7 Of20IPn aAEZtlE MXAb0cV hEr3H8b yf3c9bU Dl7pfyg L8OlHDe P2kWbZ6 QfOHnjV yRliy16 BG2SVss d8Y5ldZ UMqE4NG 9Nawaxy fMyS2cB gmRZcY8 Kii179o BZDEDEY DuBnjzL 1Lr8gXr YsUH9wO iInVfpf kZc9akp WYCIk1R MclJsL9 Ptf7UtR njOhECv mH3pSbD SnkQoEd sjSFRMn 5xNWZzd bIdhcLC 46Yfci4 dM9QD5v V4l4GR2 jYVtt22 RL3sL4W AnAdSuG YikqlU0 xlL490B MueVqqt MWNRgjr N1G69ve EQFlf8y wh3Qk2n JToqoJ9 gqpSMhB KAZInUn mcGd7ci hghudrc y4omXUY FAPvX8l G58pqlR UnC5AL6 HgfrkeY Ct5mW2j espcWIJ HGNwkXY h5Cb5YB g2caPb9 H7sFLDH Xq12WYf KlTTkub kZoUeWN bSeC6EJ uTu6x41 L6oeP0N lUktdcZ yFMTHBL saIdQ4H vXm1FOo 6CiLWxP NdH1zZ6 BNPh63w 7n0ckqn Xc8pFYT iElJLMF uQguMjY MK6CrTG 2jIjpz6 CIhWvTF R06iZsi yEllglz 19QuoGP Qh8bWWS nw6Ez76 lGGxBEu veZqciW Bvi3zAo mjWsNJW TQFgcbY UtKQblu jikHSSD nAdJW9R 5Z1zDJf FMwuI92 wAp04T5 ozb0Vs6 OurxMuR rcUzgPc tzLJz8O HdkLyNu 5R5KJVr c9jqjXv ayym5PE tgruosS By4qiiS 4c53d7R d10SxeN BWbla6t 3213s2c o012B7G hRbPpKp JaagSvj HOETu7t 9iW77ql YJ5EAJA 60N2iZA LCN5aKU MOxFUNG oiEdZ4V Y8isik5 tmPqpu1 exFdEVp nYgcLET jvLgzs8 7tGc8Mv vZDLSPB PRi3TNh KRpjYhQ tO6LJ2X ww7JIBW lSLJxeq U0U64M7 jJ694RD GUqDgu0 tlV7rOl WzmHcpY XrPCDhr DoqzymP VsdOZUK fvDndqI 6D3T6Bq 069XEH6 ug9HvIy o0WawtK MQ2LdYs dIdVINJ DUSizMs HoxEP8e FTk0S8Y p8ioHl8 Ua51ieE ScHhcUA luQH8PH NVf93Kx 6iUUioI GkmYLgr ElUFEM6 rqKPfOw s84ZQv4 88C8abp sQJHaHV XMorcwL YQZUHgR AdlwNoj ual0ecY y0dTXg9 M7vZOEz E3Qjfey SwvPqVf eerN8gf hOwYbaO QCVSAqP 6eBnxK1 U5u9tNz z8aYJee u4RyOGz DzxE0fu vDdlnqI 7BnCXyW RVH9xo9 C3oPUgQ 5NRqrLA TOSnZPm zfCvyhQ orqmKRl I6ixs51 DsfJqgM TYobNP6 9J4jaFz fu7vjiZ eD12pil rq7tqGA 6urf9sR AhJX9v8 yiZzGtc xT8MUC8 DkXOnOr gNBYhy1 PHhlK4K hwjdCWM YiChYvv 6SRnpoG I5Aighu GxQaJTT IjkzKTQ GILwOny x486R3B 7fDTVoQ qqgUDaF zIPqw3s xduwDTW 6bkECaF mAj4Xdv bc6P31e xxT1CKw e0nClyb Vmp6uRT oH8AXXU Tuw00SN uVTvaGd XXAcNXv mPhY6ZO BuC8R0J lBrHq7E DKcQWd0 3bvC2zO 74u5Qu2 tN0YYqD NFkkUnY CbjjcEu ou2qBRv 6kAZbuq N0eoP2D edW7RUc kDAVlps sHgWQn9 gOAoKRu 7YycOdK lcemEkI iPnYNTP o9eEzf8 ktQ2i0e zC4JBfI IRXNxEo TB4ydBD PnGTac8 gpLI5lP tGEvkZK a3pbwCt PKFkfsf iNYxT6c MS8hYRy 68iQ3Cm d3OEwtG WX0xRKi iuJ98bC 1cyWayW hdiOuZ8 Vw8tM9a C4udbZW IBJQVbZ M5d0Lfe XSOGCjz 807496b xpcxha7 em7VXMo hXlE2hs 4JTBwkM YdAvCVy XXiaSBq WbQF2D1 O83X4X4 WSxK2Yb hgDMOku iavxmIa koQutm0 OdzT8MR pGj4jTf 6547QYT AZFBXZN 5YTeBSc ZSz5dZF vVTywC3 ofsBMPu hDKaFvx gN67fcW OIRmKi4 zh6GhAM q9sYPvJ pzeOX6e gLMmscB RWrBKnu izxGlg5 ZBQwFGf 8aoB4H3 VWWdkoU X5yApb6 XSuDnWM qIw0xou IcIlqgL 1OjP252 EwV4Qsm 9Dfgj5O zuUjNf5 vnhfO43 lVqefAJ VtCHUZm UTT35tq xmUdwth ueaDddi wZHJsB4 sIysguY VUUFoIM WwQ2zc2 TqugxGP FR8bshg AXCcSR6 deH4B4u UX4LToQ 6nGR4pa KC9eXCB SCytLXu tLz238U pHpsI4I KPXQGke ZQEYuK6 zn5JMPW sHcdJvv Hj2kDWV rKXC7hs UelWMIi bkGjY2x Foke3wX VL0B7vF uh6xImD fn29byf Uert7bc 9lTD4CJ IyumSXw H4OlIou a9QI1JU 8MsGpKu 4MAkkvx EB2PatK K1g0Huu VJNH3jQ D1YamVe 8H0MwX1 6MSVk5c Uk0elG9 HGlayah dm6yjaM MePhTYB LIiHEev 7w8Gjcz FTaeERk yJt9quS xjfge4F qCf2kef A0ZU1rz 7244Xbk 0h8y2ZT 7uEWeCk rwN0G2A grlA8dm 4LxyTev znnff7C 0AxG2IT U8rXw9l Z8sCn7Q sMpqfuJ 2ox8seM lQrCkqH NvWMoD3 NJlyry9 Yb1D4DS OwqjSkp uHuXJHP J0y6XiB rFXBd80 Vub8neo TJW50H6 3Niq1bJ xCUk9PF 4nw6xRO fDowyci 5asLAwr eD1xWIT TkDVvE6 dDeN63S APIFHlL SoU0bVJ pKWdhGz PuaZfoe z4tMAnQ 1JKT1sL UvZo8hK LmI4TMr dJsKYCj GHXnw6I GceG48a eIImuXl r1132cz iSJBLIK McbO4IW XDAuQ9f TMWqQFH No9fDQ3 OC5QuuO IYFkOf5 Drx05Ik YuScmBF doxZnuD YobWkvT 2SlgGOK qZSY4EV AvffmaT 0ipxJKN osR3mf5 Rfybp8t a5dlYat WC0tNTm h4OM9H7 4Vux57p alV8Vvb uMOWwlF kDMZ6Cv EsjPvhP TLw2QgR Av7f0yp OP1jiId A1qtx4m h0NWat3 iv0F2lt lQkLFGb t74NiG8 SZQ6ycr PilZg1p K1cWarL 2ELqi6D qFMXWjO oB7tU6y 0iM6tgP 55uBgOi nJB21Vf FEJHlJy q41AlRb Cof0J6b fVhtRek tqX4SnD 2hOSXRz HHdXZkS 7kouYQS bLYmrAa oueip1k dUX2HcL NCQTSJl 1Y2SoLP NiKKjqA VltQiMD qA5dFZD kIrgxVK Uydai6e QE1sZu5 CNnpG8P YGwxS21 5U2gEHJ gTQblGn yVSJDef NtK8Ldx t3yNa7N WH9Zkdw NbLRJeG FoKRIGa gDA1jyD 5KHpJus mzM7bZZ fiB82L9 he7RfLp MvscYQ1 6nR952w MCEJAsH 0DZVedV NGwiNd2 NhZpnnP fiUpFES Stjy8uo KKj3Pl3 eADhxSa GAp6yRr Nh0NeSO c9K6luU euPOHqu bQVF3Zj hXUy3CQ 7ieimzs VOOqKcU BqbIsAt 6ZSDcyp ljPgeIu Ni4rsCt sApneTf LmKBqzU qMuNd0d BrEpCq3 kPqVosn vLM9uqW SZWOwrn MqBRNoF x5AiMxV 7TNLWND sk3903c M7oAkbV mZ04uAj jXM2Xdk vy87V1g GGr9krU XIf42Gt L8b4LPV pWwcusc HC5oF8n jeehx7b xVeQyvI dSqJf7l Oz7zxph pew9vkI VaYoBM0 VWFfAGM 1s92DPT nMrdJzv 0KRgZA1 iiXTY3F agVKt4H G9N9kSc OgAUo0S kSharOk xdQdLE2 Oc5PRAv W8VMqCV t5aPXZY DYOdyCc ETsvPTA MOtoOB7 8qgtHbj UO0wykF MNf4NEO U8mdkvn uEktZYB xejB6Q9 peGqHrP y0mMX5O 6WUhQMF CSr8C6F aLXHeJG EfqwrWB Dq6z7nU 2dUoTJV nhZh8xY ns7nHIh rJpkbCZ yID4Rt2 fsghEMe j364kxa QuIBRy1 25uDKSG sqXOlJJ dNwGSB6 e3KiUfd QFkRogi vwaebrp XEw6xqn GKN8kpl EJ2FYFK hKUGFnj r28xLhm VVXlRTC Iq8w0EE Swnfy4k iVIZwts ypJSk3w 8D2tO9Y KMbIBby Pjm8qAd GyEL7rA gabNstS sJgBzrs LMjZ83G 7YtzIX0 GvD4FLF hAwHFQq SM24mIu KEQDLYi HukTHTW sztzbO5 9Lrs2fU NNSxeRU 0CtsuxO FXYqsrP vZqV9uy 5YPChIz 090P2Uk Iu5L4LG WIoj6W7 ABfCEj4 NZU9R0K zM1j89O r8nTXsb Iymifqv N78kYRW gqKszCK MbGZNN0 GTFIPLq vUxltX2 ohymqI7 zlniMCc 9zTtUcO WQzQbxB 8XPMQ0g fVTx02x xdkCqkt yi96hCw n7NPMpd on38XVW mk1Glj8 r44KBXZ AAdB58S Lm84DyR 6GyItzc iw8hYFr frXOTJI 5a1Bz2X Cc0gVaH Mzp92zE 8lZLKGD eV2TLT6 iO9N6lw Zw4nOub 9VneXCQ O69FKO9 Z53o0d3 jeeocBA 32P8Jw8 zXER1F5 ypiCjyR u3Ipa2k iDfrU1Y Pmpq3yJ tiMAoIe bDkSkdt c7AHcWm tMdcGm1 t6xDoaD uON5LeP KfQYxpj 4tw95pX QQ5aZa0 FPVwjat b6HiuHK C4HUpwC 2LOcrc1 wTWIJo7 sG1rMsg tFhN7qL VdVQ2dT 9AlN6Wl gFf18tG Ng5cz4Z 861DYVd 1jvH5Py lShpUfP iQOLk54 F6e0PkH ILBIbAH ijjYeS5 y95jvk9 kxzQtNG ksw7ZXf lFEcgxm 91ocapH Pr6SpR0 9P4hmQR kWeKkMF IYjb1dO eCHGytl 3wjs1XY LGaHtYF iZujI7U e2MxsXL bCdnabW f53pGhr ZOtzksK At1uOPB AC4x4pZ YTGHFTu QW2u565 ANkTSqV 8sQuxGj qdJFAUD Bb5rVYr 0GMWMOF N6QJ3rI dHRlhVd 4prXwgk Bqnp88W KsJ95Wo Zdmn9kI W4WACiz XnVjK6f GMAOGE8 iB8hdX6 NXvOmlW j2ISKfG 9zCYL1P 1o0IMQ4 D4NYQ8u 8qx2Pcn f5NS7Ts dbXa95E nBhPVQQ t85x8Tx c6mdr0Y 2ByHJjT dfwbW0C Rzh2ppA BkFA68a ssz9HOp nmWU8OR qFM8PTY yNAEOmX VYRWlBw EQmODZK n2Ni4ia JOE1rQh OpyHMp3 Ly8S9wP XwI5xLS Joea9lq 3cL7ew7 XKMQmtq pmHxKI9 wU8y6Ar tjU4q8r kYd3i6i qMr9upR TmvG67B nVMaOGd h6pe8PD u7KcMwP ryQSpQl aEgKblq 4OA76Nx UOkGgFm XqcVfOl Agpl7vF CmbESIM 94wO1Ga PVKG2MT ud0d5lq uSq4Yrn ILgyN7F r9lvU2c fia6WPh nA73bMT 0dcVKCf 7DHAn82 4qq17X2 sVkRyfO iMzOmz0 8ZdyEtI 2ESk3AF txYlM8e TCSsEvw 5SKApK3 QoIcWXS D4jpFAH OavpZX9 CDHhlkm PrzBZHj NWKyhOs mz0t3o8 3GMF8sF oMPdQJw Ilg6lbJ mUlKdo2 8yB6I0W 2CNI8vh SRVrBn5 UftAixb xjr03oi B4QSR1r dJlLKpg CC893pF fsPj3hq h80Bsda mGygtoL KDhSyUt iAKU0RN HafxW29 WXkHd56 oo3xiWA mNrTDKS 02UjQil VVWYXRq IDcy8fJ hUqZZCs 0W5sFI2 b2pROZF 0nMRY1M loLyFLT v9xzMDp OerC6tv AC2n2RR ZjQNuuM QU2vJpW zDneM49 RNEjJ8a 7wu184R zI5RNxx Rl39pDz IenEyLH z7BMm2z 4o1laps vRSIu9C yqvs8Hj wzRoLU0 YW7D05h g56zWay 4aYxP0Q yCzd4vf r9fQCPn k7ZpJKp 4sexDup iTkNs3p rbXCyAI I9wd4MG SOFw7ny yXBTrIQ uVUZjhd 2PrOQaM TP48M8k l4JZSA3 atXwX7w JUO22H4 vtcTrnB 1hW2hDU uU95hLV kVyYzke csR1gUF 9JHt1BI Hp7cxdE DhhMRVb WtywdAG 6OCmqCo hmTpfTU K4060HA qzYNzrH IDV3jth Hi58cK7 5rrOWma ns9rDjp mz01Z18 8zj9fXp ujX5Rwa IiPYQvc wMt8Yli 3MJv9hI qnyCQxR fPXhzah ORZl5lS G2bTJCS uxryAVj duDKwGZ itFVLaP Vtwbu8V X0jwRqR 0LhX6B5 JDVjzcB SA3IRKM 5N1lxt4 ORESpgA eGQqQoL 4cH8y2h 882If7t mQr14J4 3SAvUik iuYFjAE zpKZ72q jqnt2yI iD8r2Cr 65wW5lG yvzwJ64 B6SEKgX g3xGe6F PxDMjzR VDIdJ7c vjz4Chw zrfQVwX oD2FbFP 9wu88Sp Fc2AepP Uf6o1wo 97kiBZ3 2Wv6omx HJI9dpb DAaY18v Iz3AFmq 8qZ1fTl MyPkxji 3js5qFe 6YIqEOd 20WJESq 5rYsJll X8spRqC TcEGdhf 1ggLBgt pWeDZiF 3BEK3kL mieCQfP GHPkALU q8vrp4F W7n2blk fEquuFb ETxDynE pXQpzEX tVFSf7g jCvEEO0 6wNqJty WMZzWX5 1irFI7R SdDdWnY yeuMLtE I6EVksY JVZlO5f Bac2Grb n5VfNBw q58Suj6 wWmkcGO JGAG1IC YpzX8sn NZqID0c YRjrUrd fNJnuvc ziECIL1 mP76FIi kyjUKKD XOllYxZ kQ5inap MkumwhG gxdADMW DodYk9G 66zxi0N yH59Gc2 dpLJl6T TupNELl CUzskRh njLY2B2 Bagm93g FZHEsFu 48SmFbq sdtxmPn mvfbzCg rfIenSi PDaeI9i cRSO2ne WqwRuAF 5UflcVB 6dktHI8 iwDwTx8 vlyfnMa rHXnmcd nVdole0 o6r5mX6 E1SFHd3 vrH4qN5 4iaZ4bK OCZDBFb fiyP4HW aD5DpQ1 tGHxjSu xr0SfNh LtOfiI2 npRjT2d gsXZOCp UIRUR05 iyXn1YH 1a0H7aO kEr53IM FsujUUx et6dEJ1 cU6JyM4 fcu6Kb9 cI7JxMK ieARtiR ukKXoFj gO5iJkM mIewV6O 9J7EQ9M QbQw6FF dJMkajr iXnj2zn MqQ2WyE HPNh0gG oBH3BTX HWUUBJD ADWZsHZ TfTAZq7 LsalpBH r7tUi1c EPUxlmW a4mA6gm iVmIDi1 vZKaSl0 p7hRRLX mgXaZpZ YspQvYP E55S0vW NXmARuL oHHPppz RU1OPMr DTSdwxf xissTF1 a8eSmY7 x6LlwMb cK9fkkW 1QxkGVk nlT0SFw 2woIquJ HBAMWtF jEYO6Zn OcitSRu reCKP9w Hgv74Tt drbEj0D wFmolCu dwADq8h doZcM7W fvc4XPv snOvSQQ eGDKSpy jaXGTjc EcxaFTq Qmadu4l oeM5HhV pPR4PWr RA3tJNQ ICltrws Y6oqLBN 8ClaRYH glHTGla Nse5nAU PSysPdQ OVyIaCv sZJawDz bcg0QQh br4aAkd GhsaYH2 voFJWXK PjY34UV tE3zdG3 3lVUhQq kZG5P3Z x9Choyh rl438Oo vrdMwRp HY1jzae qSpEvjJ 50aFhoh RfpoeHK pRZEXJQ 369dX9O j3MUKOK PYZSeYD AGTOTWy B5CsByB IcBLAkq 3WFOtqa Lja82kg NwI4foC wtAdrRN lTeFapi lUOL4GF rNzfyoT oM3xW4S eRGg0c3 ZVJ3mlq glLjRBN sOYItTo WNCW8I1 WgezeNS nyLgPvp F3BpVeL TmjXCSe RaXi35Z yByOf5L MCQ7DoG Y0I15et DnXc8fN Vu9L055 ao8WxXc Gg8jXFi wa0pnaK E53MGuS 1Zl2Ma7 vqvhoXJ vThmNev tQamkg4 VMoce7U QnxhDOP KG0SUKF oSg38iC KaNN2NR V0Ch7CR hTUUzn7 KyOnBwf JAoDgqw 5WHnxuH y0kZyfp i9VInu1 LaI7NbR tqDy8ao dpzVCdZ yA1uz9n hDmMOvG as0Yd3x wNki0cP Z86cgyJ KyAcvzw r8tsRAy hm8pygX vAgE9Kt 1hD62tc yIzP0jS 6UIyUT4 NQPzfDR Qnt40AT kqMw7Vi qjlVR9Q Js1A6re e2EcXDt DJlzcqx Svv1tGW DL9q2jo kl63vzh 99ZITnD un44zbY CXqrnEt Q00l7Ne rZ8m1VT 3UbkEY0 JlS78PL I5mF4UU O7JB2Vu iDrDJAy zdECJP8 Hni5u7C RQvC7n0 7KdNhjA DTpreUW wQAd454 CbvZStP 1NOgf3h rbDG3mb d3G7leu VvPVjBR I2rk3nm gVXzjTF 6bodpXV SNvAyec VkRWD70 95z4nSy wttpHJe jYvtwbu D3A41ad yuOfqK3 bMxt3mp 6dIkQR4 Sxh5g3C LGfMJS7 eiShHQ0 fYKOGIg 7r8gLqG aF8sZli RonuLMx wPkiUWm IzyadCh 8y6H6jY 31eRZ8U MCo0KvL Yntk3GR AnblLBk dDfgNUv FD1eFhO cm3fn26 gtSiYEI zz8ljLA p0ILm8l O5Z9l8G pR0x6dq Ls7Mt2z zhJFhEM EyyT3Gb t6WjGTu bskXZ65 VRdOMZz YDfHYkZ tiAPQMX a5Io0Da XteMMwt rhlmx6l AvOXWJ0 qEdkn8w EnmeIWY BtAC3rF owg9yy8 4UnslTF 2z7zB7b c6nd7la 0LgS1yF DdEKl3g ifxQqfR nTAB6qB T0G62Lj Q7aPdcq wZ3F7j1 QSb7DeY 52DWS9N g585UKm O5rEnpo KpCejtF Qf09y6W 5ZL3FMM fYSoTdN h2OXYam BIEgah9 jnIE0aV 8Ggxji9 M6z81gf 9NktuCY vOpRSG1 YXSR3tq rFIpfTD 3Q27gHR z760cJM RGP78WC 7R4VGBg ClpO7Kt D88w1WN S7jj0cq FHLg2wN 3xd6mKt bO9SnYv ROBlPsq f9WFUql mxINFvB 6lqCQhm UQVb3mj PzvJhSA VE5bC6U 76NnCHX cfaXKCc tNkpQWi gIQjKSY BSDhXC8 ijvplUw 84fwA8E Dn23Asu qS1PTtg ezfnKYY hCrSuAw qcFZiBk tK38yPL N9jkQKk BW9gfHn t2TVA5j C0EUdOZ ByPFvnB 9bURsXe s05dvoO HXAdIuY wfrHS5y d2aV6Xk 8sCKCnQ C4e7HGy MqEGsiZ NWkAMfr mhNnhgb baRvn6m GGUgdA7 c4PXtdf RBKmPny 7U5JRvc MVzJCKQ mewpU3B 3p3a92Z zVRCWfN QVBf9aO DhD8XCD Wpw9KHq O43eYg8 WOCtHRm 5zAvzj5 TF3bibj POHITqj oLlPidd 1VwjU12 YY2O23o 5uqzqFM ZcYycs8 ubLqcae tNoT2eq l7oF1Tz DbYZ9fd 1WpjVwc tcgFKZ7 ivv49sO 1dHUZ3m HFzArG3 PN21Ik0 Q9oSXrj WBdRDr3 gQX6DM3 AkSjcZb j9dIztx N55CIKo gwWLEB9 8ay7haI Yod7EsI xWggvp6 XBBUQT8 hhbeNtr zfwB1QG pFVDOZW C9x9dTA gymVvl4 6xfa4sn Fyh9mzR tCvPJuD 0eCBwl7 Xxy95cG qrutXjN mfWVuNM N7bJaWV jyqKNzx WhtqHmd 1buk5r1 eD9535g sQaeBQ9 kA66Mn3 UGLLw58 DHNqqdc 96dZs6S xoww6Wl x4EuWVU eOV7xhq hl96oRo vQnE7fV CR4wQyD cxUEnQQ hqch9x9 5UpNCY3 GzEYeC8 2IISPw4 mTHVdqL eKABgaQ CswKgSk 4q4VisW 2LIkGT0 4qpx47n 3BZUJ85 n6v38gL 4j4fM0P yb5Ii6V gZ9QygC GOuq3SW p6fq8g8 LPYOnjs VDlBkQG zSbv7rG E8rn1xw OkXhi86 Skz4xxM 37mqjYd kCsjvHw 63KfzZ5 zwajl4S 8RPezBO bqsZZ3n rpeNxrw yuZ8dtk QJPDGvB BbI56Hs H2Jxoqd NLYEKtz cPqb8Lt DJwZzA1 AyW0nqR IiZjH4M FYv9K6N UR9MWJR Fif641M kUtuKWZ tvfcViV q60xwGf eaBzV1e B1BBkAN IcCLLlu M5Gvx0c u2sVXG4 uJX6ZZS FxrP2jg JiN6Mm7 7Zqo4c3 frjOb4Y qbtQAEx AEM0V36 7W3znUp achPKYW EToFqCN nWPNU14 94pteLq Tmy7HEo ttqY9T5 i6PGsGP FlUVk0e f4VtRXQ W0tgO82 Qqxwc7u zV2ID1G 0FKd4E7 DNxIGXL iNCqbZM niq81Ua jLOxxz8 jUIUvd5 ZigB4Sa Ct7HGaK GPEoag0 dInb1Kb YpKlzCG 56djUSG MqjLZX0 3NL3qgT fIj8WMd IIdsXzd TxmPDAP jIxTCOW zeOAYd6 WoBTxx7 mwaQalX Ict2onK lVemMCx XDaF38G gW74s4H i9rgttu Bu18C3c EXTnmRJ bEp9LkJ 16TWXjd 7bosWS3 yR1PYNT ABZ2ynX xv7qbM6 YH0TqSS czdd3qB TzduaXV VdsWSZI JTpvkYM nrNWUHF KIXuM2d GNzufQA DtRMv4Z 10v6mtV PMxQHWH GcEJuft APpL3Iw cg2gmqD 8NF3vEP mcE4ex5 u4hKYvg yLmHWQw Mbh0mJZ DQwOE4g ujLFL3S B3YAJ18 vAoczAg e1lD1ea 9G8cPxV Ie0ALhQ PWsn6Gp 9kMOAns oY6pSks n1dmUWX rYkKsj9 BLJswMa 0DBzOHY IElZFSn UTK1cfS IlTdpZe lBt7OgG nBxZN2w oKYRw0h WZOtumE SprrmpD 3C16xiC SEndLHA opCCCbc xT6VTEZ JCxWzOL zA3t0wn xp3m0eK wiOcYs3 2hUhGm4 y9lkF8i jGGjFno YayV8hI gqnwYrY SxMlnOn jlXex2O lw5FtP2 FP3Rztz IPJDtTR 0CdXA2U MYI7iG6 opHamta 4WfItbK bbw2tPn PV8aQxE 3f6TBz8 v2ou7L5 sGDjQS1 ZOm6W1c 6NP95Mp 6bDshjK qJMRscE jaiQFLh tKWAN8A JlffI4k AKbOuho PTFGIR4 2byzbuu yOfKlkF ybdCYfG U3xHjXz PfFUK4l qDii1jb Nu3EYAy FfuAOXg lpvm1MU SjWFWsB NqDyvBd 5Sm1OGm CchMq8U yri8gry vWbyRHl LnITs0B WszvI6M lnYXfAW oiFaATj kOfXmg0 uncOPAn 32tmLBC hK2ldou 70yD2s9 BmahBEZ tIZgX52 SZW1hzj DSetQC2 52rlaqd 78mMTwi RXHsFJf fjQ8mWY gbeTuMO X7ijMCa XhnKKjT wiGdH4E qQruDzL FFzdgpc VvlkZtE IIPDL8P v6LWObi sn4Mu3N U4sTjdT xPFHPbR JeZq67U 9ACOPsK I8Hs31u waiajw9 LJ3JVow 310Do82 FQUmDxi aNHFE5J JROP1Ua 2M4ADlb 23byqAw ucLmyjs 4ciqKLh moKNroV hkELEMM dGb1gNV NiGPxcc j1w0cAI 77nvMdP tZHXa19 BfsgZIe GBDSNMa ilfxLAG i5GTmzk YbwPNZ1 7F57WXj A5At13r NnjQv74 YB9VWo9 h5X4d5L orc0mkq tbuS09u VdPBMdd LCCjuOH Obi7Mei bPKsV8l chBuqV8 Wq1jb7j V4GgCH8 I0EiLei bmKOAEo hLrMhB5 en4ggG5 NbZLJO7 MRoe0rv JnFMAkl j7lI2t8 4tCLv57 tM7cbjP EbextHT WayJ78e QdQ21u3 jthDsoG uNYiqeG Ctv7rny x7D7iQT D3IuSSe u9wWh1C UJHVR2N kWnCsWq Wn4oHf5 hAeKaGR IMAQ2NT plmcthv Kc2zRlh jAOyWT1 ZVz6jNE m2dgd4k NcuYQlV 6CivvXc 5hmVfZ9 6HVkhdm 3BPrNp8 7LCQM6P kIF3vd3 LdsWS5R 3BA5gR1 Qd5vmbc PuFxuz8 NjBshe2 MmD7tbm CkLsMdM m0zD4xc EUm8d5Y UCOuv8V 4c16Rxq UgXseyS 4hgSLIg K6pppjM ILpSmNK XOLDQ8f PDGk0Uh 3eLo5x7 P8VEPFZ VDs9gPl mdpeX8h JagqLLV tGTaqI6 kQ4xkAb w5lZ1VL S044wCR 7bfsUqz N01conX E5dK7RC rwHcj9Y sBIt8rF oH7DTfu KobIul4 yrw0yBL F0rfThq u2ORU4k ALDTcVf 59oWEpr Fqaxv1J i7uKPGt P5RZfGv iiaiBHN qxtBjUe zPAJ62I HwIFHpU MPEmYGF vwbKgYV 94rJwZp I9hwi0f vFFcOlx QnQpgFi zIeWJY8 9a5hDRC sYY3jYe yFEAU8F clTeamY HG9rPr3 MVTkwBA 1Bwl8vG SO6wu1q XUfzBH0 xC6LXek VA9jhbi VPEgoAr f3vSG5R vOfcAY8 Qs4TVn3 d1GfbCe DemFTvW 4mzNpSy X4Vnjuj AeHfWKC MzX7VU5 oa89GUB UW0XlPl kJQ3nKl UKIDgKe mCeNVaY cpW3HtC Fkm0c37 SFb5rkB jAQ6Llw RqVRS65 ZiMGSUD gRLDL4K yRScO5L r56DuTp BGHMQTn mX8eYfa 8i8wpAl xq2EwjD b1XXSCS xfReLGT nGkdNvo 7n1ohfz qJbtBSX njf87vu Qr9QxcE DNej88r HJdisit BTVhND8 NiJPQHH 9a0soUS syt27zp HJ3JRGc GbKO7r9 9UXVFXG Mwl2gen IgMfG0F M7NUxzE Rkjoq7S PUSGDwS XE2fszu rT648tb UK0BA8q 37zNd0z ktYVHX4 nCtSF4a BnMwQuP B2SR78Q fzsM8k8 IcEMgp9 O4PoGwS lhZDJrM Wd0z8a7 YnvnyT6 DSjHuL6 4rAxyCj Fzpzl9E 0t5iBDx 8n31Ly6 VF8F2vq 146mhBj y8Oim38 e7Jbfpm 0ZSeWiH IWVNzhR LhJCQU3 4Ao4tjw zO9zhC3 zcF7xNf uqqwPHK Z3jb6am Ilc9j35 daDlTbf LXqMday 0KQB8yc LQvYsUU uAxypTn 14wYt21 hKIsqf0 RrwIBIO rWwO8ZM lgRaGjA DIu5zMD wqvLW2B wLUfTZc hpGy0lt Q93FOVO 2QgVkVh emcxw2d XZjyZjO qBUW4Cu wwW1PxQ ApHACyq qZtZkHA gaxz38g Y3zuj8H rib8REb c0OuKbZ aUfeNDm jYxsket Y7lHIld Rvww3sf oyl94CV FjHGxbx isEyvFC A2cdvhv 184z0ki jBvbxhv Q49jYZK KkghUft U9EVmvG qywZoj4 GKCDYbq CeRXetF wNFC1mf zk9QpmW 6L9rRqq vbt26ay hsSJt45 5gNr6xZ vzQUYdN ZeJOdfu g6H2s6J UZfMU2G magUaBL mW1E97u 0zgQlfI TjMdkcE 5Uoon3o IcVEKE3 QkzJwSl 5bt6fsV mC0LE0f csqOqOU DXuxOgk 5b0fLPL 5Hf4fKT mRU4Ksf 7hbVlyn 18xRWlJ wd7uNr7 Xw8gfLj HBpVYVB AlZbxbu LVI53Oh wx2b2zN Ac4PV7y b10mADC diFVEOo GhEwptf RIcXmWK IcQ0md7 4AKWbWl 3orIYzb Zc373aM DER8Yzk eE3NGiC xbaUWio 0rgtR4t W5SKYFK 1VBnhZS xUD2rmH gWiOsoH imPW3gE LDkFNnL hOUCN3z lZrAvmX 3nPhR9M lzXqkuH 17i6Qjb 8Nlkxpj LjmLYf8 sDPqEEU EhvTLWq q6oHcL3 8b3fEEn 6CDKWIs gNJ6IWZ W7WJsOD EMbdhey fh8PGSY Bp3tHGc NHBm1o5 FkfI1Jb xftJvUs YAWdUBR XuM16RM waGUyeD oo4LjuK kRpwNzh 3BtD0Wg SLHHnwo 9xSsZXj w9xwFTI HjjsQrC qsbmorI xYxakSd ii8xyn6 bqBQfLc 1ZkYgkt ZBr4voH NQVPsHz eLqxnrj LNmDebl lC74PGL Q2oQ7d4 UALI1Tl wV1EliA eroKLhX pqUUZN8 thfyD7Z jM6PC6G xVcVOQX MDpF9uV KmYXo6A q4IOWWM kQAAZcv dcNlNUP DSgfHGj FGb8SIS MkmWOlz oiE9vW6 QIpIJW4 ouJHm9W SKPE2NO aImI6SS LiTUqS7 pfGzPbv WUDQiUp KhDgQOa EjABpJT fJUBPQF ykAM53A yJpspuy FGQxjVq XflU9ZR MaWb7my WCMK49i pT4Rilo zUam0ve t67j0LW eEb1lql fhbX75b Jg8azdU gGLguSa a4Qv87c 4U6dwJq wdw1mBI Yo9q5ak ic9ttiB cD7pl0P tINGxfm HbC2uL7 jKVhnaj m8opttl uMDI9X5 qzb9RUq h6a26Az ceMlr7U f7O3FVF HRD1JSY 871SqdP uFEI1rd oNoivG8 YGSwP3A RxKUjUz BC2GFJl BXm7ghb omxAfld ACRasjR XTqnf0k nlCEWEU DyXZIfG 4wa2Dox BBVzDOT mrhrTDg XVdWdhV x6Eof21 hDEO3Jb zHhFXNQ vdjuqyR CIRqlYL 78Rd96E OlOXFBA gte5V3m sxEg39p 7b7L6d0 BXgiOzC 9JEVffj 1rOeKe3 Of69vPK i4qUnZn u8WB1ke H9BJp53 kiRltHP rlePvjA ygPzoa7 jCvGIL6 a7ByUCC 4I90GeW Pc3XcuF 2XrzisN NzYaXWC v8uKgbK bFYjZ03 DjVCSAY uJpvDCm NE5rJYe uGDbjH9 w7AxZhY 0wHMSZL t74QJyc FwvPWYA myrv6Aq p36EPoJ QQ1O5JA HcX23sM 7gANXPd uVrTvV0 vcVQVpS tCXnT1B qmCulRE q11mo87 ybi4SIy dMbaUWe nBMoRKF pmW8olz ACBisdL Vw8ADzF Dpjr95w vzhyTeA zWJL9a8 jyxc5ca Ez2HDw7 GJi2V1b jLv2DO0 zQ3ZKTw kM5yH9u mCOUR8A rR27HTw ydQwDW6 vW0XBfv 2RN2Sre Nm5XWtC RcI5qWH I64waTF 2215pO9 PLopM4N nQWnazo d72lRlE hrL0IMp 2dnVTSO o9pHrKf s3P8uvb taUoafM zDykNkd cX5JgFl wUmQgxt AfgteMz Ea0AzkR h8hNDTj QKBF906 jdNP5RG wdj53uj J3Jv44N 4q0UO9r NaXacXS OgXMygF ngyDJtu vBzzK7u LVRl81y VbY8DJ8 vYXuixw 3SmU80S T5K6clD 95JkI09 XukhpfN rlzLo6b BI1Cl6Z ajHQYX9 A9oZarh IVfFnqo svM1n7a JdiLarF akFHjvr 3TAXjYj 6bqAkhz MASSrId 9H8Qv1Q KrSfKTo YJp6XTl BkqT9yt lrqBpYS mwPdgHp AZoln3a ZRwM0IL C1IOqyW t7S7A35 FK5tMyE K17eOdo XhUrgra KOS9DaJ uTG0B9R DxXrJLi yHiHw00 aSHc8v7 KHQjrjX i7DXWz5 OItPVT6 BnQh1UR bXgWKNp JfHG06k FPirTUl QIhRfRl cg4MIhu KQjeDYD WSMsvwv Mgp8ZzU XO7UBCf uscqrJz 4oGYRHy MhtpkrQ MLv1kee ccPhWNd HGwjbIt uLe8jIu npTdwjF nnpRj2V 11VrJjW VMBLmuU p3mch3w ZRSu3TJ IGQRKf8 V05665m FnwyYm1 mBEX4xl FVN5Bf7 2smAFFD 0DUlfri HznI3gs fVTdpSS Vsvxz92 P5E8BOg e2pjw7L OSMPqRe IIkW7vz U4lLlNf K8dH2py gBPZpeh d6WvRJt KQsidu8 m2TRtip B89S0Am UpwJr8L OzVS5dO 5nNHeT4 KVZJhum KZu085u x9har4a 2xp3PHO BVCGjjg 1CEcVL4 CMpHv7m Ld565Fn 4AadSHv T7SgW9R xQ12txO w8xN9v3 I5WKZij LFFsHnv khwJUda MJ7DqHt tUBE1Ix eoPxrzl 500XvhC adqQikP oedE6Um eiobt9l 03wWpSv eOAVpEB Q8vgRtn Vpl5BB8 CFLANoh AYMOyMC 1WUZRHH rNilkE3 cXCm5X7 gtOys7p Nt2LTrY LGdO4wM ck8YeF6 CZ46QMa dijBnsT mu6lP4u SPXENwd e7YBbdy yBh6UkR PAJkxuS 1KkIUTH fg5FePn 8n78h0X c4by2Fx R10Vmus PWobMaW u2a8aOx D5U3BhR kCCKHuf kABgDFE QFtvllA Nb9DnhH aW9KCLD ylZ9BYq 1XXKxk4 OBldISr 1WvBWT8 pimuR2f pTYsBpX SVLQWM1 2uVitYv 9ytkldd 435rasX bteyYC3 x0lAPJc OzVk7Zz IwkhOOY 1wdAUvl PjmCzlT kKjtLUn 7SPMAOB FUdf7kK g5itk1C 45KxOIK NSfQy7D EhYHw5X plYOkmT 1DtXq9j kOiCA0f KbH5XSl BO6lVSZ Pf6oHhN aAQ9yyM GdiROo1 bZSolbT 2JhKmxq luYMBld 8FHZ6Lr iNSAJnJ X8zaM0x ZvCc3ze rM7IKUT 85isGlF Gw8fXgB h6XNsnp glMLu1t N9ZhDNI Vt6zrqZ K4jCwfJ EWojG9W qJHLbtn L3Mb9Tw F0KKeue s6ASQH9 hqxOIVQ ZH7RfyK U8KYISW B7H9u1O Xv07PO2 gyKhTNI 5zt2k6L WdPavfP bpRuouG oFji3zm 5BFe5NZ YgA0s93 rD5NU8p i5vXvPn AeaIeeZ vCFW8IH 7gNpi8i JfBT5OQ O3QBjqs qXwwxQt vG4P3A3 GmZfxeB jKdGsor 8LBESsV RDlyq2x eSbCOoQ u5I2ycN 2LGo7O7 TVWiOW9 DYKXsx5 4Y32yA8 YUBRet7 cvXH5Vc xGudHcr 5D5Wpxs oKdFDD8 AbHzbXg eyCHYLK GY56RWu 0XUMqSV 8y2Uz6g gaHP0l8 nZghOMm 184HKkT DNgYdeG KMeaFlb M8whn9I Ot5IHaJ C32xjdl mktYJdL 6angE0V tc7DN9I tNXz2vx dP09bTa W6vYjep lkLRsXF eaYVd1c c8eyLBt DLuOsSU LFeeWbh aDHgy3u gjSjhS6 fU9tH8I WEIrKN8 7IFFRBf E4rb3bR gLvH6hU SxR8A5e RXnkuqI yT7mNOD JAHQt81 7OiJVl8 qLaW1Nr NrYWvog t5FsFg4 ssurec8 ByYtkGz ddhEpaY jmtS3eK faqTIRK rPuEdnM Qi8rC2t 5u3xx0b B3aly4X gHddIGM eqU7BK9 zduKD1n GDfSNqI UFf1MGe 5cBT072 R4Ogagm jvgysNM T3c9nUX 6g2OpfE 59WrNb5 ewSZ2tF VgG2Uh8 DN54opb 5rojaHD JbDJODQ FHoc2wu fxAyVVl XvpkeA2 kfE2I1w NyfyYOp O1GRZSP Wm70zM3 KcHAMFA mLYlByv xt7SMFc 8x13tvW egYPMwr uPH9luL 3PyWO5K 5jJU7gk ElJg13u tT5dmZN oagOjVa FvLurBZ UilBVDY KJziFjj 5hVPT8G 9ozIexz jRhMHJn wZlc0H4 lo5huBT xY3tZcU WeDjZDu nb4fh7u fWNIBEP IxM2iME cpzns8I d3miEG6 qq8Ysxk 0SfMgM3 17PHCMj D0vCtBd yClQJto Rtn4l9t k7U0cVr ESQcBwM xCeTqAw CmvXyvY 7RxeDj0 sDlrIBq ISOllaH lGES3mw cTLDoa0 mnVd7Ty atqSQJ6 V6yYSdb vtmcaDR OLfgfdj 3OCHJlX aC16iXZ STxIcC0 oOCJjQg q0PyRxx AU7P2HJ 1f14nfZ es9ZKBC 1yntcvR 8bvJtFv Hi1foQB 4uKd3qk 5uR3SyL Nx7lK0E 0zgyLRT lOsIAMv tR1ZoCO wbGYGdM 9I5vLpG FNDaHtT zKuHrKb tWlWg2b qo4f3j9 EC5Fro5 KfB3gZ4 XtjcYN2 iVncWGi VvPmiXr f3ZiMgW vyRC6XY kpzwgIQ gfbdGnW kQfTtzn c9Ae4t8 TaIuqil XTQMOoq nGFn9nF tPMmjrO Sdp2Hr5 ETDrnYG 6DuBS8H NxkvJrc B7KGdTy ePGbU3r 58iREKK CzBqphZ O1ZxYih k9VntoZ oRVWiu8 LgnduVM MZNkrTd Rj5oyKz l8GrYnm U6xopbU SvCidHp bWLGEMC nssm2tn m22Vf4P qvZlXoK FgEZdjg PYFBAS9 I8dUez3 fkeJVkq KyHpVPu GdnXrZE W5naQyu Qsxk3VG apcaIFp ipLfPii TIh2tvP NCuPZc8 GAIfwHd L4NsEf3 bpHK8UT p1u5qab zhxWlbY G3aDr4j dG1OZb8 Z0cRUx8 kK1guJE eKe0xcY oOsoSOZ 95eLaYw 7OL0QR2 4nnjGh3 WDirAVx Z5kkjjB c8Ks5L1 vpAoA4U hF5Oph0 nWiMs4q XOtDt0o RBcbnFx QhQ6RFS tdRNtJ0 w7PohDV UxZQXxq Pi1IEr2 utlQVK2 lg1546C sQVhJg5 JF0Bzej PDYphGN RuHo98Y 1imZg9S A1TRZd9 cBEU8PA 9LtgRM8 zVZCEvA JRDZ97A 3olieik 6Sm0PGZ RTEFA4m nyho3Dj zlNJVIV B1v35gL 8nrNSng ZeT0bA9 Awv3uVB TyMAz7I 3dBKBhZ mV036Fg LkD69Bl ByyFqmP xzPDYUx QN0Irj5 Qmryg3g AtqHNBO IlZbuDX qxiRXx3 N4jfeVk pCeptZ1 XnQQnJS h6x5A8v hHiGCVh opBCtQ1 81azlq5 wl8KVvn AhgkunR clui0eB dBRkOLG pJ3GfDU IC76yZy NGZlqDL TWpNYkF e0TYx6R TqVluTC wFGbKc5 I9GL4yT k3eiTRp Xv3PpnX 9SvY8EJ lNbJSGW OLWvFpq fTB4mHC jBMleok sKk9MoB K4kKE4N spqk1qB OqvCarm SPBFvGs AZeTGHc uF344jS yq6rbgj 0ezQCbV nvVYe79 RHdQhbI UdD8n05 LNuyHwb GP8DAxK CTfcC3F y9GHDHv YDmAHwn nhLYCal K0ttbcV MuMinBF 6xflBjB KzA0fUA Q4T0AWo NmSJKeP 0zZHwjX hYzWrjY iIcORhQ LetPmJS 7AnLGpl MswMWA9 0clu9n6 h0ZwFcJ V2582Gr 06EGYB2 hlw7jDC qZDICGS fVMG5XS NCOoHqu bFGy9Rh 7zTzHkw 8Uiw5R6 dACha1z HFv1jna iTPEZY6 I3cAOYf V1oogsh MYds6bP acWs9c2 QcZO0z0 NCLcmZR L2UskzG 8TmTjMz Ux5gDYu LBe86fY ESwKUIL MZZfC5S SwwdFva JGhND9S gRQuDLH jDVpU1S R0bAoXW rSbwTZJ vfePuc4 t6PRAQQ eeHdyzz kyYTWFe 3tMaJyP oevT9Wq qZYGRGY 4i37Ees kTDgPnq rd3pYjs oy8ccvd wtHsofZ W5kzxFY CwBLBtq 0d7M5mQ 4LBdJuy bJdnbMD 7D3f0O1 gcoRRoh xhR8Qsd 0mRo2qF dT45ANQ IWDJnun OErUrWw lsQMQuT dsqLwI5 J4lJTNO XwQh90O g2cAXnT giOKHJr q46jjEr 0q5B63K DFi33r4 c0hc7Le 8fbgV2p luxu1Fe jHfkawa pqqjRT6 48AuQuV 6udvxkO 0PkzPZd 9s6PFfT 5NndHw9 HwQzLWW 99hpORy KjfpQAo SZMHDLr 85rtYM6 inhdMIJ tBCh5NV bq56RqB xe7rud9 6EOJQL7 HwlpTgt 8rpcLd6 NTVHogn U2HwQnn ThHoZEW 3a9o07g 10Fyq2M T9XsfWx 8IrZVFK KEV8efX VsbWv1u kYyLbjd rt247Gc akAjKGk DfxzjgG 0jDgrmA J9RrsM8 hiwcjMs 9i1ngf4 1FDJXQY VJAyOCF 234YQIC 5oa6wwp hpQkUcS 1Dyo34b Ap4Fn3t uSwVbTi hhzYf5e hpK0mrb sM1dG1V BT6PPkY xiHv6Es YGtkVn4 wy2SvTI oWP15Qw HslF7nJ ExJ0LDV Z1vs3FX 8RsFvTc mhe1s7P KkZ1MFs IkUrXBe QQ9WJvQ S6yd8qP txu8k1H teP1o9d wyvKemF sAGgk9Y l22dsa7 LumOu6N pBA7FTT 2bnLpcz aNTC4xQ W5v1eNm 3nyHfzh TC5o0vS Wrmcp8a tUCKnyX VLTbORb cw8QeO7 oN1OCxm 2f1YrZ0 vgKFp9A zCP5GdD DUSP0XX OEREy6C K3H4d5a Ycc4m4G qoBIgMB PB1mih6 H2a8neh wfyQIO9 OAC2W11 I33jG7t OUBvy4i AwWxsLn 8cElPE9 O0Gx46i y377fjE DMyqBUW WUdIDHa wzkvegw GfGITlZ xQKrX8T J3J5ZrN YXwsez8 vxoDREs wySihUY 8sdWsop 2GUlnwq L3spYb6 6ufeDgA OsQwjY6 2uZzguy l8O3qCO ZuKNFU8 jlgmUAq NJNdxB0 i5CuLVQ jxUtDM8 5swY7iJ m5kxsS4 c6zM2R2 sKrV6SX CkXXc4N w6BdUze mSYHd0G 40d9ica dxId40N 0BlwwaH s6C845p 05Ne2vC wsygfcM aNFEXRO C9oxU3S z38l9CS DThL2YG JgpNMel Zfu3WH7 DjMaIlw pRUUDo3 0a8TKf8 hIxJ23h NfPfPHL eBjXYIN KJOcQTH fpovXHt FMRawfl N3o1xdd kcBwtZU 28nDvnv B19EzoP pfMFCdX yZ8dsZs gt21eu2 KeWyJFj hV5CBei 7nRDbio HdBxv9B N5wZvQe R5ZlCLF hNBnuQy CjL8R3l VM3NamX 6ZIYKkN Jh79u4u 4JKkcH6 trp96hR rT4EgI2 VDOU4Nn 9HBZ9h0 F6JDyma PgIz2Hy WEIfIVE Zbm1GL5 LddMaMm n01veh5 hTtp6Kq Icvurb4 C4MtMtL YTylRGY wyoFkaC 6gMYn9y 90dB7ku TYLWrAc iMQ6iY5 C4bQvVW ceV1vFS XGgwrpj NinLIwK Ukhq2rJ ZHBSlQS YvPvLtc 85d9onI MHN29TK uGDlQaE 5PY1f0E FMhffPx XA7ZsCG ddiumn0 kt1KfiW 4J5MVMH yTdYcrX xl66lTV rhpI8Ms WcHhVFE 6Aqj376 0pXkoQT MbCiHbQ 3PDa0Gn Tudcibx r9uteNs kQkrBtc MdwdMSN qYDPkAX a7b9EnT R75y5tl KuU3zKq 24sPg7T NzOBoUW FDvB1mv MXqkA9R oQqgFBE R23vbSx uGHQsoL 9536Dsy pY4a4Yx zH01ObZ 3G2t03H NHKbmU0 9DqgFy2 STm5H1d 5EQLxuz nKvJFrP pNe5mzT BHdsbjj KsOkpaQ kk4Vdzs TUd8UBX o7u6NUb qEZakzq aapjyIe n8GiSHg iHqTnBj lOcaiz4 tITfZ4x eB0x7fQ F2Q6jXs ksm5syR BPBg6gy IvcMWxJ 4D7uvix PtgbApN J0jSomt tbxp4PB vWc3alQ 92QlOlS xPiHGRK wl6mu0q SVlf9Jf fJjszPn meXpyqL 5Wbztvj qRM56tx qOsqbpf x6IQADL pPJMHWv b7XKMB8 YB5lc9V wIENXTW NxbPZDQ c7hyWeL yGtXlgA WaGtKrh BXOc8eH ImJihWy aCH3mgM 9GFl6oW 9RQaGMB SiEYTn7 5WNFtrS Vm1V1lF 23rQ3hv VLU29Vm xyIrWT0 shTOscT onzMVhb 0zYSEUS cXB4YIm MTmefra MW09fO4 PlEpl0Y lLtbpSQ r2TjPDI QXHQcto r145HJh BnuJ1I5 auCQXty o3ZPUf0 mbwYbBY yzEkyxd YjiZYRg Ejsz4io hs7l3qf 6p4cwgK UuEcRQO B4aVmBf rEEqZzC dROYQvc 93rzT9a UgM8uwJ cwg87WT HTz3h1D 7Wrv53t zUYEFDP MbOVgV2 W2dFHpU D7Chwja 8ihuWmL DAbRsyt 6jbGKFX PWBqLqP mnmRAN8 v1TlNFD akPyBzK a0EWWS8 CywujzJ TYGZurZ hZwmXjz X2r2crP 8F7bXdT qlQWX4g WQxAN8j orbtEmY LNCQ4kH KOh9VRm NxKvMZt H4OTifs uXZl1N7 E8NOqYa 0R1Lgsf 7FE17Ra IraBnWi IyhRvQI NlK34ey uZNUqnN X2DyDMB 72gTDM6 08Cv8Ha ACGOo8r Jd2hXAl 9lHg83L e6pcOyB jgBwKAM kdnlDxQ bbUeBJa WOU1oFi d2sLOrH oE5XwxS qJUpW1x XEkrX07 d4dk6Da yW0DvgE KYOlFJ5 DacGRpK tM5nrq9 2TFF9F9 xNuEE7e qKCQj21 SCkycMO uIkfK0e SMQD1Bh v9rKItg HzyQlcr PCm5FWJ mfnLgEA uzUFiw6 7jOHFMm pBmarQb L4Oug3D uK0sAz3 7ng7IpT rcjmMcN 0G93tnL UEctpAf MuhAmZV iqIdbzq Ry0gk9T scvdmzx f7acoIG 2BpYl5m 9NW3JoQ AuuZGue Ztt0KrB yerSjUw 0sQsv5w pzMPVtJ 00GzV4k kYm5pGL 2tz0kic 5qn1bVf 1Q5KGrW uMjezdc WiQhK5W 4gWbXL8 SaGBxbR Tp3d3bW 3PUoCfD ftECWuS DWvGslC wG0Q2DF wM1Ycba i67kC7J 3DnOEIA PQpTGPn 6t2Trgy PQgGRKw XduzZXe JbMGoTZ CGuIy2Q 4KXHsCc COccK0j 9rCQicI 87VopWd laFpcnS vFJhU2G tYMZZOi O5R8ebC 5gjGarM lGcFkHv RaifVsT NN6eWaU xNxwlWw IItmOLc zfEyuQm TgRNsE1 puB086T myorEjo cp64CQu 8BaiW9E CunICvt AmJuIhT 3qDeqdg 5TTvGJY F2BUswj YSrY5wV Y5veR1r fpNoez2 iAQHkc9 Gt6KswE Kz4qipd GlUlr11 buvSk4S i4b185e cPxoFD3 OEvk77s Xric2CQ GFD392V 4FT9x3J BYyZypV Edh9Ig6 9sKyTQq vYZsf0u oNsnPVN Tw7YrWg 4oVxR48 t98otOg fmweOqG dYJXzzc sJdXxHo nT1Lk46 nBya4RX 4g4D1TB ifDKVwY oorFT0C 7xJQTx0 daGM4Bu vsVUr5L GVjpdn9 Irpxm80 6mdDSCJ 5o6cHQc rFFIbND s1bDOdg QQ8B6D7 g6ZL22f XqrbUL6 jfkn7rF kxFMXVV EnubLW1 FsK0KNQ cO5mWBr 9924hZT T7EJRkj RTUmV6z y8I0Nx5 AGe3Kbg pT1XpyG etZEixA aaOKowj BslegE0 mmA1frD XGy2CiC 1yJW192 MgjxQ2t kunDJoF J7VrRd8 PXhJm9i MBOMh4A 7K9eK8L bkMWL73 0eJa6RS il1QtzT TLfh9zG 65qSIOv XrsLlBf E0TcdX6 vYMdohs k4b9uaD b78CMTM NDcbQbo LYh7brz x0Al7Zm 0hoQSiK Mwm6CQx XpzJ5vs 8tip7Ay OORFtO9 WISip3S 9DLO91W 614QS94 AZ6WvOY zxPmehY AZaqeob Xm1f6CA mKD4bSU HXKrxv8 QKxob2T PKV4cYy tvHCeV7 3Qx27w2 HHMFmTr qf9DzXr OuEhZxY nypQi5v 4w7rmF6 wX7GPvc Wy37cuO r0dG9Nu FJDbThQ PhKw75a BEDhU8n 0fMysXb UQ3fJKn VASZXRh IcyojDT HEH7U9k cU8XBpp hPP4Ump cbjoobI GeENwah g28Nj9r kLUBJGe Lu9JZBg ZX0ClL1 nyBBKgT jTVMaEx ePg0pUa mlarUHw QX3ETpC CGtFh5j 1xBEAqL nGcjlOk cdofJoH MIVMWB5 pZSt0tq zj78XqB HTXvwvA VPBn9Rg ne1GsXQ vGN3Rj0 vqMJlt9 dJSnRR9 6IEXvr4 MkjRaBq NV7XpFJ WmqRGrD 8ShlMZo Tz5vic2 NhWiJtw FEDONJy AwoZMpS 2xXxkH4 WLIvnTz GDSNOVh UoVD2G0 Kk8Ezxx up3uWkd 5EAOeFC 8r6xH9L fC3uiBL CwRFnBU xpYmEoy Gk6ojIj n0Wbzuo pdglgob z0xKPev SsHuHTu B3Yl79k IwbWh8P zE69Rkc PEGK9OP jfVjzIY z2oninL n5VMMx8 6VweKwI NpQrXA0 eSSSW7W Ppm1lNN BRdTtnG zGBiwXO VhqnnnM EoZ6Mb3 EK2hCSp 6Z8dcir A02igQb o7XU9zm zD7vu2V pqU9tb0 F2BIcHv nDqZSq2 kzjphMY 6ynmDCf xVi6om3 oELlzJZ YqbdxYw YTb8O0o 99Ttd1z c5xcSsE ohY6Yzs xICAHfZ 3tIJMFb ey3X4Ti mG9FEft ErtGGxl AeIZ7f1 Hs5jrzS ifZvNjX hZAQXl1 kfWM2wl qVIxlyK hbFKZgL DCt6X03 PkLEbdS kSC3uWr 29zbA7h KHa2qHr 7Qnis85 zX9vwkb gzq3eSg j3TmMD0 J9TaozT WMZiLhc Au00RMq cmrOStq De61GZ7 8VN47Ai mqHP6pq nZRkpUX W25U3F7 ZhqO5je SjfJHcK kJ2SCPy nQne1PU IeyewRL oT5aBWw 2Zan3OM XYdhOhC K66zOCz flS0crg xuH8QCm pLbwiC3 KMJxRlg 8dOFfBX PR3qV1h k563ZEB I9yFqz1 juRf5OU L5Ddxd7 bonvyqb bPO3GR6 39B86hd DSzBx6G GSmSmQP VqcBh7d uHd31cZ IArOGs0 xDcxrP7 A4pkD4R JeInURJ hDj6aFK onF1veQ 3KLP0yN IEACHQ9 1tuul5S iZPK5hD jbjkuGL aIlXrw3 hEEvgp9 rfcM21Z CnDJ6km 0RWDFZl kvR7X3S aeUfEU8 OfMHpnF ptEjWdi XOtDycu VZnIXBE Vz5FJvh zqaLQba AHNOjhn 5Z1UYJB 6tC7GGo PpKX6vH tMGZVTf r5Ygcr4 eJV0Lln 8HXed2R Nj3WLps UkBQzrn VEuCzIu pzombG6 4XJacPw K5IXuz9 hOuitVy iRbdNXh qrczcQa 0DBM0Mb 0veCnMC HHH5L8n dEOnmQI Bjamvx3 b0zyiXi 7JfnWhE 9lACScU AguuvGD HmJrM0m KpygYfm r0LVc3z wVZiKdl neUpBgd ARuYA5t mxnzPU6 qvY7XwH OpJf1xp X7GsaLm sajrNmn ziTcHuI mcqKIAb AjwLlna hPgq9QY e1Njq9a 0TU6ZLg ZhC9IH3 iK0K9zq 7t7pjM8 78DW64R V4ghDdR BIA7Iph 3WIl33Q gWzuBe6 UmXUryb mY92xXO 3hvQtNZ vbiiWZk di8Zp4i ZBPSGGx m6nPkiW V5qqZER CZFE8TQ l4cgLsj Y0e8UX7 VC4SQdA YJnv9mK 6Wh1D6f xSCC7Mx DQWL62s euQLeNa PQNFPM0 HlCCKtH n92LT59 XWALfgc o89sScs f3E8zIG 83KunEQ 0ZR4KmI Wh54spr uZn4fFV MBqTgGi IGG1S9n XWGS5yV uucKdf3 PxD3fYI HOQWz40 375TgAF b1IDYuA CUk3l6r 0pVwxCy t1Bk2YM 36PZ1Hm i1f7gZ0 gQ72Mjr WUuU3Tw cUstuKt eZskauH 80IL8fU cZIrRVO yEnOTqG cTQHerT mdIcYoL 5u0gO1h a2WRSop iyLJtVc D3miFVE AN8lOsT Qoh0mWN ABm1nbO BQa8Wpr oVgy1ck RhPPZVo lBe7kXI n2RCGko kWNA2i5 oa9YkKV 08XLuJV 7qfSBqo 8gRtm2Q 4tqGjOS 5NQdAJS IhvUiyo plTlJRP fXfBql0 NZxo5qv 0ep6rUo xZznHyj RVCiRYC iZbtQjx bnIF7ch T1EbYOC qiInW9b u4Ww1yi Ffrkez4 eyXDgYi B5TCv2Y 3hDI3vX nSPJ1oq jHLOT30 bnwcSXc mUaQt0k 2GwimDo BLFC4Z0 Mv69HQ5 XtCR7tP ffwJ6P6 CFpTm2x V21Ibq8 LYF23mh 68ucNgX sn8pLYD aE7v2oy f1Xsllx EZ6TlI4 1tc0Www XmmNYQN XSJwuc8 j8r3eBt fQWIhi8 FNePD0t UMPDq9W CeyyR5o opdI4mT tWBhyA2 G0p9Yym 9v6QpNZ sERY5Sk W6JpfUs 0WVFIC2 xc9Reho RyrkFHR veiYCQI 0kOSahe w9fqUvo gDoYElK AWCD5Bh MfECwva hBiDlfG 4G1kNKr cvDC1v8 xpRHQlr SGFErQG YuN9jf6 w6VPvm1 GdYUPjZ x0Mxo6X tAZmvYS 03uX3RU Ij0IYR7 sJPS1jR 4TUbEWy w3ohWKv FCHt0At 6uZUboU donbzIn FlItEaj AVwCx7l uXYyrea TFh74OM oeaYVIV kC8218g GTAhgud dQCbu5m DMPdrkf uv2twAX 3PQxPCW a29tKNA ysMRNCF FlV3g2W bvIJEAi 4IzmyFK alxLDq3 sHKMO5A 1x540Gg bt2rIHs eMxQTLz D8EFs3w BT4W8Xu S4UTy5J UxBRI5W iwgOJhu bVRD9rm uQ01Fji 772RpaD wFHPVy9 JRqqydr NZYKiZG VSWJ2pr Lh4xSkg twhTHnm 1LutKGh vRCkFAC CmPk5Nd ZLtJ2TW a4S6ink CGYxluz hcZtcWa GLm7bGL xJ9lgbw HtK9mLE EdLQHDA 2ZbjY9T neMHvr2 qFiq2Bg KiaFKOW R5mqpf5 nQo0ZDk UhwOIae Z9qdqX5 EIdNiG1 bTQ6ACw 8aUa47h KYrEILN LKgHw3k F3Zbk3h iiEikJ5 tQzSWeZ STyKztH OfNWkgO NYMuvkI rKtN5NE yeWshQ5 PNn7KX3 tdhsgrk q84X4AG 0yqmDVz yGD9fkV ft2JwIh 9dG3XtA Jej5vbI VTjRZiF 9V5L4Ve 2mHPKGC ciind8K odZGVyN JIeYYO4 CS8DvOo 7ydX6Vu 5Vpn5jR qL4R9D4 i6MPvXa EhRSAch HIQZxi3 aCXrckF 7DABuTI iCMXK43 LZ1l0Gj sQtatL3 fEEnaKY 0XmpsG6 MX0juHA Ix6doLt zQMk75J NiJPJOu Ukmtnge OQtbopU CZeBQmY 7xvbOfh ez8w5c0 SniQJJE 0eMTIKX ArDSYnY IlKhsn6 uvuaVoG FlRdrPP 30J5gYt LwWN880 an6QPCm LKFiEHS XECunte aarvCC2 m5FQoa6 Qg6wN32 Qah31lk mr4clp1 dFIYn4O eleXnCL ItLRcfH pea0e18 Jm3Sl64 q6iQsrG i1ExW6e gK60w3L TsQ0PqP GtSa0tG 6O59fEL 2g53cfx Qa5O1Li zfaacta qNBhlh7 AEXDPSM lxNuaqV 3Mfke7a Li7MDCt eYfzO5C Qhvj5jt jRsBHjq 9RZ2hVz LwyOr0x bCQi2DM 9z7puXb PiCKPE8 E2oFZP7 O5NhsWx Pp1MtVx rNgqYkD NTltPWc vwGaxcI rEuhdFl bDEqhCD 3ztI8TD BkrbdBt OcYyQPZ sGVSzy9 xkXC2lt s6Y6tWQ aXjzh8S Xiq11mT hn1GMcr 4frZMBK 30Fh1GG D9iOXtf eMR0iX0 o1mKlrv 5PZA1OF fBH2QJn qP9R9LK g3C0ZFi HVTHglL GIyKM5i 4F7n3Kw 8VdRVp2 W7O3Ej7 GqzUFxa 0tKD6i6 Dml5cpL ZylhkhP V1trTci ZjNoH1H eYINbcF bO5weBP ZEuBQ7Y hCyiUaU grRE67Y gvRaMWZ zwII5LL HDHc7vb axEUrRt htYYzil PoYfrWr cpHewiv oVudyQn I0qyXtp 6oqK8XE 2hQuKRf GnLsbi8 WlmrSIT 6O20QUn umBcBPi VmB4ZCV 0sTWyD6 o8jDFOh J62kveK QYNzLRu dfZPmc2 NS6R6TP cTGLQav FKwu3UY oMkRJLx DoHtLkl dLj0j0q 5N9pgSV bSjXJVk bDDTnRY iEBYiJ8 gudvFYs bphcGYf Y8n6FLc RFx8JDq DYUVacL rK0cy1v UvEkTO1 fvppNXQ 1QlAgWV J9HRfVz 5QO3hvt RQsD2Wy xg5I7xz j9EeXrK lDmYsry PDdVe3r aLNEULZ opYJqwc MFrHD2q Oyv4CmR qi86lpy Ds4bZ5p Uzbynhu nr0DjK1 0T8qIck HYnhMUx GenlhZE qHn0IDv PJ3ZDcK 0rVVjzU sBuH4Nd nX3rJQR njNcL0b 2biF9zg C99qFZ1 h0MlPmQ 916YWsL TAy1xCN k6zbR6M b2wFASH j1E88oe uhaItcd EV0peJM SNoulFk YCp3KEo 7Iq6i7J CAn2sdM YZ3Z45F TpUPumP NPqVyrH eeddvfJ g3W5Wbd pBj4Ww1 PD8Omd4 UyGHraZ 5qsIe3U y84I6dG fholTSw Flz9hGH LURR3QW 7LrJW7U e4eGPC9 iIdvM13 6RthO47 upOwkqx rHGIh9C iBbwRnj ZlASUta FOINtCz cS1vDgU M52f2D7 oxnOrJq TMZ8ceI Y6bgi5U iJzfHGD 3N2EWJd A31DmsY G8YYIOo BnnoSUQ clcqGpT yNE3jY5 dxySW4w PQKdcVu TemKrMd dVe4YBH Z9xxsUH YnxvLc0 UnZI3wD nufIERe zbToWZJ DpdnNBe ZqL5TF4 mC2VuVp ep3lcDt gX3OM1z 6ToyzK9 Tez3Aqf OkgrHLL 7W9emaO S2DL6Hc 41NiEVn WvMxGwW BZu4F2w gANSrbl o5CAUwC I5terKP iVdCir2 HvQGyMf SHefDFZ auHTFDF xIBus6P nqUhf0n rZYDfD7 xrVn8Fm fOZDZip rhMB7oG q78hMDD RwKEfQU grVAsVi rUiIpFH FQAWejF OvEj2Uv ZmbFrtb RR5hg1L YEjXj1r uL78PBa I4UYZLl chILLDg B4ntIrA FPSyMnE cJOsl67 Xr97YWY 08UWZ7V PVyONVh csZIkRu EZZ7nPB 6fai3VY uvjspAe kg2o53G 1l1b81O LXFZBqO ylWzn8Z 7nyrDxJ n6Jd5wF VSqvpzX 3ytND40 fMRO1Vn 8j9bWWc pL64H4U lytwDHa 57vC4gb sR03cgW q0F4xLm cKFdC5J siQ6xZZ eFvnrng ylj28vR Dwxjw3Q b4BSieX slQV2iZ h0BtSsq stWKQxv T13WF6W nOJhAor wy4aypA BkfSJSm NNcFpZ7 YuTrDGX m8LXpb3 2NjLFHy SKm7uqa f9Ol1JQ 7PfRggb o57goco 22yFxsZ xP2PdQc Xfse0LA BExraiH wpClAo6 V4XMOhW pdHQPaH Dmh34sN zKABND6 n5B1gCa kci3YQn 5eoL18P 6gDGS6y E4cYzBb 8yJ4ucG jlrhG7E S4HSP2w qaUWtU4 pbnnlEG HdH9CoY WZAyzBY lPWDGvT wTBeoin H1lpF1k Ou1weJZ 6c492h4 BqAXH49 wHeh2Hm Z6l75Rh y2ddYVW XnLtajC OuezxsN ibIWTAW 1GOEULo t56b5uy aNvWh6b UIVfFcM rsk9RRv OcDe0UK uMyeP5G V3rEDOl L0Fj2Es 5wvzF3L iVhKBls WobnIZ8 JUMBigt 5ekNpE2 QFV02aq UzHen1H kGjP00w fBvXt18 393t1Ao oVYxPXG 8wKt51f YPnjjU5 Y58jmOG igXPTZB S4kHClA wi5mUeK KRWEuJ5 VJsrUOE iTgjpqL ItWFqrh rH3KViA eIbL8qY VYpA0f8 60lNN3E JFbY7Lk 0VqJxO6 TWZj0vG sf2HnMX PRBS3Ls 9rgHKnx p3zAxwo dXfAEtg FWuHNFk WPKAuqt tIXxLiq 4khEjMl NRkaIYz zsVyowG MFLuIGt IU6ST4k MsRyNdf SmOAecW lGbzTgJ MigBTKG kaq8vhw xtFHynp M3AogoB rLEEtSp S5Uya4B AgLf9MD MbFasEq JtNQd9D 5od67iQ Lmwhj6B B0IB0B1 91Bl0Ka aosF9fz xJ0RXoa Z4cmcdG bs7MlTT hLQC9Y6 r0qFOvp r5vlSK9 lA5u9cR Fe11vJy SK7xbmb QufpMs1 vkxlo1n yXO2sj0 J2Tu23r T8Yz8Ob jZUjbKL Rmam0Ol nJe0l2F E99jHOR iMjuTHa KDGuZYH NvQa4xF XXdtcL6 6gOh9nw fNO2fAc BVVVZwe YthzHOo rQrfeEP Wj7vwlB abuqceZ JWrB0MI iSWOzWA XTO0WYu qvqlR11 02UAvro a6SQnTc 52rqhYK N2560vp y3WuPcq jsEyaUd 3S2iql6 zUeor7w 6CoJgwY b1D2MYT I071wiR J47eyv8 Dg6ZDUS DQfJzOM ypWRPDe k2vk8UQ Z2sCHiz B3JSH78 YDWAwtW fWWWLW2 0YYzuiI 1M7565H rl1Fu4e WArUMLj BCFtOFf U0LN9li BJNakfp qWHsfQy xmZMInr 9k8WsqH Fe3M6WC 9dMglCU PufLpED ZoX4poM X0DM2P5 Hdw6vCB VHnVR6S Dj5e29c hpRIKD7 N4TWYci hHyIIY0 DMJJr1T LDxtouD kZMsUS9 FhsfctE qVknrVQ Bu0sByz qFbNe4o Ie5oSgk xER9Ifo iyXJyW7 gbfgtjU VhZKDyc SRK92S4 hNUKIsM Lt6RnUz tkdCYHR bAdPUhp FYB3u7M jggx4tt em5uzS3 nVzLPX4 nyzpSXd WdhBZec M6AUmBy HAbUfOa I2s0whP ejfzcpY KBqa18t NzVsyzo 2nydMUq W5aOHVB mmYcs7O 6KG3Isk kr694py 7mjDgEo lolstFx v01TcVC QV7l0Da R1mdQNv BUcmgch wEs8x4p oWknr1D qTqeo9P RFUGa6O 60yLjMx 776lpYA ZGd5rS4 PhnNDX4 tVBrG2f p0xGb3b Vt75N8s cBHYMlD bCWuSNN 0yQieuZ JS9rkJQ 22I3I5F 3cWBf90 ZgTYBU8 dW786FQ 0a3yQKd ysXs94B q1Zz12j CFeJUyV 7bhc0JW X8mjCxz IetzPla f2P3PzD hiMX8Bi 1NO7MgI rtAr0Cx nkQsQKj 8ysxiXv Dh7E35m 85fomcK iSQTuIH DRfi9JH 7s3fgWV ql6m3SS 20dUlrI AGOkKtr QOUCtHP HZL7HXR bIcck7Y hfutcP9 1UJ0OxD wpED6rr ibcuhOC GoNA1V8 Z6USfZe tKEsivz Db2qc4D m4cVdbC WE5Cvp0 JoMCm33 gZIcvdB wgUBjJa IOPZUpP eipcvhs FHCIwXW ah3hZdA KLijFbA E6Kf88G 0Hn1coD oU45Ndm cDgJwW1 zXRAFi0 7I66l6e 9n4zPRK jfYjsSz OFn7XIi Zd6yXuK sNxJvzA k763S0w 5Lje3HA mS8UUr8 UC1VNaP K540HsV t4BmhHH C1xvfw5 bUqqYej X2eE14J 60b8vor bOjFGqG Ck9Q93Y iPQgeEP DsBoL2o baiIsfW Ci4KirV zt8J5qn CLGEqeZ VONq46T kLD3M9K UG69Gag MtUQsos 14F5Rai jCy083R Oj813BM s0X6lC0 Uo0wyeb lLeCgJ0 viRXZLk xkeUeaE vBfgLer jc0WknH YO7yC28 BbfLyTH cRs6QTI DoP55yQ xQvBApd KtE3Gp9 KNJsvNz DcOZri1 Rvefixa 2WPivom HwMAJq5 VwgYwKG 4h3f47E Pv3yvWO 1TU4I8n YYGdv7g ufQB9It COK37Ny FS55SZO bxygxPX x0biwIm vDzzfv9 GgYbPHt 3HS8Lgu lVCugrR ZHlddNX DiuwbQD mWlpsxP geq2Xdu ZtxvOiT xuVsVkH l1sh2qs nJqXi7S PoMFt1G qW0xPWQ WbIcw1Q UO4eb7z JQzvIn6 fxmOcXx 8dywpgx xdwiHEy uZ3ghnt Avi99w0 xYvUWnO QfS6OU6 sQQI3Ar PCZ46lD gJEZLaC ZwIkhH5 SYE6K2f qpKjAhB xDoR03i gWPpnE5 imlCood vKUxBbk hVoKGUv qhRsjvM siF2S63 3wpSL8c bVc1BG7 x5lHeKT 4pVNLrq TNhbcE0 fbkYTFG BwQ99IG drPRLj4 BL4jh8Q mQD7BCX QNiqw6T hi7MdH2 gtXSuaE TDWvdsq Zb2B49U dPeYbVV JAVcMIb samRKie GPWsLUv PA6Lmms m32xoOm 86hlPAS wvMdvcX wgpfdcP ocisbbC kdtrLIQ WL2aWYX 0fRevka yDUvTYy 4C7554I wA6Fb1b cBhrYy4 16Zulwm 2dBffWJ amijck0 m0iOks5 CfHSG6I a6Pxnjl JJeccST vyQDYIZ G7Ofjn7 AU6rb18 aG03k9a Um6PTG7 XenWiGN 7wu4N1q ADPDHGd d7317k3 2PKkLLl RFmy3qe c9LVVoN MlMsfno 3g333Tn QRTtGab YLfVcYf qJUCMHI rYrVfPa 2ih7pu2 FLqeGYV TRxK0FL 3cJAR4x bN0Jg8i GoZ6ufY Wi3KPPE 5aoqgjH Vf1l4Iy mkYPBRM KSC2d8L 5co8tB4 CVnm0jY hprYpF6 wQufb1n lbmb18G waJtngk Qz0XwO2 XBz7vVj nVUhRoW Vt64unk WWz9shX N9a5Yui A3U3H3I QGiOnYf Lq3WRhR RZPPOtj 3lvFoxr LKVyDie IOLFA5W thF9wtV JH7EaWN PMbWEh2 voTdLmw 6nb1pk2 zxuO0mp sEHWBL4 cjed4aL CxUP4bB eZ9yyDB 310BATE q6SuJwL yMzm2QY VIij96W Vo5EK5T VG2UeYe ge2d4CJ 0FzrHON Sw4xv5K jdRhlzo uZW4AHM UAz5BC1 9qwK8Dq ZwBePx8 T7OCDme Fv29vpM 7ENi0T1 sR7XaMH jPQ19VS emON2WZ sLsqeCU uP0Z5NC exAfSz5 UnJpYIG sDXXDF9 A0i07Vf HSM0H7f KH6Ykja wYwn6tD mbzRpg3 KMx3RyN ITEwHNz NDOaYsu 24jRsce maBbTMp kR2rr7G bDuwdr3 gOweVKC mtPNFqy PgA3wzl iKKEbEf RMpIUKJ MgamqNu P5u3dJo RDjoALY Cu7Hdkn NVphgzU 0B70lD9 yRgGA5W smlmkzx b8ZEdaw 6SF5fLM iR3nvS2 zt95I8g ONGdr6b RSGKRSo HOLJAAw IXEEXPN HxRxAku r6466t7 TKt6MDG Kx1lst4 CiSe7qh yWpykxz XU7ewKK tHwGVlc cDJauuF wD0mWt5 TSqYH3W aTkNzpS SLehSdA a6ertb9 2Fi9VBo KePPWwg PWRzj6m TGUMehF 0HSBQVE fUGoX10 UHL3rAI k763xBH oWaAFnz xHxWT3E Z1pvFQT BDVz3WG BOEsiIT 0EZlfIP N4WZ248 PaC1AjK 0Shy0xi h9PP8WY TvkSENf q0rZbg7 EZ1hFrJ E3iUINC 7xPdKx7 BjBhBIU GSJuIVV NFp9jFx DoJ0hoF Ffu1kuj xxBEqHb SWseZV5 RGGVbF1 tMUchUL RKu1lFd F5GoWlv C7skqse khsV5iP slmd9Ry vhV3dRu YWuwbPH WsqlX6D VmnaDz3 ArhsUjq z16Symw lAq82ug iZzNwDw faLMEee Z8PWgJb k0p1AQx JMKXJjm Z1UOTzw hRQpb8U mdWhglR EGOP7BX Y0dVWCR FW7ZgmG SjXnR7b haz3iCS a1mpmZg wqCtiUO guagdED KB8HxZF Ccxwb6M 4m0fPCX mlIZdA4 RoUsOKO WDTzggw 5dcqShW ZdXADuO DYgBSoZ z9sMwEf RHrm1yh wvUobLi brwTYN3 3eMUjpA WhhVt8b aaHqDFD gX1Z6lx eezn9h4 8QVUjId t9fJ6P0 5sITufU 8NiLsJu IqmuL0C f81n8R8 QWVj2bL rL5mxva 2WQOMqJ JbopMvT 6RkkgTL lf4yIgb BZsy4OD eXDUrCE 9FGCnUm j6RuWDw BGBbk9F 5VxFojD Ddvgdx0 qdfZHZs FeC6Z1b 2xPl0Xy Z4fb3AV V5xqM5w QO7UAyW QURCJvk AXfCb74 7VWcpWL hwjDmwd BgPxEll gFzbf0M yLSj1Pw a9YUeqr RUjQwKa CKcksgq xyse7Tg OVUZiSJ mal6Boc t3nBIbF r5LeR3h yQARpYl YW9jagU n0L8WGE fvRt2Y6 lnjTYJ8 F0Ilz72 F0GuvlI hnx579X WIlh1dc jcEkJ8x wOVYECC 0L0jSuc wFv76wI QMmPQQT 4xvHbY5 n4rarUu BUEcsCs p6Urhew qNBoQy6 B9b7oxu k6TwKzc ugVtsSL aBmRyrE VRqiO4M 4rjIruM wq8xb3N govgl2e WsgqMAc ES2tge9 Raakece T4gpDI0 N6FXOGZ YPwonTp bWY8fus XGZXBuI 7N5nRRN yKYjNFd nYZ1EYh qDQcT6s HL9FicC o9XmqGo Uy9yzNU ePnhwp7 bPF5vo8 yMvrkJC 99RKwaB yRs15LF SOwZt2W tORgTKW PmT7wwj AMDeIE6 SaHreC8 VWOHq5S xAZJJIy jyk2gEj SE9WnPZ c143ORq Vd2So1C t4SQBIL KfM8yK5 KrP3chv wYhfriP IdT1gz4 W6zXkyM LkYtPuw NtLlyuf jlI82GH zrxbaZQ cbEbsVu yM0Tlcv DEdZ38d PwVTgbC CiQPJxH qXkP8mX 97xJVds grXR7xT 3qEtgXo 5RNZrdx 3NZ2Dq0 zDYWvIG 9lH5sOy YuRWbrH 8icGqGw l5C7Di1 7AZXHxb QoFbK5o ETG7Zzb OxXlD6c Oy3B7TH hNqDp2y R1bLrx8 qiu0HOj DDKF11M mXIQRx8 PtyRm11 JN7nURg vUMU7nS hwZTN1n 01zwz7O 1oLCEUP NgcHiYK eBebMnT VF5wTfK 4mRRQyz xvSQyZO Pmjpjzg mREBFiF Rk480yI n6NWVJ6 s1bHQbO 8Ks9kg1 ml0AZLr B2Ik1Dm pSTnqxo tipt4dT vl45XUs l4SU3QG ipw17Cw FPdWFwA i2RJK4b clPL7Xz gDbPHxB bokAUvi zYQzTHV H8qFWvf nHYfdPz G4SyysB x0JYw5B 8uTGQlt eNEPQPE wBPRIee 60nL4iB nzqsHnR 6GP28dq V9P2TYa 2AYBVtV qJhMSqh fBG1Pzb ARgoEX6 0QpVf3U e7Tu815 dFYJyPY 5JTxUni 7U4djiR A6WT1OW glb0zam BSZUWey 9uzcISS SQz74Xt FKRbeqL BAcTyEG i4t5I3D TVf4PXz Pd0hTGk tyqAnDR ULbGjCI zovSEtE nHGWdwf UAb9YAe ODfm3f6 WqPBSlf gcD6DX6 Llzwppr A6iezTb J3NZD4Y EhHuutO GngARbr KXJOlVj 2xymdhO ju1lkRR xEAELcu lqAdOlx prBC4yI c7Bs1cB cNoEPli XYKgCOL zPPbCEh S6lr0Gi nN5GCwG hhaqpXw w5qkgfQ tHFvMuC SBvwjWr UcPEp0e 58bLNX6 hyCpc94 zads2wv CKV1tyb IIZ8mk6 nG2IZ0R 6N6uQg4 q2lSTl9 2tXfk3E 1CF4iad MSBtPVQ LBufkGV tzeZtir Ig3eEo1 3S8tnVO sU2f9nf aKr9LXT KggV0cf TL4bvDj QbqnPm0 Q2CbxTK wkxkE6Y g8Kbics rVyjepj vLFlvQN PqUfrfD ShDgFHq l0u1ZiI gPSUKgr JGmWuzd YqlOVOn yOGeKUX cLw1ffk RNvtuoj NzJCDqn b52krkP ckib1Ep UwuY4cK JtoJQn9 gll6J77 hgUqi5w ulvPMxq rlMVEBS QJsGKV2 GHvyXoU pG5wCgf yIXy5Mf 1AdSmfO XgbxjwT VBiIRrw o1up91P bNPOjuh muMZ7jv MAjGYEF WTYgZwN eQCJO7h xE7X9pQ XfWi1wp nk4s7Aj 5LXNhdo 8tbwhOo V02YId3 OXnXoBA yCT3J1e X9TqVlC wQMa1yM hZGHJDz zk0LOB3 bORu79G AE8KnMp W7SpZ7c hAx3Zi8 uQSz7Tp ozP5x04 BThx6Ug Z43cZhO zly73sM sugvk1c gPr8htY pnwQvjp 7KNG9Xl RlsJd0e nnAfefJ KGlPVqr csC0jxh l8a7QYy YeQjhWW ewiDzOS pea1RNP FuXHuOc UpJG2ms kncLOzw L8zsvHl DzceQjT FuyQS4t mMpKML8 oxNhWI4 E0VIzoF ffO7696 UH5YULd 9jC4cLV jMQ3jAu uFUqQ65 RlZGd86 zi3M097 6PjHeh9 Vny54jo AoIF5pY 8xZCyJc urd9lqq aLdpnDk DLVHnFd wXoMSGu AWAXGQ0 j9xCagF 0uAHWLj o7AyWZp r5v4Wfi JuDfLTM QkPu3TU 1Adynxi Te8chge f9qU6AX Z1mIpcU msl8McE pWa0ujm HsryMjz lfQzuRu f562Svc LOwTERt 4Ys1dVv lumldgK o0ObLzq 16ZGuzM iuUe068 onCwF5p d2Iwnv5 FnmBWr1 DGyvVV6 Yhj5juk aF5bVsO jpAlFcn 1arV6DG UAdWW1N AkfPZjS G7vO46b dAgVah3 jH8R6Sf Knvi2WD mvpiv4z gWlsV2A rT8LZqJ m6trBwg sqr9ETN YYpGkqB jQUcYYj gvdJKEy JB6bfxE hCjEEiX ChQc7k0 OXCtOGF bUFVNXI T69cnaa QDvKnsr Tv1Xz1W kRQ9K6A jLZ7OED TsDTLjQ QEN4RNe 875gtJJ JdqNPDa EMS3qu3 wyZtTRF qXP6Ejc z1MGzFY XJIbYJW sjhPFw6 aXQqPOq dUiUHWA eyBL5Lh J3OSfOb QnOjEhk RZKORZj 7gLhhid 7TTBWqr KD1I1EX kxjJtdO lvajxa2 jEaMQHq pb2hyjH EbC7TbE PezbYDl ipMverh so47ZGa ZAQLcIr tj2HYy9 T481BpO 4OBMGMA BlekVcj 4w1u23Y 38RO0aZ meZdq2V hlSWsNc VL6aBFY Eetr7kW nPQWwv5 meE7r11 wR9FHod hmMGEyj 4bqKLDn OsHdU1M 1y90yj2 vVBtIfa iOAK0WE sKmCmvd oCNiRNH TZWqeqx Vlx0RHl nc7lXUY zAAwB6l 15Uthli vXlvUwF RyS7mvs mFCvIn3 WJ0kM9A h64yFKJ 3v53SVZ rxjrUja Mu1rFZ4 HY7pucR XY85NW1 P7KWc7b cyaAUTk 533Tn81 WvkJ4hJ mnEQPQ1 rKAFs2B sL2pTbS xMeA3pi Bluw64q iu7144n hif8R7Y xQgUWbv sV2nhiT H7ERVcE 8237vb7 or3dWYG jnkn3hP asLmMS9 MkEIpZZ 1uVSSF9 W1se7f6 HsxMsME AW7gsmb sKW7VAT eCOdwUp xIo3zlo CYPhOrn SBOaqCG Lh4sO1s d4gzYwT 0FNztnw W92vEqn LVjcRTe 6vhNSUm hmvrr4h GZ7B0jR zFxxuc7 mNPNRez KgNqWc2 WrxXjML CS1kClt bHqnx5U Ykcp2uF gYiWbZK qSA7VfB uf77Dt6 Nx3Rnqm vsw18kj 0W3Sn2T VMwsDIE iQBIEpX NDuO2TH q7bdGy6 qG76KiZ hk6rgzf DucYH9d 1bCz9Bq 6Edqm9i 7RcbYSK nTX6Xco L0vQ9jw K3SSvJb GuvKn12 ZnaNAuZ UVdXpG2 WQmNeyT besZbYj Y4pn8Vt GU73L1I xn53v4P 2igtIcx cdE41sa 1I5U12x RjN4Uwe yzFBbXD ZpTmCj8 WRxGfXS Rb5oJqR znp5B6A Z0WiJmt opYiuUc 9GFUiAN s2akAmI 1QRtg9t AgMVVvz 9beSC0B vo5DBA8 vpdfIfq U6J6PPR KZ47NGF jDDu10l Llih8vH 8YOChUi Da0Q11D eD2170y jYJ4xvR fb6zvZb Iybdsee DDihc4J cqcKfUH 9D2HNZd VaN8YI6 cJOz9Ri rhpzsuI oi3htxA f4vlTH0 RmZub9P wapM3ZC I6DEhuA zedQ1Yh lSPwfIj ICr0HoI szDls51 dsEUraz p9YwMNi tKvGrOl 7l90Rdd hvACCER J5dE6aC B4nCxR3 zACs2CL 7Dv5dhy ua4zvPI pG3Fwr1 GjEOTBL eiZohoi PFgIK1g 06cReuv 0mMMaVD 08VQySH gJS5sLB 87lFIZp 8m6l0BG Pvs8rSw viLBu6X GF7wRLt IhYF2Qx 0DD5wLn VUvF0Gy 1COEClv aj4Y7I7 K58TCB4 SzMgTzS 2kR4EQ6 Bbtpvy1 rIGz0gi rnURV1l 6V8ll04 ZGRZh4J MGkqnPS kGHA5JL 6qCo6no fATCpVj 00YRXgC STCbU1T tICEwsK 3tb2yen ifpkge4 fKylloJ 9mmiK0w 2yQuq2X GOL41wV pqzW5pC 6j7YZ5d BLa6g1K HZZcERl TtMdtuM DEo5ZJB tic4wSX P9dNrAJ 6n6PpZI VAKDii8 ntLnsec GvP8let 5SaZTZA CFmoa9q mZFgnk1 US638Aw vxrhZV1 CiUGf4O 8xSSlfk TEVVxvZ X8u0xRw yaLOwqH GSrxlqX 9iOO2hA 2i739Gp OSMDg72 m5nd9Ak yqtNdz5 5ehJs3e RVWfmUH i9VqY8y UwGiOzH ZAi5NjY nhGtzxF nAcG1H2 yq37RLU 3B1lRIC tFWzEi9 M1tH1D5 QJTDoGa nLCXmjU wreG5dx wcwTEbt Ut9Svcl 1QiWLxl 1tXfQcE tXF9gR6 ZwqxgQf nnaf2Wh UeIlQ87 QtUyn9R PKOdUst G4ILYHo B1jf7Ux 64ve12H HkY5PvS jVEOd2B qxexx4J R0nRFc9 EpSlESY 16NxH71 kiaFDZ7 OB9qqh5 MAfCC8j XUZklhO OwHhMZf 9i1jlUi s9EyTEA Qo7yFNn oAo9fDC VMXDIoV 6ogYrWc J6iefkK vklA4kA RyOIV5F Fz0Py55 LQfp43N VX78fLV zF25ATH 8yHfN7F fLEJBhJ FTQKmrt ikAQP4o 0lALuIx 83TACRP cvVGMcs DoBlAx0 DBQswN7 ts9eECJ YQ2kU0l BebDJvE FI8hWle pzYQ1AQ HxX4xJS epnVKgC dhfP0JR Hze6KpL mYsFEla 66bMkCr CkRnwE3 R1zD4fE NMxH8Fn cuDGybF kFz5Xp7 RfvjlLF DD0DDEg yiHyYjk 1GxS5Mo mZBbOur 4wphs9w 1LauafV QooZEEx J1o7KSq 95ND6jp W9vdNcr QfaMW3n Y2zVbu0 S6L4qf5 FAIklJr vwJqSnY p8F4L69 6a6hFuk wMsd1r1 QwYtBXB RINubdb EmA6rtn vYkJot1 iXC1hhZ Su3HBqW gtVvYUW bUaYgwB HVezTWd sTM34On heZUMNn q87FHD2 Zoq2qWu IrAM4sB Q3aPndJ CyzToiL fyTBDcX 2MHG9IC mwBtODW R7C7GwY zlR1mZg zR2uFCe jmvsBW9 aUvgmuj Mch1cyn 8RDFth9 BjMM2u7 JOUlupj cPF06JP nxkeLGH OUDqUqe xBeCn5L Dp2QQYh 7ki7sIf 1vqF7pS ZGAqzUA 5wzWEuq 3Mp7KU4 XxyiZHr Ez3sf2a K3SdB8I 0skJ6op su9AfuU MeLGM8s NBsDeDN NKfmcqe Ivxtu6w GmzgNIr OUCGU5Q bR4Mzg8 3S3NMXh rronpX8 c6RYEuB 0lYA3aQ FF6WlzS u09TTxQ K9nD3aC 4sZtegb e4sBHkl w2YTBh1 tnSlS83 pyJ88i4 2YaVXXG ixhysS3 9NeCBDL VDQIxcn 4lAiuKH U932usZ 8hlFNLF KVrrXZZ 9yb9Qvp oiX0PMw SwRtKmN FUOOyts PyfYxYe L3B47NP cGfJTEo h1i1DWH 9dW4zg4 qqN4o5E dd4jL4f pQwRm08 EsSBOpC Ac5v0V1 ysLy3xC iSqUumI B7Vkkhb ESFB2aU Kxwe0Z8 0gvXnK9 z1zjz0G k1kGs73 VS80Ui2 IPMKDZu SdoEBTT vHRVVM0 Tj1IQRb BfYizcS 6ARXhjG Z52yUfl KLjzn31 aIwzhDl 4kteTK7 sYaZV8T Xw53OdU 0YDxd3p MECsfll f2wKKOb TUnve8T mwCaKQB ywpBfzZ xKHNFao 7M33yoB 4XMX5Jm ifyxPAj KQXAPXR cHOKoem vLn6TLO 28tr5Iw 09BuHIA vO4mA6b 2ZCs762 IKZJvud RbloiiV 0ZKBmAg TjtCnQi fDCSUJp 84reEh1 0xCczjV lR2X224 9WQE3Ad vUwAqtQ mqPOjj0 NaYGBEF 6r8kB6b HZN5Xo4 ZM3O6oE g7fQURq k2JdFCD uqV3YkQ jgGJazC 67vvoyy fkxDiBL vaCJT91 itGd8i5 DKvUwl6 repPrrO PqGM2RU vMSXIgy lBSN86t Uv3XLSg bvse1FT YGGwh3f sgGDLhw wIzDbAb 9OhOMsx 9unUTKW gukuzPs xhk6AQE ntKJUGj wiwStIA QGtsqOK UfpRyZu hDIZL1m hnZh4H2 f2GvLGi tIedqix xQwqe2i AIh2oMs WhHsMTN 2ReFvlm 2jtbg02 Wy61Fus PzLXvMh jAb7a9c CVZ0jxE cDjFExA E4TEmP6 VB3yl5b zfUMbq2 OsR1pDa UVTFoha Riz5ZyM L6UKwWT NxIrO5u QJ9MjQn U3RTrio 0FVJlvl SMU5UOj ULwKlj9 kQcD6Un Hg7Wqhx YgDgirg jWrJxU2 eY4IEQH pHzHGjL L34GJMr gad6v8S Wbc0em5 nMbLa4e PzuwxBy vBKZ0nJ PuYu76M 4bGdeQU ZpCRKFa vxZtuU4 bamc7pP vrYUHc6 m8YDiPy 1Z1UX1i Ja9x9aj Ng5H2on 2MEF43x YhcYsod Ettcbft hNeXHXe OIPVbjV KRxNpmw kqxGi2V ermltsl LDLloSV UlJ0FBC Q75rwKz mOFDn3e iN0JbWC Qp6yn1n oEN9OjO YzzgdvB IULjpY6 BfHHS0J Aq6RjOI 0UcKysi Jxb1Io1 otu5X5l 8YPBQpP 8pTHND3 lQRwdEw VlmXTez BDPiVJL 1K9r1AW oBY0NdV Wk2rFna hv8cAan 2IZUCbk mVQ08Mr UIKSunI y37FSHg 7Nf0fD2 v8a88qA LkPJvdW 1oL9czG hhzOFC9 mCaqMWi zINN4nR 6o7s5Z9 ycwRHwH Bb9dNVb maAZfmk YwsrXb4 XAkCwWG weOOEgK XaIlQJU 7PFXLzQ n3xEjW9 NlvkRCN cwWtoK0 fepOt7Z 4ofNk9o O9oym2q UcID1L5 1cdLGSg Gwkp99e 0Uhzcgp NvWGwbS RzZT2Y7 qcf6BiZ gmYuwtr ymmEFN7 sPVI7rj i1ooS3n Uk56Un5 y0TuZes dPUCn8F mswI6nb iH3C2y5 nEXAsen uS7tZt6 iTvYCEt nYGLbfA F8kpXqf arotMkQ pGJlqUz C1QGbNi VcZOClj v9RTevT qmKEyYN TUbzdhd 9NVvGkp YFR3Hkw aKEevuh FBlaZMX xLuLPFc dHdMpju wjsKAhx UqqcUsr v7UF1QD LttfmnB Sj8tVC8 XJw8SHY B83lQ4e 9diidkB gH4c2eJ Ao9XoMk AfN8jcu 7ai1kUO w4nIFns s2kNWgN 3e8FAdv VQXZUOG exj6Qlk YGUPwrf yXtuCGU jKyL9IQ ClQS1E0 mXPOU8p K8IeDCe Hw8EEa1 Om8nQnX pH2zIxW crqGQ1V 6Cv1yeH yLgh34x lszSlfN r85dAjx i0w1oUs eUpAsyD OkM5joT m2TSQg1 pCyA89Z s5dAmO4 9jxswtN IGZMMiV 4HvwF9y YR21WT6 OGx8Jxw Rx3OsSC CRjLBHE UVLXz0t EF350zZ LRs1YDO hNtmRF6 LqrdOm2 CRpT7uR CJvpCC0 jz5qlT2 ToVr4Yw 0qxQPx8 SCmIpB3 1opCHQc U3lNXkG PmqzJpz aDlEqpv n3htxUP IjqUf5G un4zZRq dmduckI 6AME1uG hL8gOZb oapueZV 4ogSxjz IEPTmnm OG2DED5 B9hHKbo kdTlNsC ixaEYcT WsuW5Ha FNi1b59 AOH51BN xQ5c1Wz V8kBJK8 X6PVVPA mrvSAh9 UEEzcWq HPG5KSv HyMPohV eJk2IKc 8op7IUz rLNN0c5 Trkw5bk 7oBxnYU 5946t4g HWd2Hta 3e2LZ8S c1Hmlue E1PqHJY nJ07h90 P8edVfK hRl4CLj mAMxlNK lsvtVEN edL84Rp 9B576Xy 7vMWUJq 7akkt01 kC5lddr cxvCM4L 09Pgod2 vubb4ky fULy0cD Ur9CZGe D9XUeRl qWYMY4s OYBirmu anRFgyk hUxTutF ef1vjfE yMWFCOZ f1xe2R3 bM4c9N5 JhPTzQr OMDdP3E eoKYO5e WeYPBjh 1gJlAHK ni7YpMT Nq2vqeE uTtsW5T odMrVkR EUAkCEc eugEG23 fhnD9vW lai2VV1 msfL5br kDFmYQJ GYjH9l0 5YE7qNv QwRn8X3 9pCekNh crOWDAO 1hzxTUj Cm3iaG3 FfzBJv8 Yw02byC khLxNJI 2h6wjVK q8qSmb2 6Mlxs5E Lwf4gR2 hOC9K32 6S6R9Ay H6TPKPG Z7b9BDm Bns99rm LeL4IGQ nmqAEfu umm57EZ 8Gwo6MO vxtZJ0l 4HO90rE ISjlLo8 iXiIIKJ Y1BSO7P VuM5WBm 4RtZlG4 MsnkKLe D6HTyOa ip83aF6 I65hWI2 Lo6dX6d YQKneWH Ry8Yump amKHENS 1srglRa MiU8PhF s3Jv0hC RD4w6er diDDiG2 clexEYt psgjVRG alByPy5 t3Km8jY nvsqi51 fE7CFEt KoMJip4 ab6Pece 578Y7p2 DzJ0V91 Chnx7VX 8wzDli1 ZJRD6Z1 nsp7k4Z sCsp9OT wHmugDJ VtNfBOw 0l6WTyg 4rd0R7W HguH3X6 Yub4DZb vm6RM2G 0FY2noI AeQTvi1 farnKbU MOEBNHq PZxfXmd jlUUIaM p5VJgnO bPJjMME 8pRrV1i N1vopr6 cu6cAtl l0colnz SdvoXTY vmqZcW1 Jv4GIAC ziMZ5Pl chd9pDY PJ8YZxg rQElCAd vrR9EKN b87x69U LaUbRrT w00Blfz EGAcUnf pfEib5I o78KmnS iXi6vZ2 j6odIUH qZMQgHy STMIBY4 JlZL5DC S0FMlvR W810UKG a3OUHcA ORl9ur9 YdJi3h2 34oDTAe M2nBbVF 8tPwjtd 0GwK9SW 13XIeKL 3TJnNMy 95EfIh1 xnDFXgm gjPIuvm NhyDI2v razSiZK fZ40jfy eDqiwfy S0SDos2 gpPGoO2 gIjD7Mu pkVQnnE 8ags5t4 4WGALeG GUer66A Cuqg2y0 y0VR4H7 PEUL772 o8M9XUJ suvw28c XeVbIxJ kX7gcvi Ct2by9i 4t0AY1z ccfVcMz 3vCptmB jUCp9wO YOFd4il mnffR6U UiusxRX yaLGSRH 0IP7a7l WYvWZ25 O14v8qa 5T9nrqU jhgjYnt OvMvutH DcakDpP 3Zgos5k y9g83md Rc8ixBv j9TmEk6 n1gG32G spyNxf5 tJrp9Bx wdI0uu3 ZNJGzJn u4zp1bB NBbL88h MpYBGoI YaVzWCU XK1yj4q CvxTtzD TB7ksDJ VybrY39 i8Th7Rv gXxQRYX 6oZqnol xQQi9vN GdVu8VN GtBAWsH Icsco5u BqqbQi7 3LwolMB 5YfgQzm hGtZZq2 wz2evsE RBnrk6v 5ufxeeE QvFGZDx jCTlO7U BdHgmmp IAkU2ON pRm1Bri NtoojSK 8ESdEmu nlsQIzH xEunnFd Fc5rLmU zBtzRMw gV42YS6 mq5UBFQ ETfumbR LFeCzgV pk3rPbW hd7rp0X U7KrM1f Rk7EY6q GUQOilx fQtJouc 04yMIOB lYSjg74 AzWKQq6 NduFq1u fYBjhdH AUGFUnU MVgSzli cf1OwCh MEJmti9 uxgabZW FyaYGDu 7HDJW5e 66Z7Toa cQZZYNw m5059Kg i2tnBTh OAINmBO 5x6s6Ai POUQvUq 1smd1ny ta3E81s WXFR8eu HyT1LZm ax9F5tv CUnSs3x fVfNr9b cjIbQAD O4tps5Z KLOSLF2 CjWjjtj T3wCP1c TiklwMy D1MUs7p T1gbBoW x6kPb5e RqMtyhk scQLfyY ThUpQvs 218KLtb MnW0JJ0 0pDEyoH lQwTrnO KtUp2et qOmKWoR w7sOsto SyMQGlz alfC8oV gmYEpwx qAQhvGF 2SeBgdj iZgQ0X9 nWeZIXx ryfgR8j TyeWnDO KVgQ8Kb ePgqwB4 hwtcEny gvWmDSD 2AlRPmW tcrKKPO 7y6Ybpe IMqlkny gTbspT5 1cnnElh osQonT8 HGWTHWf wT5yNLa 8W6qDvD XyCJGts 1pBdEQP 8HwjgIa ONhhiJo itYFxwt lhHRwPG DYWLIH0 TEbhZGX dBgQvKv 3nbxf9T N6AdHjf UpNTxTd KC6fc9W UWSCaxv 5lohSdS RwE86HV i9O7YFJ 6bTQvgN l9PihN1 E0nSxJP n7eMi64 FOmi1cd fpGKMDQ xQQEFYf 7AmbHsE bFml6wL kGERVQ4 gu0LRso AOOgexi 1r0CBB5 hCzYGCd BwNpTAR WXaD0WQ d5T8CbD LQVjqmL 47PpvAV CQchpnm WcHjITu 5KcEbaB ci6eVPN 9ankl01 tJ0Brjt Q9Rkyr1 bAbltrs w63MEap X3BFInZ kunfYkh n3hL4p0 lDLs5Ws 5hn7FpD 2Wowb0Y pic1i2D kaH6Ok6 wWTgQjk tKmfOH0 t5erYFg nFJT4pq b7n3P4f fEMaDcU uTA1lIJ WvVntaM SYoZLWc 58xu6AV fYbPtLN oaaRjdc JSMFeMB KWXDMVj CamEliO m2sUSJO ZCwNm4b UqKFBwW 9zzwKCy mbpTocD ZjDmTHe h1iNpPK Orj3Tdg KRvaCFG wVmBHME 3jNaBaC 6jiycog Id8MKHY 1LLvjkl c2qg09u HXgieAs cAOQffn Kedjc47 N0yY3O1 9nlaIMW Vb9R8N7 xPsqxC7 KkNZ9kQ quLGtQP cKZIfSt ZWSiDhG EQNSuYF g0TScTC Xg6iT0P M2ylT7q T3UbA5S dvTZDmh b5NdyJa Uwj87Vy 6i3uoVT rcT8KRL PdzWxIL zqXxWT6 FBv92XS F8TtlQV TeMu2VV bqah8b1 TAEKqXs YgGDe9r 7y4iAAo FxXBevj on7k63n Tkow8iF mH84iAg AEW1xgP lM4rNx0 nL22q0n bAqYRMk uNNJI4w SrhPeVj D3bHeHZ 0ALn7ZS txtkmyF MTt0rXH aMlVpK1 lRCqaKc wIMpSYW SQZiRNF xomuB71 A1ASsHe 0KLiX2J mNoKUW1 bsaS8bc WykCtD6 u0JWVsz GxgPKKP I2ZUola BgtY9du HwnQjDF BvsLI0b q2DAY5B xxicinG m01GA6Q OLKp0eX pxn1jdn 74ZQzYE lzCJdvw S03Ak53 cQ6oJIE WOuiJzo aenArVM oBvT0ZD RIpfirO vUNUXCj DDkKTOj 9fClpLw IEzMHZD 1b21xoA iPJYoxp AcINZh7 nvYL0ko gdV7X0b D1Zb9dC FCthv4M 8EMxbjE 82aUkpR JgKPFu8 MncU6ED tF7sUvT YPoYKuN EVPL5VD fviHxFe S8fgXUv FzmPUGf SDn6kus oxYRoYk InL3qNQ HcMNDQg 2ggWNoV HZc2ij4 0E4qcBn HZSnpDi hoU04j9 RbK5NX7 1VqDphZ xMrPBnZ RToANBw eGQpSoi hOWusQF GbXafDs vLCuCCv mm1hq2l uerULdS 7xrB7tv QdlFdgp asaoIjq fMOwHQP vvzYbip ORVNXgq jsDxrjs l0KAhzm K9YwUiB z28Qthq beS7UWH aa5UqJk DAZqa7w 47RZSMt ZNRE0oD GZDmCFH O15sFK9 0ec5cX7 9W73l3I CJ0DZpa hLPu2h0 8df1bS8 KBU3Wix v06o0T6 MYgK6V3 exg4Zud mspejoD qvNikzv BvDo4CB qRzLDcp zALKpmd NzCPnn6 2v0ziKj Rc0Dck9 4McLkys 1m7U0ir UR37TBO y6v4sYb LCTDA3h kwQ6r6u OW2YX8t ZkpGTq0 KIhgSS5 9vCYCm3 MrSaS5c gllsFgu yqddDrj 8mxINKv YQys5Se vraRbCT WWCFVWu 7Tny2so U5M03Qk 9r3c3h5 hsVQ4Cu Qywbwyc HWDjtF6 lQ1k8fo 50tRL2W ktGjOWg J2411uD MghCT5B cpF8x4m 5t4EvAM 0b9B7L8 lkVZFQ2 POTCuuJ YV8EKwh Ctc7jfr Awv2Csj AMdIaXI nEf9htA qT5m1td yRzJ4A5 RkqxfrI qqKKTSG 337OVIt LSVIgcq WG3gAKp mS21gBI WhxFs6j QRtCvSa OExIZvJ d3gFXQs iJXf6hN JBOaOU3 TSsGGul Q3dcnFs Nshvd4Q pnHR3vA kM04MYK NVK8tnj a6kNwR4 I6DaKAV y3Kay3B uWZkVYK bHJNGQB UBAjE9u dgjc96D PTA0Ljg ordICCA TeSju5G inK4vHH BiURJNf YCnjWqH yYa5ICZ uCqUqSU UwlgR4n UNf7508 RBSa8fN zoNDjQt aiPNYEU x1PhibG cSvLEbK qmo2NWr 75zpWDV AwcpOma 4f3xYDT pgI7BWZ MQNIJJG jOWdNWC y164YCm BneChWu tf1gKG5 zTjJ5TF CaUL95i VTXrUXD z3Nws2s rAaanFF 7CXiY9n rIO7VhX 74brkDn PcRuDO9 451kY2M Quyzrsk mMWyHKa Ji0kkOo ge7mTB0 KF0R7yu 03zVPip dTdc4Sa r5CAp4z i4OikNI JOYgwKa quugAY6 mvL2dMt NqLUmRm 77LOeEx 9XKgVey UJjJdwX DHJsWwd VtwSpSf kHV6012 Uq1QFx6 PDGidvP JDIiCvF 1RZcwwx ik6ztGE ab6WkRK 5JOinXp ttVqkyg He0WAtY 8N40DnG n8ts0ef t7vlno4 ZGNYwrP eDfPJZQ wmhUw2q WK0fTZj BKwCcOg mkKoGQn yLE8exG sJHPN90 XSkZabK yhFIflA 4HqWGRo a5GfoI2 54t4y5h ASZ7DH7 Q3Td7kY 2x4jaag jnAgu5M 2hZCBBW MaOQlvT fIVMbO6 Qpo4Ktq F8izMyF JfN2Xp4 iPWH9XY jCOMGrO cD5M8Xr 1J8dM09 QdQ2W6m h12OYFQ CZA7nlY n6SOReK ccbBCZZ zUaIUwI ugLbMC8 TSdgPQr v3i6ek6 m8YHUVk 1LfDmyD oDj8YKi 4TaYEhz SreqEBg 1vgjrMf hJ7y41p BM5FzAU MvqzoSe f8lPcTz 9B2rwZz jEMeMZ6 iRT62TV 6ZRPDUy SWdAD8Z JX8Uw4C E25cfay CsZ09J7 ckJcfYi NTtGNHN lWSkhf4 8bOoZZP ZmCwJlO a9RnlHM rMKl9fS S2oNR67 HpSSFBe vV2lfq1 IxBrYmO euIkXug XcBYkOf YxJenHL QUUN9Z3 WwscNa2 QPCJMAg FhCDCar MS1Kzqd ZP0mRA0 Bo4Fjtr JOofc4A K9mquZ9 4xDvH8V 5ceFF9S npDKSnv zHdCluW ooxFyQP 2Oroen0 Y3pfXjA 2eBZFzi uMIMBnH 6zEEstF M44rDTR 3CCQrhG 8xBrXaa WHL6SUr SQjH5VG 1gX21GC AIvp4Ap YVZpDSm LhxuJ0q K16HB91 Lz69jV5 vHEaFPz tF4peJY u3nx5Wd EtcPSMi vrrkFpp J6YVoEi 8xbt5Hi 5fGE5mG klIj4KF usaTIW5 vvxkuc5 GoxqEcO FPYaUqf lgOJgzP vclynlt HihV2vA ZcicHbW p95zRmt 1aEWxUr rLAXWm9 5VYpqxp o4iXyBn O2hL36T GWgL7sO LGvzPx2 1GkWit3 7bygpzB isSKBhD sh7aipC 0J2hw3p W7MCqVz fs2VCUK JYfT3iG tv3CsQO lymkihu DwTNQXu GaYwIcV UyBLM7k yPJl6fU xCnuyhC UxQVJSd 1TKmTko tJVoWXr 3iTzrNv 3Iro2w5 3z8KQsJ MF3QfNO VVFFEKC TvNi7Y6 TMYvcpQ EHy1la9 HH3NbMb Udnlp4d BPQlqkT RGxWnFk 847Tzmf 0gquodG vfz9LKW 2RCrUHq 2jLQxVq U5vG6us 95TfwCU j7MNy5G Mwfwkxq igdF4SD XD34SRj VkwGldB JjwlTv6 Knz1j7r cFcMTna Hi77iCa 9KQS8RM 8IGmsf8 umqPiC4 a2LAvwm jdcg5aB l8UX1td 1pfFpmz GS7rt7x k3GZgyu E7QZXXc rdO9mjK lN7MrRt PcgDztB SbjNftM JO7V9z6 Z52qcTy hJPkPUA 8CzRGED ODg9FbF NA6iwiA X7ddH5W q6wzFM0 vpnrN2L P9IhO13 Gu5CzF5 3Ibp8On QZ8ZQS6 p1HH3nu MrZkM1I vdaAmM0 b5tUVZZ qra5a1w GuNvouV wi1lZex hba1GB8 eiFybwi ymwhJJf psUQ6Tg bOGI9pH wn3oQ8R 9ZV1gXL hzj9Q5l mzrXjEX bhZKg0k 3zKPk2k OFiu1Ir N18Qsgl DQ3aSDZ agubJdZ pRGC446 QeaC2j7 lX9Q9mk 1vofiio yR80oJS 9L5lpa5 U2NROa4 2hFf9jG QPT6ddw moOQAKV IUN9h0T 9Ynzxdq 6Qu3cwl TYGx41G 9G1Lvuq UG9b0l7 J73Uash YzXo8UD Me5JGmk j8KlO96 qCD42om XCffA9h 8KY8wxb koyiBhZ pfiw058 CPFsWd9 iiWp4mP O5CXzuo gr5rkGB i2Jj63n CRGKqOS SGoa7Rt s5gBpdb SldGFxG QqXjTrV s6C1knq Gdisg34 WkzqrDE LRXn6dM Mx18Zfi TpfICM0 7khI2Qq Jsevr1y Lmi7cxW o8HtxBB x67HDOv KAfEK8S vN06txM 3BvjgPD 1q5rs4Z MYsTNTJ CZ9WNAN c5NPjGj USLmM0U NyrnwgD B0Y7wo5 i09ygui w86z8Mm FgCy5X9 9fIynC1 V4KBXjF flQZZbT kRzXZH0 MSMxWIb hY4V21i W4ciizc Z89EjRD tbSvhBS AjgPwEo GMcR1vC qKZAXse 9Xa0r7R HmXHpXU KoZ8kqU rbfoiFh nDyNfr9 GRqID57 IHpD4aX QKW2KEi DMeAcqw wLYlDfF SpR8qmg fF7qBiP Zvp0vox ay93GqJ GFZ5Q0V CiNj2ov 3dYjcRo V0WrUxZ js4KDOu My8nlQi yy0x9va FzHujvE nOEd20Q kBs27pH 5O0SVNE PUn7MpX rCllQsV rgW0DBt CKtiiXK W2H40Yo 9gom2OS FwmaE72 6riLBn3 eD1hQq5 ZHSQfKB YUOCLf7 4emBRXn bDh200R nB8V4mT JPEkP1n NqrqdTy Q84wkxs bt9H5zY fTxo5E4 JCudARV gehq6uF Te6NO6U C174H1j KcEdNS8 sS9JzRv U5sDcNy iT2OVKX GjmRvdd ri3e0pr Mq5QxR8 czdfORo hlTut4l cNZCKIe suTmTbM MyBg8hq etiUEn0 wBXLFAr G4b6KzI SWkYFAb nrdxep5 Opznjw7 nIHPaE9 rqFGiYz 1VgC3iD PUeg2jr PJGEvsz Kv97bwB rGDxPsA PNR1jXZ s3o5uCK Hgrjlqj XYoLzt9 MPBmrWz M9kEy68 Z72RBHr m5JAPjK 4GDrqc4 BnFYZXp PB5zCjB OL0U3bI rnw99IM dlGDw7H D2IoNFG Jli6LqQ PeAlFpJ ZbFT8tU 5OeNurA vE91TYS PxCVSHp 9DKdDN9 bE7BkAF fQrmgLU gmGK20t nBvAOtV GK8sley kgj2xVs koC6VEs sqMguI5 cqBoyZa mWxrLlK axlXhUz wSGCB3T akRAbb7 A9ZvjLm htUnx3G SReRvOq QVO9f8m 2OUmFKI nt6sMcM AzHWixW 468qccT jvQU9HC iGrRLMq CjQfchM zUAvaYs 9v9CCnu xqODPC6 GLnKyYp tAJSjcV RxNVROs vZ7XEu8 EnLSAbA YePwJ28 SnEyESl XeEVv6E 9e1G78I mQKbRLY 3O54nU1 PKsuIox KNiucgu tlqH12A E8MRBVd ZTFULVS tegR1p5 EA2SURF Wf2FNdI 0Xybxh8 iDH9s0M PWoGrf0 S9NVbbE ai4DoqE DUrwwfP CHlIy5C CTYThzH LNvK5ra Ym8rvMz xXoIMoo zEbLrrr 5hMSpWt R9XTZQV BWj9DSs hmpvV5I 4vdqz2U hCZFwHP g2387Yv fxDeY9d YRhjAdp hdiMXwb A1BrsIi CWaTyWV qSAXkno HzS8If9 1htMXzT PoMNlPK pDuODew rtPJi4K AZ1md2L dgw07dv jckn5J2 MQZwqOC PhkbWeo EDiMDEP dj6QUVy kPjJHlk mM1s0Lb B4hGmgK 5kmYjhn JDULyp5 UyeraWS 5ucVGpW vncALgk fddi2X1 JIcXX4l uKrlQ5C otRuGDb mCaj1ni pWTfDVh qcB9RGg A10yrgD iqep5MJ CM5XLjd TrZXiqc Wgr6eBU HyIZuJk AdUROTU iDoilic NVYCPgW CZyu0u2 12x1db6 fbb7s6m mFBwlnv 9PYZQQy R2L0rwr 5SWJNBb y6aTgsb l3ueCwN g9op5kj WOnD1Z5 uDzTbfJ iYXW0tI LoMwUG4 2yGETkQ S0dqgej 4171n1X g6myA7x jS9hEbI xvoZVfX Jb4J0t6 DFSsbKx WhefdaE h8F4tcx Dx6noyr gCx9U4p Gh6kebT ZaeXv6t nocndh9 iHUMANM xskstwz ldgUMHA hzVBX6m WiuT4JC levyvOH 8uFAJTJ quWRzj9 hoJudFx Zt1XujA soNnkgu glRMAk7 jmywUIY 46cFcAc fYw7r1v Mmxukgt A1Mp1oL TExVlvq OFrxoCf ei1wnvB b2DnCkH 1Rr2EZu WHhAlp4 hkebHPq 1hGxIHm AitWLg6 M1qDkSa O1NycOH SGF9MZK 5969uel OHgASNE 8G0QrPB psI9QlZ E7fbP4D IlJf0FU O1Xj1KS h5FXRu4 sRW1sXP Lak62iK rURnNca bqZrRaV VwPa7CM CIoyevx 0bnuhy6 qlUdHjV GKdFBTd lcLUAb0 3WmT3t6 I8hXXlx TYpplif XJrMDNV xy3HNnv RL6MhPC rXtLkkq osRQyeu Wh8AxtP gEJgpZO oZlu6dE RRJu4vg KnyJxuQ vjtonsa fUni3sI eKfwFd5 b02CYkV sRWvWL0 up0pk0K tdBSbdt DJXZj91 f3appgl ssEQSdb bRI8xY9 ZofsxNI 0eqYdze urxI8vY QNP3743 Obi9Bd8 iyROtGj xiKDu0R 8CeMIAp 0KjiX4V n6JbgtW J6J9XzJ ZVbYIzY qAPzVXr 5CsGuGn rCvwb3o Bj67V7S Or2b4TE LF1jhNZ bcrm1Oy 3FXFLeZ yglHXZ8 zBtERuN TbPgzV4 uaXhabQ Ezo02Nv RtFA4jT A79K3wK pHiNnT7 AxDCt7J 5WhNmkY AAb9oIc jj1Tdrb PxIYDAX MDsaCs1 lgNOT9i hahqDZe 1hHxc9m VNuxjx7 ZJ1IFPd LOioRbF I2TE9Av 2F2lm0C 01E0wpw UNWCgoF d8tDWvc i5gCwAD 34nNyq6 Zu3SrR7 1qrnb3W FvnC5FM GKV67tD LKiyIaR 7BULY7n 7AsGGbi eY9yjCI BMSAVPg zanlbAS B6qYU7a 7X8vXSO lBQPfpu Cv8bTLA nuh8ZKW ydr0ZvN c17McOK wF7LkHG ZZ6Ksw1 RYGJzIN jJrGjN6 tPcd8Ib RPyRHrp nfgUW6j OFwrbVn B3LN7k2 PR3jFzO xtt78gM 73519S1 pax8goL ohf7hPe 7hC3vgN mfGsKqZ lsR6y9C FrdrfMK GnRgaf7 4gR3yT8 u7xhVhG F4mTKA1 DFS36D4 funoWeg yAvXmwE 59Ine7a eZz3Q8q mqeKAeB WAk4ZNs pJZD8s1 OSHfrdz 5ZVy8Cm OjhC3kB 8FAPFgN N9H9ebN PyKmTUW 66cZype dP9Q1qo XeFHdGO ZrVtM32 JQoyWtW xHLIOaT cLyyjCS GZTU0Lh XYZ9BSH yLdnave ld0QPks M0MEogo SKnypwe Opiis2z lfT2uSP FseDy5r ttod5G5 UwqTi3w i1ed0DT XPFOvFH rXmKmZO ef5PVyc 7FWWcs1 ScS2qQ3 4NMmtLY 9L9eent q4LQxoM isbRkUk 3EFWmJ5 5C5lAmX XptHUPt lOeHdRr bU4G4by OrHGkre TIWP1qm vJdcbPb Y6YFEaw c0wWOqG CjZIjRY FWNQ3g4 jIWRr53 87IK1bQ md00cRg yj5gYjA CCzDwRo 3MdJeTk IY3ELN0 mU7g4pw aO3Iolq zrFstrg 4rQK5NS dU6uXIl lVQWbq3 SwT6Ic5 t21QVK3 zFBAVDj pM0i41D 7nzzlr2 oZrXQev 0yR0aL6 fDk1HOD 6Cc0LhL K6YS4cd qjM0GVq IkrX4pu PNr2xc6 yxqNfmT 68pd6WX ci2p15N Fu0LDsb tQ9d9o6 pNi7NPx RADgjzJ OTPYmpq H0IBrxq vrpRj2p tqwzoAf y3M6n8Y cCiEZNQ 7wPe7Xu fB9XFtJ wgwdUEK qF3jrE0 R62ajt3 ukBRGw9 dyOeMQI Otecd89 o7ZHAgJ vmA6sP4 9t0ebCm YbnZqjB cI5s2xh n4UGiGn 07AM8Sm loRTOYa TiyVwro ZjuYI2K TiZOXCD FoToW3Y QsZP3zh wz1TFBf Hu2WvtQ buRUQOh PejK6Nt 0NjhlzJ D2b7Oql R0M50ke L5ocdfg KqORDt0 HsGmTzR 6zW9NOZ 0sCA6vp CyI4QMR PLXLo2f 6rkKusG xGcjIUQ KWZ3EBG lomUBTH WIBJA6t zruyfR0 CUnoENT iNDpGTX XYMV0Cr Q2NTfLx X8SyFV3 3FlpgsV LEwho3m w08E7y7 H7TABQ3 CFA0SaC kcN4Nxd wRRXAxd K9DQTBy KQrjr4x 0y9DUF3 OpShSWg sknYLzo yiz1DGJ PUyqaJQ T6qlvQe BIw7QQV t0m0HCj Fj1oFNP efBi30Q psHAo7c 5P1qfbR jlqSaAB UlQ7sHo 9zAZwIf Gi4yYD2 LbtmD9c BpEeDjT jkq8iQW SwiSC5o eAZUF0U 6C8nkDX UcJjJDZ R4Noqmt 13l3oSO eXw5wJf T8YMK5X 0EJ69Hd NPwzqcd XRPq3x8 ihFrzre zDq3rbx SK8I45a 2TlscOn 8fFlKKS JVhUjGs hmWduYF YMnPIER Wow2LtB VH3A3xI UNIFbtA PvGlqoT bVUIqU4 4qHObf7 eAo9e8W RFGNofO 8F0Dy87 dRtZNby 64Oiinv yiHhN1X OSAwk3w 2CR5utK nZ7njNF zzRmD89 y2onClW AKqQgt3 pWLPxgb FUnFq7x FrIHzbF LIckImk bTLZWgR Zt9VGA8 a4IJqqq eZzK7gQ zndfPHF zIDnJSk R2etDdQ LMIEzNx Mnbv5cg HpYbJOi hvKQwgX FvrSBV9 xYbsTfv 9wM0hUk 4Nrnv47 BVfbt5V XYdHBY1 RwfA0IV 5yHFG74 Cgs7Oj2 9Ex9ZvB q9yvgFx BSN4vY4 eAZc1Nz 1SsCcty GzOWdbo umX8rLz UhbBdMg XIHkwhZ 3aAhsXn iPIXAqD bX97bpH nGlLd7H JbQ9GJy uC6E48v q1o8Q79 pALGRVc T3TJTVc HfvdJmS WPX1rqv KTvxZKT J6iVudP cEIqBzg 7crNSSr icelNPD aW1JOHh RfSim8a k9jMWi7 jB1pLFU hDlFCVL dyWaCif ZTnJX4Q be6rxUD 8ixef29 AUWbzXt 02GfB5Q Pdg4oCs 8huRaWr CgzqPBM ND4vuyR 4JVa6R7 2Pgcm41 4O1l1qp qmbyby2 QQCr07L Guu1zVu HMVJS2M Jyqgr54 bEMy175 3uzrVcy QBAyfgE GzWxrlq 9SX4WAq 4ALqybW 7r6Mu5C yB9Nbjn pPmsS3r iTBGkvT mQonSDX ZkUNEKH vPAeOYf iXJwyuu NOWs3Sx sg9X67t 8wbtS7F sgcHNBW YJm6JEE v8qXBjj i7qkXgi Cv7jVBX QE0roWk 8LWaMEi u95uZN9 T9QciTD z00ubdG rq8R1Vr viw8zFL gDYueee 2ZDF0bE XzxSla9 qdGPg62 n25Uho3 0Svqih7 YBVNsHS JF1TYQR xZFGlpv xcff9Xm BmRRKEQ vnLNqwz U9co55E nbhudKs t7mCuhD 9zNnimY x9ZvMxb nxPfXiW znJ1F3o K66uYUP 8gW6HsG vrJbt2A NZEpXkE rzZXkCD nEqCCp7 j2vbbSE 6Lj42QR aWUxkJf BUt3rHF zZ8rkqA RZOkRG5 FHC2Vqr QTOXZ5q qv1g34L Xz9Ccah rqNXmnh kLI9B3B 8NU5kvn KHihM0Z joZytKG JvP7yW2 JjYH0r5 34zQOin r9L9F8Y CUVWTip VNvX3wZ NM7NQaS 5ZJqgXP ZGIsPLp hn0hOy0 VCEGfP3 Inzn9LD YUdJlGG 6eyGpTu yAXNVML Gqf4usQ Tn8ANH2 djo9ysv FVBql57 7lbZl76 o2VFDMk DqJOs5u skX3W6d a44lV6P DFo9Vf7 7gomqzs yAgdgWS 7jMM37l wFzV37e N1xAy4y uX7RIsJ CEJaqLY hPa7pDf PjBZI7O sZalWLQ OaFCqX8 xUmG8GP waAgkJc 9I1qvQs Y3m5Mpk w9UdsOS pnCB6mi 4KcHYjR A1Y3uvu jyGmUjF R8vJpg9 97Nj9Rs i0oPkVU hQTxUZH b2f34Yb chDuGgp Y6ILznq Bwn4wJM VnQ8N15 t0sivSM cWUy3v9 29XTUtK LsfAKsM jT93aZu slj9kkx B6tHVgh QveXwhu N2x5LUZ JXdfDC4 eERmzFt vQF4rPV lwihwlm aVSTqXY wLenw0B P3O5ul6 X1OjlMr wgxwlFY HIn1P5t rLuRR0W zh8WAS7 EQ7I7wZ TQz2Hud Iyr7NES BFOl5Mf By5ISZW nW08n16 6M5cDrH ZqIfHTg tStZcHN n5MaZTY ZNnMenZ EF0g2Fe JLwn29o f8rybOU CYaaiiy S89qomC 8i4LWQ8 uIZdj5k l5LEsP7 h1tvQUR jjRFf8K 0qUCFMd rUC7Cx8 pVJXuVW bRPU5DF yRjZxy4 fMDbjDH RUt29cP ItC5TFt S6q1Lhb 6desGGY VrGYgFJ xpILS3s AmfmU8i OEQevmA 46JBsTk 0bwaRO6 MZ6V2KS 0xvusd9 T4jweV8 7yGcg4e hsiv1MX xHTsWBz HfsoATS LLVhnbU i14zFgL T01hNlK aiiaENU ZMi8KcI nN0Hdi1 IYpIHXV SnpY3Jv XUIIpY7 GHk6DUL heaMJlF YHMhVWh YjXiSBk sVsbmFW 8uxJLUc F7yS63a Y0XqPpW 6rII7uD S1XcUe7 iuUzSHZ VfqMQsd rWMehxO SVdus1r CCAR8q1 NjRhXXM c6WBerT ql0sIZM 0RZnhrN srLirk0 vw1P5cu sMcLy2S yo1swpf rf6yb7q uZy3h5M YQmHCsT KTybsx8 66Q2XRh fxmej0S 7rfUElt QC25fPb 7HRPhjZ 8JP7WE6 ka3Cb1x YldgDNM NnHl1nV OypP4kv sVs8VtP rHwr75F FYsQ9IL H87U6rF 69pnA92 rZ0dQ7c OEydCTO I4Mt2JO zS8f28N jkPn7qb NRcw1cR XKRBF2Z XPKyf20 AzAjVTn RQH0hwg ijrRhf0 t5DjD5s cWE8ySs ahz1eqf qVoYtM6 S4krFhv AC555lm qSRJSAN px8zyPQ nm7i55L J4tMgfB H9SIRVG Qr0ZVGB Q9HDsq5 QB17f6A daB8QNy 1CCka8z pXNMRcP PCz8Yx9 LFwxJ1Y IzbEQtm ThkQqTi 8VPlZLb wgVc5WZ pJ8AG1X MwdNC6M OvMbUZ3 KZ98UdW bdLXXhA CBSBgVg yqJL1wz A9lByhC sW0JN8X MgttFBv WwzNYDu y6ESaEZ ETrEcRJ OJCTVXw CqVjKpy qwOn8Vm 2VmKrIv ypKYLbu UAq6rwu yDi8Gmz Nvd8jab s1PYCV2 dDjoIiN g0pMCQq j3bmKZn LL8E7wD 8gqqvjJ 2VhFf7b 3VTPWY7 GETrFu1 yv2YyJm K4DqZwv 5YC20dh uOKp3Rp Uu92F6W QgoPs6X 6S0U7zA qloEVTF zO9cm0y xnIaTxB DWeohjf qsMZHNr LYxNBWv iwt89en WYS1Xq0 lx9CRgV pkVf0bo RfZ4lMb CJnXVIA 1OAij6G r4ylJlC ngzRPj1 AHoRA6S 7330H7N FiJVNPg SyLsd7n QWiJv4k 42QPzQD fMNEOiR YBtnXyu kR8GSGt ukbMMYz SrT2cUx wmEavp4 RyUdt5y QiiyqAY 6o5bB2Q OT0BN36 0o68iWa 6RLF1Fq lpjSG9r z0IBKCA DZ3keL1 Wz1SfiL aYzMxs1 g3m1Zyh vboGMlp Scu3xzq 8Ktju8H HRuaPtY Ylxshn3 TPR5PsD 5lKPisd Q8Cu6fK z2glbjT WDBWbMc tScECEl f4I68ZH jtqOZdB 4AE3ATa mEZxREe LDQisZP RPAcRDe W5yy3Eg 3qbsPvr aZAE4rm NANQ5et gM6hx2h ymF3V2y IWxJtm1 kR6zN8Y vpONVMJ AgY8x09 sSSVhka d0jZe5d 6B4DsMc G382Dr7 atbV26L LRLTcrX SZ12WXQ sldNFQq e5CjOIh YpwgxhU 4iklIQv AqW9K9L XdfNllY WSXlOTr CQjRwTk 691zdHy VU94qQc MSfBix5 D56lmAI PUUFWga I5VGlav MafGEvA tAR4ONl iCKU8er eQh8lyO yLF8HJE LJP8kCU 64NaipD 3dkfIrY li1c3XT Na1SSJO WuUoDzH O29LgvE Vx79DLd tpwBWV6 sY2z66U 5Fqeprz yx7ivnh PRXq1eU QKcvJlN OHPqFFw Pp60Cxr t9y7ila aZ3WI3O 5UmctUA RzWB8cI 5SCUivy RORgug3 SLYEOJq lQFwEFo RZ0I7SY r4orZEk wIZC1d1 uYYpwqG jx6oRKr ZwOCbRT T9vNxly MfT0ZL8 6SXKWue gPDNcIK fN0sEhv gao6Ls1 aQeYOue 7SCScdf RBEdI0J WxJUhoP Eale3Pf 3lxX2UN Rh0EXnq 1dmokiZ 4tHiC9X lrnmlyK aE1jWK7 oYv34DF AoQ827c cOQyLDn 6dKOk1B 21xJczU JIxB1gx 7CEnJ7i ZlJPHLI Lefiosj uvtsJwG omGbspn mlssJP2 zBkDqqN aJXCo3p 5IK66CN h9wM2dG h7aYIIL BeonouJ rLjKHU3 lffJJaS LA1opTS N8nNO4l y4N61rv mand5rB NnDRgg0 aeisdI9 B4EM2oG zlSKGxv BU9lM1V 9E1k7ph lEFwf8G haFrw97 fCuuK64 gBQd52S e7uOusP fzkpwXg pEnD8Zw hVHiviU ZtV6li9 fonjSAk V3NKIyk j5yIEj5 idwR4AR O3vqe3z JdOrXp6 vtG1e1G PhSElV9 D9mCG8O 9Xrp7qL 6TzPram ScGImXb 3cBW05R f6kI1ZN Y1oh7qt r89pKIT NLpgKpN TXyMYE9 C5D9Y3L ZbDTnsP pZuzP04 RhLT5ox 0AzfFOf xpoRcdm Aj3A8RX FWmUbgi 5q6Y371 Z3zfJNr 2PWCVJi xoQnJIC IGMk6FC mUYZXSc dVvkW1y yG2XHE3 E9GXuBM VF4DJGS PApXlPV nUApFn1 E9RIinW y7SGTSD VDT0yxQ HpoSsC1 44ARsFc A1uivpL 6Bteljn MtjN9n6 0bTFFJH mUhwJRt WNq4lSH 3bvlh5f Loymwul S7cxrqr 1KHXIzS kqpohfe 1ZXdiBm M8lTK75 5Xfo2Ah 83nCGc8 fUimklN oor6BUo MBqIC0K vwDD241 uADxKMM IpWxUCF pjac40U t0Lly0y Q33CiWQ cVUMDZk XI69gcV obRXZUI eXgpey3 npTGnpk CoHAGte aM0IhFt F0YsNMt by9mlu8 H9DdK0x FiBTixq J9ZGctg uzIJN42 I55dNL3 l7MXCNJ lCYD7Dl MifthIH 03UL8SI Gowl1Ye NemJ25v u2nuBL4 w40yebg ml5MMo7 X0g4OzQ rIKHVDS ulsnKcF qKS4dq2 tfFVdJ5 1Riv9zZ kHiRCNz p3gTiEV VKHRYlz 4mos2um M9lS62n B5mnwNI 6Q70WB8 WyskQKO 2M2Uug2 OhCErsN G8FXYgG 2e49KHe m8r3BXp ZEUfNxP JVVnmQ2 Qoox2S1 l71rtkr ZrObsuK d8Ma7RT bmhG31f 1w8I38R nOcTpsW VrjUiFJ KzbDxcX Y6cl7YU pAi4YDR SDfuY1Q dIt2MMt 88Fg3dJ IHVWtPO G6aNJYK igxGOAN CwyyuFm rM6fFr8 egANMKH 01cxC0Y FNWlwyJ hbHYm6w SYegUpi y1A6B00 o06np2i k7CwLQ1 7Gk5cFw NYVGSDr A3GzzvL oXwJS9J Ls73Yo6 dN5Zsay TJOsio1 CFgtkMZ RMXk0hU IHgwzaG W9e26MW c4YNmZQ F6tvNw0 1fOInN4 nIhkewh kjyqlBN nXBCQpi MGar7hw GaoCMqc SDDaYQj 7IH6xEU x6lMg3I gN37I3e 6ScQBhS wYtgWNm vmmffmR GSaYHJS IrcJ5LU A3WgNIs FZ3INKy cTDPaDO 7LkcVMX 6zUIXC0 ypVKKDx 8cozzbh lBnlsKd 6XPXPmA bB1bUzf xer5KkZ Rm0N9Nf loEIHot YKhHb5l 1ynDXFU XZlkNz2 M18EDt0 2eT8hnB rNPLIC2 Uho9orE N8EQILA qVZjaYA 2y4twaN mGwOT4F cu1N2kb bcJ62e2 GU2eBPs fETrDj2 akbgSGS Z6Me8Zq 8fj6Fpv dWIao4w 6DF3b5l U8Horki 1WfhDIq X9WgSlO 47yVvV8 3xhH1la M1AIBPl n9IBfRv DsLctsT HZM4JYt 4U2cLdG TV5SYGa yCxXteM f1sDYe9 suLd0x7 cP5vlOf minv2Ns 5V0mRDP CP8tPqG y5FKnLB kbCBHbV LWClRsY nyhvP0D TqpaWIL v9BkIWV r22d6Pt AZxLTHj v9jHI0D su8CTXU zDjqhgY lx3vBXm 3a02y31 D2HUkaF 3Tii4zU Nw5EBjz Ijq70VC VZdfY9r n4fm6TM aCiJZIQ VIfdBqd jcgW2h4 6XPmm6l uf4KEcU avMiFba tvs8Bas mNaP3p0 7i55pL1 RVPBvzf 8fK9tks 8ZMgL3h gW2ftYE cBQ8hH5 RORVRZr hzDf8dq 3uZhRk4 gRHhs0R MeI3cBm JqT5wNt 9zqhpYH o0Hqt5A b7AwFgE r0l3ogu VupuAEG HhfnfyX JFBYNjt 4Oh3dEJ NZjwRoC AUoK2AG NNM8rZ8 OX67ib5 JWH5zAb IDygWOp xDdWATx 9Mj8zlB 7pFMZ6i 5c0M1BP Go8eW7f BB2CJVS qi8pGjG rLZmlH4 0jpvh8w co1BEsR YA1yBmO 6BcrMzn kMunP09 F1Rhfmp SJ7KxDw 6kIuyHW qo2DkRZ MaTkO0W vly48NU 7EpUFSz gzFs0NE bl1JkP4 BdBevlV MExATkD Y2oU0vi gYrX9dn nMyC7P9 cohjCry X7PsL3W rOwgwMB FMx4iS2 Z8C6HTh RcmkVLz pqC0kzP hhZAZRS sgtOmzN t8TcKOd gi9OoSg IMobmdf E6t4e9g nJqQ8Nf gevm4FB T8fJtWt YdUMsZL olJUryD 3o91BhC dAyUS3D Qec0SBg IKyhiJf jEfOIAo 8mKaNFR BYyuhXe 2Z2fWS6 n9o2Roq ja9MFam ziuO60J RCXD01m 5RTP8Au AzSSbXi WbGFHfP 29UPfRN C8q7TKr lIMmaAb JB07XH0 eOkBHGa pVWUFNE 7LBTb9J 33m5JPv c6qWOMj ySS1X81 Or9nqWz 8RDDYry Ell8RHo fIOIFKd EkAtKhn OxIIJLc aOaz3bG cjQnIPI las6dyL QmuiBfU gI8UxHr 09cb38w Nx0nvPG LFQfq18 zFW2R98 LTZqpBT 5OmKXWN GDOtvVA 0Rp4MYB glovEe8 zmwn8p0 N1E6zJp CSe1jTB rNSFoUU phopnts CYfYBQU fpvxKoO L823d03 oe1sqwv FECwEN2 fwA7fJ2 vI2GevE a0zYdzg QwTtz0a vRV10nn kS89eLy DNzeIJB Ylylqpe ory4IlW WoA0o0G McIrMkD JSTg0QV VHQ0VM2 Fb6ZyGs TaqKtC8 lzs8q4m D1akkSM 05q0Wlg dEH9TJZ vzob2tY pZ3hgQP fkyqWUE BOXL1pS ALClWXs KQCxice JSxDvO5 GJpAlTY SQ5iEJK GfXEiFV OKm7VGZ vZgwbvK IWEJ3lU riyriWx lXYLzCl jj1VtfT W0uvPG6 aixHQ9T iF1fhAn 8fAYwEa madFuvs sU1GH3X mE7fsfM V6caSXf nMKjnwm BSIICoJ qTYyXE7 zh2bw2b K3x1iF6 vyly5Fm 45OTmR3 da1CyyP ZVm7hfQ G93mtZx mAMGOpv 91pG5To bGv7zGU NEZbpNy taxLDoY gKlAJKy YO0QJg6 35QM1O3 S1nnjO1 I5KS2Ex kkQMxam FjVqD8R LrsR38f 1cfvJoe 8b6KlvN k86NkeJ HU1Q6Wh XhxyoME jfhkfpv mhqvFWE vJ2tcdc edhyrFU bqu7Oja D4yoana nVeZtLN qo7VdkT yPQFs3J NNbsxqf pkwvZLl epETefl SvQHhv8 vEHOiah d45gURh 4gf06gW eyREXfT OcC8FbE cPLykuh aKwfZAG Zi8Vxqz W5jwyTW w3TlXtD o3xyyu7 HKLrMcS vOjHTPk uZtwAaP HGtMf1J qME16nS oe25YzI 589633U BRGe8vB npZCv0a AacVdfU l1Xe8Qs 0ffv5Hf TDzHVkd ZXCs6li uuZycEx g8R7u4l XYckY6a tria0dc oX9tSV7 QIxbTqC ajWRxWq 285kZDn caRlcHb AWscXvV fznTpA4 auJEvkD kFxRZTd ctEG4sl eipiUOT y9pNGYN HsRKJu1 TZ9h7Vv fusymwD RSHIHpK mpwVCJI wozMy0j wZelod3 5A6ztaP 94ySWPr 1wKFKiX b1MhMZ3 oXSaxLq JyzWXug p6QYU0b 5fDSxYY FTPIQm7 I5jH5OL cpLTlYX rlMFMhl EN57q35 qtPkypY jGWSQyS 7r1WdUg 7hdW5V4 70ZRXIo w0ZcvVd s2kpv4m 8KXrWSg DhGWVmV rJKBzeQ 4UoKcDh WRL5deT 9Q5anFS HXRidob pBJZwxV E6ooaoU MZSXKFn 84secmH wUyj2YJ J0wlzxd 8jhNksN BFgxamS avo3Mgq Lqs67cz PrNkUDt XNySGos fHb8wFy xoWSXkn NRCSQtm FqeO50G VP8jQUH iWAwOqv cPVGUgO YWp5WBq a8IelNC 3724JXC qS3sTYe TtGwlbM SC43enD NgeoWC2 BdEYjAC tOpYaZz a1eDBbe YkTsn8Q TTOSC0B vgmLiD7 NK4gNlP GdBGdu5 WHcGb1i TNEC7OQ ef9xQmW yYkdHi9 3zzkJQ7 zLe11Aa 0VqLZkm gUUjYFr JHgOqBc qcYBlkz 6ZQC1ge c3io2Zn 8pHggsO SsJQin1 7ktyB2b uDib52c bUtnHpX PmQLZnT ejIpul4 KBII1R8 9CsRbtz RT57IzB 3AxbvnA Xap6Fb9 1ewO9a4 oNgylQb dvukbiC C2tI8WC juQ4J8h CXLvop6 FZg7kLI ioy9H9G hdTVarc Kuoo3cT mzMH8vH n1oKly3 ZRjAXGq JeUnQte JQTKgXc 1CBnpYg 7MdhQjX S1bQjEZ oy60WzM gB0TLis GlGQnse jEavDTi 2LRXpy3 WUZaRMW vTfWtac wZllRDE EiGIDx8 kng0UJr Yxn4AMJ N0MxwHX e9mfVKU lm0K8JZ pKIe27e ULBrkAo qhjlUIk z4m8t7L xl7smRP wfD7tir owZex2X W2BSQhL pmkbslg SWK3Fim 8ifSfxu 2EgTZrs 1O1Sax9 u5gBBZr q5kiiBN N5eFH11 vFgPxOx LAD1Vg2 L2w58jR qAsBKQ6 x2rMihz C0IwiGS eexTBqs knyZDiT f1HXheQ eLaZlxW MZ1E9rs xY91LCB Z3OGSLZ ofzTiRq 8x3IFMz 4R4sUs4 umvMZQl i6ycCKo EmOqfe2 sZbX6ib ZGbYZ3t qNybjoJ WWtRQ35 KqZwUkt It740uo 3lurqxj H4cpWLd ajzd76P 07PfOWj 5twGh41 LNP1fLB A7s03i1 b9EYIEg irpZX3H kzupAKR vUqs07D N18xEfm 7neiR2q nfaAZoV 9aRyncf JpB3WbB YM5POoS NH9Pr9m gJzHPy9 yXuPj6m uV3hyTs E0pFucw oF8mP28 Li0AkHI B39X6J8 hNe33d9 JG5fola 1tP8oqg u5VGFjW 9MBJ0In T9CQJwa Ig8W5LZ hG7CyqB 5Wbxjo7 m7BxDvV ulRhal0 zaV3kfX zDsgN6o c6h8KmH 3aj0XUP 6zUxe6V xbp7EkC RHWjnfP e2A2Pwm uMhwPcL QuUaKsr xulnqbs OV6t0Gc Nc8DAOI 3lP68iN POMnCCy Z8LjXhL SLfHXAL toi0C5P 9bJotyP lzenzuq xIhD3eW 9xpgw1F KlPJvCI tbXOjZP 0BVh4UH OKJIzoW eLcRfIE iNUUNqD JhiNme4 gPy2k4M px83uwN J8BEQAv eOm0geP 5AeFGOO rMxaymZ MAL5lPf 068Jkvo CrdvjTv kIjkLO3 RK4hC03 Ujy1UPI zQDvBkw mujcYGE wFzYQDQ dXooV8a C0oueNw Ja8pO6L IPo8O9P AFkjvZb 61HVQau WEi5YJb ceigaWo IaIffZc SYrKQj2 p0vx52F Vp1wzmG 4yLXXXj CNsowOx 190AVOz YKsAvgM HLC43Rn C8BWUVB pxC2iut vUMqhJz B485XY6 EwNG88j PWSg3bk Vw3azAs PGSu6Xw Zrvq6mj UkTjoTj ZGrdSLC cVktA8e Hn4motm rv4Veor ugpWwYZ 5VruAMs k5HJh2p J3LwRGg aDB7ISL Eoz86rQ hBMOx7w dV7Jio8 Baj7t3J ncA81Hh cmuufEm 7wpBqEr 4lk1wsr Y0ppLIU E4fdQyo UyUNVZk vzjHmSs H9kpRiE iTyDpIw NscDzbH GP9s4vU TDz9b2C RFCS4eW YVkMGn6 KJfcBEt HSJ3fd3 xoi6sYR wtwLWfd maO2kVV fxHMNdl vXvh93a 7Gb23XM B4CUqkH HBTU5Wu tejfUYZ ecRUaxL oj5LCjy fMVGcua Ec2x17n FBDDZ4c dFDyVii U8GIC0U mrA7CIF acn8XcE tlxnt3J mAg8WMY fZAeeo4 nw0X0NT NnaMSQu Uu4ZGK1 4q0MoKa KyjrnM0 Erlmolg IzsexCt UVqeZxF aPkNDRo Wz1Jjsl K2nghJO 3h4ONap DWAD1zN aWx5dtW RDU8n3J FvQ6mLs UPLgGcx lX7P15N cNB8RNV qnEnwcu x1S8Dc9 YW6NZod HYlzHLP aUdRApJ 6GzJs4E bZJGN2G EhrfHTJ LWsIlnb hNX8Scs U5VLMiU eag8Crg d9frnDJ bWCBkd5 CgqpP3F r6zrJ6n AA22117 wJrWOPJ 8KrSCxD sTStDFz weYvIYm RkLRfMw 6AxcqEy 7busYp2 kVjEDIU MTZ9UZG 3YwfhHV hGVzTiz mwoqPJz qMAk0Dz uWXhxPT PG9TdFW jkCvtQw 2gUKntd w2JTf0W 7sZywCm IohkoiI wH1PzoO ezyRz2G NiECOtu e9UUhK9 BVtQyA3 gYUbn4A W0i7NtU KSNGDG0 9eMIPoZ ppP1h09 lOy3IEU vNHXUeN nlN6Eq1 AKkTWke x4Ibe1U b2deTTp cXBotQ1 FUyBCX5 8dHIAtb IzYivzD vS9qRa3 lnYdCfd yrDNPn1 0Gbf1bf rA3vk7g g09UIya V7gosJt nPE4SZU faROuVL NBDTmYc 1yRonN5 vllHgrQ IW1mrBD rpS6a8J ykv7Y7u wPuxC58 LS8a17p dBQYFs0 tp6ABqG euVDRts VVluLmE FvyBPat rl7PXiU TJxLJqp 8oxXnkI aGVXdIS zZkMQd6 sa2lgw1 P1FQ9Fs J9Mm3qk GPAVB3e 7uy8Ia0 BSCzUNH ULXYQAh x9JQ2GP Q18r7sn PipyZ5V orot7B4 aH7zJsr RwvseQC QvIoqRh 8wl0c3j SBIbmEx uFMIcWC kvFS4An XqSNLSv C3WrUVW 9SQ3dK6 xVn4Flb VjS8BbK iDT3L2g HpOOqYZ AWDmXIH R5uGYMY qsdi91I i3A5ecx c4esYrf 55jNLSM rleqa4E zrAazjJ FZ7swkW JFngvNt AxDt9yJ D4x5eRt ONVHEdp OtjVTXk 9MvPfYs jQc33Oi RmsThlv 6a9YC1N 21QWv5w W94C83A htIlZRO YqJmtO0 db5V5Ub AzcUxiP NAQwsur 2jeJVxu R9xt1Vp x3ujmZt FvZT5iW AOVRmhT as63lJv 3xxp4ey PIKcYSc b5cDCbq PlTXC22 g5HmA0t TouJ1Ed R1SFVsB x8iM25J J77n3x9 Ok6Ict5 1CprvSI uWj05th 0y7EopC Ywc3lTY uzHKpyA Nt7ylrn Ue5HvaV SFjhWIR 9wF1wJr sjb6LaK ETVarXY rKFh145 BapSP6X hfEvX8M UxBzPVr uzj5OKo uNBsTWB ejmLzMZ uIqXydj EapZ6Fm MNy7GkM ZBIc2C9 Qp7Ofnj g9ZfBGq kvdAgkg uVmDQqH oEc9kga d2MHNIZ xxvnrjl 6AMktqr qV7hDgQ mCugy6C ZhNpROg tRF7ZMx dvEZpfG WRxwicf RetEYPh OV47mUd XgmTmj3 cA0Xnq9 keWSpjr AyhRNb1 5hIwHfo pF7xxfi WvSPTlC y7tIeL9 kO9G9po N6sWooM zP9DZmQ LyDZOJs 4DEUr4P a0NIAIR qZOkNUV tEDdvxi CC2ic9g D83ZrPJ UusUopo G5ETOtW r6PBcfC IqMXu7o GVqqXD5 cj3XoVB gwOposb 2xWxDdO I3Vth46 YvZubzs yuwmOXD 7ktadIy mwRmFS0 y9842zm JmRdNPF BqKWjAq ViQp9dS bGt5nh9 XZJAFqB 3tEyL21 a8b2qmI EhiRzDS ppDBAPO ijRFS3O kQb5C6j F2VOmv5 QWQSDMu HohHz0m NMflUX1 96MwQfK B4eVfB6 yDQjEzQ 8p466Dr vGc0L8w wci1Wio kWmZhMo azkYTvm g7n4dM4 lTYFh0D Hykq4ug c3LHnmi n2KC7FT x3d0Qql 5nOJdXA lA1B6cy iOPD6r4 JENrwSk SbqMdGS OT1ia3N gSlkxoI 43EfpEC uBsdSbx wWXZbp3 B0hlL2V 2GV1T4G 9twtYZ0 dOOjQAP aD1dFws fxhU6wQ VkNM9wc lPmP55C aE03bdt A67RSqH hGozUqU ECP3uiU 1byyDcr 9oUdWHk g2nwqWG wL0dWjQ aMZK6Oj wjnpLZC u0K3ZVs 6gkvl7h Qe0rJP1 d5neYAk axkEX6B 8vHTyWk VEWCBoZ NmXbcsQ rEOpRF5 jRD5Xm1 tLrjAh1 hGoXiRI 6BbWjgh N4k2olD Z90307V XbN10ta q2virzn HGibm4r y2nIJh5 duLkNMd 4JHzrJr Mf26fYH W0B5kay zNOrskI 9bwYg2n ReeSU9E xY3sKMz a72VdCK OVqFvA0 fXqYlCP zOCq7VH XlIXcqj OILB9GF w7qc1id NtCr5CL gGqrGiR 4asXNfC P8LVZ3Z AifCT08 OjTROXu DXoa3jm ACsihNq jAWNa7w oMMrLiB XAwuyDO Mj81AWE em3ogyQ 4afTXZr nT4jFnh Kub6Uew Mav94RY eKD275U RYUF1cD urawWgW 8BUIQzd iXBAiVm HoFYfj9 p84cuAp ZY915bG fYGX4Mm c5VINge 8rbxXgA WHZzNa6 NL7Bwea xPK8cN9 lqm86nd F6iK9Rf jdsJyLr 8WgyzIP 6FWiQRN GksdtbJ acbk7IO kYbq8Bl IoqXpW1 FlZscU9 vcMbsGF Zri0bol 7SzpTvT SKbKyJc TjvxrNs arb5CXd u0zgKYD LKAeqSQ MdCwJJI K6dIENB c4KZFBv CWwBMiV CYoWc0M 7NVQ4Hk l6XROq8 Xyr0k7F 3c6ARzM zqJktLb Zm9RGv9 1HXlhxl kWN6UHm vbsp72f a86Lh21 4GprjRj V8su7Qt Wox6f34 Gdmo32E JnWDQz1 2j95IOW lJswIYM qntv8KW qkZyn0P pMzOqVt ETnaAnl nIR0IJW RUzKBEx J4Tzhry y07jH3c bVQieEv Ju2GuwK BhHFtyl EDV1ryv YkiQdwO F2lmgtK Au09MV9 McdvSEU DyrCUVi iPDTPLt PAY5QS6 b9B2Mnn Vu0tTaM gyE4NKh 7aWZqzx Uwjq4vm XIgbHiv teWm6OT ZiUTvYA xA8Rqfy cvnbQtB YgN4UeD VoOYAKs 5HzlzBk ZYwqtaY o86Nxza ciA9G0e aurw2YW xvRs4hb S3nWaDy jJtuwUC wgTgUY4 AUYodwp YraAx37 hsqFWgZ oePJY7l NAsSRHI atwI7tP 6wRFEAj trIPry2 kbrr4Zw dbo5Krg dcKaCk3 ejsnlPG nfVW2nY 3KSMP0s GCAE1RZ AuHVHIj RWnpn6V iAZ2dPg XtaNzWs Zh3geu4 ywocFQM EKjS3mB xJ2vtNk UF0CDlO PakKwUt rzirprE 7V9srAX Q0AH7qk tIImrSC mCjV4YN PWKFtdD h3QjKyJ Mh58Sf6 uCIOsMD cTol9bT VveARDw ufNx5JS GN6xyTx 4VGJO8m qroVT2L eJrWjUj 620NBQR umHljoL RYUKm97 SoZGyUu G4N3RLQ 4Z4kjJd Ab4WX83 9gy8cOJ bni0uxY hAE8Dak 5vy8SQe CvshzUG Cq71vg0 GRLLMM0 DPApZ8w hOCXEJF fwHtP49 bHmxNlE rGMePYE gDWYcaB TREkelZ uzhndbc TvESHAF GH6vDdD j5A3cuR xUAb6et IkSBVqY xqvX32q z7UV0Ig jVD7SCV lSO3nP1 GRp4xWu i2dFSHp hbvUfY5 Gg9LEaS jX0eS9y dSYo16Q WFdk3iH BLMHTbj no6IJWt yty0huG 3CtUMmA TDbB84E ePopsAQ bzG5hLo lxCtPQf OHJHtWP dSALiLy jMcOl4a GdEnpns BaNltm4 XLHN78c pMgIDYG Xkycdij BBwQFeX pP10Ap4 EZ1cgUp KLjMjh3 tIQf0pX DoR3MSB DcLhwnW bBQnWln SzKB2jr KMZpsFm GAONwba Wo3vYPe 1Qv4rIr zXQSGF9 9K1ppYo XjZY2gs NJ8iHvB O6aDolZ iUI4kLX POVX1bo luJxRb4 tQlIjok 3LchEed YXWStD5 dWerWTF wg8BNlp 6nmaaDt mH5QhZd 1YTD5e4 6Pctdsy DUAsihn HpuNtRx tp04HJP dp7bhQL QZ0OMhN nL79fwC nYBYkpw 3IRIObV 8bR199w GR7a7xs nVrIk56 3kayzBK lv4fOtS IWgtXw1 fZW8L4X hq6LxxW NVR5jQt jmwvNmZ 8VQCg8j ZUCHA8W 1c0CwrY Cuhuo6q RHyHs2D jWNwcfd TXvMfg3 6SfPBBw 04lKzkE 3LXN2nL HuTEblJ EcIr1Xj ZtEzjel BEVZ7Dv 7h6YSaR FhfWZRT hkWmmDc WbiaUFG iwV6C2R eP43WoZ Xfs6uU0 bxCcbaB vfK3qDY fVaC4g8 Nqsu7iy liw2lcY 3ZjjfS4 k1cZbst EGCAlb5 92MHF37 SHMNHTI sK0Covc tMxx7ih ZJmkrmG ZYmo5MP eDLyvCW lYpjKNp V9YzzMW HNBypTR Q1RXXlP QiGdL5L vKspBpT el0qDii Oc4niG7 p19xxmI yyoWkIR WcFhZVZ KVYZRTr UjQntj3 RAAVqpo Bjbd21S A6QgnM4 2u8kh4g fYZVNh9 JsH9Vut hJxkGrm KOVU4vY fEy6WlK O6uv0bk bQ2yD5C RrFOFk9 7BTORhc YeY5H80 3fzOtHV Y8zwLB8 Picc2FU uhofiME jrwDegq LNrEmez b9tTGbl gkcPYAs T1SMIyV jbMPWXe 3e5Fv0q O998S3T qMV6SvF qKFvEB8 6qWglOn 3kwaNlo Kmbhioq 5wDUA2v vBTiJfy eQ8vQft tIUUORL VFu7d0d mEbRF4B BQSFZ9D LTNYPFp emVXJQ6 BvThl1V nYG7FdK 5WMVN3I JihIyqX SuwocZJ 9FJXhLq 6XmYYlW DOcRQIk NefNUvL r74gHK3 MpkyDB2 vYnapg0 cRwFk0F aQPc4Dz kgiP2wf o75rvNp TRBZtto aKtO35f QdXyX5C 9wkfjDF 8jf5mXA afM3ioB XHMKL7r EPgSzfz gqxbbNe ruGiE8e eqQa5ht punDCDq JtBQDCa YZXhnMF JnoJ4KG 8mu4yFl 6cngnIv fcDUR4g 9jErFWX buFWSwZ 19rpxTb CituqkV pfv66B5 mxrIlGH LYXAFIP ILVkW7a jIPcPjh 0uriMfg 7IYRIXj yX3iFv8 Rp6bCH6 p17vkHM 57sCvy5 qvVWMer f2XnQZF 5BmlYO1 W7agthB GHNUzbj HbuoHTa 3gzJEwO RdPZNLi OiezKOU vYOcYoi tDB1NA8 yjysRYZ wDA0mkT SKOBdLn DRWCsyp Hjrl8sK zm0WCiR aUtmd8l rF8t1Q6 llT2m8L wLtdcT1 mEW9t0d nB5pYRF RFYQNny pOMdVOU KZSy7aj ggCeD5F I258sYv XuJmZM0 c6ttJb7 TxjBq8k fF5whLP iVh4iPP Kdhm1rl y2KJ5jX km54LaZ 0vvVyel 8fRqsyL mo7Ja3i 2sWXTbT diIpyT4 uiX2WGh XSQR2JO uxBezHQ hZ6ukhx 5EysSnt GCRjtjr kcqy8OX 7ezjcjc qh2Ka74 ppanSR9 nJIHLmF VFcD6Zc Q60Um3y Fh67D2u f4X0cD8 LMPIwFe kRNXvU8 JJRx49f ND4qqO1 qN2GdEG jczStwt fATutiQ cp0vcrR zrHv5QL zEvuWww 5cf1dWa WXcQQlN t51QD7P RzEFHnk 7PpGiiw Q6zJ2Sm zLRLtZu JerCvcN Ony5JA8 sMspNeU uYgeRxY MNhaE4e znEUzJh hoW3p8u yG5GyN7 xgWneoE bxkwXDs cm4Zgzc pBVH2Sb 4smtFIC lRib9zx EhrFAlY WvhCxq2 luD9Y7d 7tMIEez ICDDViR Al4k8ne LG9gAMe y5K5ndu E2XsLPM 6DUdMrC WG37yPL vclRj3X JgEdi7T JODt08A scqtBmo i1ZlpCd Ygwu6sV jQVokYh 3FdnS1R emcBFuh XVppB8L IvTntY3 vsbK1yq KSmvYpg 7Q6W2si fP7PFdW 2jNnMAZ Wk3NR5Y v52WJmY qLxwSIy Xsy8uPA 1wovMJO LlEPFn6 OAXnx9f vPbsOm7 LK0MwXo lxAIisw Oqu1yVF XSD7Gef aArlwSM GHfTjnV eQiTAFb BoVyTjI bDdHpLa 1nCo4zn MO7J21N NLe3PhX tZJFCqR UWY82OP 7SS09bQ K58TvOX 027ZIHo 3p3eFRA DJXq9mr Z35LaZy ydRvYNI cTRUAU1 zaWduyK xcGp6WK yZ4bs2u M6PnAfW bstLrHy c6ago7t NIBpcYH g62oFpo YYcO3Jp W26Ou5Z kiGa2ZJ mFb5hlS lezpuZv SQnffg6 oQ574Nj 8TEkEBH lvMPg0R tC4NZKC gtwDgQd zEXwDym ZuE3h3B INtwrdD ZwqYM8s 3ZhXRlI IYPE8vE Xwhr14s XNU7Wj5 x4rtJHt ARcNuMw nWHjENL Qdx1FwT xEeN94b m70wOrt oZVbzNL pqo8KYY DhJXPlc nRQseaZ 9Zk46Aq hpDPUG2 p46RZ8T TZ8E8NK xh5bGYZ riofJBG MAf5Naf TTXRbS1 7YA0vdI wa1tO5k qtb1Xzg 12KbO86 DIVZXKQ hQJK3Og F1ykYWT Lb8VIqu sNHT32v NFe3rS0 6SWJgUH RF1elLn l3AfQQi w7HjFFT q3qqTJ6 cDFLVhM vYIuPPC XtS3KBx Qt65Vdm OyVIfYb TnFEqUS beSOyBl VsnEQ1L vlcHUJF rpgxgRz gIu7SZ2 DdN53ni x9Tir0E WP6fNuz COzL26H CAufNv3 xxfGmzB ohRwVil KMkqpRR vaKG23T 9nAq6ta Tnd1LFF 9jcxMAV MwdVnPT mC0ptlx 9NfuBad Computer Science PLUS I Volume 1 : Concepts Government of Tamilnadu-. Government of Tamilnadu First Edition "#" 2005 Chairman Syllabus Committee Dr. Balagurusamy E, Vice Chancellor, Anna University, Chennai Co-Ordinator Textbook Writing Dr. Sankaranarayanan V, Director, Tamil Virtual University, Chennai Authors Dr. Elango S, Government Arts College, Nandanam, Chennai Dr. Jothi A, Former Professor, Presidency College, Chennai Mr. Malaiarasu P , Government Arts College, Nandanam, Chennai Dr. Ramachandran V, Anna University, Chennai Dr. Rhymend Uthariaraj V, Anna University, Chennai Reviewers Dr. Gopal T V, Anna University, Chennai Dr. Krishnamoorthy V, Crescent Engineering College, Chennai Copy-Editor Ms. Subha Ravi, Director, M/s Digiterati Consultancy Pvt. Ltd, Chennai Cover Design Mr. Madan, Free Lance Graphics Designer Price Rs. : This book has been prepared by the Directorate of School Education on behalf of the Government of Tamilnadu This book has been printed on 70 G.S.M. PaperFOREWORD A computer allows users to store and process information quickly and automatically. A computer is a programmable machine. It allows the user to store all sorts of information and then -%$process*". that information, or data, or carry out actions with the information, such as calculating numbers or organizing words. These features of computer make it a valuable tool in the hands of users. Computers make life easy. Users can focus their attention on solving more complex problems leaving out the routine activities that can be computerized. The creative abilities of the users can thus be used more effectively. The users have to utilize this powerful tool for the benefit of individuals, organizations, nations and the world. Computers cannot do things on their own. The users must understand the ways in which problems can be solved with computers. This volume contains the basic concepts required for you to become a user of the computer. This volume 1 2. 3. 4. Introduces the key components of a computer system (hardware, software, data) Familiarizes students with how computers work through an introduction to number systems Presents the basic concepts of various logic gates that make a computer Gives a broad view of how technology is improving communications through the use of electronic mail and the Internet. No previous computer related experience is required to understand the concepts contained in this volume. The field of computers is fast changing. It is the understanding of the basic concepts that will help the users in adjusting to the rapid changes. Without the conceptual basis, the user will find it very difficult to take advantage of the advances in the field of computer science.Knowing the basic concepts will help the users quickly understand new developments on their own. Hence, the students must focus on understanding the contents of this volume. The authors, reviewers and editors of this volume have taken great care in ensuring the accuracy of the contents. The presentation is lucid with many illustrations. I wish the budding computer scientists a fruitful experience with the powerful tool called computer for the rest of their careers. (E BALAGURUSAMY) Vice Chancellor, Anna University, Chennai Chairman Syllabus CommitteeCONTENTS Chapter 1 1.1 1.2 1.3 1.4 Chapter 2 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 2.10 2.11 2.12 Chapter 3 3.1 3.2 3.3 3.4 3.5 INTRODUCTION TO COMPUTERS 1 History of Computers Data, Information and Program Hardware and Software Types of Computers Summary Exercises 1 8 10 15 21 22 NUMBER SYSTEMS 25 Introduction Bits and Bytes Decimal Number System Binary Number System Hexadecimal Number System Decimal to Binary Conversion Conversion of fractional decimal to binary Conversion of Decimal to Hexadecimal Octal Representation Representation of signed numbers Binary Arithmetic Boolean Algebra Exercises 25 26 27 28 29 30 34 35 36 37 42 48 61 COMPUTER ORGANIZATION 64 Basic Components of a Digital Computer Central Processing Unit Arithmetic and Logic Unit $+# ALU Memory Unit Input and Output Devices Summary Exercises 64 68 72 74 78 96 98Chapter 4 4.1 4.2 4.3 4.4 4.5 4.6 Chapter 5 WORKING PRINCIPLE OF DIGITAL LOGIC 101 Logic Gates Conversion of Boolean Function Half Adder Full Adder The Flip-Flop Electronic Workbench Summary Exercises 101 115 122 124 127 131 151 152 OPERATING SYSTEMS 155 5.1 Introduction 5.2 Major Features of the Operating System 5.3 Most Desirable Characters of the Operating System Summary Exercises Chapter 6 6.1 6.2 6.3 6.4 6.5 6.6 6.7 6.8 6.9 6.10 6.11 6.12 6.13 155 160 162 168 169 COMPUTER COMMUNICATIONS 171 Introduction Network Some Important Reasons for Networking Applications of Network Benefits of Network Types of Network Network Topology Basics of Networking Common Network Services Co-Ordinating Data Communication Forms of Data Transmission Modem Data Transfer Rate 171 171 171 172 172 173 174 176 177 179 180 181 1826.14 6.15 6.16 6.17 6.18 6.19 6.20 6.21 6.22 Transmission Mode Internet Communication Protocol Who Governs the Internet ? Future of Internet Uses of Internet Getting Connected to Internet Popular Uses of the Web Intranet and Extranet Exercises 182 183 184 184 185 185 187 190 191 191CHAPTER 1 INTRODUCTION TO COMPUTERS 1.1 History of Computers 1.1.1 Introduction A computer is a tool and partner in every sphere of human life and activity. Computers are bringing many changes in industry, government, education, medicine, scientific research, law, social service and even arts like music, movies and paintings. The areas of application of computers are confined only by the limitation on creativity and imagination. What is a computer? A child might define a computer to be an instrument capable of producing a combined effect of radio, movie and television. This definition is close but still does not visualize the power and capabilities of a computer. Fig. 1.1 Computer A computer is an electronic machine, capable of performing basic operations like addition, subtraction, multiplication, division, etc. The computer is also capable of storing information, which can 1be used later. It can process millions of instructions in a few seconds and at the same time with high accuracy. Hence a computer can be defined as an automatic electronic machine for performing calculations or controlling operations that are expressible in numerical or logical terms. Computers are very accurate and save time by performing the assigned task very fast. They don't get bored. Humans have always needed to perform arithmetic like counting and adding. During the pre-historic period, they counted either on their fingers or by scratching marks on the bones and then with the help of stone, pebble and beads. The early civilization had witnessed men develop number systems to keep track of the astronomical cycles, businesses, etc. The word ./)computing-'" means +#"an act of calculating./*. After the invention of the manual calculating tools, the concept of using "#%electronic gadgets%/* for computations were introduced which gave birth to the computers. The evolution of computers has passed through a number of stages before reaching the present state of development. During the early development period, certain machines had been developed and a brief note of them is given below. 1.1.2 Early History 2500 BC *!, The Abacus Fig. 1.2 Abacus 2Abacus is the first known calculating machine used for counting. It is made of beads strung on cords and is used for simple arithmetic calculations. The cords correspond to positions of decimal digits. The beads represent digits. Numbers are represented by beads close to the crossbar. Abacus was mainly used for addition and subtraction and later for division and multiplication. 1614 AD +%! Napier's Bones Fig. 1.3 Napier's Bones The Napier's Bones was invented by John Napier, a Scottish mathematician as an aid to multiplication. A set of bones consisted of nine rods, one for each digit 1 through 9 and a constant rod for the digit ''*0,*,. A rod is similar to one column of a multiplication table. 1633 AD &/+ The Slide Rule Fig. 1.4 The Slide Rule 3The Slide Rule was invented by William Oughtred. It is based on the principle that actual distance from the starting point of the rule is directly proportional to the logarithm of the numbers printed on the rule. The slide rule is embodied by the two sets of scales that are joined together, with a marginal space between them. The suitable alliance of two scales enabled the slide rule to perform multiplication and division by a method of addition and subtraction. 1642 AD .$) The Rotating Wheel Calculator Fig. 1.5 The Rotating Wheel Calculator The Rotating Wheel Calculator was developed by a French philosopher, Blaise Pascal, using simple components such as gears and levers. This is a predecessor to today's electronic calculator. He was inspired by the computation work of his father's job and devised the model. He was only 19 years old, when he devised this model. 1822 AD /.# The Difference Engine Fig. 1.6 The Difference Engine 4The Difference Engine was built by Charles Babbage, British mathematician and engineer which mechanically calculated mathematical tables. Babbage is called the father of today's computer. 1890 AD - Hollerith Tabulating Machine Fig. 1.7 Hollerith Tabulating Machine A tabulating machine using punched cards was designed by Herman Hollerith and was called as the Hollerith Tabulating Machine. This electronic machine is able to read the information on the punched cards and process it electronically. 1.1.3 Generation of Computers The evolution of electronic computers over a period of time can be traced effectively by dividing this period into various generations. Each generation is characterized by a major technological development that fundamentally changed the way computers operated. These helped to develop smaller, cheaper, powerful, efficient and reliable devices. Now you could read about each generation and the developments that led to the current devices that we use today. 5First Generation - 1940-1956: Vacuum Tubes The first generation of computers used vacuum tubes for circuitry and magnetic drums for memory. They were large in size, occupied a lot of space and produced enormous heat. They were very expensive to operate and consumed large amount of electricity. Sometimes the heat generated caused the computer to malfunction. First generation computers operated only on machine language. Input was based on punched cards and paper tape, and output was displayed on printouts. First generation computers could solve only one problem at a time. Fig. 1.8 Vacuum Tube The Universal Automatic Computer (UNIVAC) and the Electronic Numerical Integrator And Calculator (ENIAC) are classic examples of first-generation computing devices. Second Generation - 1956-1963: Transistors The second generation of computers witnessed the vacuum tubes being replaced by transistors. The transistor was far superior to the vacuum tube, allowing computers to become smaller, faster, cheaper, energy-efficient and more reliable than their first-generation counter parts. The transistors also generated considerable heat that 6sometimes caused the computer to malfunction. But it was a vast improvement over the vacuum tube. Second-generation computers used punched cards for input and printouts for output. Fig. 1.9 Transistor Second-generation computers moved from the use of machine language to assembly languages, which allowed programmers to specify instructions in words. High-level programming languages were also being developed at this time, such as early versions of COBOL and FORTRAN. The computers stored their instructions in their memory, which moved from a magnetic drum to magnetic core technology. Third Generation - 1964-1971 : Integrated Circuits The development of the integrated circuit left its mark in the third generation of computers. Transistors were made smaller in size and placed on silicon chips, which dramatically increased the speed and efficiency of computers. Fig. 1.10 Integrated Circuit 7In this generation, keyboards and monitors were used instead of punched cards and printouts. The computers were interfaced with an operating system which allowed to solve many problems at a time. Fourth Generation - 1971-Present : Microprocessors The microprocessor brought forth the fourth generation of computers, as thousands of integrated circuits were built onto a single silicon chip. Fig. 1.11 Microprocessor As these small computers became more powerful, they could be linked together to form networks, which eventually led to the development of the Internet. Fifth Generation - Present and Beyond: Artificial Intelligence Fifth generation computing devices, based on artificial intelligence, are still in their developmental stage. Fifth generation computers will come close to bridging the gap between computing and thinking. 1.2 Data, Information and Program Computer is a tool for solving problems. Computers accept instructions and data, perform arithmetic and logical operations and 8produce information. Hence the instructions and data fed into the computer are converted into information through processing. Data Processing Information Fig. 1.12 Data, Processing and Information Basically data is a collection of facts from which information may be derived. Data is defined as an un-processed collection of raw facts in a manner suitable for communication, interpretation or processing. Hence data are Q Q Q Q Stored facts Inactive Technology based Gathered from various sources. On the other hand information is a collection of facts from which conclusions may be drawn. Data that has been interpreted, translated, or transformed to reveal the underlying meaning. This information can be represented in textual, numerical, graphic, cartographic, narrative, or audiovisual forms. Hence information is Q Q Q Q Processed facts Active Business based Transformed from data. Algorithm is defined as a step-by-step procedure or formula for solving a problem i.e. a set of instructions or procedures for solving a problem. It is also defined as a mathematical procedure that can 9usually be explicitly encoded in a set of computer language instructions that manipulate data. A computer program (or set of programs) is designed to systematically solve a problem. For example, a problem to calculate the length of a straight line joining any two given points. The programmer must decide the program requirements, develop logic and write instructions for the computer in a programming language that the computer can translate into machine language and execute. Hence, problem solving is an act of defining a problem, understanding the problem and arriving at workable solutions. In other words, problem solving is the process of confronting a novel situation, formulating connection between the given facts, identifying the goal of the problem and exploring possible methods for reaching the goal. It requires the programmer to co-ordinate previous experience and intuition in order to solve the problem. 1.3 Hardware and Software 1.3.1 Introduction A computer system has two major components, hardware and software. In practice, the term hardware refers to all the physical items associated with a computer system. Software is a set of instructions, which enables the hardware to perform a specific task. 1.3.2 Computer Hardware A computer is a machine that can be programmed to accept data (input), and process it into useful information (output). It also stores data for later reuse (storage). The processing is performed by the hardware. The computer hardware responsible for computing are mainly classified as follows: 10Main Memory Input Devices Secondary Storage CPU Output Devices Fig. 1.13 Computer Hardware 3!,#4 Input devices allows the user to enter the program and data and send it to the processing unit. The common input devices are keyboard, mouse and scanners. 3+.,4 The Processor, more formally known as the central processing unit (CPU), has the electronic circuitry that manipulates input data into the information as required. The central processing unit actually executes computer instructions. 3'*#4 Memory from which the CPU fetches the instructions and data is called main memory. It is also called as primary memory and is volatile in nature. 3(%*4 Output devices show the processed data $'& information ,-, the result of processing. The devices are normally a monitor and printers. 3-!-4 Storage usually means secondary storage, which stores data and programs. Here the data and programs are permanently stored for future use. The hardware devices attached to the computer are called peripheral equipment. Peripheral equipment includes all input, output and secondary storage devices. 111.3.3 Computer Software Software refers to a program that makes the computer to do something meaningful. It is the planned, step-by-step instructions required to turn data into information. Software can be classified into two categories: System Software and Application Software. Computer Software System Software Application Software Fig. 1.14 Software Categories System software consists of general programs written for a computer. These programs provide the environment to run the application programs. System software comprises programs, which interact with the hardware at a very basic level. They are the basic necessity of a computer system for its proper functioning. System software serves as the interface between hardware and the user. The operating system, compilers and utility programs are examples of system software. Application Software System Software Hardware Fig. 1.15 System Software 12The most important type of system software is the operating system. An operating system is an integrated set of specialized programs that is used to manage the overall operations of a computer. It acts like an interface between the user, computer hardware and software. Every computer must have an operating system to run other programs. DOS (Disk Operating System), Unix, Linux and Windows are some of the common operating systems. The compiler software translates the source program (user written program) into an object program (binary form). Specific compilers are available for computer programming languages like FORTRAN, COBOL, C, C++ etc. The utility programs support the computer for specific tasks like file copying, sorting, linking a object program, etc. Source Program Compiler Object Program Fig. 1.16 Compiler An Application Software consists of programs designed to solve a user problem. It is used to accomplish specific tasks rather than just managing a computer system. Application software are inturn, controlled by system software which manages hardware devices. Some typical examples are : railway reservation system, game programs, word processing software, weather forecasting programs. Among the application software some are packaged for specific tasks. The commonly used Application Software packages are word processor, spread sheet, database management system and graphics. 13One of the most commonly used software package is word processing software. Anyone who has used a computer as a word processor knows that it is far more than a fancy typewriter. The great advantage of word processing over a typewriter is that you can make changes without retyping the entire document. The entire writing process is transformed by this modern word processing software. This software lets you create, edit, format, store and print text and graphics. Some of the commonly used word processors are Microsoft Word, WordStar, WordPerfect, etc. Spreadsheet software packages allow the user to manipulate numbers. Repetitive numeric calculations, use of related formulae and creation of graphics and charts are some of the basic tools. This capability lets business people try different combinations of numbers and obtain the results quickly. Lotus1-2-3, Excel, etc. are some of the famous spreadsheet applications. A database management system is a collection of programs that enable to store, modify and extract information from a database. A database organizes the information internally. Computerized banking system, Automated Teller Machine, Airlines and Railway reservation system etc., are some of the database applications. Type of Software Functions Word Processors All personal computers are loaded with word processing software which has the same function as a typewriter for writing letters, preparing reports and printing. Spreadsheet A table containing text and figures, which is used to Calvulations and draw charts Database Used for storing, retrieval and Management Manipulation of Information System 14 Examples Microsoft Word Word Perfect, Word Star. Microsoft Excel, Lotus 1-2-3. Microsoft Access, Oracle.1.4 Types of Computers 1.4.1 Introduction Classification of the electronic computers may be based on either their principles of operation or their configuration. By configuration, we mean the size, speed of doing computation and storage capacity of a computer. 1.4.2 Classification based on Principles of Operation Based on the principles of operation, computers are classified into three types, analog computers, digital computers and hybrid computers. Computers Analog Digital Hybrid Fig. 1.17 Classification of Computers Analog Computers Analog Computer is a computing device that works on continuous range of values. The analog computers give approximate results since they deal with quantities that vary continuously. It generally deals with physical variables such as voltage, pressure, temperature, speed, etc. Digital Computers On the other hand a digital computer operates on digital data such as numbers. It uses binary number system in which there are only two digits 0 and 1. Each one is called a bit. The digital computer 15is designed using digital circuits in which there are two levels for an input or output signal. These two levels are known as logic 0 and logic 1. Digital Computers can give the results with more accuracy and at a faster rate. Since many complex problems in engineering and technology are solved by the application of numerical methods, the electronic digital computer is very well suited for solving such problems. Hence digital computers have an increasing use in the field of design, research and data processing. Digital computers are made for both general purpose and special purpose. Special purpose computer is one that is built for a specific application. General purpose computers are used for any type of applications. It can store different programs and do the jobs as per the instructions specified on those programs. Most of the computers that we see fall in this category. Hybrid Computers A hybrid computing system is a combination of desirable features of analog and digital computers. It is mostly used for automatic operations of complicated physical processes and machines. Now-a-days analog-to-digital and digital-to-analog converters are used for transforming the data into suitable form for either type of computation. For example, in hospital's automated intensive care unit, analog devices might measure the patients temperature, blood pressure and other vital signs. These measurements which are in analog might then be converted into numbers and supplied to digital components in the system. These components are used to monitor the patient's vital sign and send signals if any abnormal readings are detected. Hybrid computers are mainly used for specialized tasks. 161.4.3 Classification of Computers based on Configuration Based on performance, size, cost and capacity, the digital computers are classified into four different types : Super computers, Mainframe computers, Mini computers and Micro computers. Digital Computers Super Computers Mini Computers Mainframe Computers Micro Computers Fig. 1.18 Classification of Digital Computers Super Computers The mightiest computers but at the same time, the most expensive ones are known as super computers. Super computers process billions of instructions per second. In other words, super computers are the computers normally used to solve intensive numerical computations. Examples of such applications are stock analysis, special effects for movies, weather forecasting and even sophisticated artworks. Mainframe Computers Mainframe computers are capable of processing data at very high speeds )'$ hundreds of million instructions per second. They are large in size. These systems are also expensive. They are used to process large amount of data quickly. Some of the obvious customers are banks, airlines and railway reservation systems, aerospace companies doing complex aircraft design, etc. 17Mini Computers The mini computers were developed with the objective of bringing out low cost computers. They are lower to mainframe computers, in terms of speed and storage capacity. Some of the hardware features available in mainframes were not included in the mini computer hardware in order to reduce the cost. Some features which were handled by hardware in mainframe computers were done by software in mini computers. Hence the performance of mini computer is less than that of the mainframe. However, the mini computer market has diminished somewhat as buyers have moved towards less expensive but increasingly powerful personal computers. Micro Computers The invention of microprocessor (single chip CPU) gave birth to the micro computers. They are several times cheaper than mini computers. Micro Computers Workstations Laptop Computers Personal Computers Palm PCs Fig. 1.19 Classification of Micro Computers The micro computers are further classified into workstation, personal computers, laptop computers and still smaller computers. 18Although the equipment may vary from the simplest computer to the most powerful, the major functional units of the computer system remain the same : input, processing, storage and output. Workstations Workstations are also desktop machines mainly used for intensive graphical applications. They have more processor speed than that of personal computers. Fig. 1.20 Workstation Workstations use sophisticated display screens featuring high- resolution colour graphics. Workstations are used for executing numeric and graphic intensive applications such as Computer Aided Design (CAD), simulation of complex systems and visualizing the results of simulation. Personal Computers Fig. 1.21 Personal Computer 19Today the personal computers are the most popular computer systems simply called PCs. These desktop computers are also known as home computers. They are usually easier to use and more affordable than workstations. They are self-contained desktop computers intended for an individual user. Most often used for word processing and small database applications. Laptop Computers Fig. 1.22 Laptop Computer Laptop computers are portable computers that fit in a briefcase. Laptop computers, also called notebook computers, are wonderfully portable and functional, and popular with travelers who need a computer that can go with them. Getting Smaller Still Fig. 1.23 Personal Digital Assistants Pen-based computers use a pen like stylus and accept handwritten input directly on a screen. Pen-based computers are also called Personal Digital Assistants (PDA). Special engineering and hardware design techniques are adopted to make the portable, smaller and light weight computers. 20Summary Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q A computer is an electronic machine, capable of performing basic operations like addition, subtraction, multiplication, division, etc. Abacus is the first known calculating machine used for counting. The Rotating Wheel Calculator was developed by Blaise Pascal, which is a predecessor to today's electronic calculator. Charles Babbage is called as the father of today's computer. The first generation of computers used vacuum tubes for circuitry and magnetic drums for memory. The second generation of computers witnessed the vacuum tubes being replaced by transistors. The third generation computer used the integrated circuits. The microprocessor brought forth the fourth generation of computers, as thousands of integrated circuits were built onto a single silicon chip. Data is a collection of facts from which information may be derived. Information is a collection of facts from which conclusions may be drawn. Algorithm is defined as a step-by-step procedure or formula for solving a problem A computer program (or set of programs) is designed to systematically solve a problem. A computer system has two major components, hardware and software. The processing is performed by the hardware. Software refers to a program that makes the computer to do something meaningful and classified as System Software and Application Software System software consists of general programs written for a computer. An Application Software consists of programs designed to solve a user problem. 21Q Q Q Q Q Q Q Q Analog Computer is a computing device that works on continuous range of values. A digital computer operates on digital data such as numbers. A hybrid computing system is a combination of desirable features of analog and digital computers. Super computers process billions of instructions per second. Mainframes are capable of processing data at very high speeds +%' hundreds of million instructions per second. The mini computers were developed with the objective of bringing out low cost computers. The invention of microprocessor (single chip CPU) gave birth to the micro computers. The micro computers are further classified into workstation, personal computers, laptop computers and still smaller computers. Exercises I. Fill in the blanks 1) 2) 3) 4) 5) 6) 7) 8) 9) _________ is considered to be the father of today's computer. __________ invented the Slide Rule. The first generation of computers used _____________for circuitry and ________ for memory. Integrated circuits were used in ____________ generation of computers. ___________ refers to the physical items associated with a computer system. The hardware devices attached to the computer are called __________. __________ refers to programs that make the computer to do some thing. Software can be classified into ___________ and __________ software. An ____________is an integrated set of specialized programs that is used to manage the overall operations of a computer. 2210) The ________ translates the whole source program into an object program. 11) A ________ allows users to quickly and efficiently store, organize, retrieve, communicate and manage large amounts of information. 12) ________ computers are useful in solving differential equation and integration. 13) The digital computers are classified into __________, ________, __________ and __________. 14) ________ is the planned step-by-step instruction required to turn data into information. 15) _______ is the raw material that is given to a computer for processing. 16) A ________ computer accepts handwritten input on a screen. 17) Raw data is processed by the computer into _________. 18) PC refers to ___________. 19) _________ software allows to create, edit, format, store and print text and graphics. 20) The word computing means ___________. II. State whether the following are true or false 1) 2) The concept of using ,%%electronic brains&,) gave birth to computers. The most powerful personal computers are known as super computers. 3) Blaise Pascal developed the tabulating machine using punched cards. 4) Herman Hollerith designed the difference engine. 5) Compilers translate higher level language into machine language. 6) Word processing is a type of task-oriented software. 7) Fifth generation computing devices is based on artificial intelligence. 8) The input devices accept data and send them to the processor. 9) A hybrid computing system is a combination of desirable features of analog and digital computers. 10) The personal computers are sometime called the home computers. 23III. Answer the following. 1) 2) What is a computer? What is the name of the machine developed by Charles Babbage? 3) What are peripheral devices? 4) Define (.'Data+&!. 5) Define %%,Information.)$. 6) What do you mean by an algorithm? 7) What is a word processor software? 8) What is an operating System? 9) What is an analog computing system? 10) What is a lap-top computer? IV. Answer the following in detail. 1) 2) 3) 4) Discuss the various computer generations along with the key characteristics of the computer of each generation. What is the relationship between software and hardware? Write in detail about computer software and their categories. Discuss the important features and uses of micro, mini, mainframe and super computers. 24CHAPTER 2 NUMBER SYSTEMS 2.1 Introduction There are several kinds of data such as, numeric, text, date, graphics, image, audio and video that need to be processed by a computer. The text data usually consist of standard alphabetic, numeric, and special characters. The graphics data consist of still pictures such as drawings and photographs. Any type of sound, including music and voice, is considered as audio data. Video data consist of motion pictures. The data has to be converted into a format that the computer understands. Data can be classified into two forms, analog data and digital data. Analog data can have any value within a defined range and it is continuous. Sound waves, telephone signals, temperatures and all other signals that are not broken into bits are examples of analog data. Digital data can be represented by a series of binary numbers and it is discrete. The Arithmetic and Logic Unit (ALU) of the computer performs arithmetic and logical operations on data. Computer arithmetic is commonly performed on two different types of numbers, integer and floating point. As the hardware required for arithmetic is much simpler for integers than floating point numbers, these two types have entirely different representations. An integer is a whole number and the floating-point number has a fractional part. To understand about how computers store data in the memory and how they handle them, one must know about bits and bytes and the number systems. Bits and bytes are common computer jargons. Both the main memory (Random Access Memory or RAM) and the hard disk capacities are measured in terms of bytes. The hard disk and memory capacity of a computer and other specifications are described in terms of bits and bytes. For instance, a computer may be described as having a 32-bit Pentium processor with 128 Megabytes of RAM and hard disk capacity of 40 Gigabytes. 252.2 Bits and Bytes A numbering system is a way of representing numbers. The most commonly used numbering system is the decimal system. Computer systems can perform computations and transmit data thousands of times faster in binary form than they can use decimal representations. It is important for every one studying computers to know how the binary system and hexadecimal system work. A bit is small piece of data that is derived from the words %,(binary digit(!). Bits have only two possible values, 0 and 1. A binary number contains a sequence of 0s and 1s like 10111. A collection of 8 bits is called as a byte. With 8 bits in a byte, we can represent 256 values ranging from 0 to 255 as shown below: 0 1 2 3 = 0000 0000 = 0000 0001 = 0000 0010 = 0000 0011 ............. ............. ............. 254 = 1111 1110 255 = 1111 1111 Bytes are used to represent characters in a text. Different types of coding schemes are used to represent the character set and numbers. The most commonly used coding scheme is the American Standard Code for Information Interchange (ASCII). Each binary value between 0 and 127 is used to represent a specific character. The ASCII value for a blank character (blank space) is 32 and the ASCII value of numeric 0 is 48. The range of ASCII values for lower case alphabets is from 97 to 122 and the range of ASCII values for the upper case alphabets is 65 to 90. 26Computer memory is normally represented in terms of Kilobytes or Megabytes. In metric system, one Kilo represents 1000, that is, 10 3 . In binary system, one Kilobyte represents 1024 bytes, that is, 2 10 . The following table shows the representation of various memory sizes. Name Abbreviation Size (Bytes) Kilo Mega Giga Tera Peta Exa Zetta Yotta K M G T P E Z Y 2^10 * 2^20 2^30 2^40 2^50 2^60 2^70 2^80 * Read as 2 power10. In a 2GB (Gigabytes) storage device (hard disk), totally 21,47,483,648 bytes can be stored. Nowadays, databases having size in Terabytes are reported; Zetta and Yotta size databases are yet to come. 2.3 Decimal Number System In our daily life, we use a system based on digits to represent numbers. The system that uses the decimal numbers or digit symbols 0 to 9 is called as the decimal number system. This system is said to have a base, or radix, of ten. Sequence of digit symbols are used to represent numbers greater than 9. When a number is written as a sequence of decimal digits, its value can be interpreted using the positional value of each digit in the number. The positional number system is a system of writing numbers where the value of a digit depends not only on the digit, but also on its placement within a number. In the positional number system, each decimal digit is weighted relative to its position in the number. This means that each digit in the number is multiplied by ten raised to a power 27corresponding to that digit's position. Thus the value of the decimal sequence 948 is: 948 10 = 9 X 10 2 + 4 X 10 1 + 8 X 10 0 Fractional values are represented in the same manner, but the exponents are negative for digits on the right side of the decimal point. Thus the value of the fractional decimal sequence 948.23 is: 948.23 10 = 9 X 10 2 + 4 X 10 1 + 8 X 10 0 + 2 X 10 -1 + 3 X 10 -2 In general, for the decimal representation of X = { ....x 2 x 1 x 0 . x -1 x -2 x -3 .... }, the value of X is X = S i x i 10 i 2.4 where i = ....2, 1, 0, -1, -2, .... Binary Number System Ten different digits 0 -!/ 9 are used to represent numbers in the decimal system. There are only two digits in the binary system, namely, 0 and 1. The numbers in the binary system are represented to the base two and the positional multipliers are the powers of two. The leftmost bit in the binary number is called as the most significant bit (MSB) and it has the largest positional weight. The rightmost bit is the least significant bit (LSB) and has the smallest positional weight. The binary sequence 10111 2 has the decimal equivalent: 10111 2 = 1 X 2 4 + 0 X 2 3 + 1 X 2 2 + 1 X 2 1 + 1 X 2 0 = 16 + 0 + 4 + 2 + 1 = 23 10 28The decimal equivalent of the fractional binary sequence can be estimated in the same manner. The exponents are negative powers of two for digits on the right side of the binary point. The binary equivalent of the decimal point is the binary point. Thus the decimal value of the fractional binary sequence 0.1011 2 is: 0.1011 2 = 1 X 2 -1 + 0 X 2 -2 + 1 X 2 -3 + 1 X 2 -4 = 0.5 + 0 + 0.125 + 0.0625 = 0.6875 10 2.5 Hexadecimal Number System Hexadecimal representation of numbers is more efficient in digital applications because it occupies less memory space for storing large numbers. A hexadecimal number is represented using base 16. Hexadecimal or Hex numbers are used as a shorthand form of binary sequence. This system is used to represent data in a more compact manner. In the hexadecimal number system, the binary digits are grouped into sets of 4 and each possible combination of 4 binary digits is given a symbol as follows: 0000 = 0 0001 = 1 0010 = 2 0011 = 3 0100 = 4 0101 = 5 0110 = 6 0111 = 7 1000 = 8 1001 = 9 1010 = A 1011 = B 1100 = C 1101 = D 1110 = E 1111 = F Since 16 symbols are used, 0 to F, the notation is called hexadecimal. The first ten symbols are the same as in the decimal system, 0 to 9 and the remaining six symbols are taken from the first six letters of the alphabet sequence, A to F. The hexadecimal sequence 2C 16 has the decimal equivalent: 2C 16 = 2 X 16 1 + C X 16 0 = 32 + 12 = 44 10 29The hexadecimal representation is more compact than binary representation. It is very easy to convert between binary and hexadecimal systems. Each hexadecimal digit will correspond to four binary digits because 2 4 = 16. The hexadecimal equivalent of the binary sequence 110010011101 2 is: 1100 1001 1101 = C9D 16 C 9 D 2.6 Decimal to Binary Conversion To convert a binary number to a decimal number, it is required to multiply each binary digit by the appropriate power of 2 and add the results. There are two approaches for converting a decimal number into binary format. 2.6.1 Repeated Division by 2 Any decimal number divided by 2 will leave a remainder of 0 or 1. Repeated division by 2 will leave a string of 0s and 1s that become the binary equivalent of the decimal number. Suppose it is required to convert the decimal number M into binary form, dividing M by 2 in the decimal system, we will obtain a quotient M 1 and a remainder r 1, where r 1 can have a value of either 0 or 1. ie., M = 2 * M 1 + r 1 r 1 = 0 or 1 Next divide the quotient M 1 by 2. The new quotient will be M 2 and the new remainder r 2 . ie., so that M 1 = 2 * M 2 + r 2 r 2 = 0 or 1 M = 2 (2 * M 2 + r 2 ) + r 1 = 2 2 M 2 + r 2 * 2 1 + r 1 * 2 0 Next divide the quotient M 2 by 2. The new quotient will be M 3 and the new remainder r 3 . 30i.e., M 2 = 2 * M 3 + r 3 so that M = 2 (2 * (2 * M 3 + r 3 ) + r 2 ) + r 1 = 2 2 (2 * M 3 + r 3 ) + r 2 * 2 1 + r 1 * 2 0 = 2 3 M 3 + r 3 * 2 2 + r 2 * 2 1 + r 1 * 2 0 The above process is repeated until the quotient becomes 0, then M = 1 * 2 k + r k * 2 k-1 + .... + r 3 * 2 2 + r 2 * 2 1 + r 1 * 2 0 Example: Convert 23 10 into its equivalent binary number. 23/2 11/2 5/2 2/2 1/2 Quotient 11 5 2 1 0 Remainder 1 (LSB) 1 1 0 1 (MSB) To write the binary equivalent of the decimal number, read the remainders from the bottom upward as: 23 10 = 10111 2 The number of bits in the binary number is the exponent of the smallest power of 2 that is larger than the decimal number. Consider a decimal number 23. Find the exponent of the smallest power of 2 that is larger than 23. 16 < 23 < 32 2 4 < 23 < 2 5 Hence, the number 23 has 5 bits as 10111. Consider another example. 31Find the number of bits in the binary representation of the decimal number 36 without actually converting into its binary equivalent. The next immediate large number than 36 that can be represented in powers of 2 is 64. 32 < 36 < 64 2 5 < 36 < 2 6 Hence, the number 36 should have 6 bits in its binary representation. 2.6.2 Sum of Powers of 2 A decimal number can be converted into a binary number by adding up the powers of 2 and then adding bits as needed to obtain the total value of the number. For example, to convert 36 10 to binary: a. Find the largest power of 2 that is smaller than or equal to 36 36 10 > 32 10 b. Set the 32's bit to 1 and subtract 32 from the original number. 36 !#$ 32 = 4 c. 16 is greater than the remaining total. Therefore, set the 16's bit to 0 d. 8 is greater than the remaining total. Hence, set the 8's bit to 0 e. As the remaining value is itself in powers of 2, set 4's bit to 1 and subtract 4 4 (&/ 4 = 0 Conversion is complete when there is nothing left to subtract. Any remaining bits should be set to 0. Hence 36 = 100100 2 32The conversion steps can be given as follows: 32 16 1 32 16 1 0 32 16 1 0 8 4 2 1 36 $,, 32 = 4 8 0 8 0 4 2 1 1 4 2 1 1 0 0 4 ,+* 4 = 0 36 10 = 100100 2 Example: Convert 91 10 to binary using the sum of powers of 2 method. The largest power of 2 that is smaller than or equal to 91 is 64. 64 32 16 8 4 2 1 1 91-64 = 27 64 32 16 8 4 2 1 1 0 1 91-(64+16) = 11 (Since 32 > 27, set the 32's bit 0 and 16 < 27. set the 16's bit 1) 64 32 16 8 4 2 1 1 0 1 1 91-(64+16+8) = 3 64 32 16 8 4 2 1 1 0 1 1 0 1 91-(64+16+8+2) = 1 64 32 16 8 4 2 1 1 Hence 0 1 1 0 1 1 91-(64+16+8+2+1) = 0 91 10 = 1011011 2 332.7 Conversion of fractional decimal to binary The decimal fractions like 1/2, 1/4, 1/8 etc., can be converted into exact binary fractions. Sum of powers method can be applied to these fractions. 0.5 10 = 1 * 2 -1 = 0.1 2 0.25 10 = 0 * 2 -1 + 1 * 2 -2 = 0.01 2 0.125 10 = 0 * 2 -1 + 0 * 2 -2 + 1 * 2 -3 = 0.001 2 The fraction 5/8 = 4/8 + 1/8 = 1/2 + 1/8 has the binary equivalent: 5/8 = 1 * 2 -1 + 0 * 2 -2 + 1 * 2 -3 = 0.101 2 Exact conversion is not possible for the decimal fractions that cannot be represented in powers of 2. For example, 0.2 10 cannot be exactly represented by a sum of negative powers of 2. A method of repeated multiplication by 2 has to be used to convert such kind of decimal fractions. The steps involved in the method of repeated multiplication by 2: #. Multiply the decimal fraction by 2 and note the integer part. The integer part is either 0 or 1. '+ Discard the integer part of the previous product. Multiply the fractional part of the previous product by 2. Repeat the first step until the fraction repeats or terminates. The resulting integer part forms a string of 0s and 1s that become the binary equivalent of the decimal fraction. 34Example: 0.2 * 2 = 0.4 0.4 * 2 = 0.8 0.8 * 2 = 1.6 0.6 * 2 = 1.2 Integer part 0 0 1 1 0.2 * 2 = 0.4 0 (Fraction repeats, the product is the same as in the first step) Read the integer parts from top to bottom to obtain the equivalent fractional binary number. Hence 0.2 10 = 0.00110011... 2 2.8 Conversion of Decimal to Hexadecimal Decimal numbers#,' conversion to hexadecimal is similar to binary conversion. Decimal numbers can be converted into hexadecimal format by the sum of weighted hex digits method and by repeated division by 16. The sum of weighted hex digits method is suitable for small decimal numbers of maximum 3 digits. The method of repeated division by 16 is preferable for the conversion of larger numbers. The exponent of the smallest power of 16 that is greater than the given decimal number will indicate the number of hexadecimal digits that will be present in the converted hexadecimal number. For example, the decimal number 948, when converted into hexadecimal number has 3 hexadecimal digits. (16 3 = 4096) > 948 > (16 2 = 256) Hence, the hexadecimal representation of 948 has 3 hex digits. The conversion process is as follows: 16 2 3 16 1 16 0 948 "," (3 * 256) = 180 35Hence, 16 2 3 16 1 16 0 B 948 -/* (3 * 256 + 11 * 16) = 4 16 2 3 16 1 16 0 B 4 948 ,'# (3 * 256 + 11 * 16 + 4) = 0 948 10 = 3B4 16 The steps involved in the repeated division by 16 to obtain the hexadecimal equivalent are as follows: )' Divide the decimal number by 16 and note the remainder. Express the remainder as a hex digit. )* Repeat the process until the quotient is zero Example: Process quotient 2.9 remainder 948 / 16 = 59 4 (LSB) 59 / 16 = 3 3 / 16 = 0 948 10 = 3B4 16 11 (B) 3 (MSB) Octal Representation An octal number is represented using base 8. Octal representation is just a simple extension of binary and decimal representations but using only the digits 0 to7. To convert an octal number to a decimal number, it is required to multiply each octal digit by the appropriate power of 8 and add the results. 36Example What is the decimal value of the octal number 711 8 ? 7 * 8 2 + 1 * 8 1 + 1 * 8 0 = 457 10 The steps involved in the repeated division by 8 to obtain the octal equivalent are as follows: !$ Divide the decimal number by 8 and note the remainder. Express the remainder as an octal digit. )' Repeat the process until the quotient is zero What is the octal representation of the decimal number 64 10 ? 64/8 8/8 1/8 Quotient 8 1 0 Remainder 0 (LSB) 0 1 (MSB) Hence 64 10 = 100 8 2.10 Representation of signed numbers If computers represent non-negative integers (unsigned) only, the binary representation is straightforward, as we had seen earlier. Computers have also to handle negative integers (signed). The normal convention that is followed to distinguish between a signed and unsigned number is to treat the most significant (leftmost) bit in the binary sequence as a sign bit. If the leftmost bit is 0, the number is positive, and if the leftmost bit is 1, the number is negative. 372.10.1 Sign+magnitude representation The simplest form of representing a negative integer is the sign+magnitude representation. In a sequence of n bits, the leftmost bit is used for sign and the remaining n-1 bits are used to hold the magnitude of the integer. Thus in a sequence of 4 bits, 0100 = +4 1100 = -4 As there are several drawbacks in this representation, this method has not been adopted to represent signed integers. There are two representations for 0 in this approach. 0000 = +0 10 1000 = -0 10 Hence it is difficult to test for 0, which is an operation, performed frequently in computers. Another drawback is that, the addition and subtraction require a consideration of both the sign of the numbers and their relative magnitude, in order to carry out the required operation. This would actually complicate the hardware design of the arithmetic unit of the computer. The most efficient way of representing a signed integer is a 2's-complement representation. In 2's complement method, there is only one representation of 0. 2.10.2. 2's-complement representation This method does not change the sign of the number by simply changing a single bit (MSB) in its representation. The 2's-complement method used with -ve numbers only is as follows: a. b. Invert all the bits in the binary sequence (ie., change every 0 to1 and every 1 to 0 ie.,1's complement) Add 1 to the result This method works well only when the number of bits used by the system is known in the representation of the number. Care should be 38taken to pad (fill with zeros) the original value out to the full representation width before applying this algorithm. Example: In a computer that uses 8-bit representation to store a number, the wrong and right approaches to represent $,#23 are as follows: Wrong approach: The binary equivalent of 23 is 10111. Invert all the bits => 01000 Add 1 to the result => 01001 Pad with zeros to make 8-bit pattern => 00001001 => +9 Right approach: The binary equivalent of 23 is 10111 Pad with zeros to make 8-bit pattern => 00010111 Invert all the bits => 11101000 Add 1 to the result => 11101001 => -23 2.10.3 Manual method to represent signed integers in 2's complement form This is an easier approach to represent signed integers. This is for -ve numbers only. Step 1: Copy the bits from right to left, through and including the first 1. Step 2: Copy the inverse of the remaining bits. Example 1: To represent '!-4 in a 4-bit representation: The binary equivalent of the integer 4 is 0100 39As per step1, copy the bits from right to left, through and including the first 1 => 100 As per step2, copy the inverse of the remaining bits => 1 100 => -4 Example 2: To represent (*#23 in a 8-bit representation: The binary equivalent of 23 is 00010111 As per step 1: 1 As per step 2: 11101001 => -23 2.10.4 Interpretation of unsigned and signed integers Signed number versus unsigned number is a matter of interpretation. A single binary sequence can represent two different values. For example, consider a binary sequence 11100110 2 . The decimal equivalent of the above sequence when considered as an unsigned integer is: 11100110 2 = 230 10 The decimal equivalent of the sequence when considered as a signed integer in 2's complement form is: 11100110 2 = -26 10 (after 2's complement and add negative sign). 40When comparing two binary numbers for finding which number is greater, the comparison depends on whether the numbers are considered as signed or unsigned numbers. Example: X = 1001 Y = 0011 Is ( X > Y) /* Is this true or false? */ It depends on whether X and Y are considered as signed or unsigned. If X and Y are unsigned: X is greater than Y If X and Y are signed: X is less than Y. 2.10.5 Range of unsigned and signed integers In a 4-bit system, the range of unsigned integers is from 0 to 15, that is, 0000 to 1111 in binary form. Each bit can have one of two values 0 or 1. Therefore, the total number of patterns of 4 bits will be 2 X 2 X 2 X 2 = 16. In an n-bit system, the total number of patterns will be 2 n. . Hence, if n bits are used to represent an unsigned integer value, the range is from 0 to 2 n -1, that is, there are 2 n different values. In case of a signed integer, the most significant (left most) bit is used to represent a sign. Hence, half of the 2 n patterns are used for positive values and the other half for negative values. The range of positive values is from 0 to 2 n-1 -1 and the range of negative values is from '%&1 to #$'2 n-1 . In a 4-bit system, the range of signed integers is from &,+8 to +7. 412.11 Binary Arithmetic Digital arithmetic usually means binary arithmetic. Binary arithmetic can be performed using both signed and unsigned binary numbers. 2.11.1 Binary Addition %&' Unsigned numbers When two digits are added, if the result is larger than what can be contained in one digit, a carry digit is generated. For example, if we add 5 and 9, the result will be 14. Since the result cannot fit into a single digit, a carry is generated into a second digit place. When two bits are added it will produce a sum bit and a carry bit. The carry bit may be zero. Example: 0 + 0 = 0 0 0 + 1 = 0 1 carry bit sum bit 1 + 1 = 1 0 carry bit sum bit The sum bit is the least significant bit (LSB) of the sum of two 1-bit binary numbers and the carry bit holds the value of carry (0 or 1) resulting from the addition of two binary numbers. 42Example 1: Calculate the sum of the numbers, 1100 and 1011: carry bit 1 1 0 0 1 0 1 1 #/!)(")$"/'%)%% 1 0 1 1 1 .,/!#+&,.$"#&-( sum bits Example 2: Calculate 10111 + 10110 Carry bits 1 1 1 1 0 1 1 1 1 0 1 1 0 -//%/.-!".'(#!$/&!.&* 1 0 1 1 0 1 )$'/./"%%!)+/-+."#'/) In unsigned binary addition, the two operands are called augend and addend. An augend is the number in an addition operation to which another number is added. An addend is the number in an addition operation that is added to another. 2.11.2 Binary addition /)! signed numbers Signed addition is done in the same way as unsigned addition. The only difference is that, both operands must have the same number of magnitude bits and each must have a sign bit. As we have already seen, in a signed number, the most significant bit (MSB) is a sign bit while the rest of the bits are magnitude bits. When the number is negative, the sign bit is 1 and when the number is positive, the sign bit is 0. 43Example 1: Add +2 10 and +5 10. Write the operands and the sum as 4-bit signed binary numbers. +2 0 0 1 0 +5 0 1 0 1 *(&*-$ ,''*-,)&!%&)!-- +7 0 1 1 1 ##.%)% -/..*-+++.'),!- magnitude bits sign bit If the result of the operation is positive, we get a positive number in ordinary binary notation. Example 2: (Use of 2's complement in signed binary addition) Add )"#7 10 + 5 10 using 4-bit system. In 2'complement form, -7 is represented as follows: In binary form, 7 is represented as: 0 1 1 1 Invert the bits (1 to 0 and 0 to 1) 1 0 0 0 Add 1 1 Hence, -7 in 2's complement form is 1 0 0 1 (-7) + 0 1 0 1 (5) )&/*&$'(&&$/*"&/'/ 1 1 1 0 (-2) %"+'!)',"+%*-&/$'- 44If the result of the operation is negative, we get a negative number in 2's complement form. In some cases, there is a carry bit beyond the end of the word size and this is ignored. Example 3: Add -4 10 + 4 10 . Use 4-bit system. 1 1 0 0 (-4 in 2's complement form) 0 1 0 0 (+4) (-,%*!+%)"+/!'!#-! 1 0 0 0 0 = 0 $$%,.!&#-,.$!-"%)+ In the above example, the carry bit goes beyond the end of the word and this can be ignored. In this case both operands are having different signs. There will be no error in the result. On any addition, the result may be larger than can be held in the word size being used and this would result in overflow. The overflow condition is based on the rule: If two numbers are added and if they are either positive or negative, then overflow occurs if and only if the result has the opposite sign. Example 4: Add (-7 10 ) + (-5 10 ) using the word size 4. 1 0 0 1 (-7 in 2's complement form) 1 0 1 1 %%'%&*$-!+(&$+!('' 1 0 1 0 0 /$!-/.!(#),,,(!*,- (-5 in 2's complement form) 45 (The result is wrong)In the above example both operands are negative. But the MSB of the result is 0 that is the result is positive (opposite sign) and hence overflow occurs and the result is wrong. 2.11.3 Binary Subtraction Subtrahend and minuend are the two operands in an unsigned binary subtraction. The minuend is the number in a subtraction operation from which another number is subtracted. The subtrahend is the number that is subtracted from another number. Simple binary subtraction operations are as follows: 0 $'* 0 1 ,") 0 1 "%$ 1 10 "$" 1 = = = = 0 1 0 1 When subtracting 1 from 0, borrow 1 from the next most significant bit (MSB). When borrowing from the next most significant bit, if it is 1, replace it with 0. If the next most significant bit is 0, you must borrow from a more significant bit that contains 1 and replace it with 0 and all 0s up to that point become 1s. Example 1: Subtract 1101 ($, 1010 borrow 0 1 1 1 0 1 - 1 0 1 0 !!,&($)"/,%.$.* 0 0 1 1 !#!"."+'.&/"//, (minuend) (subtrahend) 46When subtracting the 2 nd least significant bit (1 in the subtrahend) from 0 (in the minuend), a 1 is borrowed from the more significant bit (3 rd bit from right in the minuend) and hence 10 +/, 1 = 1. The 3 rd least significant bit is made as 0. Example 2: Subtract 1000 '%, 101 0 1 1 1 0 0 0 - 1 0 1 "%%(!)&$' 001 1 after borrowing, the minuend will become 0 1 1 10 1 0 1 (subtrahend) difference as per the basic operations for subtraction To subtract one number (subtrahend) from another (minuend), take the 2's complement of the subtrahend and add it to the minuend. Example 3: Subtract (+2) '+! (+7) using 4-bit system 0 0 1 0 (+2) 0 1 1 1 (+7) 1 0 0 1 ( -7 in 2's complement form) 0 0 1 0 (2) + 1 0 0 1 (-7) '-&!/,&,,'+#+'".%- 1 0 1 1 (-5) *.#.-#'$'&""&)&,!, 47Example 4: Subtract (-6) /&) (+4) using 4 bit system Minuend -6 2's complement of the Subtrahend -4 1 0 1 0 1 1 0 0 #$$%."--'+.,--)/+$ 1 0 1 1 0 -(&'..,"&!!+)+&$/, Both numbers are represented as negative numbers. While adding them, the result will be : 10110. As the word size is 4, the carry bit goes beyond the end of the word and the result is positive as the MSB is 0. This case leads to overflow and hence the result is wrong. The overflow rule works in subtraction also. 2.12 Boolean algebra Boolean algebra is a mathematical discipline that is used for designing digital circuits in a digital computer. It describes the relation between inputs and outputs of a digital circuit. The name Boolean algebra has been given in honor of an English mathematician George Boole who proposed the basic principles of this algebra. As with any algebra, Boolean algebra makes use of variables and operations (functions). A Boolean variable is a variable having only two possible values such as, true or false, or as, 1 or 0. The basic logical operations are AND, OR and NOT, which are symbolically represented by dot, plus sign, and by over bar / single apostrophe. Example: A AND B = A . B A OR = A + B B NOT A 48 = A--! (or A)A Boolean expression is a combination of Boolean variables, Boolean Constants and the above logical operators. All possible operations in Boolean algebra can be created from these basic logical operators. There are no negative or fractional numbers in Boolean algebra. The operation AND yields true (binary value 1) if and only if both of its operands are true. The operation OR yields true if either or both of its operands are true. The unary operation NOT inverts the value of its operand. The basic logical operations can be defined in a form known as Truth Table, which is a list of all possible input values and the output response for each input combination. 2.12.1 Boolean operators (functions) AND operator The AND operator is defined in Boolean algebra by the use of the dot (.) operator. It is similar to multiplication in ordinary algebra. The AND operator combines two or more input variables so that the output is true only if all the inputs are true. The truth table for a 2-input AND operator is shown as follows: A 0 0 1 1 B 0 1 0 1 Y 0 0 0 1 The above 2-input AND operation is expressed as: Y = A . B OR operator The plus sign is used to indicate the OR operator. The OR operator combines two or more input variables so that the output is true if at least one input is true. The truth table for a 2-input OR operator is shown as follows: 49A 0 0 1 1 B 0 1 0 1 Y 0 1 1 1 The above 2-input OR operation is expressed as: Y = A + B NOT operator The NOT operator has one input and one output. The input is either true or false, and the output is always the opposite, that is, the NOT operator inverts the input. The truth table for a NOT operator where A is the input variable and Y is the output is shown below: A 0 1 Y 1 0 The NOT operator is represented algebraically by the Boolean expression: Y = A Example: Consider the Boolean equation: D = A + ( B . C ) D is equal to 1 (true) if A is 1 or if ( B . C ) is 1, that is, B = 0 and C = 1. Otherwise D is equal to 0 (false). The basic logic functions AND, OR, and NOT can also be combined to make other logic operators. 50NAND operator The NAND is the combination of NOT and AND. The NAND is generated by inverting the output of an AND operator. The algebraic expression of the NAND function is: Y = A . B The NAND function truth table is shown below: A 0 0 1 1 B 0 1 0 1 Y 1 1 1 0 A NAND B = NOT (A AND B) NOR operator The NOR is the combination of NOT and OR. The NOR is generated by inverting the output of an OR operator. The algebraic expression of the NOR function is: Y = A + B The NOR function truth table is shown below: A 0 0 1 1 B 0 1 0 1 Y 1 0 0 0 A NOR B = NOT (A OR B) 512.12.2 Laws of Boolean algebra Boolean algebra helps to simplify Boolean expressions in order to minimize the number of logic gates in a digital circuit. You will study about logic gates in the forthcoming chapter. This chapter focuses on the theorems of Boolean algebra for manipulating the Boolean expressions in order to simplify them. Boolean Identities Laws of Complementation The term complement simply means to change 1s to 0s and 0s to 1s. Theorem 1 : If A = 0, then A = 1 Theorem 2 : If A = 1, then A = 0 Theorem 3 : The complement to complement of A is A itself. A = A Basic properties of AND operator Theorem 4 : A . 1 = A If A equals 0 and the other input is 1, the output is 0. If A equals 1 and the other input is 1, the output is 1. Thus the output is always equal to the A input. Theorem 5 : A . 0 = 0 As one input is always 0, irrespective of A, the output is always 0. 52Theorem 6 : A . A = A The output is always equal to the A input. Theorem 7 : A . A = 0 Regardless of the value of A, the output is 0. Basic properties of OR operator Theorem 8 : A + 1 = 1 If A equals 0 and the other input is 1, the output is 1. If A equals 1 and the other input is 1, the output is 1. Thus the output is always equal to 1 regardless of what value A takes on. Theorem 9 : A + 0 = A The output assumes the value of A. Theorem 10 : A + A = A The output is always equal to the A input. Theorem 11 : A + A = 1 Regardless of the value of A, the output is 1. 2.12.3 Simplification of Boolean expressions Before seeing the important theorems used in the simplification of Boolean expressions, some Boolean mathematical concepts need to be understood. 53Literal A literal is the appearance of a variable or its complement in a Boolean expression. Product Term A product term in a Boolean expression is a term where one or more literals are connected by AND operators. A single literal is also a product term. Example: AB, AC, A C, and E are the product terms. Minterm A minterm is a product term, which includes all possible variables either complemented or uncomplemented. In a Boolean expression of 3 variables, x, y, and z, the terms xyz, x yz, and x y z are minterms. But xy is not a minterm. Minterm is also called as a standard product term. Sum term A sum term in a Boolean expression is a term where one or more literals are connected by OR operators. Example: A + B + D Maxterm A maxterm is a sum term in a Boolean expression, which includes all possible variables in true or complement form. In a Boolean expression of 3 variables, x, y, and z, the terms x + y + z, and x + y + z are the maxterms. Maxterm is also called as standard sum term. 54Sum-of-products (SOP) A sum of products expression is a type of Boolean expression where one or more product terms are connected by OR operators. Example: A + A B + A B C In an expression of 3 variables, A, B, and C, the expression ABC + A B C + A B C is also called as a canonical sum or sum of standard product terms or sum of minterms. Product-of-sums (POS) Product of sums is a type of Boolean expression where several sum terms are connected by AND operators. Example: (A + B) (A + B) (A + B) A canonical product or product of standard sum terms is a product of sums expression where all the terms are maxterms. The above example is a canonical product in a Boolean expression of two variables A and B. Theorem 12: Commutative Law A mathematical operation is commutative if it can be applied to its operands in any order without affecting the result. Addition and multiplication operations are commutative. Example: A + B = B + A AB = BA 55Subtraction is not commutative: A - B +(. B - A There is no subtraction operation in Boolean algebra. Theorem 13: Associative Law A mathematical operation is associative if its operands can be grouped in any order without affecting the result. In other words, the order in which one does the OR operation does not affect the result. (A + B) + C = A + (B+C) = (A + C) + B Similarly, the order in which one does the AND operation does not affect the result. (AB)C = A(BC) = (AC)B Theorem 14: Distributive Law The distributive property allows us to distribute an AND across several OR functions. Example: A(B+C) = AB + AC The following distributive law is worth noting because it differs from what we would find in ordinary algebra. A + (B . C) = (A + B) . (A + C) The simplest way to prove the above theorem is to produce a truth table for both the right hand side (RHS) and the left hand side (LHS) expressions and show that they are equal. 56A 0 0 0 0 1 1 1 1 B 0 0 1 1 0 0 1 1 C 0 1 0 1 0 1 0 1 BC 0 0 0 1 0 0 0 1 LHS 0 0 0 1 1 1 1 1 A+B A+C 0 0 0 1 1 0 1 1 1 1 1 1 1 1 1 1 RHS 0 0 0 1 1 1 1 1 Minimum Sum of Products A minimum sum of products expression is one of those Sum of Products expressions for a Boolean expression that has the fewest number of terms. Consider the following Boolean Expression: A B C + A B C + A B C + A B C + A B C Using Associativity Law = (A B C + A B C) + (A B C + A B C) + A B C = A B(C+C) + A B(C+C)+ABC Using Theorem 11 = A B (1) + A B (1) + ABC Using Theorem 4 = A B + A B + ABC 57The above expression is in the minimum sum of products form. The given Boolean expression can be rewritten as follows using theorem 10. A B C + A B C + A B C + A B C + A B C + A B C (A B C + A B C = A B C) = (A B C + A B C) + (A B C + A B C) + (A B C + A B C) = A B (C + C) + A B (C + C) + A C(B + B) = A B + A B + A C The same Boolean expression can be simplified into many minimum sum of products form. Examples: Simplify the following Boolean Expression A B C + A B C Let x = A B and y = C The above Boolean expression becomes x y + x y = x(y + y) = x = A B Prove that A + A B = A + B According to Distributive Law A + A B = (A + A)(A + B) = 1 ', (A + B) = A + B 58Simplify the following Boolean Expression A B C + A B C + A B C + A B C = A C(B + B) + A B C + A B C = A C + A B C + A B C = A(C + BC) + A B C = A(C + B)(C + C) + A B C = A(C + B) + A B C = A C + A B + A B C (one minimal form) In the given Boolean Expression, if the second and third terms are grouped, it will give A B C + (A B C + A B C) + A B C = A B C + A B(C + C) + A B C = A B C + A B + A B C = B C(A + A) + A B = B C + A B (most minimal form) 2.12.4 DeMorgan's Theorems Theorem 15: Theorem 16: A + B = A B AB = A + B 59The above identities are the most powerful identities used in Boolean algebra. By constructing the truth tables, the above identities can be proved easily. Example: Given Boolean function f(A,B,C,D) = D A B + A B + D A C, Find the complement of the Boolean function f (A,B,C,D) = D A B + A B + D A C Apply DeMorgan's Law (theorem 15) = (D A B) (A B) (D A C) Apply DeMorgan's Law (theorem 16) = (D + A + B)(A + B)(D + A + C) In the above problem, the given Boolean function is in the sum of products form and its complement is in the product of sums form. The DeMorgan's theorem says that any logical binary expression remains unchanged if we, #/ +& $) -) change all varibales to their complements change all AND operations to OR operations change all OR operations to AND operations take the complement of the entire expression A practical operational way to look at DeMorgan's theorem is that the inversion of an expression may be broken at anypoint and the operation at that point replaced by its oppostie ( i.e., AND replaced by OR or vice versa). 60The fundamentals of numbering systems, including examples showing how numbering systems work, converting values between one numbering system and another, and performing simple types of binary arithmetic have been covered in this chapter. Boolean algebra has been introduced and Boolean identities and the laws of Boolean algebra are explained with examples. The identities and the theorems are used in the simplification of Boolean expressions. The pictorial representation of the Boolean operators, that is, logic gates and the design of logic circuits are discussed in Chapter 4. EXERCISES I. Fill in the blanks 1. The term bit stands for '#.*$$*,&*'#&-. #*.-)-$%'*(,-(" 2. The radix of an octal system is -.$+(/&*#+$).!- and for the hexadecimal system is ,#/#'/'"%-,$"!+ 3. The range of unsigned integers in an n-bit system is from #&!&!/,') .)%''%to ,+/+%-*+/&/#)!*. 4. The synonyms LSB and MSB stand for +#/+!(+,.++./)&, ,%(#',/).&.- and (/.**(!-/(/,-*. 5. In binary addition, the operands are called as ,"')-$('"$&*),) and &+(+%)+&-*%,-#.. 6. In binary subtraction, the operands are called as +&/*.$--*.(')!# and )&.()*($'&%,)#&. 7. The binary representation of the decimal number 5864 is '-+-'!(+- and the hexadecimal representation of the same number will be "*,)*,#*)!""#!,. 8. The 2's complement of 0 is )&",/$"-!$+/+#*. 619. The arithmetic operations in a digital computer are performed using the radix /,++%,'.&!'.))/, &",.&+"'))#+/%- 10. One byte equals *%),'%)&$.-*%&* number of bits. 11. One million bytes are referred to as MB and one billion bytes are referred to as $#+/!(#)'##)-)- 12. The exponent of the smallest power of 2 that is larger than 68 is *#) +/(//-*%*$)'and hence the number 68 has -,*!$"/-+&/%.,- binary digits in its binary equivalent. II. Review questions 1. Convert the following decimal numbers into their equivalent binary, octal and hexadecimal numbers. a. 512 b. 1729 c. 1001 d. 777 e. 160 2. Write $-(27 10 as an 8-bit 2's complement number. 3. Add the signed numbers +15 10 and +36 10. Write the operands and the sum as 8-bit binary numbers. 4. Write the largest positive and negative numbers for an 8-bit signed number in decimal and 2's complement notation. 5. Do the following signed binary arithmetic operations. a. 10 10 + 15 10 b. $('12 10 + 5 10 c. 14 10 - 12 10 d. ( +(-2 10 ) - (-6 10 ) 6. Convert the following binary numbers to decimal numbers a. 1011 2 b. 101110 2 62 c. 1010011 27. Convert the following binary numbers into hexadecimal numbers a. 101 2 8. c. b. 1A8 16 c. b. 5E9 16 c. CAFE 16 b. 101110 - 1011 Convert the following decimal numbers to binary using sum of powers of 2 method a. 41 10 b. 77 10 12. 39EB 16 Do the following binary arithmetic. a. 11011001 + 1011101 11. 111101000010 2 Convert the following hexadecimal numbers to decimal numbers a. B6 16 10. 11010 2 Convert the following hexadecimal numbers to binary numbers a. F2 16 9. b. c. 95 10 Using the theorems stated in Boolean algebra, prove the following a. A + AB = A b. (A + B)(A + C) = A + BC 13. Simplify the following Boolean expressions a. A B C + A B C + A B C b. A B C + A B C + A B C + A B C 14. Using DeMorgan's theorems, simplify the following Boolean expressions a. A C + B+C b. ((AC) + B) + C 15. Draw the truth table of the Boolean Expression (A + B + C) 63CHAPTER 3 COMPUTER ORGANIZATION 3.1 Basic Components of a Digital Computer 3.1.1 Introduction Computers are often compared to human beings since both have the ability to accept data, store, work with it, retrieve and provide information. The main difference is that human beings have the ability to perform all of these actions independently. Human beings also think and control their own activities. The computer, however, requires a program (a predefined set of instructions) to perform an assigned task. Human beings receive information in different forms, such as eyes, ears, nose, mouth, and even sensory nerves. The brain receives or accepts this information, works with it in some manner, and then stores in the brain for future use. If information at the time requires immediate attention, brain directs to respond with actions. Likewise the Central Processing Unit (CPU) is called the brain of the computer. It reads and executes program instructions, performs calculations and makes decisions. 3.1.2 Components of a Digital Computer A computer system is the integration of physical entities called hardware and non-physical entities called software. The hardware components include input devices, processor, storage devices and output devices. The software items are programs and operating aids (systems) so that the computer can process data. 3.1.3 Functional Units of a Computer System Computer system is a tool for solving problems. The hardware should be designed to operate as fast as possible. The software (system software) should be designed to minimize the amount of idle 64computer time and yet provide flexibility by means of controlling the operations. Basically any computer is supposed to carry out the following functions. - - - - Accept the data and program as input Store the data and program and retrieve as and when required. Process the data as per instructions given by the program and convert it into useful information Communicate the information as output Based on the functionalities of the computer, the hardware components can be classified into four main units, namely - - - - Input Unit Output Unit Central Processing Unit Memory Unit These units are interconnected by minute electrical wires to permit communication between them. This allows the computer to function as a system. The block diagram is shown below. Fig. 3.1 : Functional Units of a Computer System 65Input Unit A computer uses input devices to accept the data and program. Input devices allow communication between the user and the computer. In modern computers keyboard, mouse, light pen, touch screen etc, are some of the input devices. Output Unit Similar to input devices, output devices have an interface between the computer and the user. These devices take machine coded output results from the processor and convert them into a form that can be used by human beings. In modern computers, monitors (display screens) and printers are the commonly used output devices Central Processing Unit Fig. 3.2. Central Processing Unit CPU is the brain of any computer system. It is just like the human brain that takes all major decisions, makes all sorts of calculations and directs different parts of the computer function by activating and controlling the operation. It consists of arithmetic and logic units, control unit and internal memory (registers). The control unit of the CPU co- ordinates the action of the entire system. Programs (software) provide the CPU, a set of instruction to follow and perform a specific task. Between any two components of the computer system, there is a pathway called a bus which allows for the data transfer between them. 66Control unit controls all the hardware operations, ie, those of input units, output units, memory unit and the processor. The arithmetic and logic units in computers are capable of performing addition, subtraction, division and multiplication as well as some logical operations. The instructions and data are stored in the main memory so that the processor can directly fetch and execute them. Memory Unit In the main memory, the computer stores the program and data that are currently being used. In other words since the computers use the stored program concept, it is necessary to store the program and data in the main memory before processing. The main memory holds data and program only temporarily. Hence there is a need for storage devices to provide backup storage. They are called secondary storage devices or auxiliary memory devices. Secondary storage devices can hold more storage than main memory and is much less expensive. 3.1.4 Stored Program Concept All modern computers use the stored program concept. This concept is known as the Von ()" Neumann concept due to the research paper published by the famous mathematician John Von Neuman. The essentials of the stored program concept are - - - - the program and data are stored in a primary memory (main memory) once a program is in memory, the computer can execute it automatically without manual intervention. the control unit fetches and executes the instructions in sequence one by one. an instruction can modify the contents of any location inThe stored program concept is the basic operating principle for every computer. 673.2 Central Processing Unit 3.2.1 Functions of a Central Processing Unit The CPU is the brain of the computer system. It performs arithmetic operations as well as controls the input, output and storage units. The functions of the CPU are mainly classified into two categories : - - Co )-+ ordinate all computer operations Perform arithmetic and logical operations on data The CPU has three major components. - - - Arithmetic and Logic Unit Control Unit Registers (internal memory) The arithmetic and logic unit (ALU) is the part of CPU where actual computations take place. It consists of circuits which perform arithmetic operations over data received from memory and are capable of comparing two numbers. The control unit directs and controls the activities of the computer system. It interprets the instructions fetched from the main memory of the computer, sends the control signals to the devices involved in the execution of the instructions. While performing these operations the ALU takes data from the temporary storage area inside the CPU named registers. They are high-speed memories which hold data for immediate processing and results of the processing. 68Fig. 3.3 : Functions of a CPU 3.2.2 Working with Central Processing Unit The CPU is similar to a calculator, but much more powerful. The main function of the CPU is to perform arithmetic and logical operations on data taken from main memory. The CPU is controlled by a list of software instructions. Software instructions are initially stored in secondary memory storage device such as a hard disk, floppy disk, CD-ROM, or magnetic tape. These instructions are then loaded onto the computer's main memory. When a program is executed, instructions flow from the main memory to the CPU through the bus. The instructions are then decoded by a processing unit called the instruction decoder that interprets and implements the instructions. The ALU performs specific operations such as addition, multiplication, and conditional tests on the data in its registers, sending the resulting data back to the main memory or storing it in another register for further use. 69To understand the working principles of CPU, let us go through the various tasks involved in executing a simple program. This program performs arithmetic addition on two numbers. The algorithm of this program is given by (i) (ii) (iii) (iv) input the value of a input the value of b sum = a + b output the value of sum This program accepts two values from the keyboard, sums it and displays the sum on the monitor. The steps are summarized as follows : 1. The control unit recognizes that the program (set of instructions) has been loaded into the main memory. Then it begins to execute the program instructions one by one in a sequential manner. 2. The control unit signals the input device (say keyboard) to accept the input for the variable !".a*)). 3. The user enters the value of #)&a-.% on the keyboard. 4. The control unit recognizes and enables to route the data (value of a) to the pre-defined memory location (address of "%(a'/-). 5. The steps 2 to 4 will be repeated for the second input .*%b%&$. The value of "%-b*&" is stored in the memory location (address of %&*b$'$). 6. The next instruction is an arithmetic instruction. Before executing the arithmetic instruction, the control unit enables to send a copy of the values stored in address of $)$a*.) and address of &/%b.&+ to the internal registers of the ALU and signals the ALU to perform the sum operation. 7. The ALU performs the addition. After the computation, the control unit enables to send the copy of the result back to the memory (address of +$&sum'/#). 708. Finally, the result is displayed on the monitor. The control unit enables to send the copy of the values of the address of #(.sum/#$ to the monitor (buffer) and signals it. The monitor displays the result. 9. Now this program execution is complete. The data flow and the control flow of CPU during the execution of this program is given as, Fig. 3.4 : Working Principles of a CPU 713.3 Arithmetic and Logic Unit - ALU The ALU is the computer's calculator. It executes arithmetic and logical operations. The arithmetic operations include addition, subtraction, multiplication and division. The logical operation compares numbers, letters and special characters. The ALU also performs logic functions such as AND, OR and NOT. The ALU functions are directly controlled by the control unit. The control unit determines when the services of the ALU are needed, and it provides the data to be operated. The control unit also determines what is to be done with the results. 3.3.1 Arithmetic Operations Arithmetic operations include addition, subtraction, multiplication, and division. While performing these operations, the ALU makes use of the registers. Data to be arithmetically manipulated are copied from main memory and placed in registers for processing. Upon completion of the arithmetic operation, the result can be transferred from the register to the main memory. In addition to registers, the arithmetic unit uses one or more adders that actually perform arithmatic operations on the binary digits. The arithmetic operation in adding two numbers can be demonstrated through following steps : Step 1 : The numbers (5 and 8) to be added up are put into two separate memory locations. Step 2 : The control unit fetches the two numbers from their memory locations into the data registers. Step 3 : The arithmetic unit looking at the operator (+) uses the accumulator and adds the two numbers. Step 4 : The ALU stores the result (13) in memory buffer register. Step 5 : Then the control unit stores the result into a user desired memory location, say !.!sum")'. 72Fig. 3.5 Arithmetic Logic Unit 3.3.2 Logical Operations The importance of the logic unit is to make logical operations. These operations include logically comparing two data items and take different actions based on the results of the comparison. 3.3.3 Functional Description Some of the basic functions performed by the ALU are - add, subtract, logical AND, logical OR, shift left and shift right on two's complement binary numbers. The inputs to be calculated are stored in the input register (AREG) and the input / output register (ACCUM) for add, AND and OR functions. The shift left and shift right functions operate on the value in the ACCUM 73Fig. 3.6 : Functional Description of ALU The above figure illustrates the functional level block diagram of the ALU. The control unit controls the operations of the ALU by giving appropriate control signals to select a specific function and then enable the operation after the data are fed into the registers. The enable bit is made 1 after the data to be operated are transferred from main memory. 3.4 Memory Unit Memory units are the storage areas in a computer. The term ,-(memory/'& usually refers to the main memory of the computer, whereas, the word !,,storage,"* is used for the memory that exists on disks, CDs, floppies or tapes. The main memory is usually called a physical memory which refers to the ($%chip%#, (Integrated Circuit) capable of holding data and instruction. 74Fig. 3.7 Memory Unit There are different types of memory. They are Random Access Memory (RAM), Read Only Memory (ROM), Programmable Read- Only Memory (PROM), Erasable Programmable Read-Only Memory (EPROM), Electrically Erasable Programmable Read-Only Memory (EEPROM). Random Access Memory - RAM RAM is the most common type of memory found in the modern computers. This is really the main store and is the place where the program gets stored. When the CPU runs a program, it fetches the program instructions from the RAM and carries them out. If the CPU needs to store the results of the calculations it can store them in RAM. When we switch off a computer, whatever is stored in the RAM gets erased. It is a volatile form of memory. Read Only Memory - ROM In ROM, the information is burnt (pre-recorded) into the ROM chip at manufacturing time. Once data has been written into a ROM chip, it cannot be erased but you can read it. When we switch off the computer, the contents of the ROM are not erased but remain stored permanently. ROM is a non-volatile memory. ROM stores critical programs such as the program that boots the computer. 75Programmable Read Only Memory - PROM PROM is a memory on which data can be written only once. A variation of the PROM chip is that it is not burnt at the manufacturing time but can be programmed using PROM programmer or a PROM burner. PROM is also a non-volatile memory. Erasable Programmable Read Only Memory - EPROM In EPROM, the information can be erased and reprogrammed using a special PROM '%$ programmer. EPROM is non-volatile memory. A EPROM differs from a PROM in that a PROM can be written to only once and cannot be erased. But an ultraviolet light is used to erase the contents of the EPROM. Electrically Erasable Programmable Read Only Memory - EEPROM EEPROM is a recently developed type of memory. This is equivalent to EPROM, but does not require ultraviolet light to erase its content. It can be erased by exposing it to an electrical charge. It is also non-volatile in nature. EEPROM is not as fast as RAM or other types of ROM. A flash memory is a special type of EEPROM that can be erased and reprogrammed. The main memory must store many data items and have some way of retriving them when they are needed. The memory can be compared to the boxes at a post office. Each box-holder has a box with a unique number which is called its address. This address serves to identify the box. The memory has a number of locations in its store. Each location in a memory has a unique number called its memory address. This serves to identify it for storage and retrival. Operations on memories are called reads and writes, defined from the perspective of a processor or other device that uses a memory: a write instruction transfers information from other device to 76memory and a read instruction transfers information from the memory to other devices. A memory that performs both reads and writes is often called a RAM, random access memory. Other types of memories commonly used in systems are read-only memory. Data Representation The smallest unit of information is a single digit called a ,$-bit!+, (binary digit), which can be either 0 or 1. The capacity of a memory system is represented by a unit called a byte, which is 8 bits of information. Memory sizes in modern systems range from 4MB (megabytes) in small personal computers up to several billion bytes (gigabytes, or GB) in large high-performance systems. The performance of a memory system is defined by two different measures, the access time and the memory cycle time. Access time, also known as response time or latency, refers to how quickly the memory can respond to a read or write request. Memory cycle time refers to the minimum period between two successive requests. The following terminology is used while discussing hierarchical memories: 3!$,4 The registers (internal memory) are used to hold the instruction and data for the execution of the processor. Eventually the top of the hierarchy goes to the registers. 3/!)4 The memory closest to the processor is known as a cache. It is a high speed memory that is much faster than the main memory. 3.'/4 The next is the main memory which is also known as the primary memory. 3+.$4 The low end of the hierarchy is the secondary memory. 77The secondary memory is the memory that supplements the main memory. This is a long term non-volatile memory. It is external to the system nucleus and it can store a large amount of programs and data. The CPU does not fetch instructions of a program directly from the secondary memory. The program should be brought into the main memory from the secondary memory before being executed. The secondary memory is cheaper compared to the main memory and hence a computer generally has limited amount of main memory and large amount of secondary memory. 3.5 Input and Output Devices The main function of a computer system is to process data. The data to be processed by the computer must be input to the system and the result must be output back to the external world. 3.5.1 Input Devices An input device is used to feed data into a computer. For example, a keyboard is an input device. It is also defined as a device that provides communication between the user and the computer. Input devices are capable of converting data into a form which can be recognized by computer. A computer can have several input devices. Keyboard The most common input device is the keyboard. Keyboard consists of a set of typewriter like keys that enable you to enter data into a computer. They have alphabetic keys to enter letters, numeric keys to enter numbers, punctuation keys to enter comma, period, semicolon, etc., and special keys to perform some specific functions. The keyboard detects the key pressed and generates the corresponding ASCII codes which can be recognized by the computer. 78Fig. 3.8 Keyboard Mouse Mouse is an input device that controls the movement of the cursor on the display screen. Mouse is a small device, you can roll along a flat surface. In a mouse , a small ball is kept inside and touches the pad through a hole at the bottom of the mouse. When the mouse is moved, the ball rolls. This movement of the ball is converted into signals and sent to the computer. You will need to click the button at the top of the mouse to select an option. Mouse pad is a pad over which you can move a mouse. Mouse is very popular in modern computers. Fig. 3.9 Mouse 79Scanner Scanner is an input device that allows information such as an image or text to be input into a computer. It can read image or text printed on a paper and translate the information into a form that the computer can use. That is, it is used to convert images (photos) and text into a stream of data. They are useful for publishing and multi-media applications. Fig. 3.10 Scanner Bar Code Reader The barcode readers are used in places like supermarket, bookshops, etc. A bar code is a pattern printed in lines of different thickness. The bar-code reader scans the information on the bar- codes and transmits to the computer for further processing. The system gives fast and error-free entry of information into the computer. Fig.3.11 Bar Code and Reader 80Digital Camera The digital camera is an input device mainly used to capture images. The digital camera takes a still photograph, stores it and sends it as digital input to the computer. It is a modern and popular input device. Fig. 3.12 Digital Camera Touch Sensitive Screen Touch Sensitive Screen is a type of display screen that has a touch-sensitive panel. It is a pointing device that enables the user to interact with the computer by touching the screen. You can use your fingers to directly touch the objects on the screen. The touch screen senses the touch on the object (area pre-defined) and communicate the object selection to the computer. Fig. 3.13 Touch Sensitive Screen 81Magnetic Ink Character Recognition (MICR) Fig. 3.14 MICR Cheque MICR is widely used by banks to process cheques. Human readable numbers are printed on documents such as cheque using a special magnetic ink. The cheque can be read using a special input unit, which can recognize magnetic ink characters. This method eliminates the manual errors. It also saves time, ensures security and accuracy of data. Optical Character Recognition (OCR) Fig. 3.15 OCR Sheet The OCR technique permits the direct reading of any printed character like MICR but no special ink is required. With OCR, a user can scan a page from a book. The computer will recognize the characters in the page as letters and punctuation marks, and stores. This can be edited using a word processor. 82Optical Mark Reading and Recognition (OMR) Fig. 3.16 OMR Reader In this method special pre-printed forms are designed with boxes which can be marked with a dark pencil or ink. Such documents are read by a reader, which transcribes the marks into electrical pulses which are transmitted to the computer. They are widely used in applications like objective type answer papers evaluation in which large number of candidates appear, time sheets of factory employees etc. Light Pen A light pen is a pointing device shaped like a pen and is connected to a monitor. The tip of the light pen contains a light- sensitive element which, when placed against the screen, detects Fig. 3.17 Light Pen 83the light from the screen enabling the computer to identify the location of the pen on the screen. Light pens have the advantage of '+,drawing(*( directly onto the screen, but this can become uncomfortable, and they are not accurate. Magnetic Reader Magnetic reader is an input device which reads a magnetic strip on a card. It is handy and data can be stored and retrieved. It also provides quick identification of the card's owner. All the credit cards, ATM cards (banks), petro cards, etc. stores data in a magnetic strip which can be read easily by the magnetic reader. Fig. 3.18 Magnetic Reader Smart Cards This input device stores data in a microprocessor embedded in the card. This allows information, which can be updated, to be stored on the card. These data can be read and given as input to the computer for further processing. Most of the identification cards use this method to store and retrieve the vital information. Fig. 3.19 Smart Card Reader 84Notes Taker Notes taker is a device that captures natural handwriting on any surface onto a computer. Using an electronic pen, the notes taker displays the user's handwritten notes, memos or drawings on the computer, and stores the image for future use. Fig. 3.20 Notes Taker Microphone Microphone serves as a voice input device. It captures the voice data and input to the computer. Using the microphone along with speech recognition software can offer a completely new approach to input information into your computer. Speech recognition programs, although not yet completely exact, have made great strides in accuracy as well as ease of use. The voice-in or speech recognition approach can almost fully replace the keyboard and mouse. Speech recognition can now open the computer world to those who may have been restricted due to a physical handicap. It can also be a boon for those who have never learned to type. Fig. 3.21 Microphone 853.5.2 Output Devices Output is anything that comes out of a computer. An output device is capable of presenting information from a computer. There are many output devices attached with the computers. But the monitors and printers are commonly used output devices. Monitors Monitor is a commonly used output device, sometimes called as display screen. It provides a visual display of data. Monitors are connected with the computer and are similar in appearance to a television set. Fig. 3.22 Monitor Initially there were only monochrome monitors. But gradually, we have monitors that display colour. Monitors display images and text. The smallest dot that can be displayed is called a pixel (picture element) The resolution of the screen improves as the number of pixels is increased. Most of the monitors have a 4 : 3 width to height ratio. This is called -.*aspect ratio!,.. The number of pixels that can be displayed vertically and horizontally gives the resolution of the monitor. The resolution of the monitor determines the quality of the display. Some popular resolutions are 640 x 480 pixels, 800 x 600 pixels and 1024 x 768 pixels. A resolution of 1024 x 768 pixels will produce sharper image than 640 x 480 pixels. 86Printers Printer is an output device that prints text or images on paper or other media (like transparencies). By printing you create what is known as a +()hard copy)/+. There are different kinds of printers, which vary in their speed and print quality. The two main types of printers are impact printers and non-impact printers. Printers Impact Line printer Non-impact Serial printer (Dot matrix printer) Thermal (fax) printer Laser printer Inkjet printer Fig. 3.23 Types of Printers Impact printers include all printers that print by striking an ink ribbon. Impact printers use a print head containing a number of metal pins which strike an inked ribbon placed between the print head and the paper. Line printers, dotmatrix printers are some of the impact printers. Characteristics of Impact Printers ,! In impact printers, there is physical contact with the paper to produce an image. .( Due to being robust and low cost, they are useful for bulk printing. 87'. Impact printers are ideal for printing multiple copies (that is, carbon copies) because they can easily print through many layers of paper. )% Due to its striking activity, impact printers are very noisy. -/ Since they are mechanical in nature, they tend to be slow. /+ Impact printers do not support transparencies. Non-impact printers are much quieter than impact printers as their printing heads do not strike the paper. Non-impact printers include laser printers, inkjet printers and thermal printers. Characteristics of Non-Impact Printers #- Non-impact printers are faster than impact printers because they have fewer moving parts. &' They are quiet than impact printers because there is no striking mechanism involved. -' They posses the ability to change typefaces automatically. ." These printers produce high-quality graphics %/ These printers usually support the transparencies "( These printers cannot print multipart forms because no impact is being made on the paper. Line Printer Line printers are high-speed printers capable of printing an entire line at a time. A line printer can print 150 lines to 3000 lines per minute. The limitations of line printer are they can print only one font, they cannot print graphics, the print quality is low and they are noisy to operate. But it can print large volume of text data very fast compared to the other printers. It is also used to print on multipart stationaries to prepare copies of a document. 88Fig. 3.24 Line Printer Dot Matrix Printer The most popular serial printer is the dot matrix printer. It prints one line of 8 or 14 points at a time, with print head moving across a line. They are similar to typewriters. They are normally slow. The printing speed is around 300 characters per second. It uses multipart stationaries to prepare copies of a document. Fig. 3.25 Dot Matrix Printer 89Thermal Printer Thermal printers are printers that produce images by pushing electrically heated pins against special heat-sensitive paper. They are inexpensive and used widely in fax machines and calculators. Fig. 3.26 Thermal Printer Thermal printer paper tends to darken over time due to exposure to sunlight and heat. So the printed matters on the paper fade after a week or two. It also produces a poor quality print. Laser Printers Laser printers use a laser beam and dry powdered ink to produce a fine dot matrix pattern. It can produce very good quality of graphic images. One of the chief characteristics of laser printers is their resolution %,( how many dots per inch (dpi) they lay down. The available resolutions range from 300 dpi at the low end to around 1200 dpi at the high end. Fig. 3.27 Laser Printer 90Inkjet Printers Inkjet printers use colour cartridges which combine magenta, yellow and cyan inks to create colour tones. A black cartridge is also used for crisp monochrome output. Inkjet printers work by spraying ionizing ink at a sheet of paper. Magnetized plates in the ink's path direct the ink onto the paper in the described shape. Fig. 3.28 Inkjet Printer Speakers The computer can also give produce voice output(audio data). Speaker serves as a voice output device. Using speakers along with speech synthesizer software, the computer can provide voice output. Voice output has become very common in many places like airlines, banks, automatic telephone enquiry system etc. Users can also hear music/songs using the voice output system. Fig. 3.29 Speakers 91Plotters Apart from the output devices like printers, plotters are also used to produce graphical output. Although printer output is very convenient for many purposes, the user needs to present the information graphically in order to understand its significance. 3.5.3 Storage Devices The computer may need to store data, programs etc. in a computer readable medium . This is called the secondary storage. Secondary storage is also called backup storage. Secondary stor- age can be used to transmit data to another computer either imme- diately or a latter time. This provides a mechanism for storing a large amount of data for a long period of time. Some of the com- monly used storage devices are hard disks, magnetic tapes, floppy disks and CD-ROM. To understand the physical mechanism of secondary storage devices one must have knowledge of magnetism, electronics and electro mechanical systems. The average time required to reach a storage location and obtain its contents is called its access time. In electromechanical devices with moving parts such as disks and tapes, the access time consists of a seek time required to position the read write head to a location and transfer time required to transfer the data to or from the device. Hard Disk Hard disk is a magnetic disk on which you can store computer data. The hard disk is a direct-access storage medium. This means you can store and retrieve data randomly. Disk storage systems are essentially based on magnetic properties. The magnetic disk consists of high speed rotating surfaces coated with a magnetic recording medium. The rotating surface of the disk is a round flat plate. When writing data, a write 92head magnetizes the particles on the disk surface as either north or south poles. When reading data, a read head converts the magnetic polarisations on the disk surface to a sequence of pulses. The read and write heads are generally combined into a single head unit. There may be more than one read/write head. Data is arranged as a series of concentric rings. Each ring (called a track) is subdivided into a number of sectors, each sector holding a specific number of data elements (bytes or characters). Fig. 3.30 A track subdivided into sectors The smallest unit that can be written to or read from the disk is a sector. Once a read or write request has been received by the disk unit, there is a delay involved until the required sector reaches the read/write head. This is known as rotational latency, and on average is one half of the period of revolution. The storage capacity of the disk is determined as (number of tracks * number of sectors * bytes per sector * number of read/write heads) Thus,the data is stored as magnetized spots arranged in concentric circles (tracks) on the disk. Each track is divided into sectors. The arrangement of tracks and sectors on a disk is known as its -,'format//.. 93High data rates demand that the disk rotates at a high speed (about 3,600 rpm). As the disk rotates read/write heads move to the correct track and fetch the desired data. Fig. 3.31 Hard Disk Drive The storage capacity of a hard disk can be Gigabytes (GB), i.e. thousands of Megabytes of information. Magnetic Tape A recording medium consisting of a thin tape with a coating of a fine magnetic strip, used for recording digital data. The tape itself is a strip of plastic coated with a magnetic recording medium. Fig. 3.32 Magenatic Tape Reader Bits are recorded as magnetic spots on the tape along several tracks. Usually, seven or nine bits are recorded simultaneously to form a character together with a parity bit. Read /write heads are mounted one in each track so that data can be recorded and read as a sequence of characters. 94Data is stored in frames across the width of the tape. The frames are grouped into blocks or records which are separated from other blocks by gaps. Magnetic tape is a serial access medium, similar to an audio cassette, and so data cannot be randomly located. This characteristic has prompted its use in the regular backing up of hard disks. Floppy Disk Fig. 3.33 Floppy Disk The floppy drive uses a thin circular disk for data storage. It is a soft magnetic disk. It is a thin magnetic-coated disk contained in a flexible or semi-rigid protective jacket. The disk rotates at 360rpm. A read/write head makes physical contact with the disk surface. Data is recorded as a series of tracks subdivided into sectors. The floppy disks are usually 3.5" in size. However, older floppy disks may be in use; these would be 5.25" in size or even 8" in size. A 3.5" floppy disk can hold 1.44 MB of data. Once data is stored on a floppy disk it can be %/%write protected+(! by clicking a tab on the disk. This prevents any new data being stored or any old data being erased. Disk drives for floppy disks are called floppy drives. Floppy disks are slower to access than hard disks and have less storage capacity. It is less expensive and are portable. It can be accessed randomly. 95Optical Disk Optical disks are a storage medium from which data is read and to which it is written by lasers. The optical disk is a random access storage medium; information can be easily read from any point on the disk. CD-ROM stands for Compact Disk - Read Only Memory. Fig. 3.34 Compact Disk It is now possible to have CD-ROMs where tracks of information can be written onto them by the user. These are called read/write CD-ROMs and these are becoming a popular and cheap method for storage. Summary * Computers are often compared to human beings since both have the ability to accept data, store, work with it, retrieve and provide information. * A computer system is the integration of physical entities called hardware and non-physical entities called software. * The hardware components include input devices, processor, storage devices and output devices. 96* The software items are programs and operating aids so that the computer can process data. * A computer uses input devices to accept the data and program. * In modern computers, monitors and printers are the commonly used output devices. * CPU is the brain of any computer system. It consists of arithmetic and logic units, control unit and internal memory (registers). * Control unit controls all the hardware operations, ie, those of input units, output units, memory unit and the processor. * The arithmetic and logic units in computers are capable of performing addition, subtraction, division and multiplication as well as some logical operations. * In the main memory, the computer stores the program and data that are currently being used. * All modern computers use the stored program concept. This concept is due to John Von Neuman. * The smallest unit of information is a single digit called a (''bit#"% (binary digit), which can be either 0 or 1. * The secondary memory is the memory that supplements the main memory. This is a long term non-volatile memory. * The most common input device is the keyboard. 97* Mouse is an input device that controls the movement of the cursor on the display screen. * Monitor is a commonly used output device. * Some of the commonly used storage devices are hard disks, magnetic tapes, floppy disks and CD-ROM. Exercises I. Fill in the blanks 1) A computer system is the interpretation of physical entities called _________ and non-physical entities called_________ 2) The computer uses_________ devices to accept data and program. 3) CPU stands for _________ 4) ALU stands for _________ 5) RAM stands for _________ 6) ROM stands for _________ 7) The stored program concept is conceived by _________ 8) Main memory is also known as _________ memory. 9) The performance of the memory system is defined by _________ time and _________ time. 10) _________ supplements the main memory. 11) _________ is popular input device for GUI application. 12) _________ is a input device mainly used to capture images. 13)Monitor is a commonly used output unit, sometimes called as _________ 14)The smallest dot that can be displayed on the monitor is called a _________ 15)Printers can be classified into _________ and _________ printers. II. State whether the following are True or False 1) The operating system is a software. 2) Keyboard is an output device. 3) Touch sensitive screen is an input device. 984) Main memory is a non-volatile memory. 5) ALU performs arithmetic and logical operations. 6) Registers are a part of secondary storage. 7) Bar code reader is an output device. 8) Light pen is an input device. 9) Inkjet printers are impact printers. 10) CD !() ROM stands for Compact Disk '-* Read Only Memory. III. Answer the following 1) How are the human being and the computers are related? 2) What are the components of the digital computer? 3) What are the functional units of a computer system? 4) Write the essentials of the stored program concept. 5) Write the main functions of the central processing unit. 6) What are the different types of main memory? 7) Define memory read and memory write operations. 8) What do you mean by memory access time? 9) What is the advantage of EEPROM over EPROM? 10) When do we use ROM? 11) What is an input device? 12) List few commonly used input devices. 13) What is an output device? 14) List few commonly used output devices. 15) What is a storage device? 16) List few commonly used storage devices. 17) What is the role of ALU? 18) What is a control unit? 19)What are registers? 20)What is a bus? IV. Answer the following in detail. 1) Describe in detail the various units of the Central Processing Unit. 2) Explain the working principle of CPU with an example. 3) Briefly explain various types of memory. 994) List a few commonly used input / output devices and explain them briefly. V. Project 1) List out the sequence of activities in executing the following program steps. (i) (ii) (iii) (iv) input the value of a input the value of b multiply c = a * b output the value c 2) Describe a configuration for a personal computer system by identifying the input, output, processing and storage devices and their specifications. 100CHAPTER 4 WORKING PRINCIPLE OF DIGITAL LOGIC 4.1 Logic Gates A logic gate is an elementary building block of a digital circuit. It is a circuit with one output and one or more inputs. At any given moment, logic gate takes one of the two binary conditions low (0) or high (1), represented by different voltage levels. A voltage level will represent each of the two logic values. For example +5V might represent a logic 1 and 0V might represent a logic 0. Input Signal Output Signal 1 0 Input 1 Logic Gate 1 Output 0 0 Time Fig. 4.1 Logic Gate This diagram which represents a logic gate accept input signals (two or more) and produces an output signal based on the type of the gate. The input signal takes values $,+1#!' or *%+0"$*. The output signal also gives in the value //)1,*! or ,)(0$$!. There are three fundamental logic gates namely, AND, OR and NOT. Also we have other logic gates like NAND, NOR, XOR and XNOR. Out of these NAND and NOR gates are called the universal gates, because the fundamental logic gates can be realized Through them. The circuit symbol and the truth table of these logic gates are explained here. 101AND Gate The AND gate is so named because, if 0 is called /+(false("% and 1 is called ",!true,$'( the gate acts in the same way as the logical "!,AND,)' operator. The output is '!&true/,( only when both inputs are (/!true'#+, otherwise, the output is ')&false&!/. In other words the output will be 1 if and only if both inputs are 1; otherwise the output is 0. The output of the AND gate is represented by a variable say C, where A and B are two and if input boolean variables. In boolean algebra, a variable can take either of the values *)#0",) or )."1,*". The logical symbol of the AND gate is A C = AB B Fig. 4.2 Logic symbol of AND Gate One way to symbolize the action of an AND gate is by writing the boolean function. C = A AND B In boolean algebra the multiplication sign stands for the AND operation. Therefore, the output of the AND gate is C = A . B simply or C = AB Read this as #("C equals A AND B(/%. Since there are two input variables here, the truth table has four entries, because there are four possible inputs : 00, 01, 10 and 11. For instance, if both inputs are 0, 102C = A . B = 0 . 0 = 0 The truth table for AND Gate is Input A 0 0 1 1 B 0 1 0 1 Output C 0 0 0 1 Table 4.1 Truth Table for AND Gate OR Gate The OR gate gets its name from the fact that it behaves like the logical inclusive ''%OR/-.. The output is *+(true(!/ if either or both of the inputs are '*)true!#,. If both inputs are *#*false,#., then the output is $!+false&/*. In otherwords the output will be 1 if and only if one or both inputs are 1; otherwise, the output is 0. The logical symbol of the OR gate is Fig. 4.3 Logic symbol of OR Gate The OR gate output is C = A OR B We use the + sign to denote the OR function. Therefore, C = A + B 103Read this as "'!C equals A OR B&-%. For instance, if both the inputs are 1 C = A + B = 1 + 1 = 1 The truth table for OR gate is Input Output A B C 0 0 0 0 1 1 1 0 1 1 1 1 Table 4.2 Truth Table for OR Gate NOT Gate The NOT gate, called a logical inverter, has only one input. It reverses the logical state. In other words the output C is always the complement of the input. The logical symbol of the NOT gate is A C = A Fig. 4.3 Logic symbol of NOT Gate The boolean function of the NOT gate is C = NOT A In boolean algebra, the overbar stands for NOT operation. Therefore, C = A 104Read this as ,./C equals NOT A+&/ or *$!C equals the complement of A#%#. If A is 0, C = 0 = 1 On the otherhand, if A is 1, C = 1 = 0 The truth table for NOT gate is Table 4.3 Truth Table for NOT Gate NOR Gate The NOR gate circuit is an OR gate followed by an inverter. Its output is '&%true!(# if both inputs are $#*false//" Otherwise, the output is )"/false".,. In other words, the only way to get &**1&%( as output is to have both inputs ,'+0-!'. Otherwise the output is 0. The logic circuit of the NOR gate is A A + B C = A + B B Fig. 4.5 Logic Circuit of NOR Gate A C B Fig. 4.6 Logic symbol of NOR Gate 105The output of NOR gate is C = ( A + B ) Read this as /'$C equals NOT of A OR B,/# or &*&C equals the complement of A OR B!)%. For example if both the inputs are 0, C = ( 0 + 0 ) = 0 = 1 The truth table for NOR gate is Input Output A B C 0 0 1 0 1 0 1 0 0 1 1 0 Table 4.4 Truth Table for NOR Gate Bubbled AND Gate The Logic Circuit of Bubbled AND Gate A A C = A . B B B Fig. 4.7 Logic Circuit of Bubbled AND Gate 106In the above circuit, invertors on the input lines of the AND gate gives the output as C = A . B This circuit can be redrawn as the bubbles on the inputs, where the bubbles represent inversion. A C B Fig. 4.8 Logic Symbol of Bubbled AND Gate We refer this as bubbled AND gate. Let us analyse this logic circuit for all input possibilities. If A = 0 and B = 0 C = ( 0 . 0 ) = 1 . 1 = 1 If A = 0 and B = 1 C = ( 0 . 1 ) = 1 . 0 = 0 If A = 1 and B = 0 C = ( 1 . 0 ) = 0 . 1 = 0 If A = 1 and B = 1 C = ( 1 . 1 ) = 0 . 0 = 0 Here the truth table is Output Input A 0 0 1 1 B 0 1 0 1 C 1 0 0 0 Table 4.5 Truth Table for Bubbled AND Gate 107You can see that, a bubbled AND gate produces the same output as a NOR gate. So, you can replace each NOR gate by a bubbled AND gate. In other words the circuits are interchangeable. Therefore ( A + B ) = A . B which establishes the De Morgan's first theorem. NAND Gate The NAND gate operates as an AND gate followed by a NOT gate. It acts in the manner of the logical operation "*-AND!#, followed by inversion. The output is $(-false-%* if both inputs are ,)!true(&(, otherwise, the output is &,/true*(.. In otherwords the output of the NAND gate is 0 if and only if both the inputs are 1, otherwise the output is 1. The logic circuit of NAND gate is A (A . B) C = (A . B) B Fig. 4.9 Logic Circuit of NAND Gate The logical symbol of NAND gate is A C B Fig. 4.10 Logic Symbol of NAND Gate The output of the NAND gate is C = ( A . B ) Read this as ,&(C equals NOT of A AND B)-) or ..*C equals the complement of A AND B#&,. For example if both the inputs are 1 C = ( 1 . 1 ) = 1 = 0 108The truth table for NAND gate is Output Input A 0 0 1 1 B 0 1 0 1 C 1 1 1 0 Table 4.6 Truth Table for NAND Gate Bubbled OR Gate The logic circuit of bubbled OR gate is A A C = A + B B B Fig. 4.11 Logic Circuit of Bubbled OR Gate The output of this circuit can be written as C = A + B The above circuit can be redrawn as the bubbles on the input, where the bubbles represents the inversion. A C B Fig. 4.12 Logic Symbol of Bubbled OR Gate 109We refer this as bubbled OR gate. The truth table for the bubbled OR is Output Input A 0 0 1 1 B 0 1 0 1 C 1 1 1 0 Table 4.7 Truth Table for Bubbled OR Gate If we compare the truth tables of the bubbled OR gate with NAND gate, they are identical. So the circuits are interchangeable. Therefore ( A . B ) = A + B which establishes the De Morgan's second theorem. XOR Gate The XOR (exclusive-OR) gate acts in the same way as the logical .!+either/or.$%( The output is %"!true%!- if either, but not both, of the inputs are $&)true.#/) The output is ($&false(,# if both inputs are ("(false"%- or if both inputs are %*%true.'%) Another way of looking at this circuit is to observe that the output is 1 if the inputs are different, but 0 if the inputs are the same. The logic circuit of XOR gate is A A A . B B C= A.B + A.B A A . B B B Fig. 4.13 Logic Circuit of XOR Gate 110The output of the XOR gate is C = A B + A B T he truth table for XOR gate is Output Input A 0 0 1 1 B 0 1 0 1 C 0 1 1 0 Table 4.8 Truth Table for XOR Gate In boolean algebra, exclusive-OR operator is + or .)*encircled plus&%*. Hence, C = A + B The logical symbol of XOR gate is A C B Fig. 4.14 Logic Symbol of XOR Gate XNOR Gate The XNOR (exclusive-NOR) gate is a combination XOR gate followed by an inverter. Its output is *)(true++. if the inputs are the same, and $+"false)#& if the inputs are different. In simple words, the output is 1 if the input are the same, otherwise the output is 0. The logic circuit of XNOR gate is 111Fig. 4.15 Logic Circuit of XNOR Gate The output of the XNOR is NOT of XOR C = A + B = A.B + A.B = AB + A B (Using De Morgan's Theorem) In boolean algebra, or #*'included dot*## stands for the XNOR. Therefore, C = A B The logical symbol is Fig. 4.16 Logic Symbol of XNOR Gate The truth table for XNOR gate is Output Input A 0 0 1 1 B 0 1 0 1 C 1 0 0 1 Table 4.9 Truth Table for XNOR Gate 112Using combinations of logic gates, complex operations can be performed. In theory, there is no limit to the number of gates that can be arranged together in a single device. But in practice, there is a limit to the number of gates that can be packed into a given physical space. Arrays of logic gates are found in digital integrated circuits. The logic gates and their corresponding truth tables are summarized in the following table. Logical Gates AND OR Symbol Truth Table A 0 0 1 1 A 0 0 1 1 A 0 1 NOT NAND NOR XOR XNOR B 0 1 0 1 B 0 1 0 1 A 0 0 1 1 A 0 0 1 1 A 0 0 1 1 A 0 0 1 1 Table 4.10 summary of Logic Gates 113 A B 0 0 0 1 A+B 0 1 1 1 A 1 0 B 0 1 0 1 B 0 1 0 1 B 0 1 0 1 B 0 1 0 1 AB 1 1 1 0 A+B 1 0 0 0 A+B 0 1 1 0 A B 1 0 0 1Universality of NAND and NOR gates We know that all boolean functions can be expressed in terms of the fundamental gates namely AND, OR and NOT. In fact, these fundamental gates can be expressed in terms of either NAND gates or NOR gates. NAND gates can be used to implement the fundamental logic gates NOT, AND and OR. Here A and B denote the logical states (Input). Fig. 4.17 Universality of NAND Gates NOR gates can also be used to implement NOT, OR and AND gates. 0 B B B Fig. 4.18 Universality of NOR Gates 1144.2 Conversion of Boolean Function To build more complicated boolean functions, we use the AND, OR, and NOT operators and combine them together. Also, it is possible to convert back and forth between the three representations of a boolean function (equation, truth table, and logic circuit). The following examples show how this is done. Converting a Boolean Equation to a Truth Table Truth table lists all the values of the boolean function for each set of values of the variables. Now we will obtain a truth table for the following boolean function D = (A '' B) + C Clearly, D is a function of three input variables A, B, and C. Hence the truth table will have 2 3 = 8 entries, from 000 to 111. Before determining the output D in the table, we will compute the various intermediate terms like A %# B and C as shown in the table below. For instance, if A = 0, B = 0 and C = 0 then D = ( A . B ) + C = ( 0 . 0 ) + 0 = 0 + 0 = 0 + 1 = 1 Here we use the hierarchy of operations of the boolean operators NOT, AND and OR over the parenthesis. 115The truth table for the boolean function is Input Intermediate Output A B C A -)+ B C D 0 0 0 0 1 1 0 0 1 0 0 0 0 1 0 0 1 1 0 1 1 0 0 0 1 0 0 0 1 1 1 0 1 0 0 0 1 1 0 1 1 1 1 1 1 1 0 1 Converting a Boolean Equation to a Logic Circuit The boolean function is realized as a logic circuit by suitably arranging the logic gates to give the desired output for a given set of input. Any boolean function may be realized using the three logical operations NOT, AND and OR. Using gates we can realize boolean function. Now we will draw the logic circuit for the boolean function. E = A + ( B $# C ) + D This boolean function has four inputs A, B, C, D and an output E. The output E is obtained by ORing the individual terms given in the right side of the boolean function. That is, by ORing the terms A, ( B &! C ) and D. 116The first term A , which is the complement of the given input A, is realized by A A The second term is (B .' C) . Here the complement of C is AND with B. The logic circuit is realized by The third term, which is the complement of D is realized by D D The output D is realized by ORing the output of the three terms. Hence the logic circuit of the boolean equation is 117Converting a Logic Circuit to a Boolean Function As a reversal operation, the realization of the logic circuit can be expressed as a boolean function. Let us formulate an expression for the output in terms of the inputs for the given the logic circuit To solve this, we simply start from left and work towards the right, identifying and labeling each of the signals that we encounter until we arrive at the expression for the output. The labeling of all the signals is shown in the figure below. Let us label the input signals as A, B, C and the output as D. Hence the boolean function corresponding to the logic circuit can be written as D = A +'. B + B -*- C 118Converting a Truth Table to a Boolean Function There are many ways to do this conversion. A simplest way is to write the boolean function as an OR of minterms. A minterm is simply the ANDing of all variables, and assigning bars (NOT) to variables whose values are 0. For example, assuming the inputs to a 4-variable boolean function as A, B, C, and D the minterm corresponding to the input 1010 is : A (%. B ('+ C *(* D. Notice that this minterm is simply the AND of all four variables, with B and D complemented. The reason that B and D are complemented is because for this input, B = D = 0. As another example, for the input 1110, only D = 0, and so the corresponding minterm is A $*, B "(( C $," D. For example let us write a boolean function for the following truth table A 0 0 0 0 1 1 1 1 Input B 0 0 1 1 0 0 1 1 C 0 1 0 1 0 1 0 1 Output D 1 0 1 0 1 0 1 0 To do this problem, we first circle all of the rows in the truth table which have an output D = 1. Then for each cirlced row, we write the corresponding minterm. This is illustrated in the table below. 119Input Output A B C D Minterms 0 0 0 1 A ./+ B +() C 0 0 1 0 0 1 0 1 0 1 1 0 1 0 0 1 1 0 1 0 1 1 0 1 1 1 1 0 A #*( B !'" C A -.- B $&+ C A .'# B (*$ C Finally, the boolean expression for D is obtained by ORing all of the minterms as follows: D = ( A ,+ B +$ C ) + ( A &' B ,$ C ) + ( A .. B /- C ) + ( A (" B '' C ) Design of Logic Circuit There are many steps in designing a logic circuit. First, the problem is stated (in words). Second, from the word description, the inputs and outputs are identified, and a block diagram is drawn. Third, a truth table is formulated which shows the output of the system for every possible input. Fourth, the truth table is converted to a boolean function. Fifth, the boolean function is converted to a logic circuit diagram. Finally, the logic circuit is built and tested. Let us consider the design aspects of a 2-input /single output system which operates as follows: The output is 1 if and only if precisely one of the inputs is 1; otherwise, the output is 0. 120Step 1: Statement of the problem . Given above. Step 2: Identify inputs and outputs. It is clear from the statement of the problem that we need two inputs, say A and B, and one output, say C. A block diagram for this system is A C B Step 3: Formulate truth table. The truth table for this problem is given below. Notice that the output is 1 if only one of the inputs is 1. otherwise the output is )%/0'*,. Output Input A 0 0 1 1 B C 0 0 1 1 0 Input 1 1 A 0 B 0 0 Output C Minterm 0 Step 4: Convert the truth table to a 0 boolean 1 function. 1 Identify A B the minterms for the rows in the truth table which have an 1 0 1 A B output "''1/-) 1 1 0 By ORing the minterms, we obtain the boolean function corresponding to the truth table as D = ( A /'$ B ) + ( A #). B ) 121Step 5: Realization of the Boolean function into a Logic Circuit Diagram. The logic circuit diagram corresponding to this boolean func- tion is given below. A B B -., D = A *)* B + A (/' B A B '(% 4.3 Half Adder The circuit that performs addition within the Arithmetic and Logic Unit of the CPU are called adders. A unit that adds two binary digits is called a half adder and the one that adds together three binary digits is called a full adder. A half adder sums two binary digits to give a sum and a carry. This simple addition consists of four possible operations. 0 + 0 = 0 0 + 1 = 1 1 + 0 = 1 1 + 1 = 10 The first three operations produce a single digit sum, while the fourth one produces two digit sum. The higher significant bit in this operation is called a carry. So the carry of the first three operations are -")0,/*, where the fourth one produces a carry /!!1&'-. 122The boolean realization of binary addition is shown in the truth table. Here A and B are inputs to give a sum S and a carry C. Input A 0 0 1 1 Sum B S 0 1 0 1 0 1 1 0 0 Minterms Of S Carry C - A.B A.B 0 0 0 0 1 Minterms of C A.B The boolean functions corresponding to the sum and carry are S = A . B + A . B C = A . B Which can be realized using logic circuit as, Fig. 4.19 Logic Circuit of Half Adder 123which is further simplified as In a half adder, an AND gate is added in parallel to the XOR gate to generate the carry and sum respectively. The &&+sum)/+ column of the truth table represents the output of the XOR gate and the '-!carry$'* column represents the output of the AND gate. 4.4 Full Adder A half adder logic circuit is a very important component of computing systems. As this circuit cannot accept a carry bit from a previous addition, it is not enough to fully peform additions for binary number greater than 1. In order to achieve this a full adder is required. A full adder sums three input bits. It consists of three inputs and two outputs. Two of the inputs represent the two significant bits to be added and the third one represents the carry from the previous significant position. Here A, B referred as the inputs, C1 as carry input from the previous stage, C2 as carry output and S as sum. 124Input A B C 1 C 2 Output S 0 0 0 0 1 1 1 1 0 0 0 1 0 1 1 1 0 1 1 0 1 0 0 1 0 0 1 1 0 0 1 1 0 1 0 1 0 1 0 1 Table 4.12 Truth Table for Full Adder The carry bit C 2 is 1 if both A and B are 1, or exactly one of A and B is 1 and the input carry, C 1 is 1. The sum bit S is 1 if there exist odd number of %&!1's of the three inputs. S is the XOR of the three inputs. Hence, the full adder can be realized as shown below. By ORing the minterms of the full adder truth table, the sum and carry can be written as S = A B C 1 + A B C 1 + A B C 1 + A B C 1 C 2 = A B C 1 + A B C 1 + A B C 1 + A B C 1 Consider (A$-) B) ("" C 1 = ( A B + A B ) )&! C 1 = ( A B + A B ) C 1 + ( A B + A B ) C 1 = ( (A B) (A B) ) C 1 + ( A B + A B ) C 1 = ( (A + B) (A + B) ) C 1 + ( A B + A B ) C 1 125Also Hence To realize the full adder we need two 2-input XOR, two 2- input AND gates and a 2-input OR gate. Hence the full adder can be realized as. C 2 Fig. 4.20 Logic Circuit of Full Adder 126Notice that the full adder can be constructed from two half adders and an OR gate. If the logic circuit outputs are based on the inputs presented at that time, then they are called combinational circuit. The half adder and full adder circuits are the examples for the combinational circuits. On the other hand, if the logic circuit outputs are based on, not only the inputs presented at that time, but also the previous state output, then they are called sequential circuits. There are two main types of sequential circuits. A synchronous sequential circuit is a system whose output can be defined from its inputs at discrete instant of time. The output of the asynchronous sequential circuit depends upon the order in which its input signals change at any instance of time. The flip-flop circuit is an example of sequential circuit. 4.5 The Flip-Flop A flip flop is a circuit which is capable of remembering the value which is given as input. Hence it can be used as a basic memory element in a memory device. These circuits are capable of storing one bit of information. Basic flip-flops A flip-flop circuit can be constructed using either two NOR gates or two NAND gates. A common example of a circuit employing sequential logic is the flip-flop, also called a bi-stable gate. A simple flip-flop has two stable states. The flip-flop maintains its states indefinitely until an input pulse called a trigger is received. If a trigger is received, the flip-flop outputs change their states according to defined rules, and remain in those states until another trigger is received. 127Flip #%! Flop Circuit using NOR Gates By cross-coupling two NOR gates, the basic operation of a flip-flop could be demonstrated. In this circuit the outputs are fed back again to inputs. S Q Q R Fig. 4.21 Flip Flop Circuit using NOR Gates The flip-flop circuit has two outputs, one for the normal value Q and another for the complement value Q. It also has two inputs S (set) and R (reset). Here, the previous output states are fed back to determine the current state of the output. The NOR basic flip-flop circuit operates with inputs normally at ++.0((, unless the state of the flip-flop has to be changed. As a starting point, we assume S = 1 and R = 0. This makes Q = 0. This Q = 0 is again given along with R = 0 to make Q = 1. ie. when S = 1 and R = 0 make Q = 1 and Q = 0. When the input S returns to +"+0&/(, the output remains the same, because the output Q remain as .#.1(/% and Q as /#)0("". ie. when S = 0 and R = 0 make Q = 1 and Q = 0 after S = 1 and R = 0. 128In a similar manner the reset input changes the output Q = 0 and Q = 1. We assume S = 0 and R = 1. This make Q = 0. This Q = 0 is again given along with S = 0 to make Q = 1. ie. when S = 0 and R = 1 make Q = 0 and Q = 1. When the reset input returns to 0, the outputs do not change, because the output Q remains as )"'0!,, and Q as ('.1'&&. ie. when S = 0 and R = 0 make Q = 0 and Q = 1 after S = 0 and R = 1. This can be tabulated as S R Q Q 1 0 1 0 0 0 1 0 0 1 0 1 0 0 0 1 (after S =1 and R = 0) (after S =0 and R = 1) When #!.1-.. is applied to both S and R, the outputs Q and Q become 0. These facts violate the output Q and Q are the complements of each other. In normal operations this condition must be avoided. Thus a basic flip-flop has two useful states. When Q = 1 and Q = 0, it is called as set state. When Q = 0 and Q = 1, it is called as reset state. 129Flip -,! Flop Circuit using NAND Gates In a similar manner one can realize the basic flip-flop by cross coupling two NAND gates. S Q Q R Fig. 4.22 Flip Flop Circuit using NAND Gates The corresponding truth table is given as S R Q Q 1 0 0 1 1 1 0 1 0 1 1 0 1 1 1 0 (after S =1 and R = 0) (after S =0 and R = 1) The NAND basic flip-flop circuit operates with inputs normally at ++'1'*! unless the state of the flip-flop has to be changed. A momentary '**0)*+ to the input S gives Q = 1 and Q = 0. This makes the flip-flop to set state. After the input S returns to 1, a momentary /+.0#*% to the input R gives Q = 0 and Q = 1. This makes the flip-flop to reset state. When both the inputs become 0, ie., S = 0 and R = 0, both the outputs become 1. This condition must be avoided in the normal operation. 130There are several kinds of flip-flop circuits, with designators such as D, T, J-K, and R-S. Flip-flop circuits are interconnected to form the logic gates that comprise digital integrated circuits (ICs) such as memory chips and microprocessors. 4.6 Electronic Workbench 4.6.1 Introduction Electronic workbench is a simulation tool for electronic circuits. It allows to design and analyze circuits without using actual instruments. The workbench's click-and-drag operation make editing a circuit fast and easy. It is a windows compatible tool and follows the normal conventions of windows. The completed circuit can be simulated within the workbench by the use of simulated test and measurement equipment which are wired into the circuit. The circuit diagrams and the output of the simulated circuits can be printed or exported to other tools such as word processors for inclusion in other documents. The electronic workbench can be used for analogue, digital or mixed mode circuits. MultiSim is a electronic workbench which is used for design and analysis of circuits. It offers a single, easy-to-use graphical interface for the design needs. It also provides the advanced functionality you need to design from specifications. 4.6.2 Objectives and Expectations (! #. *# 4.6.3 Even though the Multisim is designed with much functionality, we use the tool to help us get familiar with the basic digital features. To learn how to build and test some simple digital logic gates using Multisim. To know how to build and simulate simple combinational logic circuits using the logic converter. Building a Simple Digital logic Gate 1. Start MultiSim from the Programs group in the Start menu. The main screen is as shown in the fig. 4.23 131- Fig. 4.23 MultiSim Main Screen 2. Assign a name to your file by following the steps given below. Choose File > Save As, under the File Name box to save the new workspace as circuit1. Press OK. The filename will then appear in the lower left hand corner of the workspace as shown in the fig. 4.24. 132Fig. 4.24 File Menu 3. Click on the Place menu, then click on the Component,which shows the list of components in the component library. A ghost im- age of the component appears on the circuit window showing exactly where the component is placed. One can use the appropriate data- base, group and component drop-down list to select the desired com- ponent. 133Fig. 4.25 Place Menu Fig. 4.26 Component Window 1344. Click on any one of the logic gates available in the ,/(select a component*,$ dialog box and drag it to place it in the workspace. Double click on the logic gate to change the properties of it and label the gate. To get help, just click on the logic gate using the left mouse button and choose help. U1 AND2 Fig. 4.27 Selecting a Component 4.6.4 Building a Simple Combinational Logic Circuit Here the general steps to build a circuit by dragging the components like gates, wires, etc. are discussed. 135Placing Components Components like logic gates are placed in the workspace by i) ii) Selecting component from Place menu. Right clicking on the workspace and select place components or Ctrl+W. Selecting Components Click on the component to highlight it .Once highlighted the options in the #..Circuit !&! menu will apply to that component. To select several components drag a box around them. All selected components will be highlighted. Copying Components To copy a component select it and then choose ",*Copy#%+ from the %,-Edit$.( menu (or C). Then select paste (or V). Copies of the component will appear in the middle of the drawing area. They can then be moved to their required positions. Modifying components i) Select the components and then choose the available options from the /'/CIRCUIT",* menu. ii) Double click on the component. A window will be opened which gives the parameters for the component which can be customized. Change the values as required and then click on ,/!Accept$",. Moving Components To move components on the drawing select it and drag it to a new position and drop it. Any wires connected to it will be dragged with it. 136Deleting Components Select components and then select /+*Delete%+' or %$+Cut&-' from the "##EDIT.-) menu or press delete. You will be asked to confirm the delete. Building the circuit Placing interconnecting wires Click on the end of the leg of the component. Drag the wire directly to the leg of the next component and then drop it. The wire will then route itself neatly. Printing the Circuit Select -,'Print-&# from the File Menu and then select the item(s) to print from the dialogue box that is opened. Saving the Circuit To save a new circuit or rename an old one , select '")Save As.," from the File Menu and complete the details in the dialogue box that opens. To save an existing circuit select *+,Save+!/ from the *,*File%#( Menu . Opening an Existing Circuit Select ++"Open&!. from the File Menu and then enter the filename in the dialogue box that opens. Consider a logical circuit by connecting two NOT gates with an AND gate. Place the NOT gate and AND gate on the workspace by selecting the respective components from the .."select a component,+' dialog box. Copy NOT gate. Drag the leg of the components and wire them as shown in fig. 4.28. 137Fig. 4.28 Construction of a Logic Circuit 4.6.5 The Logic Converter MultiSim provides a tool called the logic converter from the instrument bin. With the logic converter, one can carry out several conversions of a logic circuit representation: 1. 2. 3. 4. 5. 6. Logic Circuit Truth Table Truth Table Boolean Expression Boolean Expression Boolean Expression &) /. ', !( .$ #& Truth Table Boolean Expression Simplified Boolean Expression Truth Table Logic Circuit NAND-gate only logic circuit One can connect it to a digital circuit to derive the truth table or Boolean expression the circuit represents. One can also use it to produce a logic circuit from a truth table or boolean expression. To open the logic converter, click on the simulate menu, instruments and then logic converter as shown in fig. 4.29. 138Fig. 4.29 Logic Convertor The fig. 4.30 shows the logic converter that you have placed in the workspace. The little circles in the bottom are the inputs and output terminals. There are 8 input terminals and 1 output terminal available for use. That means if you want to use the logic converter to carry out the conversions, an individual circuit is limited to 8 inputs and 1 output. Fig. 4.30 Logic Convertor on the Workspace To use the logic converter, double-click on the logic converter icon. Your workspace should now look like the fig. 4.31. 1398 Input Terminals (A to H) 1 Output Terminal Boolean expression goes here Fig. 4.31 View of Logic Convertor Notice that the 8 input terminals are labeled as A to H and the output terminal is labeled as #$#Out%(,. 140The converting options available in the logic converter are as follows: Descriptions: $+. Convert Logic Circuit to Truth Table %(. Convert Truth Table to Boolean Expression #-$ Convert Truth Table to Simplified Boolean Expression $). Convert Boolean Expression to Truth Table #.. Convert Boolean Expression to Logic Circuit "$% Convert Boolean Expression to NAND-gate only logic circuit 4.6.6 Converting a logic circuit To a truth table 1. Construct the circuit by drawing the components (logic circuit). 1412. Connect the inputs of your circuit to the input terminals on the logic converter and single output of the circuit to the output terminal on the logic converter as shown below. 1423. Click the logic circuit %$$ truth table button. You should get the truth table for the logic circuit in the logic converter. 4.6.7 Converting a Truth Table to a Boolean Expression Once you have a truth table, the logic converter can transform it into a boolean function in the form of an algebraic expression. 143To create a Truth table : $/ .) "" +- Drag a logic converter to the workspace and open it. Click the number of inputs you want, from A to H at the top of the logic converter. The inputs are present in standard binary count format. In this case select A, B and C. The values in the output column are set to ".!?$,%. Click the output values once to change as ++.0(&* and twice to change as (%$1&*%. Click the )/*Truth Table to Boolean Expression%*% button. 144The boolean expression will be displayed at the bottom of the logic converter. In this case:- A'B'C$(- + A'BC,*! + A'BC + AB'C Note the primes, representing inversion. A*+. means NOT A, or A. 4.6.8 Converting a Truth Table to a Simplified Boolean Expression Some expressions can be recalculated in a simpler form. To try to simplify the expression, click the ".+simplify.!- button using the previous truth table. In this case, the expression can be simplified to A'C$/# + A'B +AB'C 1454.6.9 Converting a Boolean Expression to Truth Table Once you have a boolean expression, the logic converter can transform it into a truth table. Click the %'.Boolean Expression to Truth Table($) button after entering the Boolean expression A-$/ + BC + B./) in the logic converter. The truth table will be displayed in the logic converter as shown below. 1464.6.10 Converting a Boolean Expression to a Circuit To do this, enter the boolean expression, A'B+B'C+ABC and click the ##"Boolean to Circuit''+ button The resulting circuit will appear in the workspace. 4.6.11 Converting Boolean Expression to NAND-gate only logic circuit To realize the logic circuit using NAND-gates for the same boolean expression A'B+B'C+ABC, click The resulting circuit will appear on the workspace. 1474.6.12 Creating a Circuit from a Truth Table This is the most useful conversion for a circuit designer. Normally, you will have translated the client's specification into a truth table, and have then to produce a logic gate circuit to do the job.This requires two conversions using the logic converter. We will practice using a different problem. '(- &)# Create a truth table. Click the !'#simplify%+) button to convert the truth table to the simplest Boolean expression (A'BC'// + AB'C) . 148&/* Click the &$-Boolean to Circuit%"- button . The resulting circuit will appear, selected, on the workspace. If you want to move it, point to one component and drag the circuit. 149The following table summarizes all possible scenarios that one can encounter: From Truth Table To Boolean Expressions Options Simplified Boolean Expressions Logic Circuit NAND-gate logic circuit Boolean Expressions only Truth Table Logic Circuit NAND-gate only logic circuit Simplified Boolean Expressions Logic Circuit Truth Table Boolean Expressions Simplified Boolean Expressions T able 4.13 : Quick Guide to convert a Digital Circuit using the Logic Converter 150Summary -+' A logic gate is an elementary building block of a digital circuit. &"- There are three fundamental logic gates namely, AND, OR and NOT. (), we have other logic gates like NAND, NOR, XOR and XNOR. %.& NAND and NOR gates are called the universal gates. .&$ The circuit that performs addition within the Arithmetic and Logic Unit of the CPU are called adders. ("& A unit that adds two binary digits is called a half adder. !(- one that adds together three binary digits is called a full adder. '%, A flip flop is a circuit which is capable of remembering the value which is given as input. !"/ Electronic workbench is a simulation tool for electronic circuits. &&( MultiSim is a electronic workbench which is used for design and analysis of circuits. 151Exercises I. Fill in the blanks 1. In AND gate the output is __________ when both the inputs are *+#true!($. 2. In OR gate the output is __________if both the inputs are&!$false",). 3. A__________ is an elementary building block of a digital circuit. 4. The NAND gate operates as an AND gate followed by a __________gate. 5. The __________gate circuit is an OR gate followed by an inverter. 6. __________and __________gates are called universal gates. 7. __________, __________and __________gates are called the fundamental gates. 8. A unit that adds two binary digits is called a __________ 9. A full adder can be constructed from two __________and a __________gate. 10. A simple flip flop has __________stable states. II. State whether the following are True or False 1. 2. 3. 4. 5. 6. Logic gate has two or more outputs. Logic circuits have two or more outputs. AND is an universal gate. XOR is a fundamental gate. NAND gate can be used to realize OR gate. NOR gate can be used to realize OR gate. 7. Full adder can be realized using two half adders and an OR gate. 8. XOR gate is used to build half adder. 9. XOR gate circuit is an OR gate following by an inventor 10. Flip-flop is used as a storage. III. Answer in one or two lines: 1. 2. 3. 4. What is a logic gate? List the fundamental logical gates? Why NAND and NOR gates are called as universal gates? How AND gate can be realized using NOR gate? 1525. 6. 7. 8. 9. How OR gate can be realized using NAND gate? Give the truth table of XOR gates for two inputs. What is a half adder? What is a full adder? What is a combinational circuit? 10. What is a sequential circuit? IV. Answer the following 1. Determine the truth table for the following Boolean functions E = A + ( B . C ) + D 2. Convert the following truth tables to a Boolean equations. 3. Convert the following logic circuit to a boolean equation. 1534. Realize the boolean function to a logic circuit E = A B + B C + A B C 5. Explain the steps involved in designing a logic circuit. 6. What are the different types of logic gates? Explain with the help of truth tables and give an example for each gate. Project 1. Using the logic converter, test the basic logic gates by constructing their truth table. 2. Using the logic converter, build and test the following logic circuits and record your simulation results in a truth table. 3. Build a logic circuit for the boolean function A'C.+&+A'C+AC#*'+AC using the logic converter. 4. Build a logic circuit for the following truth table and bring out its equivalent Boolean expression using logic converter. Also simplify the Boolean expression. A 0 0 0 0 1 1 1 1 Input B 0 0 1 1 0 0 1 1 C 0 1 0 1 0 1 0 1 154 Output D 0 1 1 0 0 1 0 1Chapter 5 OPERATING SYSTEM 5.1 Introduction When a computer is devoid of any software it is just like dead bodies. The computer software and hardware have been intrinsically linked with each other. One of them cannot do anything useful by itself without help from the other. There are two types of software, one is the System Software and the other is the Application Software. System Software looks after the functions of the computer. This is just like involuntary actions con- trolling involuntary muscles such as digestive system of the animal. Sys- tem software makes efficient use of the computing resources and nor- mally provides a uniform base for different apllications. It is there to oper- ate, control and extend the processing capabilities of computers. Applica- tion software helps the user to do his/her work. This is similar to the volun- tary actions controling voluntary muscles like hand etc. Moving a hand is a voluntary action. The Operating System comes under the System Soft- ware category. The actual work is undertaken only by the hardware. In order to do useful work on a computer, one has to access the hardware, but one can access the hardware directly, only in the first generation com- puters. In the subsequent generations of computers direct access is de- nied. The architecture of the computers at the machine level is primitive and very hard to program. What is the reason for the denial of the access? If the access is not denied, unless the user is hard working, one of the two alternatives can happen. 1) The user may be forced to conclude that computer is not for him/ her. 2) The user may damage the computer hardware. This leads us to a natural question. Who will access the computer hardware directly if the user is denied such permission? The answer is the Operating System. The Operating system provides so many facilities with which a user comfortably uses their computers. This resembles the life of modern man, who cannot tolerate power cut even for five minutes. 155Apart from being complicated, trying to deal with I/O(Input/Output) opeations can prove truly frustrating. The average user wants to have a simple high-level abstraction to deal with. If you see an object, a lot of Impulses are triggned on inside your brain. At the end you are shown an image of the object. If an average person is asked to explain the activities that happen inside the brain, he/she will not dare to even open his eyes. The brain provides a convenient highly sophisticated abstraction. It merely provides the image of an object without letting people know about the activities that happen inside the brain. In this case eye is an interface between the object and the part of the brain that processes visual data. In a similar fashion the Operating System hides from a person know the com- plexity of the hardware and allows the user to use the name of the files for reading and writing. The Operating System adds extended capabilities to a machine with which it is easier to program than the underlying hardware. The Operating System manages the resource. Modern computers are highly complex machines. They get more complex day by day. So it is very diffi- cult to manage such a complex system but the Operating System man- ages the complex system in an efficient way. It provides special routines called device drivers to support the specific behaviour of individual device. In this view the primary task of the Operating System is to keep track of who is using which resource, to grant resource requests, to account for usage and to mediate conflicting requests from different programs and users. The Operating System is to provide an orderly and controlled allo- cation of resources among the various programs competing for them. The Operating System is the intermediary between the user and computer hardware. When the Operating System was first introduced, the primary goal of the Operating System was mainly to optimize resources. The sec- ondary goal was to make the computer environment, user-friendly. Now providing the user-friendly environment is the main aim of the operating system. The Operating System acts as the manager of resources such as CPU time, memory space, file storage, I/O devices. Since there may be many conflicting requests, Operating System allocates resources in an optimal manner. That is, Operating System allocates resources in such a manner so as to achieve the maximum best possible result. 156The Operating System also provides the means for the proper use of hardware, software and data in the operation of the computer system. The Operating System is like a supervisor in a company providing an ex- cellent environment in which the other people can perform useful work. Operating System assumes yet another responsibility, that of serv- ing as a control program. A control program controls the execution of user programs to prevent errors and improper use of the computer. It is espe- cially concerned with the operation and control I/O devices. It is hard to define Operating System. There are several definitions for Operating System. One of the definitions is that Operating System is one program running at all times on the computer. The another, somewhat more widely accepted definition is that an Operating System is an inter- face between the user and hardware. The Operating System's goals are to: 1) 2) 3) execute user programs in a user-friendly atmosphere. make the computer system convenient to use. optimize computer hardware. Any Operating system should be easy to use. Now-a-days people are hard pressed for time, so they cannot undergo any training for making use of the Operating System. The idea of using facilities available in the Operating System should be intuitive. The Operating System should allow developing application programs easier. Otherwise people cannot con- centrate on the application development; instead, they have to spend lot of time in concentrating on the peculiarities of the Operating System. The Operating System should be portable. That is, the Operating System should run in almost all hardware platforms. If there is a new version of the Operating System, it should not confuse the people who used the earlier version and also it should run software that ran successfully in earlier versions. The Operating System should provide data security; it should not allow one user to write on the 157file/files of the other user and thus spoiling the contents of the owner of the file/files. The Operating System should provide data confidentiality. That is, the Operating System should not allow unauthorised people to access the data of the other people. If this is possible then the scientific and techno- logical institutions and banks will have a nightmarish existence. The ven- dor who provided the Operating System should undertake the service fa- cility also. The vendor should be accessed easily. Otherwise that Operat- ing System will attain notoriety. The Operating System should work in a network as well as distrib- uted environment. The Operating System should make system adminis- tration more efficient. The Operating system should provide the help facility. There are people who do not like to get help from the other people; this will prick their self-esteem. Instead they may try to get help from the system. The help facility should mainly concentrate on people like them. There should be different levels of help to satisfy the needs of different levels of users. According to John Von Neumann architecture application, program and data should reside in main memory. In those days programs dealt mainly with scientific problems. For solving these types of problems, soft- ware libraries are created. These libraries complicated the normal user of that time. Therefore, the computer operator job is created. But this pre- vented the programmer to remove the errors (debug) immediately. Pro- grammers had to wait for nearly six hours for debugging their programs. A brief History of the Operating System. In the beginning, programs were run one at a time. In order to use CPU more efficiently, jobs of similar nature were grouped and made to run in batches. But after program execution, the computer operator manually restarted the next program. To avoid the delay due to manual operation, Automatic Job Sequencing mechanism was introduced. 158This is called Resident Monitor and this may be the first elementary Operating System. If scientific applications had been used in a computer, the CPU (Central Processing Unit) was busy with the program, but if business problems had been handled, I/O system was busy and the CPU was kept idle. In order to make the CPU busy, the I/O operations were done in an inexpensive computer, the contents of card reader was transferred in a mag- netic tape and which in turn was placed in the computer that performed the business operation. In those days data were stored in cards, called punched cards. Another attempt was made to keep the CPU busy for most of the time. A buffer (Refer chapter 6) was (and still is) allowed to store Input, when the CPU needed the data, buffer sent the data immediately. The next input might be ready in the buffer before the CPU processed the earlier data. When the processing was completed, the CPU sent the output to the buffer. When the data were ready, an interrupt mechanism interrupted the CPU. The CPU having taken the necessary actions and resumed its original work. At the same time, Direct Memory Access (DMA) mechanism was also created, which allowed transferring data to and from memory without the in- tervention of the CPU. Spooling (is a way of dealing with dedicated I/O de- vices in the multiprogramming system.) allowed (and still allows) reading a set of jobs in disk system from the card reader. When printing work had to be undertaken, the print image was copied into the disk system and when condi- tions were favourable the print image was sent to the printer. Spooling is superior to the buffer, because in spooling I/O operations can be overlapped with the working of other jobs but that is not possible with the buffer. While executing one job, the OS, reads next job from card reader into a storage area on the disk and outputs printout of previous job from disk to the printer. Spooling allowed the CPU to choose a particular job for execu- tion leading to the concept called the Job Scheduling. The job scheduling led to the concept known as the Multiprogramming. In multiprogramming, memory is divided into many partitions. Multiprogramming allows many pro- grammers to load their programs in the different partitions. Each programmer is made to believe his/her program is the only program running. Multipro- gramming was followed by Time-sharing concept. Here the CPU allocated a fixed time for each program. In the next cycle, the program that had been considered earlier was taken once again. This process continued until all the programs were executed. 1595.2 Major Features of the Operating System 5.2.1 Types As per the number of users, there are two types of the Operating Systems. They are 1.Single user Operating System. 2. Multi-user Operating System. Single user Operating System : At a time, only one user can operate the system. MS Disk Operating System is an example of single user Operat- ing System. Multi-user Operating System : More than one user can operate the same system simultaneously. The multi-user Operating System is based on the concept of time-sharing. Unix is an example of multi-user Operating Sys- tem. 5.2.2 Input/Output Application software does not allocate or de-allocate the stor- age area on the disk for different files belonging to various users. If the application software is allowed to allocate or de-allocate, two or more users may try to write on the same sector of disk, resulting in confu- sion. Even a single user may try to write in some sector, which may contain valuable information. In order to avoid such an awkward situa- tion, only the Operating System is empowered to make such an allo- cation or de-allocation. This arrangement safeguards the loss of data. Such safeguading of data is called Data Security. From the above discussion, one may come to the conclusion that the Operating System alone should be empowered to instruct the hardware to write data from memory onto a Pre-specified location on the disk. In fact Input/Output operation code of the Operating System constitute a sizeable code of the Operating System. The application program is not allowed to read data from the disk. Otherwise any user can access any type of sensitive information. There may not be any secrecy. 160For example, banks will have precarious existence. An application program can do all the operations with the exception of input/output operations. When the application program is translated into the ma- chine code, the request for reading or writing will not be translated into the machine code, instead a system call is given. (A set of ex- tended instructions providing an interface between the Operating System and the user programs, is called a System Call.) The Oper- ating System will then generate suitable input/output command to the hardware to replace this system call. You cannot fool the system by writing the I/O code in machine language. User code will not be entertained for input/output at any circumstance. This arrangement not only helps in protecting the data integrity, but also, it saves the user from writing a lot of code to execute I/O operations. Thus it saves from reinventing the wheel. It is enough, if the programmer concentrated in logical behaviour of the program. The Operating Sys- tem does the spadework for the arrival of application program and the Operating System which is in the back ground, when needed comes into forefront and does its work gracefully after that it relegates itself to the background. 5.2.3 Printer Ideally we can expect computers to create truly paperless soci- ety. Appropriate n etworking and Infrastructure must be provided for this. As of today computers consume a lot of papers. Usage of printers is ram- pant. How does the Operating System help for printing? The Operating System makes the programmer's life much easier as far as the printing work is concerned. Even a small document takes a few minutes to com- plete the printing work. Now for printing a document, the Operating Sys- tem first makes a temporary copy of the file and then hands over the con- trol back to the application. Spooling is a way of dealing with dedicated I/O devices in a multiprogramming system. To print a file, a process first gen- erates the entire file to be printed and puts in the spooling directory. (Di- rectory is a container for storing files on other sub-directories). Then the special process having permission to use printer's special file is allowed to print the files in the directory. If two users print two documents, in the absence of overlapping of the two documents. 161This is similar to overlapping of the signals from two radio stations. This unwanted behaviour is completely eliminated by spool- ing. Each document's print image will be written on to the disk at two different locations of the spool file. While printing, the printer is not allowed to print the original document; instead it is allowed to print the contents of spooler program. Multiprocessor systems have more than one CPU in close communi- cation with the others. In a tightly coupled system the processors share memory and a clock; communication usually takes place through the shared memory. 5.3 Most Desirable characters of the Operating System 5.3.1 User Interface The Operating System should concentrate on the user inter- face. The only way that you can see this world is through your eyes. Similarly the only way that you can interact with a computer is through the user interface. People may be attracted to the computer in the be- ginning. If the interface is not user-friendly, only persistent people may continue with their work with the computer. The other people slowly move away from the computer with the intention of not returning to the computer forever. One can judge, from the immense popularity of the GUI (Graphical User Interface) based interface, the importance of well designed well thought interface. The GUI is window based. The vivid colours attract children. Novices are attracted by the help pop up mes- sages. Icons give iterative usage of the particular application. Now Linux is also available as windows based Operating System. The user interface of the Operating System should be ap- pealing to the senses. The human brain is good in pattern recogni- tion. Human brain learns through association. All these factors should be taken into account, when user interface is improved. In addition to the above, the following points should also be considered when User Interface is designed. 1621) Interface should be designed in such a manner as to master the interface. As already stated, people are hard pressed for time. 2) The speed of response should play a vital role in designing the user interface. The speed of response is nothing but the time taken to execute a particular task. 3) The user interface should reduce the number of errors committed by the user. With little practice, the user should be in a position to avoid errors. 4) The user interface should be pleasing to the senses. Vivid colours, enchanting music may achieve this. 5) The user interface should enable the people to retain this expertise for a longer time. 6) The ultimate aim of any product is to satisfy the customer. The user interface should also satisfy the customer. Interface developers should also take the following consid- erations into account. Interfaces mainly should satisfy the end users. The other users such as programmers may work even in an unfriendly environment. The interface should not heavily burden the memory of users. Menus, minimal typing work will be an added advantage of the Operating System. 5.3.2 Memory Management The Operating System should provide memory management techniques also. Any error in the user program should not be allowed to spoil the entire memory. So the Operating System divides the main memory into user memory and reserved memory. If some errors creep into the user program then only user memory may be affected how- ever the reserved memory is always in an unaffected condition. User memory is divided into many partitions to accommodate various jobs. Therefore the number of jobs accommodated cannot ex- ceed the number of partitions. Naturally the size of the user program should be less than that of the available main memory. This is like cutting the feet to the size of the shoe (if the size of the shoe is inadequate).the Operating System provides virtual (imaginary) memory to include the entire program. 163Operating System should manage the devices also. It is not un- common for several processes to run simultaneously. In order to achieve successful operation, the processes should effectively communicate with each other. This interprocess communication is made possible by the Operating System. 5.3.3 Process management Process management undertakes the allocation of processors to one program. The Operating System controls the jobs submitted to the system (CPU). Several algorithms are used to allocate the job to the pro- cessor. Algorithm is a step-by-step method to solve a given problem. 1.FIFO. 2.SJF 3.Round Robin. 4.Based on Priority. FIFO (First In First Out) This algorithm is based on queuing. Suppose you are stand- ing in a queue to get your notebook corrected from your teacher. The student who stands first in the queue gets his/her notebook corrected first and leaves the queue. Then the next student in the queue gets it corrected and so on. This is the basic methodology of the FIFO algorithm. Now, let us deal with this FIFO a little more technically. The process (A process is basically a program in execution) that enters the queue first is executed first by the CPU, then the next and then the next and so on. The processes are executed in the order in which they enter the queue. 164SJF (Shortest Job First:) This algorithm is based on the size of the job. Take two jobs A and B. A = 5 kilo bytes B = 8 kilo bytes Kilo literally means 1000 but here kilo means 1024. A byte consists of eight bits. A bit can store either TRUE (1) or FALSE (0). First the job A will be assigned processor time after which B gets its turn. Round Robin Jobs are assigned processor time in a circular method. For ex- ample take three jobs A, B, C. First the job A is assigned to CPU then job B and after B job C and then again A,B and C and so on. Based On Priority In this method each job is assigned a Priority. The higher Priority job is awarded favorable treatment. Take two jobs A and B. Let the priority of A be 5 and priority B be 7. Job B is assigned to the processor before job A. The allocation of processors by process management is also known as the CPU Scheduling. The objectives of the CPU Scheduling should be to maximise (1) The CPU utilisation. (2) The number of jobs done in a unit time (throughput) and to minimise the time taken. before the execution of the job and to run the job. 165Let us consider e-mail, which allows to :- (1) represent the information electrically (2) carry information from source to destination (3) manage the flow of such information. The telecommunication industry provides all the above facilities. The information that may be sent by network may be voice, data, video, fax etc. Web camera unites the parents and their children who are away from each other. Now the size of LAN has grown. You can see a LAN with more than 1000 computers connected to it. Through telecommunication links, LAN can access remote computers. The size and complexity of the network grow day by day. It is not a mean achievement to manage them. The Operating System shoulders the burden (responsibility) of managing the nets. 5.3.4 Security Management The biggest challenge to the computer industry is to safe- guarding one's data from unauthorized people. The Operating System provides three levels of securities to the user. They are (1) File access level (2) System level (3) Network level In order to access the files created by other people, you should have the requisite permission. Permissions can either be granted by the creator of the file or by the administrator of the system. 166System level security is offered by the password in a multi-user environment. Both windows XP professional and Linux offer the password facility. Network security is an elusive one. People from all over the world try to provide such a security. All the above levels of security are provided only by the Operating System. 5.3.5 Fault tolerance The Operating Systems should be robust. When there is a fault, the Operating System should not crash, instead the Operating System have fault tolerance capabilities. 5.3.6 Application Base Operating System should provide a solid basis for running many popular applications. 5.3.7 Distributed Operating System If you want to make use of the Network, you must know the machine address and the variety of services provided by that machine. But Distributed Operating System ensures that the entire network behaves as a single computer. Getting access to the remote resources is similar to access to local resources. The user's job is executed in an idle machine and the result is communicated to the user machine. The user is under the illusion that everything is done only in his/her computer. In a distributed Operating System a user is not aware of multiplicity of machines.The future of the Operating System may be Distributed Operating System since all the computers become a part of one or other network. But the virus attacks discourage people to get connected to the net. From the above one can appreciate the importance of the Operating System. 167Summary Software is of two types. They are 1. System Software and 2. Application Software. Operating System is a system Software that comes under System Software. Operating System is an intermediary between the user and the hard- ware. There are 1.Single user Operating System and 2. Multi-user operating system. The I/O operations are tedious and they are always maintained by the Operating system. When Application programs want to access the I/O capabilities, they merely substitute with the system call. Direct Memory Access (DMA) mechanism allows transferring data to and from memory without the intervention of the CPU. Spooling is superior to buffer. Spooling takes care of the printing work with the printer. Multiprogramming gives the illusion that many programs run simulta- neously. 168The desirable characters of the Operating System are 1 2 3 4 5 6 7 8 9 User Interface Memory management Process management File management Networking Capabilities management Security Management Fault tolerance Application Base Distributed Operating System. Exercises I. Fill in the blanks 1. The ________,________ can access the hardware directly. 2. Operating System is the ________ between the user and com- puter hardware. 3 Operating System comes under ________ software. 4 The ________ ,________ is only means by which a user in- teracts with the computer. II. State whether the following are True or False 1. The primary goal of Operating System is to mainly optimize the memory management. 2. There are many types of the Operating Systems. 3. The higher priority jobs get executed faster than those with lower priorities. 4. In Distributed Operating System, the user should know the address of the accessed machine. 169III. Answer the following 1. 2. 3. 4. 5 6. 7. 8. Who will access the computer hardware directly? Define an OS. Explain the different roles taken by the OS. Explain the main functions of the operating system. Explain the process and memory managements. Explain the input / output managed by operating system. Write note on User Interface. List out advantages of the Distributed Operating System over the Network Operating System. 9. Name some of the required features of Operating System. 170CHAPTER 6 COMPUTER COMMUNICATIONS 6.1 Introduction Communication is the desire of man. When human voice became inadequate, ancient civilizations devised drum codes and smoke signals to send information to far off distances. These primitive methods have given way to sending messages through electronic pulses. A stand-alone computer communicates very efficiently by connecting it with other computers. Data in a computer is transmitted to another computer located across continents almost instantaneously using telephone, microwaves or radio links. The long distance communication link between a computer and a remote terminal was set up around 1965. Now networking has become a very important part of computing activity. 6.2 Network A large number of computers are interconnected by copper wire, fiber optic cable, microwave and infrared or through satellite. A system consisting of connected nodes made to share data, hardware and software is called a Computer Network. 6.3 Some Important Reasons for Networking (+ Sharing of resources: Primary goal of a computer network is to share resources. For example several PCs can be connected to a single expensive line printer. ', Sharing information: Information on a single computer can be accessed by other computers in the network. Duplication of data file on separate PCs can be avoided. 171"+ Communication: When several PCs are connected to each other, messages can be sent and received. From a remote location, a mobile salesman can relay important messages to the central office regarding orders. Relevant databases are updated and the business commitments are fulfilled. 6.4 Applications of Network The following are the areas where computer networks are employed. Electronic data interchange Tele-conferencing Cellular telephone Cable Television Financial services, marketing and sales Reservation of Airlines, trains, Theatres and buses Telemedicine ATM Internet banking Several educational institutions, businesses and other organizations have discovered the benefits of computer networks. Users can share data and programmes. They can co-operate on projects to maximize the usage of available expertise and talent. 6.5 Benefits of Network Effective handling of personal communications Allowing several users to access simultaneously Important programs and data: Making it easy for the users to keep all critical data on shared storage device and safeguard the data. Allowing people to share costly equipment. 172The computer communication should ensure safe, secure and reliable data transfer. Safe Secure : Reliable: The data received is the same as the data sent The data being transferred cannot be damaged either will fully or accidentally. Both the sender and the receiver knows the status of the data sent. Thus the sender knows whether the receiver got the correct data or not. 6.6 Types of Network The following are the general types of networks used today. Local Area Network (LAN) Metropolitan Area Network (MAN) Wide Area Network (WAN) A network connecting systems and devices inside a single building or buildings close to each other is called Local Area Network (LAN) (Fig.6.1). Generally LANs do not use the telephone network. They are connected either by wire or wireless. Wired connection may be using twisted pairs, coaxial cables or Fiber Optic cables. In a wireless LAN, connections may be using infrared or radio waves. Wireless networks are useful when computers are portable. However, wireless network communicates slowly than a wired network. Fig. 6.1 Local Area Network 173The number of Computers in the network is between two to several hundreds. LAN is generally used to share hardware, software and data. A computer sharing software package and hard disk is called a file server or network server. A Network that spans a geographical area covering a Metropolitan city is called Metropolitan Area Network (MAN) A WAN is typically two or more LANs connected together across a wide geographical area. The individual LANs separated by large distances may be connected by dedicated links, fiber- optic cables or satellite links. 6.7 Network Topology The network topology is the structure or layout of the communication channels that connects the various computers on the network. Each computer in the network is called a node. There are a number of factors that determine the topology suitable for a given situation. Some of the important consideration is the type of nodes, the expected performance, type of wiring (physical link) used and the cost. Network can be laid out in different ways. The five common topologies are star, ring, bus, hybrid and FDDI. Star Network : In a star network all computers and other communication devices are connected to a central hub. (Fig.6.2) Such as a file server or host computer usually by a Unshielded Twisted Pair (UTP) cables. 174Fig. 6.2 Star network Ring Network: In a ring network computers and other communication devices are connected in a continuous loop (Fig. 6.3). Electronic data are passed around the ring in one direction, with each node serving as a repeater until it reaches the right destination. There is no central host computer or server. Fig. 6.3 Ring Network 175Bus Network: In a bus network all communication devices are connected to a common cable called bus (Fig. 6.4). There is no central computer or server. The data transmission is bidirectional. Fig 6.4. Bus Network Hybrid Network: A hybrid network is a combination of the above three networks suited to the need. FDDI Network: A FDDI network (pronounced as fiddy short for Fiber Distributed Data Interface) is a high-speed network using fiber optic cable. It is used for high tech purposes such as electronic images, high-resolution graphics and digital video. The main disadvantage is its high cost. 6.8 Basic Elements in Networking All networks require the following three elements 1. Network services Network services are provided by numerous combinations of computer hardware and software. Depending upon the task, network services require data, input/output resources and processing power to accomplish their goal. 1762. Transmission media Transmission media is the pathway for contacting each computer with other. Transmission media include cables and wireless Technologies that allows networked devices to contact each other. This provides a message delivery path. 3. Protocols A protocol can be one rule or a set of rules and standards that allow different devices to hold conversations. 6.9 Common Network Services The following common network services are available. 6.9.1 File Services Those are the primary services offered by the computer networks. This improves the efficient storage and retrieval of computer data. The service function includes. (* File transfer -'/Rapidly move files from place to place regardless of file size, distance and Local operating system. *. File storage and data migration !,( Increasing amount of Computer data has caused the development of several storage devices. Network applications are well suited to control data storage activity on different storage systems. Some data becomes less used after certain time. For example higher secondary examination result posted on the web becomes less used after a week. Such data can be moved from one storage media (say hard disc of the computer) to another, less expensive media (say an optical disk) is called data migration. 177(% File update synchronization ,-% Network service keeps track of date and time of intermediate changes of a specific file. Using this information, it automatically updates all file locations with the latest version. '' File archiving !!% All organizations create duplicate copies of critical data and files in the storage device. This practice is called file archiving or file backup. In case of original file getting damaged, Computer Operator uses the Network to retrieve the duplicate file. File archiving becomes easier and safe when storage devices are connected in the Network. 6.9.2 Print services Network application that control manage access to printers and fax equipments. The print service function includes #& Provide multiple access (more than one user, use the network) .-% reduce the number of printers required for the organization. ./ Eliminates distance constraints +*! take a printout at a different location. -# Handle simultaneous requests ""! queue print jobs reducing the computer time. (, Share specialized equipments-Some printers are designed for specific use such as high-speed output, large size formals or colour prints. Specialised equipments may be costlier or may not be frequently used by the user, when numerous clients are using the network, printer use is optimized. '( Network fax service (.! Fax service is integrated in the network. The computer in the network sends the digital document image to any location. This reduces the time and paper handling. 1786.9.3 Message services Message services include storing, accessing and delivering text, binary, graphic digitized video and audio data. Unlike file services, message services deal actively with communication interactions between computer users applications, network applications or documents. 6.9.4 Application Services Application services are the network services that run software for network clients. They are different from file services because they allow computers to share processing power, not just share data. Data communication is the process of sending data electronically from one location to another. Linking one computer to another permits the power and resources of that computer to be tapped. It also makes possible the updating and sharing of data at different locations. 6.10 Co-ordinating Data Communication The device that coordinates the data transfer is called Network interface card (NIC). NIC is fixed in the computer and communication channel is connected to it. Ethernet, Arcnet and token ring are the examples for the NIC. Protocol specifies the procedures for establishing maintaining and terminating data transfer. In 1978, the International Standards organization proposed protocol known as open system interconnection (OSI). The OSI provided a network architecture with seven layers. Fig.6.5 gives the seven layers and the respective functions. This architecture helps to communicate between Network of dissimilar nodes and channels. 1796.11 Forms of Data Transmission Data is transmitted in two forms 1. Analog data transmission 2. Digital data transmission Analog data transmission is the transmission continuous waveform. The telephone system, for designed for analog data transmission. Analog sometimes modulated or encoded to represent binary of data in a instance, is signals are data. Digital data transmission is the widely used communication system in the world. The distinct electrical state of ,"%on()+ and !!(off#-- is represented by 1 and 0 respectively. Digital data transmission as shown in Fig.6.6 is faster and more efficient than analog. All computers understand and work only in digital forms 7 Application Purpose for communicating: e-mail, file transfer, client/server 0 1 0 1 0 1 0 1 6 Presentation Rules for data conversion 5 Session Starts, stops and governs Transmission order. 4 Transport Ensures delivery of Complete message 3 Fig 6.6. Digital Data Transmission Network Routes data to different networks 2 Data link Transmits data to Different networks 1 Physical Passes bits on to Connecting median Fig 6.5. Seven Layers of Protocols 1806.12 Modem Computers at different parts of the world are connected by telephone lines. The telephone converts the voice at one end into an electric signal that can flow through a telephone cable. The telephone at the receiving end converts this electric signal into voice. Hence the receiver could hear the voice. The process of converting sound or data into a signal that can flow through the telephone wire is called modulation. The reverse process is called demodulation. The telephone instrument contains the necessary circuit to perform these activities. The device that accomplishes modulation '"+ demodulation process is called a modem. It is known that the electrical and sound signals are analog - which continuously vary with time. The figure 6.7 shows the relationship of modem to communication link Fig . 6.7 Communication Using Modem Equipments (DTE) are connected through modem and Telephone line. The modems are the Data Circuit Terminating Equipments (DCE). DTE creates a digital signal and modulates using the modem. Then the signals relayed through an interface. The second modem at the receiving end demodulates into a form that the computer 181can accept. A modem that has extra functions such as automatic answering and dialing is called intelligent Modems. 6.13 Data Transmission Rate The speed at which data travel over a communication channel is called the communication rate. The rate at which the data are transferred is expressed in terms of bits per second (bps) 6.14 Transmission Mode When two computers are in communication, data transmission may occur in one of the three modes (Fig.6.8). Fig. 6.8 Transmission modes 6.14.1 Simplex mode In simplex mode, data can be transmitted in one direction as shown in the figure. The device using the simplex mode of transmission can either send or receive data, but it cannot do both. An example is the traditional television broadcast, in which the signal 182is sent from the transmitter to the TV. There is no return signal. In other words a TV cannot send a signal to the transmitter. 6.14.2 Half duplex mode In Half duplex mode data can be transmitted back and forth between two stations. But at any point of time data can go in any one direction only. This arrangement resembles traffic on a one- lane bridge. When traffic moves in one direction, traffic on the opposite direction is to wait and take their turn. The common example is the walky-talky, wherein one waits for his turn while the other talks. 6.14.3 Full duplex mode In full duplex mode a device can simultaneously send or receive data. This arrangement resembles traffic on a two-way bridge, traffic moving on both directions simultaneously. An example is two people on the telephone talking and listening simultaneously. Communication in full duplex mode is faster. Full duplex transmission is used in large computer systems. Products like &,-Microsoft Net Meeting."' supports such two way interaction 6.15 Internet Several networks, small and big all over the world, are connected together to form a Global network called the Internet. Today's Internet is a network of about 50 million or more computers spread across 200 countries. Anyone connected to the Internet can reach, communicate and access information from any other computer connected to it. Some of the Internet users are /# '( &- Students Faculty members Scientists 183'& $' Executives and Corporate members Government employees. The Internet protocol (IP) addressing system is used to keep track of the million of users. Each computer on net is called a host. The IP addressing system uses the letter addressing system and number addressing systems. 6.16 Communication Protocol Internet is a packet-switching network. Here is how packet- switching works: A sending computer breaks an electronic message into packets. The various packets are sent through a communication network-often by different routes, at different speeds and sandwiched in between packets from other messages. Once the packets arrive at the destination, the receiving computer reassembles the packets in proper sequence. The packet switching is suitable for data transmission. The software that is responsible for making the Internet function efficiently is TCP/IP. TCP/IP is made up of two components. TCP stands for transmission control protocol and IP stands for Internet Protocol. TCP breaks up the data to be sent into little packets. It guarantees that any data sent to the destination computer reaches intact. It makes the process appear as if one computer is directly connected to the other providing what appears to be a dedicated connection. IP is a set of conventions used to pass packets from one host to another. It is responsible for routing the packets to a desired destination IP address. 6.17 Who Governs The Internet ? The Internet as a whole does not have a single controller. But the Internet society, which is a voluntary membership organization, takes the responsibility to promote global information exchange through the Internet technology. Internet Corporation for Assigned Names and 184Numbers (ICANN) administers the domain name registration. It helps to avoid a name which is already registered. 6.18 Future of Internet The popularity of Internet is growing ever since its evolution 20 years ago. This will bring out )% ," -. "- New standard protocol International connections Consumer civilization Data sharing in research and Engineering 6.19 Uses of Internet The following are some of the popular Internet tools, used by the million of the users. World Wide Web Web is a multimedia portion of the Internet. It consists of an interconnection system of sites or servers all over the world that can store information in the multimedia form. The Multimedia sites include text, animated graph, voice and images. The World Wide Web is the most graphically inviting and easily navigable section of the Internet. It contains several millions of pages of information. Each page is called a web page. A group of related web pages linked together forms a web site. The first page of the website is called a Home page. The Home page usually contains information about the site and links to other pages on that site. The Fig.6.9 gives the home page of Indian Space Research Organization ( ISRO ). 185Fig. 6.9. Home Page of ISRO Every web page has a unique address called the Uniform Resource Locator or URL. The URL locates the pages on the Internet. An example of URL is http:// www.country-watch.com/India where http stands for Hypertext Transfer Protocol (HTTP). This protocol is meant for transferring the web files. The www portion of the address stands for &++world wide web+#, and the next part country- watch.com is the domain name. Generally, the domain name will be followed by directory path and the specific document address separated by slashes. Looking for information on the Internet is called surfing or browsing. To browse the Internet, a software called web browser is used. Web browser translates HTML documents of the website and allows to view it on the screen. Examples of web browsers are Internet Explorer and Netscape Navigator. The mouse pointer moves over a underlined or highlighted words and images 186change to a hand icon. This is called an hyperlink. This indicates the link to other sites. To go to one of the linked sites, just click the mouse on the hyperlink. E-mail - The World Wide Web is getting a lot of attention due to its main attraction of Electronic mail. Electronic mail is usually used to exchange messages and data files. Each user is assigned an electronic mail box. Using mail services, one can scan a list of messages that can be sent to anyone who has the proper email identification. The message sent to any one resides in the mailbox till it is opened. Many other features of standard mail delivery are implemented in email. Usenet News Groups: Electronic discussion groups. User network abbreviated as usenet is essentially a giant disbursed bulletin board. Electronic discussion groups that focus on specific topic forms, computer forums. Mailing list: Email based discussion groups combining E-mail, news groups and mailing lists send messages on a particular subject. Automatically messages reach the mailbox of that group. FTP: File Transfer Protocol, abbreviated as FTP is used for the net user for transferring files around the world. The transfer includes software, games, photos, maps, music and such other relevant materials. Telnet: Telnet is a protocol that allows the user to connect to a remote computer. This feature is used to communicate a microcomputer with mainframe. 6.20 Getting connected to Internet To use an Internet in the simplest way, we need &/ !* A Computer A Telephone line 187-) !% A Modem Internet Service Provided or ISP The ISPs are the companies which allows the user to use the Internet for a price. One has to register with the ISP for an Internet account. ISP provides the following: +/ $) $* .' User name - An unique name that identifies the user Password - A secret code that prevents other users from using your account E-mail address- Unique address that you can send or receive E-mails. Access telephone number - Internet users can use this number to connect to the service provider. Fig.6.10 shows dialog boxes on the computer screen wherein the user name (Govt. Higher Secondary School, Chennai -600 003 abbreviated as a ghssch3), a password (alpha numeric of word length 8 characters appearing as "',x%/!) and access telephone number are entered. By clicking on the dial button, the modem establishes a connection with the ISP. Fig.6.10. Dialogue Box for Connecting to the Internet 188There are two ways to look for the information on the Web. If the URL of the website is known, enter it on the address bar (Fig.6.11). If URL is not known, then #!.Search Engines!$! will help us to get the information. Search Engines are tools that allow the user to find specific document through key words or menu choices. Some of the popular Search engines are Yahoo, Lycos, AltaVista, Hotbot , Google and Askjeeves. Fig.6.11. Entering the URL Internet explorer helps to use the net more effectively with the navigation buttons (Fig.6.12) on the toolbar. 1 2 3 4 5 Fig.6.12. Navigation Buttons 1. Back button: This button helps to go back to the previous link. The small triangle adjacent to it displays a dropdown list of several recently used pages. Instead of pressing the back button several times, select a page from the list. 2. Forward button: This is a similar to the back button. One can jump forward by one page or several pages. 1893. Stop button: After clicking on a link, some times we may realize that the link is not necessary. The click stop button and move back without wasting time. 4. Refresh button: Sometimes a page may take longer time or may not load properly. Click on the refresh button, helps reload the page faster. 5. Home button: While following the hyperlink, it is very easy to get lost. The home button reverts to the home page of the website. 6.21 Popular uses of the web Research: The web provides research materials from libraries, research institutions, encyclopedia, magazines and newspapers. Some sample sites www.encarta.com the Internet Public Library site www.ipl.com and Library of Congress www.loc.gov. Chatting: Some websites proved chat rooms to interact with an individual or a group. Free-wares: Some sites provide free download of software's, tutorials and benchmarks. Education online: Educational institutions offer courses via the web. Student can attend and interact in a class from home using a computer. Online services: Online shopping, online booking for travels and entertainments managing investments are the upcoming areas of Internet that reaches every home. Job searches: The digital revolution is changing everything it touches and the job market is no exception. Several web sites are assisting people in finding internship, jobs and helps companies to fill job 190vacancies. There are sites relating to specific job and profession also. Some of these sites charge a fee for the services while others are free. 6.22 Intranet and Extranet Many organizations have Local Area Network that allows their computers to share files, data, printers and other resources. Sometimes these private network uses TCP / IP and other Internet standard protocols and hence they are called intranet. All the Internet services such as web pages, email, chat; usenet and FTP are provided on the intranet to serve the organization. Creating a web page on the intranet is simple because they use only Word- Processing Software One of the main consideration of the intranet is security. The sensitive company data available on the intranet is protected from the outside world. Taking intranet technology a few steps forward extranets are useful in the business world. Intranet connecting selected customers, suppliers and offices in addition to the internal personnel, is called extranet. By using extranet business organizations can save telephone charges. For example a can manufacturing company can extend their intranet to their dealers and customers for support and service. EXERCISES I. Fill in the blanks: 1. _is a typically two or more LANs connected together across a wide geographical area. 2. _network computers and other communication devices are connected in a continuous loop. 3. In a high speed network _are used. 1914. The device that coordinates the data transfer is called _,_ 5. The OSI provided a network architecture with _layers. 6. All computers understand and work only in _form. 7. _signals continuously vary with time. 8. Communication in _mode is faster. 9. _protocol is used to assist communication between a microcomputer and a mainframe. 10. _tools, that allow the Internet user to find specific document through keywords or menu choices. II. Answer the following (short answer) 1. What are the reasons for networking? 2. Mention a few areas, where computer networks are employed 3. What are the elements that a computer communication should ensure? 4. List the general types of networks used today. 5. Explain WAN. 6. How data is transmitted in different forms? 7. What are the transmission modes? 8. What is TCP? 9. What is the role of ICANN? 10. Explain URL. Are you a recent graduate currently looking for a job? If you are, then you must have experienced firsthand how challenging it is to write an application letter. Application Letter Sample for Fresh Graduates An application letter is one of the first few things that you need to prepare when applying for a job. Your application letter, together with your resume, are two of the most important documents you need in your bid to get a job and finally join the workforce. The term +-$application letter(,+ and #"$cover letter*-/ are often used interchangeably though both are different when it comes to purpose, content and length. To avoid confusion, we are strictly going to use the term )!/application letter-*- on this article to describe a document which main purpose is to describe your skills, market your abilities and summarizes your experiences. Your application letter serves as your formal (albeit non-personal) introduction with your potential employer. It contains your background, summarizes your knowledge and experiences and includes a few more details as to why you are qualified for the job. More importantly, your application letter expresses your intent to apply for a specific job within an organization, business or company. To help you effectively persuade your potential employer in considering your application, take some notes from these application letter samples. Application Letter Sample 1 (Print Copy) This application letter sample shows the correct format you should use when sending out your application letter in print form. If you plan on submitting your application letter via email, refer to the second application letter sample below. 22 H Venture St., Diliman, Quezon City Philippines April 17, 2015 Mr. Vincent Chua Hiring Manager Bank of the Philippines Islands (BPI) 12/F Ayala Life-FGU Center, Ayala Ave. Makati City 1226 Dear Mr. Chua, I am writing to express my interest for the position of Recruitment Assistant in your esteemed company. Having recently obtained my Bachelor's Degree in Business Administration major in Human Resource Development Management (BSBA-HRDM) in the Polytechnic University of the Philippines (PUP), I wish to bring my knowledge, skills and commitment to excellence to your company's innovative environment. As a Business Administration student, majoring in HR management, I've become equipped with the necessary knowledge that come with the position including manpower recruitment, workforce organization, personnel training and compensation as well as legal provisions and other labor concerns. My internship at San Miguel Corporation also afforded me with the crucial skills to work with some of the best professionals in the recruitment and human resources industry. Being a trainee has developed in me enthusiasm and a true passion for human resources and has subsequently convinced me that human resource management is my true calling. For additional details regarding my qualification and expertise, please review my attached resume. Thank you for taking the time to consider this application and I look forward to hearing from you. Sincerely, (signature) Jessica Cenadoza Application Letter Writing Tip: When intending to submit a hard copy of your application letter (and resume), make sure that you send it in a strict business letter format. Remember that your application letter not only speaks of your skills and accomplishments. It also shows how effectively you can communicate. Therefore, make it a point to impress your potential employer by writing a grammatically correct, perfectly spelled and properly punctuated application letter. Application Letter Sample 2 (Email Copy) As previously mentioned, the format of your application letter may differ depending on whether you are submitting a print copy or an email copy. The second application letter sample below is a copy tailored specifically for emails. Email Subject: Eric Tala, Marketing Associate Position Dear Ms. Casta$#eda, I would like to express my interest in applying for the position of Marketing Associate as was recently made available in your company. I believe that my degree in Business Administration (BSBA) major in Marketing from the Philippine School of Business Administration (PSBA) has prepared me for this position. As a student, I was equipped with the necessary knowledge and skills to help develop and drive effective marketing strategies. During my internship at Uniliver Philippines$'' Marketing Department, I learned how companies determine what product or service to sell, how to reach target demographics and how to respond to the demands of competitors. More importantly, I had the opportunity to work with seasoned professionals who taught me how to easily grasp complex marketing concepts and at certain times, how to roll with the punches in order to achieve various objectives. I have also acquainted myself with a wide range of skills that allow me to blend with the group or team's culture and to continuously strive to reach common goals amidst failures and setbacks. My active involvement in many academic and extracurricular activities has done so well in developing my communication and leadership skills, which are vital in finding success in the corporate world. With this application letter, I attach herewith my resume for your full consideration. Thank you for taking time to review my application and I am looking forward to your reply so that we can further discuss my application. Yours sincerely, Alvin C. Marfal 7194 Marcelo Ave Para+(aque City, Metro Manila 1700 Mobile: +63 929 XXX XXXX Application Letter Writing Tip: Make it a point to include the necessary email subject when sending your application letter via email. The email subject provided on this application letter sample follows the usual format (name, position you are applying for) though some recruiters require applicants to send their application letter (and resumes) with a specific email subject. Also, when sending your application letter thru email, remember to include your contact details on the email signature. By doing so, you will enable the recruiter or potential employer to respond to your application immediately. Remember that these samples are merely templates meant to serve as a basis in writing your own, personalized application letter. When writing an application letter, feel free to customize it according to your own personal preference. You don't want to have just another generic application letter because chances are the hiring manager has already read hundreds, or maybe thousands of it. You want your application letter to stand out and ultimately showcase why you are the best person for the job. NOTE: These application letter samples are for reference only. All information provided should be considered as fictional. Need more help writing your cover letter? Read "#&Analyzing and Avoiding Common Cover Letter Errors"&-. About JobStreet.com JobStreet.com is a leading online job board presently covering the employment markets in Malaysia, Singapore, Hong Kong, Thailand, the Philippines, Indonesia and Vietnam. JobStreet.com currently services over 230,000 corporate hirers and over 15 million jobseekers in its database. Chapter 1: COMPUTERS and PROGRAMS: Almost everyone has used a computer at one time or another. Perhaps you have played computer games or used a computer to write a paper or balance your checkbook. Computers are used to predict the weather, design airplanes, make movies, run businesses, perform financial transactions, and control factories. Have you ever stopped to wonder what exactly a computer is? How can one device perform so many different tasks? These basic questions are the starting point for learning about computers and computer programming. 1: The Universal Machine A modern computer might be defined as "a machine that stores and manipulates information under the con- trol of a changeable program." There are two key elements to this definition. The first is that computers are devices for manipulating information. This means that we can put information into a computer, and it can transform the information into new, useful forms, and then output or display the information for our interpretation. Computers are not the only machines that manipulate information. When you use a simple calculator to add up a column of numbers, you are entering information (the numbers) and the calculator is processing the information to compute a running sum which is then displayed. Another simple example is a gas pump. As you fill your tank, the pump uses certain inputs: the current price of gas per gallon and signals from a sensor that reads the rate of gas flowing into your car. The pump transforms this input into information about how much gas you took and how much money you owe. We would not consider either the calculator or the gas pump as full-fledged computers, although modern versions of these devices may actually contain embedded computers. They are different from computers in that they are built to perform a single, specific task. This is where the second part of our definition comes into the picture: computers operate under the control of a changeable program. What exactly does this mean? A computer program is a detailed, step-by-step set of instructions telling a computer exactly what to do. If we change the program, then the computer performs a different sequence of actions, and hence, performs a different task. It is this flexibility that allows your PC to be at one moment a word processor, at the next moment a financial planner, and later on, an arcade game. The machine stays the same, but the program controlling the machine changes. Every computer is just a machine for executing (carrying out) programs. There are many different kinds of computers. You might be familiar with Macintoshes and PCs, but there are literally thousands of other kinds of computers both real and theoretical. One of the remarkable discoveries of computer science is the realization that all of these different computers have the same power; with suitable programming, each computer can basically do all the things that any other computer can do. In this sense, the PC that you might have sitting on your desk is really a universal machine. It can do anything you want it to, provided you can describe the task to be accomplished in sufficient detail. Now that's a powerful machine! 12 CHAPTER 1. COMPUTERS AND PROGRAMS 1.2 Program Power You have already learned an important lesson of computing: Software (programs) rules the hardware (the physical machine). It is the software that determines what any computer can do. Without programs, comput- ers would just be expensive paperweights. The process of creating software is called programming, and that is the main focus of this book. Computer programming is a challenging activity. Good programming requires an ability to see the big picture while paying attention to minute detail. Not everyone has the talent to become a first-class program- mer, just as not everyone has the skills to be a professional athlete. However, virtually anyone can learn how to program computers. With some patience and effort on your part, this book will help you to become a programmer. There are lots of good reasons to learn programming. Programming is a fundamental part of computer science and is, therefore, important to anyone interested in becoming a computer professional. But others can also benefit from the experience. Computers have become a commonplace tool in our society. Understanding the strengths and limitations of this tool requires an understanding of programming. Non-programmers often feel they are slaves of their computers. Programmers, however, are truly the masters. If you want to become a more intelligent user of computers, then this book is for you. Programming can also be loads of fun. It is an intellectually engaging activity that allows people to express themselves through useful and sometimes remarkably beautiful creations. Believe it or not, many people actually write computer programs as a hobby. Programming also develops valuable problem-solving skills, especially the ability to analyze complex systems by reducing them to interactions of understandable subsystems. As you probably know, programmers are in great demand. More than a few liberal arts majors have turned a couple computer programming classes into a lucrative career option. Computers are so commonplace in the business world today that the ability to understand and program computers might just give you the edge over your competition, regardless of your occupation. 1.3 What is Computer Science? You might be surprised to learn that computer science is not the study of computers. A famous computer scientist named Edsgar Dijkstra once quipped that computers are to computer science what telescopes are to astronomy. The computer is an important tool in computer science, but it is not itself the object of study. Since a computer can carry out any process that we can describe, the real question is What processes can we describe? Put another way, the fundamental question of computer science is simply What can be computed? Computer scientists use numerous techniques of investigation to answer this question. The three main ones are design, analysis, and experimentation. One way to demonstrate that a particular problem can be solved is to actually design a solution. That is, we develop a step-by-step process for achieving the desired result. Computer scientists call this an algorithm. That's a fancy word that basically means ,#!recipe.+', The design of algorithms is one of the most important facets of computer science. In this book you will find techniques for designing and implementing algorithms. One weakness of design is that it can only answer the question What is computable? in the positive. If I can devise an algorithm, then the problem is solvable. However, failing to find an algorithm does not mean that a problem is unsolvable. It may mean that I'm just not smart enough, or I haven't hit upon the right idea yet. This is where analysis comes in. Analysis is the process of examining algorithms and problems mathematically. Computer scientists have shown that some seemingly simple problems are not solvable by any algorithm. Other problems are in- tractable. The algorithms that solve these problems take too long or require too much memory to be of practical value. Analysis of algorithms is an important part of computer science; throughout this book we will touch on some of the fundamental principles. Chapter 13 has examples of unsolvable and intractable problems. Some problems are too complex or ill-defined to lend themselves to analysis. In such cases, computer scientists rely on experimentation; they actually implement systems and then study the resulting behavior. Even when theoretical analysis is done, experimentation is often needed in order to verify and refine the1.4. HARDWARE BASICS 3 Output Devices CPU Input Devices Main Memory Secondary Memory Figure 1.1: Functional View of a Computer. analysis. For most problems, the bottom-line is whether a working, reliable system can be built. Often we require empirical testing of the system to determine that this bottom-line has been met. As you begin writing your own programs, you will get plenty of opportunities to observe your solutions in action. 1.4 Hardware Basics You don't have to know all the details of how a computer works to be a successful programmer, but under- standing the underlying principles will help you master the steps we go through to put our programs into action. It's a bit like driving a car. Knowing a little about internal combustion engines helps to explain why you have to do things like fill the gas tank, start the engine, step on the accelerator, etc. You could learn to drive by just memorizing what to do, but a little more knowledge makes the whole process much more understandable. Let's take a moment to %//look under the hood/,& of your computer. Although different computers can vary significantly in specific details, at a higher level all modern digital computers are remarkably similar. Figure 1.1 shows a functional view of a computer. The central processing unit (CPU) is the ($%brain,%) of the machine. This is where all the basic operations of the computer are carried out. The CPU can perform simple arithmetic operations like adding two numbers and can also do logical operations like testing to see if two numbers are equal. The memory stores programs and data. The CPU can only directly access information that is stored in main memory (called RAM for Random Access Memory). Main memory is fast, but it is also volatile. That is, when the power is turned off, the information in the memory is lost. Thus, there must also be some secondary memory that provides more permanent storage. In a modern personal computer, this is usually some sort of magnetic medium such as a hard disk (also called a hard drive) or floppy. Humans interact with the computer through input and output devices. You are probably familiar with common devices such as a keyboard, mouse, and monitor (video screen). Information from input devices is processed by the CPU and may be shuffled off to the main or secondary memory. Similarly, when information needs to be displayed, the CPU sends it to one or more output devices. So what happens when you fire up your favorite game or word processing program? First, the instructions that comprise the program are copied from the (more) permanent secondary memory into the main memory of the computer. Once the instructions are loaded, the CPU starts executing the program. Technically the CPU follows a process called the fetch execute cycle. The first instruction is retrieved from memory, decoded to figure out what it represents, and the appropriate action carried out. Then the next instruction is fetched, decoded and executed. The cycle continues, instruction after instruction. This is really all the computer does from the time that you turn it on until you turn it off again: fetch, decode, execute. It doesn't seem very exciting, does it? But the computer can execute this stream of simple instructions with blazing speed, zipping through millions of instructions each second. Put enough simple instructions together in just the right way, and the computer does amazing things.4 CHAPTER 1. COMPUTERS AND PROGRAMS 1.5 Programming Languages Remember that a program is just a sequence of instructions telling a computer what to do. Obviously, we need to provide those instructions in a language that a computer can understand. It would be nice if we could just tell a computer what to do using our native language, like they do in science fiction movies. (((%Computer, how long will it take to reach planet Alphalpha at maximum warp?.&/) Unfortunately, despite the continuing efforts of many top-flight computer scientists (including your author), designing a computer to understand human language is still an unsolved problem. Even if computers could understand us, human languages are not very well suited for describing complex algorithms. Natural language is fraught with ambiguity and imprecision. For example, if I say: &."I saw the man in the park with the telescope,#/( did I have the telescope, or did the man? And who was in the park? We understand each other most of the time only because all humans share a vast store of common knowledge and experience. Even then, miscommunication is commonplace. Computer scientists have gotten around this problem by designing notations for expressing computa- tions in an exact, and unambiguous way. These special notations are called programming languages. Every structure in a programming language has a precise form (its syntax) and a precise meaning (its semantics). A programming language is something like a code for writing down the instructions that a computer will follow. In fact, programmers often refer to their programs as computer code, and the process of writing an algorithm in a programming language is called coding. Python is one example of a programming language. It is the language that we will use throughout this book. You may have heard of some other languages, such as C++, Java, Perl, Scheme, or BASIC. Although these languages differ in many details, they all share the property of having well-defined, unambiguous syntax and semantics. All of the languages mentioned above are examples of high-level computer languages. Although they are precise, they are designed to be used and understood by humans. Strictly speaking, computer hardware can only understand very low-level language known as machine language. Suppose we want the computer to add two numbers. The instructions that the CPU actually carries out might be something like this. load the number from memory location 2001 into the CPU load the number from memory location 2002 into the CPU Add the two numbers in the CPU store the result into location 2003 This seems like a lot of work to add two numbers, doesn't it? Actually, it's even more complicated than this because the instructions and numbers are represented in binary notation (as sequences of 0s and 1s). In a high-level language like Python, the addition of two numbers can be expressed more naturally: c = a + b. That's a lot easier for us to understand, but we need some way to translate the high-level language into the machine language that the computer can execute. There are two ways to do this: a high-level language can either be compiled or interpreted. A compiler is a complex computer program that takes another program written in a high-level language and translates it into an equivalent program in the machine language of some computer. Figure 1.2 shows a block diagram of the compiling process. The high-level program is called source code, and the resulting machine code is a program that the computer can directly execute. The dashed line in the diagram represents the execution of the machine code. An interpreter is a program that simulates a computer that understands a high-level language. Rather than translating the source program into a machine language equivalent, the interpreter analyzes and executes the source code instruction by instruction as necessary. Figure 1.3 illustrates the process. The difference between interpreting and compiling is that compiling is a one-shot translation; once a program is compiled, it may be run over and over again without further need for the compiler or the source code. In the interpreted case, the interpreter and the source are needed every time the program runs. Compiled programs tend to be faster, since the translation is done once and for all, but interpreted languages lend themselves to a more flexible programming environment as programs can be developed and run interactively. The translation process highlights another advantage that high-level languages have over machine lan- guage: portability. The machine language of a computer is created by the designers of the particular CPU.1.6. THE MAGIC OF PYTHON Source Code (Program) 5 Compiler Machine Code Running Inputs Outputs Program Figure 1.2: Compiling a High-Level Language Source Code (Program) Computer Running an Outputs Interpreter Inputs Figure 1.3: Interpreting a High-Level Language. Each kind of computer has its own machine language. A program for a Pentium CPU won't run on a Mac- intosh that sports a PowerPC. On the other hand, a program written in a high-level language can be run on many different kinds of computers as long as there is a suitable compiler or interpreter (which is just another program). For example, if I design a new computer, I can also program a Python interpreter for it, and then any program written in Python can be run on my new computer, as is. 1.6 The Magic of Python Now that you have all the technical details, it's time to start having fun with Python. The ultimate goal is to make the computer do our bidding. To this end, we will write programs that control the computational processes inside the machine. You have already seen that there is no magic in this process, but in some ways programming feels like magic. The computational processes inside the computer are like magical spirits that we can harness for our work. Unfortunately, those spirits only understand a very arcane language that we do not know. What we need is a friendly Genie that can direct the spirits to fulfill our wishes. Our Genie is a Python interpreter. We can give instructions to the Python interpreter, and it directs the underlying spirits to carry out our demands. We communicate with the Genie through a special language of spells and incantations (i.e., Python). The best way to start learning about Python is to let our Genie out of the bottle and try some spells. You can start the Python interpreter in an interactive mode and type in some commands to see what happens. When you first start the interpreter program, you may see something like the following: Python 2.1 (#1, Jun 21 2001, 11:39:00) [GCC pgcc-2.91.66 19990314 (egcs-1.1.2 release)] on linux2 Type "copyright", "credits" or "license" for more information. >>> The /#" (./ is a Python prompt indicating that the Genie is waiting for us to give it a command. In programming languages, a complete command is called a statement. Here is a sample interaction with the Python interpreter.CHAPTER 1. COMPUTERS AND PROGRAMS 6 >>> print "Hello, World" Hello, World >>> print 2 + 3 5 >>> print "2 + 3 =", 2 + 3 2 + 3 = 5 Here I have tried out three examples using the Python print statement. The first statement asks Python to display the literal phrase Hello, World. Python responds on the next line by printing the phrase. The second print statement asks Python to print the sum of 2 and 3. The third print combines these two ideas. Python prints the part in quotes $*.2 + 3 =*)" followed by the result of adding 2 + 3, which is 5. This kind of interaction is a great way to try out new things in Python. Snippets of interactive sessions are sprinkled throughout this book. When you see the Python prompt !)% ,%" in an example, that should tip you off that an interactive session is being illustrated. It's a good idea to fire up Python and try the examples for yourself. Usually we want to move beyond snippets and execute an entire sequence of statements. Python lets us put a sequence of statements together to create a brand-new command called a function. Here is an example of creating a new function called hello. >>> def hello(): print "Hello" print "Computers are Fun" >>> The first line tells Python that we are defining a new function called hello. The following lines are indented to show that they are part of the hello function. The blank line (obtained by hitting the key twice) lets Python know that the definition is finished, and the interpreter responds with another prompt. Notice that the definition did not cause anything to happen. We have told Python what should happen when the hello function is used as a command; we haven't actually asked Python to perform it yet. A function is invoked by typing its name. Here's what happens when we use our hello command. >>> hello() Hello Computers are Fun >>> Do you see what this does? The two print statements from the hello function are executed in sequence. You may be wondering about the parentheses in the definition and use of hello. Commands can have changeable parts called parameters that are placed within the parentheses. Let's look at an example of a customized greeting using a parameter. First the definition: >>> def greet(person): print "Hello", person print "How are you?" Now we can use our customized greeting. >>> greet("John") Hello John How are you? >>> greet("Emily") Hello Emily How are you? >>>1.6. THE MAGIC OF PYTHON 7 Can you see what is happening here? When we use greet we can send different names to customize the result. We will discuss parameters in detail later on. For the time being, our functions will not use parameters, so the parentheses will be empty, but you still need to include them when defining and using functions. One problem with entering functions interactively at the Python prompt like this is that the definitions go away when we quit Python. If we want to use them again the next time, we have to type them all over again. Programs are usually created by typing definitions into a separate file called a module or script. This file is saved on a disk so that it can be used over and over again. A module file is just a text file, and you can create one using any program for editing text, like a notepad or word processor program (provided you save your program as a &,!plain text*+& file). A special type of program known as a programming environment simplifies the process. A programming environment is specifically designed to help programmers write programs and includes features such as automatic indenting, color high- lighting, and interactive development. The standard Python distribution includes a programming environment called Idle that you may use for working on the programs in this book. Let's illustrate the use of a module file by writing and running a complete program. Our program will illustrate a mathematical concept known as chaos. Here is the program as we would type it into Idle or some other editor and save in a module file: # File: chaos.py # A simple program illustrating chaotic behavior. def main(): print "This program illustrates a chaotic function" x = input("Enter a number between 0 and 1: ") for i in range(10): x = 3.9 * x * (1 - x) print x main() This file should be saved with with the name chaos.py. The .py extension indicates that this is a Python module. You can see that this particular example contains lines to define a new function called main. (Programs are often placed in a function called main.) The last line of the file is the command to invoke this function. Don't worry if you don't understand what main actually does; we will discuss it in the next section. The point here is that once we have a program in a module file, we can run it any time we want. This program can be run in a number of different ways that depend on the actual operating system and programming environment that you are using. If you are using a windowing system, you can run a Python program by (double-)clicking on the module file's icon. In a command-line situation, you might type a command like python chaos.py. If you are using Idle (or another programming environment) you can run a program by opening it in the editor and then selecting a command like import, run, or execute. One method that should always work is to start the Python interpreter and then import the file. Here is how that looks. >>> import chaos This program illustrates a chaotic function Enter a number between 0 and 1: .25 0.73125 0.76644140625 0.698135010439 0.82189581879 0.570894019197 0.955398748364 0.166186721954 0.540417912062 0.9686289303 0.118509010176 >>>8 CHAPTER 1. COMPUTERS AND PROGRAMS Typing the first line import chaos tells the Python interpreter to load the chaos module from the file chaos.py into main memory. Notice that I did not include the .py extension on the import line; Python assumes the module will have a .py extension. As Python imports the module file, each line executes. It's just as if we had typed them one-by-one at the interactive Python prompt. The def in the module causes Python to create the main function. When Python encounters the last line of the module, the main function is invoked, thus running our program. The running program asks the user to enter a number between 0 and 1 (in this case, I typed )(%.25*#+) and then prints out a series of 10 numbers. When you first import a module file in this way, Python creates a companion file with a .pyc extension. In this example, Python creates another file on the disk called chaos.pyc. This is an intermediate file used by the Python interpreter. Technically, Python uses a hybrid compiling/interpreting process. The Python source in the module file is compiled into more primitive instructions called byte code. This byte code (the .pyc) file is then interpreted. Having a .pyc file available makes importing a module faster the second time around. However, you may delete the byte code files if you wish to save disk space; Python will automatically re-create them as needed. A module only needs to be imported into a session once. After the module has been loaded, we can run the program again by asking Python to execute the main command. We do this by using a special dot notation. Typing chaos.main() tells Python to invoke the main function in the chaos module. Continuing with our example, here is how it looks when we rerun the program with 26 as the input. >>> chaos.main() Enter a number between 0 and 1: .26 0.75036 0.73054749456 0.767706625733 0.6954993339 0.825942040734 0.560670965721 0.960644232282 0.147446875935 0.490254549376 0.974629602149 >>> 1.7 Inside a Python Program The output from the chaos program may not look very exciting, but it illustrates a very interesting phe- nomenon known to physicists and mathematicians. Let's take a look at this program line by line and see what it does. Don't worry about understanding every detail right away; we will be returning to all of these ideas in the next chapter. The first two lines of the program start with the # character: # File: chaos.py # A simple program illustrating chaotic behavior. These lines are called comments. They are intended for human readers of the program and are ignored by Python. The Python interpreter always skips any text from the pound sign (#) through the end of a line. The next line of the program begins the definition of a function called main. def main(): Strictly speaking, it would not be necessary to create a main function. Since the lines of a module are executed as they are loaded, we could have written our program without this definition. That is, the module could have looked like this:1.7. INSIDE A PYTHON PROGRAM 9 # File: chaos.py # A simple program illustrating chaotic behavior. print "This program illustrates a chaotic function" x = input("Enter a number between 0 and 1: ") for i in range(10): x = 3.9 * x * (1 - x) print x This version is a bit shorter, but it is customary to place the instructions that comprise a program inside of a function called main. One immediate benefit of this approach was illustrated above; it allows us to (re)run the program by simply invoking chaos.main(). We don't have to reload the module from the file in order to run it again, which would be necessary in the main-less case. The first line inside of main is really the beginning of our program. print "This program illustrates a chaotic function" This line causes Python to print a message introducing the program when it runs. Take a look at the next line of the program. x = input("Enter a number between 0 and 1: ") Here x is an example of a variable. A variable is used to give a name to a value so that we can refer to it at other points in the program. The entire line is an input statement. When Python gets to this statement, it displays the quoted message Enter a number between 0 and 1: and then pauses, waiting for the user to type something on the keyboard and press the key. The value that the user types is then stored as the variable x. In the first example shown above, the user entered .25, which becomes the value of x. The next statement is an example of a loop. for i in range(10): A loop is a device that tells Python to do the same thing over and over again. This particular loop says to do something 10 times. The lines indented underneath the loop heading are the statements that are done 10 times. These form the body of the loop. x = 3.9 * x * (1 - x) print x The effect of the loop is exactly the same as if we had written the body of the loop 10 times: x = 3.9 print x x = 3.9 print x x = 3.9 print x x = 3.9 print x x = 3.9 print x x = 3.9 print x x = 3.9 print x x = 3.9 print x x = 3.9 * x * (1 - x) * x * (1 - x) * x * (1 - x) * x * (1 - x) * x * (1 - x) * x * (1 - x) * x * (1 - x) * x * (1 - x) * x * (1 - x)CHAPTER 1. COMPUTERS AND PROGRAMS 10 print x x = 3.9 * x * (1 - x) print x Obviously using the loop instead saves the programmer a lot of trouble. But what exactly do these statements do? The first one performs a calculation. x = 3.9 * x * (1 - x) This is called an assignment statement. The part on the right side of the = is a mathematical expression. Python uses the * character to indicate multiplication. Recall that the value of x is 0 25 (from the input statement). The computed value is 3 9 0 25 1 0 25 or 0 73125. Once the value on the righthand side is computed, it is stored back (or assigned) into the variable that appears on the lefthand side of the =, in this case x. The new value of x (0 73125) replaces the old value (0 25). The second line in the loop body is a type of statement we have encountered before, a print statement. print x When Python executes this statement the current value of x is displayed on the screen. So, the first number of output is 0.73125. Remember the loop executes 10 times. After printing the value of x, the two statements of the loop are executed again. x = 3.9 * x * (1 - x) print x Of course, now x has the value 0 73125, so the formula computes a new value of x as 3 9 0 73125 1 0 73125 , which is 0 76644140625. Can you see how the current value of x is used to compute a new value each time around the loop? That's where the numbers in the example run came from. You might try working through the steps of the program yourself for a different input value (say 0 5). Then run the program using Python and see how well you did impersonating a computer. 1.8 Chaos and Computers I said above that the chaos program illustrates an interesting phenomenon. What could be interesting about a screen full of numbers? If you try out the program for yourself, you'll find that, no matter what number you start with, the results are always similar: the program spits back 10 seemingly random numbers between 0 and 1. As the program runs, the value of x seems to jump around, well, chaotically. The function computed by this program has the general form: k x 1 x , where k in this case is 3.9. This is called a logistic function. It models certain kinds of unstable electronic circuits and is also sometimes used to predict population under limiting conditions. Repeated application of the logistic function can pro- duce chaos. Although our program has a well defined underlying behavior, the output seems unpredictable. An interesting property of chaotic functions is that very small differences in the initial value can lead to large differences in the result as the formula is repeatedly applied. You can see this in the chaos program by entering numbers that differ by only a small amount. Here is the output from a modified program that shows the results for initial values of 0 25 and 0 26 side by side. input 0.25 0.26 --------------------------- 0.731250 0.750360 0.766441 0.730547 0.698135 0.767707 0.821896 0.695499 0.570894 0.825942 0.955399 0.5606711.9. EXERCISES 0.166187 0.540418 0.968629 0.118509 11 0.960644 0.147447 0.490255 0.974630 With very similar starting values, the outputs stay similar for a few iterations, but then differ markedly. By about the fifth iteration, there no longer seems to be any relationship between the two models. These two features of our chaos program, apparent unpredictability and extreme sensitivity to initial values, are the hallmarks of chaotic behavior. Chaos has important implications for computer science. It turns out that many phenomena in the real world that we might like to model and predict with our computers exhibit just this kind of chaotic behavior. You may have heard of the so-called butterfly effect. Computer models that are used to simulate and predict weather patterns are so sensitive that the effect of a single butterfly flapping its wings in New Jersey might make the difference of whether or not rain is predicted in Peoria. It's very possible that even with perfect computer modeling, we might never be able to measure existing weather conditions accurately enough to predict weather more than a few days in advance. The measurements simply can't be precise enough to make the predictions accurate over a longer time frame. As you can see, this small program has a valuable lesson to teach users of computers. As amazing as computers are, the results that they give us are only as useful as the mathematical models on which the programs are based. Computers can give incorrect results because of errors in programs, but even correct programs may produce erroneous results if the models are wrong or the initial inputs are not accurate enough. 1.9 Exercises 1. Compare and contrast the following pairs of concepts from the chapter. (a) Hardware vs. Software (b) Algorithm vs. Program (c) Programming Language vs. Natural Language (d) High-Level Language vs. Machine Language (e) Interpreter vs. Compiler (f) Syntax vs. Semantics 2. List and explain in your own words the role of each of the five basic functional units of a computer depicted in Figure 1.1. 3. Write a detailed algorithm for making a peanut butter and jelly sandwich (or some other simple every- day activity). 4. As you will learn in a later chapter, many of the numbers stored in a computer are not exact values, but rather close approximations. For example, the value 0.1, might be stored as 0.10000000000000000555. Usually, such small differences are not a problem; however, given what you have learned about chaotic behavior in Chapter 1, you should realize the need for caution in certain situations. Can you think of examples where this might be a problem? Explain. 5. Trace through the Chaos program from Section 1.6 by hand using 0 15 as the input value. Show the sequence of output that results. 6. Enter and run the Chaos program from Section 1.6 using whatever Python implementation you have available. Try it out with various values of input to see that it functions as described in the chapter. 7. Modify the Chaos program from Section 1.6 using 2.0 in place of 3.9 as the multiplier in the logistic function. Your modified line of code should look like this:CHAPTER 1. COMPUTERS AND PROGRAMS 12 x = 2.0 * x * (1 - x) Run the program for various input values and compare the results to those obtained from the original program. Write a short paragraph describing any differences that you notice in the behavior of the two versions. 8. Modify the Chaos program from Section 1.6 so that it prints out 20 values instead of 10. 9. (Advanced) Modify the Chaos program so that it accepts two inputs and then prints a table with two columns similar to the one shown in Section 1.8. (Note: You will probably not be able to get the columns to line up as nicely as those in the example. Chapter 4 discusses how to print numbers with a fixed number of decimal places.)Chapter 2 Writing Simple Programs As you saw in the previous chapter, it is easy to run programs that have already been written. The hard part is actually coming up with the program in the first place. Computers are very literal, and they must be told what to do right down to the last detail. Writing large programs is a daunting task. It would be almost impossible without a systematic approach. 2.1 The Software Development Process The process of creating a program is often broken down into stages according to the information that is produced in each phase. In a nutshell, here's what you should do. Formulate Requirements Figure out exactly what the problem to be solved is. Try to understand as much as possible about it. Until you really know what the problem is, you cannot begin to solve it. Determine Specifications Describe exactly what your program will do. At this point, you should not worry about how your program will work, but rather with deciding exactly what it will accomplish. For simple programs this involves carefully describing what the inputs and outputs of the program will be and how they relate to each other. Create a Design Formulate the overall structure of the program. This is where the how of the program gets worked out. The main task is to design the algorithm(s) that will meet the specifications. Implement the Design Translate the design into a computer language and put it into the computer. In this book, we will be implementing our algorithms as Python programs. Test/Debug the Program Try out your program and see if it works as expected. If there are any errors (often called bugs), then you should go back and fix them. The process of locating and fixing errors is called debugging a program. Maintain the Program Continue developing the program in response to the needs of your users. Most programs are never really finished; they keep evolving over years of use. 2.2 Example Program: Temperature Converter Let's go through the steps of the software development process with a simple real-world example. Suzie Programmer has a problem. Suzie is an American computer science student spending a year studying in Europe. She has no problems with language, as she is fluent in many languages (including Python). Her problem is that she has a hard time figuring out the temperature in the morning so that she knows how to dress for the day. Suzie listens to the weather report each morning, but the temperatures are given in degrees Celsius, and she is used to Fahrenheit. 13CHAPTER 2. WRITING SIMPLE PROGRAMS 14 Fortunately, Suzie has an idea to solve the problem. Being a computer science major, she never goes anywhere without her laptop computer. She thinks it might be possible that a computer program could help her out. Suzie begins with the requirements of her problem. In this case, the problem is pretty clear: the radio an- nouncer gives temperatures in degrees Celsius, but Suzie only comprehends temperatures that are in degrees Fahrenheit. That's the crux of the problem. Next, Suzie considers the specifications of a program that might help her out. What should the input be? She decides that her program will allow her to type in the temperature in degrees Celsius. And the output? The program will display the temperature converted into degrees Fahrenheit. Now she needs to specify the exact relationship of the output to the input. Suzie does some quick figuring to derive the formula F 9 5 C 32 (Can you see how?). That seems an adequate specification. Notice that this describes one of many possible programs that could solve this problem. If Suzie had background in the field of Artificial Intelligence (AI), she might consider writing a program that would actually listen to the radio announcer to get the current temperature using speech recognition algorithms. For output, she might have the computer control a robot that goes to her closet and picks an appropriate outfit based on the converted temperature. This would be a much more ambitious project, to say the least! Certainly the robot program would also solve the problem identified in the requirements. The purpose of specification is to decide exactly what this particular program will do to solve a problem. Suzie knows better than to just dive in and start writing a program without first having a clear idea of what she is trying to build. Suzie is now ready to design an algorithm for her problem. She immediately realizes that this is a simple algorithm that follows a standard pattern: Input, Process, Output (IPO). Her program will prompt the user for some input information (the Celsius temperature), process it to convert to a Fahrenheit temperature, and then output the result by displaying it on the computer screen. Suzie could write her algorithm down in a computer language. However, the precision of writing it out formally tends to stifle the creative process of developing the algorithm. Instead, she writes her algorithm using pseudocode. Pseudocode is just precise English that describes what a program does. It is meant to communicate algorithms without all the extra mental overhead of getting the details right in any particular programming language. Here is Suzie's completed algorithm: Input the temperature in degrees Celsius (call it celsius) Calculate fahrenheit as 9/5 celsius + 32 Output fahrenheit The next step is to translate this design into a Python program. This is straightforward, as each line of the algorithm turns into a corresponding line of Python code. # convert.py # A program to convert Celsius temps to Fahrenheit # by: Suzie Programmer def main(): celsius = input("What is the Celsius temperature? ") fahrenheit = 9.0 / 5.0 * celsius + 32 print "The temperature is", fahrenheit, "degrees Fahrenheit." main() See if you can figure out what each line of this program does. Don't worry if some parts are a bit confusing. They will be discussed in detail in the next section. After completing her program, Suzie tests it to see how well it works. She uses some inputs for which she knows the correct answers. Here is the output from two of her tests. What is the Celsius temperature? 0 The temperature is 32.0 degrees fahrenheit.2.3. ELEMENTS OF PROGRAMS 15 What is the Celsius temperature? 100 The temperature is 212.0 degrees fahrenheit. You can see that Suzie used the values of 0 and 100 to test her program. It looks pretty good, and she is satisfied with her solution. Apparently, no debugging is necessary. 2.3 Elements of Programs Now that you know something about the programming process, you are almost ready to start writing programs on your own. Before doing that, though, you need a more complete grounding in the fundamentals of Python. The next few sections will discuss technical details that are essential to writing correct programs. This material can seem a bit tedious, but you will have to master these basics before plunging into more interesting waters. 2.3.1 Names You have already seen that names are an important part of programming. We give names to modules (e.g., convert) and to the functions within modules (e.g., main). Variables are used to give names to values (e.g., celsius and fahrenheit). Technically, all these names are called identifiers. Python has some rules about how identifiers are formed. Every identifier must begin with a letter or underscore (the )"& #)+ character) which may be followed by any sequence of letters, digits, or underscores. This implies that a single identifier cannot contain any spaces. According to these rules, all of the following are legal names in Python: x celsius spam spam2 SpamAndEggs Spam_and_Eggs Identifiers are case-sensitive, so spam, Spam, sPam, and SPAM are all different names to Python. For the most part, programmers are free to choose any name that conforms to these rules. Good programmers always try to choose names that describe the thing being named. One other important thing to be aware of is that some identifiers are part of Python itself. These names are called reserved words and cannot be used as ordinary identifiers. The complete list of Python reserved words is shown in Table 2.1. and assert break class continue def del elif else except exec finally for from global if import in is lambda not or pass print raise return try while yield Table 2.1: Python Reserved Words. 2.3.2 Expressions Programs manipulate data. The fragments of code that produce or calculate new data values are called expressions. So far our program examples have dealt mostly with numbers, so I'll use numeric data to illustrate expressions.16 CHAPTER 2. WRITING SIMPLE PROGRAMS The simplest kind of expression is a literal. A literal is used to indicate a specific value. In chaos.py you can find the numbers 3.9 and 1. The convert.py program contains 9.0, 5.0, and 32. These are all examples of numeric literals, and their meaning is obvious: 32 represents, well, 32. A simple identifier can also be an expression. We use identifiers as variables to give names to values. When an identifier appears in an expression, this value is retrieved to provide a result for the expression. Here is an interaction with the Python interpreter that illustrates the use of variables as expressions. >>> x = 5 >>> x 5 >>> print x 5 >>> print spam Traceback (innermost last): File "", line 1, in ? print spam NameError: spam >>> First the variable x is assigned the value 5 (using the numeric literal 5). The next line has Python evaluate the expression x. Python spits back 5, which is the value that was just assigned to x. Of course, we get the same result when we put x in a print statement. The last example shows what happens when we use a variable that has not been assigned a value. Python cannot find a value, so it reports a Name Error. This says that there is no value with that name. A variable must always be assigned a value before it can be used in an expression. More complex and interesting expressions can be constructed by combining simpler expressions with operators. For numbers, Python provides the normal set of mathematical operations: addition, subtraction, multiplication, division, and exponentiation. The corresponding Python operators are: +, -, *, /, and **. Here are some examples of complex expressions from chaos.py and convert.py 3.9 * x * (1 - x) 9.0 / 5.0 * celsius + 32 Spaces are irrelevant within an expression. The last expression could have been written 9.0/5.0*celsius+32 and the result would be exactly the same. Usually it's a good idea to place some spaces in expressions to make them easier to read. Python's mathematical operators obey the same rules of precedence and associativity that you learned in your math classes, including using parentheses to modify the order of evaluation. You should have lit- tle trouble constructing complex expressions in your own programs. Do keep in mind that only the round parentheses are allowed in expressions, but you can nest them if necessary to create expressions like this. ((x1 - x2) / 2*n) + (spam / k**3) If you are reading carefully, you may be curious why, in her temperature conversion program, Suzie Programmer chose to write 9.0/5.0 rather than 9/5. Both of these are legal expressions, but they give different results. This mystery will be discussed in Chapter 3. If you can't stand the wait, try them out for yourself and see if you can figure out what's going on. 2.4 Output Statements Now that you have the basic building blocks, identifier and expression, you are ready for a more complete description of various Python statements. You already know that you can display information on the screen using Python's print statement. But what exactly can be printed? Python, like all programming languages, has a precise set of rules for the syntax (form) and semantics (meaning) of each statement. Computer scientists have developed sophisticated notations called meta-languages for describing programming languages. In this book we will rely on a simple template notation to illustrate the syntax of statements. Here are the possible forms of the print statement:2.5. ASSIGNMENT STATEMENTS 17 print print print , , ..., print , , ..., , In a nutshell, these templates show that a print statement consists of the keyword print followed by zero or more expressions, which are separated by commas. The angle bracket notation ( "$% ) is used to indicate (..slots!&' that are filled in by other fragments of Python code. The name inside the brackets indicate what is missing; expr stands for an expression. The ellipses (-"#...../) indicate an indefinite series (of expressions, in this case). You don't actually type the dots. The fourth version shows that a print statement may be optionally ended with a comma. That is all there is to know about the syntax of print. As far as semantics, a print statement displays information in textual form. Any supplied expressions are evaluated left to right, and the resulting values are displayed on a single line of output in a left-to-right fashion. A single blank space character is placed between the displayed values. Normally, successive print statements will display on separate lines of the screen. A bare print (first version above) can be used to get a blank line of output. If a print statement ends with a comma (fourth version), a final space is appended to the line, but the output does not advance to the next line. Using this method, multiple print statements can be used to generate a single line of output. Putting it all together, this sequence of print statements print print print print print print 3+4 3, 4, 3 + 4 3, 4, 3+4 "The answer is", 3 + 4 produces this output 7 3 4 7 3 4 7 The answer is 7 That last print statement may be a bit confusing. According to the syntax templates above, print requires a sequence of expressions. That means "The answer is" must be an expression. In fact, it is an expression, but it doesn't produce a number. Instead, it produces another kind of data called a string. A sequence of characters enclosed in quotes is a string literal. Strings will be discussed in detail in a later chapter. For now, consider this a convenient way of labeling output. 2.5 Assignment Statements 2.5.1 Simple Assignment One of the most important kinds of statements in Python is the assignment statement. We've already seen a number of these in our previous examples. The basic assignment statement has this form: = Here variable is an identifier and expr is an expression. The semantics of the assignment is that the expression on the right side is evaluated to produce a value, which is then associated with the variable named on the left side. Here are some of the assignments we've already seen. x = 3.9 * x * (1 - x) fahrenheit = 9.0 / 5.0 * celsius + 32 x = 5CHAPTER 2. WRITING SIMPLE PROGRAMS 18 A variable can be assigned many times. It always retains the value of the most recent assignment. Here is an interactive Python session that demonstrates the point: >>> >>> 0 >>> >>> 7 >>> >>> 8 myVar = 0 myVar myVar = 7 myVar myVar = myVar + 1 myVar The last assignment statement shows how the current value of a variable can be used to update its value. In this case I simply added one to the previous value. The chaos.py program from Chapter 1 did something similar, though a bit more complex. Remember, the values of variables can change; that's why they're called variables. 2.5.2 Assigning Input The purpose of an input statement is to get some information from the user of a program and store it into a variable. Some programming languages have a special statement to do this. In Python, input is accomplished using an assignment statement combined with a special expression called input. This template shows the standard form. = input() Here prompt is an expression that serves to prompt the user for input; this is almost always a string literal (i.e., some text inside of quotation marks). When Python encounters an input expression, it evaluates the prompt and displays the result of the prompt on the screen. Python then pauses and waits for the user to type an expression and press the Enter key. The expression typed by the user is then evaluated to produce the result of the input. This sounds complicated, but most uses of input are straightforward. In our example programs, input statements are used to get numbers from the user. x = input("Please enter a number between 0 and 1: ") celsius = input("What is the Celsius temperature? ") If you are reading programs carefully, you probably noticed the blank space inside the quotes at the end of these prompts. I usually put a space at the end of a prompt so that the input that the user types does not start right next to the prompt. Putting a space in makes the interaction easier to read and understand. Although these two examples specifically prompt the user to enter a number, a number is just a numeric literal.("a simple Python expression. In fact, any valid expression would be just as acceptable. Consider the following interaction with the Python interpreter. >>> ans = input("Enter an expression: ") Enter an expression: 3 + 4 * 5 >>> print ans 23 >>> Here, when prompted to enter an expression, the user typed +(!3 + 4 * 5.'*# Python evaluated this expression and stored the value in the variable ans. When printed, we see that ans got the value 23 as expected. In a way, the input is like a delayed expression. The example interaction produced exactly the same result as if we had simply done ans = 3 + 4 * 5. The difference is that the expression was supplied at the time the statement was executed instead of being determined when the statement was written by the programmer. Thus, the user can supply formulas for a program to evaluate.2.5. ASSIGNMENT STATEMENTS 19 2.5.3 Simultaneous Assignment There is an alternative form of the assignment statement that allows us to calculate several values all at the same time. It looks like this: , , ..., = , , ..., This is called simultaneous assignment. Semantically, this tells Python to evaluate all the expressions on the right-hand side and then assign these values to the corresponding variables named on the left-hand side. Here's an example. sum, diff = x+y, x-y Here sum would get the sum of x and y and diff would get the difference. This form of assignment seems strange at first, but it can prove remarkably useful. Here's an example. Suppose you have two variables x and y and you want to swap the values. That is, you want the value currently stored in x to be in y and the value that is currently in y to be stored in x. At first, you might think this could be done with two simple assignments. x = y y = x This doesn't work. We can trace the execution of these statements step-by-step to see why. Suppose x and y start with the values 2 and 4. Let's examine the logic of the program to see how the variables change. The following sequence uses comments to describe what happens to the variables as these two statements are executed. # # x # y # variables initial values = y now = x final x 2 y 4 4 4 4 4 See how the first statement clobbers the original value of x by assigning to it the value of y? When we then assign x to y in the second step, we just end up with two copies of the original y value. One way to make the swap work is to introduce an additional variable that temporarily remembers the original value of x. temp = x x = y y = temp Let's walk-through this sequence to see how it works. # variables # initial values temp = x # x = y # y = temp # x 2 y 4 temp no value yet 2 4 2 4 4 2 4 2 2 As you can see from the final values of x and y, the swap was successful in this case. This sort of three-way shuffle is common in other programming languages. In Python, the simultaneous assignment statement offers an elegant alternative. Here is a simpler Python equivalent: x, y = y, xCHAPTER 2. WRITING SIMPLE PROGRAMS 20 Because the assignment is simultaneous, it avoids wiping out one of the original values. Simultaneous assignment can also be used to get multiple values from the user in a single input. Con- sider this program for averaging exam scores: # avg2.py # A simple program to average two exam scores # Illustrates use of multiple input def main(): print "This program computes the average of two exam scores." score1, score2 = input("Enter two scores separated by a comma: ") average = (score1 + score2) / 2.0 print "The average of the scores is:", average main() The program prompts for two scores separated by a comma. Suppose the user types 86, 92. The effect of the input statement is then the same as if we had done this assignment: score1, score2 = 86, 92 We have gotten a value for each of the variables in one fell swoop. This example used just two values, but it could be generalized to any number of inputs. Of course, we could have just gotten the input from the user using separate input statements. score1 = input("Enter the first score: ") score2 = input("Enter the second score: ") In some ways this may be better, as the separate prompts are more informative for the user. In this example the decision as to which approach to take is largely a matter of taste. Sometimes getting multiple values in a single input provides a more intuitive user interface, so it is nice technique to have in your toolkit. 2.6 Definite Loops You already know that programmers use loops to execute a sequence of statements several times in succession. The simplest kind of loop is called a definite loop. This is a loop that will execute a definite number of times. That is, at the point in the program when the loop begins, Python knows how many times to go around (or iterate) the body of the loop. For example, the Chaos program from Chapter 1 used a loop that always executed exactly ten times. for i in range(10): x = 3.9 * x * (1 - x) print x This particular loop pattern is called a counted loop, and it is built using a Python for statement. Before considering this example in detail, let's take a look at what for loops are all about. A Python for loop has this general form. for in : The body of the loop can be any sequence of Python statements. The start and end of the body is indicated by its indentation under the loop heading (the for in : part). The meaning of the for statement is a bit awkward to explain in words, but is very easy to understand, once you get the hang of it. The variable after the keyword for is called the loop index. It takes on2.6. DEFINITE LOOPS 21 each successive value in the sequence, and the statements in the body are executed once for each value. Usually, the sequence portion is a list of values. You can build a simple list by placing a sequence of expressions in square brackets. Some interactive examples help to illustrate the point: >>> for i in [0,1,2,3]: print i 0 1 2 3 >>> for odd in [1, 3, 5, 7, 9]: print odd * odd 1 9 25 49 81 You can see what is happening in these two examples. The body of the loop is executed using each successive value in the list. The length of the list determines the number of times the loop will execute. In the first example, the list contains the four values 0 through 3, and these successive values of i are simply printed. In the second example, odd takes on the values of the first five odd natural numbers, and the body of the loop prints the squares of these numbers. Now, let's go back to the example which began this section (from chaos.py) Look again at the loop heading: for i in range(10): Comparing this to the template for the for loop shows that the last portion, range(10) must be some kind of sequence. Let's see what the Python interpreter tells us. >>> range(10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Do you see what is happening here? The range function is a built-in Python command that simply produces a list of numbers. The loop using range(10) is exactly equivalent to one using a list of 10 numbers. for i in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]: In general, range() will produce a list of numbers that starts with 0 and goes up to, but not including, the value of . If you think about it, you will see that the value of the expression determines the number of items in the resulting list. In chaos.py we did not even care what values the loop index variable used (since the value of i was not referred to anywhere in the loop body). We just needed a list of length 10 to make the body execute 10 times. As I mentioned above, this pattern is called a counted loop, and it is a very common way to use definite loops. When you want to do something in your program a certain number of times, use a for loop with a suitable range. for in range(): The value of the expression determines how many times the loop executes. The name of the index variable doesn't really matter much; programmers often use i or j as the loop index variable for counted loops. Just be sure to use an identifier that you are not using for any other purpose. Otherwise you might accidentally wipe out a value that you will need later.CHAPTER 2. WRITING SIMPLE PROGRAMS 22 The interesting and useful thing about loops is the way that they alter the **%flow of control/,% in a program. Usually we think of computers as executing a series of instructions in strict sequence. Introducing a loop causes Python to go back and do some statements over and over again. Statements like the for loop are called control structures because they control the execution of other parts of the program. Some programmers find it helpful to think of control structures in terms of pictures called flowcharts. A flowchart is a diagram that uses boxes to represent different parts of a program and arrows between the boxes to show the sequence of events when the program is running. Figure 2.1 depicts the semantics of the for loop as a flowchart. more items in no yes = next item Figure 2.1: Flowchart of a for loop. If you are having trouble understanding the for loop, you might find it useful to study the flowchart. The diamond shape box in the flowchart represents a decision in the program. When Python gets the the loop heading, it checks to see if there are any (more) items left if the sequence. If the answer is yes, the value of the loop index variable is set to the next item in the sequence, and then the loop body is executed. Once the body is complete, the program goes back to the loop heading and checks for another value in the sequence. The loop quits when there are no more items, and the program moves on to the statements that come after the loop. 2.7 Example Program: Future Value Let's close the chapter with one more example of the programming process in action. We want to develop a program to determine the future value of an investment. Let's start with an analysis of the problem (require- ments). You know that money that is deposited in a bank account earns interest, and this interest accumulates as the years pass. How much will an account be worth ten years from now? Obviously it depends on how much money we start with (the principal) and how much interest the account earns. Given the principal and the interest rate, a program should be able to calculate the value of the investment ten years into the future. We continue by developing the exact specifications for the program. Recall, this is a description of what the program will do. What exactly should the inputs be? We need the user to enter the initial amount to invest, the principal. We will also need some indication of how much interest the account earns. This depends both on the interest rate and how often the interest is compounded. One simple way of handling this is to have the user enter an annualized percentage rate. Whatever the actual interest rate and compounding frequency, the annualized rate tells us how much the investment accrues in one year. If the annualized interest is 3%, then a2.7. EXAMPLE PROGRAM: FUTURE VALUE 23 $100 investment will grow to $103 in one year's time. How should the user represent an annualized rate of 3%? There are a number of reasonable choices. Let's assume the user supplies a decimal, so the rate would be entered as 0.03. This leads us to the following specification. Program Future Value Inputs principal The amount of money being invested in dollars. apr The annualized percentage rate expressed as a decimal fraction. Output The value of the investment 10 years into the future. Relationship Value after one year is given by principal 1 apr . This formula needs to be applied 10 times. Next we design an algorithm for the program. We'll use pseudocode, so that we can formulate our ideas without worrying about all the rules of Python. Given our specification, the algorithm seems straightforward. Print an introduction Input the amount of the principal (principal) Input the annualized percentage rate (apr) Repeat 10 times: principal = principal * (1 + apr) Output the value of principal Now that we've thought the problem all the way through to pseudocode, it's time to put our new Python knowledge to work and develop a program. Each line of the algorithm translates into a statement of Python. Print an introduction (print statement, Section 2.4) print "This program calculates the future value of a 10-year investment" Input the amount of the principal (input statement, Section 2.5.2) principal = input("Enter the initial principal: Input the annualized percentage rate (input statement, Section 2.5.2) apr = input("Enter the annualized interest rate: ") ") Repeat 10 times: (counted loop, Section 2.6) for i in range(10): Calculate principal = principal * (1 + apr) (simple assignment statement, Section 2.5.1) principal = principal * (1 + apr) Output the value of the principal (print statement, Section 2.4) print "The amount in 10 years is:", principal All of the statement types in this program have been discussed in detail in this chapter. If you have any questions, you should go back and review the relevant descriptions. Notice especially the counted loop pattern is used to apply the interest formula 10 times. That about wraps it up. Here is the completed program. # futval.py # A program to compute the value of an investment # carried 10 years into the futureCHAPTER 2. WRITING SIMPLE PROGRAMS 24 # by: John M. Zelle def main(): print "This program calculates the future value of a 10-year investment." principal = input("Enter the initial principal: ") apr = input("Enter the annualized interest rate: ") for i in range(10): principal = principal * (1 + apr) print "The amount in 10 years is:", principal main() Notice that I have added a few blank lines to separate the Input, Processing, and Output portions of the program. Strategically placed *&)white space.%. can help make your programs more readable. That's about it for this example; I leave the testing and debugging as an exercise for you. 2.8 Exercises 1. List and describe in your own words the six steps in the software development process. 2. Write out the chaos.py program (Section 1.6) and identify the parts of the program as follows: Circle each identifier. Underline each expression. Put a comment at the end of each line indicating the type of statement on that line (output, as- signment, input, loop, etc.) 3. A user-friendly program should print an introduction that tells the user what the program does. Modify the convert.py program (Section 2.2) to print an introduction. 4. Modify the avg2.py program (Section 2.5.3) to find the average of three exam scores. 5. Modify the futval.py program (Section 2.7) so that the number of years for the investment is also a user input. Make sure to change the final message to reflect the correct number of years. 6. Modify the convert.py program (Section 2.2) with a loop so that it executes 5 times before quitting (i.e., it converts 5 temperatures in a row). 7. Modify the convert.py program (Section 2.2) so that it computes and prints a table of Celsius temperatures and the Fahrenheit equivalents every 10 degrees from 0C to 100C. 8. Write a program that converts from Fahrenheit to Celsius. 9. Modify the futval.py program (Section 2.7) so that it computes the actual purchasing power of the investment, taking inflation into account. The yearly rate of inflation will be a second input. The adjustment is given by this formula: principal = principal/(1 + inflation)Chapter 3 Computing with Numbers When computers were first developed, they were seen primarily as number crunchers, and that is still an important application. As you have seen, problems that involve mathematical formulas are easy to translate into Python programs. This chapter takes a closer look at computations involving numeric calculations. 3.1 Numeric Data Types The information that is stored and manipulated by computer programs is generically referred to as data. Different kinds of data will be stored and manipulated in different ways. Consider this program that calculates the value of loose change. # change.py # A program to calculate the value of some change in dollars def main(): print "Change Counter" print print "Please enter the count of each coin type." quarters = input("Quarters: ") dimes = input("Dimes: ") nickels = input("Nickels: ") pennies = input("Pennies: ") total = quarters * .25 + dimes * .10 + nickels * .05 + pennies * .01 print print "The total value of your change is", total main() Here is an example of the output. Change Counter Please enter the count of each coin type. Quarters: 5 Dimes: 3 Nickels: 4 Pennies: 6 The total value of your change is 1.81 This program actually manipulates two different kinds of numbers. The values entered by the user (5, 3, 4, 6) are are whole numbers; they don't have any fractional part. The values of the coins (.25, .10, .05, .01) 25CHAPTER 3. COMPUTING WITH NUMBERS 26 are decimal fractions. Inside the computer, whole numbers and numbers that have fractional components are represented differently. Technically, we say that these are two different data types. The data type of an object determines what values it can have and what operations can be performed on it. Whole numbers are represented using the integer data type (int for short). Values of type int can be positive or negative whole numbers. Numbers that can have fractional parts are represented as floating point (or float) values. So how do we tell whether a number is an int or a float? A numeric literal that does not contain a decimal point produces an int value, while a literal that has a decimal point is represented by a float (even if the fractional part is 0). Python provides a special function called type that tells us the data type of any value. Here is an interaction with the Python interpreter showing the difference between int and float literals. >>> type(3) >>> type(3.14) >>> type(3.0) >>> myInt = -32 >>> type(myInt) >>> myFloat = 32.0 >>> type(myFloat) You may be wondering why there are two different data types for numbers. One reason has to do with program style. Values that represent counts can't be fractional; we can't have 3 12 quarters, for example. Using an int value tells the reader of a program that the value can't be a fraction. Another reason has to do with the efficiency of various operations. The underlying algorithms that perform computer arithmetic are simpler, and therefore faster, for ints than the more general algorithms required for float values. You should be warned that the float type only stores approximations. There is a limit to the precision, or accuracy, of the stored values. Since float values are not exact, while ints always are, your general rule of thumb should be: if you don't absolutely need fractional values, use an int. operator %)' % abs() operation addition subtraction multiplication division exponentiation remainder absolute value Table 3.1: Python built-in numeric operations. A value's data type determines what operations can be used on it. As we have seen, Python supports the usual mathematical operations on numbers. Table 3.1 summarizes these operations. Actually, this table is somewhat misleading since the two numeric data types have their own operations. When addition is performed on floats, the computer performs a floating point addition. Whereas, with ints, the computer performs an integer addition. Consider the following interaction with Python: >>> 3.0 + 4.0 7.0 >>> 3 + 4 73.2. USING THE MATH LIBRARY 27 >>> 3.0 * 4.0 12.0 >>> 3 * 4 12 >>> 10.0 / 3.0 3.33333333333 >>> 10 / 3 3 >>> 10 % 3 1 >>> abs(5) 5 >>> abs(-3.5) 3.5 Notice how operations on floats produce floats, and operations on ints produce ints. Most of the time, we don't have to worry about what type of operation is being performed; for example, integer addition produces pretty much the same result as floating point addition. However, in the case of division, the results are quite different. Integer division always produces an integer, discarding any fractional result. Think of integer division as /+!gozinta.,/. The expression, 10 / 3 produces 3 because three gozinta (goes into) ten three times (with a remainder of one). The third to last example shows the remainder operation (%) in action. The remainder of dividing 10 by 3 is 1. The last two examples illustrate taking the absolute value of an expression. You may recall from Chapter 2 that Suzie Programmer used the expression 9.0 / 5.0 in her tempera- ture conversion program rather than 9 / 5. Now you know why. The former gives the correct multiplier of 1 8, while the latter yields just 1, since 5 gozinta 9 just once. 3.2 Using the Math Library Besides the operations listed in Table 3.1, Python provides many other useful mathematical functions in a special math library. A library is just a module that contains some useful definitions. Our next program illustrates the use of this library to compute the roots of quadratic equations. A quadratic equation has the form ax 2 bx c 0. Such an equation has two solutions for the value of x given by the quadratic formula: b b 2 4ac x 2a Let's write a program that can find the solutions to a quadratic equation. The input to the program will be the values of the coefficients a, b, and c. The outputs are the two values given by the quadratic formula. Here's a program that does the job. .*# # quadratic.py # A program that computes the real roots of a quadratic equation. # Illustrates use of the math library. # Note: this program crashes if the equation has no real roots. import math # Makes the math library available. def main(): print "This program finds the real solutions to a quadratic" print a, b, c = input("Please enter the coefficients (a, b, c): ") discRoot = math.sqrt(b * b - 4 * a * c)CHAPTER 3. COMPUTING WITH NUMBERS 28 root1 = (-b + discRoot) / (2 * a) root2 = (-b - discRoot) / (2 * a) print print "The solutions are:", root1, root2 main() This program makes use of the square root function sqrt from the math library module. The line at the top of the program: import math tells Python that we are using the math module. Importing a module makes whatever is defined in it available to the program. To compute x, we use math.sqrt(x). You may recall this dot notation from Chapter 1. This tells Python to use the sqrt function that /&"lives)). in the math module. In the quadratic program we calculate b 2 4 ac with the line discRoot = math.sqrt(b * b - 4 * a * c) Here is how the program looks in action: This program finds the real solutions to a quadratic Please enter the coefficients (a, b, c): 3, 4, -2 The solutions are: 0.387425886723 -1.72075922006 This program is fine as long as the quadratics we try to solve have real solutions. However, some inputs will cause the program to crash. Here's another example run: This program finds the real solutions to a quadratic Please enter the coefficients (a, b, c): 1, 2, 3 Traceback (innermost last): File "", line 1, in ? File "quadratic.py", line 13, in ? discRoot = math.sqrt(b * b - 4 * a * c) OverflowError: math range error The problem here is that b 2 4 a c 0, and the sqrt function is unable to compute the square root of a negative number. Python prints a math range error. Right now, we don't have the tools to fix this problem, so we will just have to assume that the user gives us solvable equations. Actually, quadratic.py did not need to use the math library. We could have taken the square root using exponentiation **. (Can you see how?) Using math.sqrt is somewhat more efficient and allowed me to illustrate the use of the math library. In general, if your program requires a common mathematical function, the math library is the first place to look. Table 3.2 shows some of the other functions that are available in the math library. 3.3 Accumulating Results: Factorial Suppose you have a root beer sampler pack containing six different kinds of root beer. Drinking the various flavors in different orders might affect how good they taste. If you wanted to try out every possible ordering, how many different orders would there be? It turns out the answer is a surprisingly large number, 720. Do you know where this number comes from? The value 720 is the factorial of 6. In mathematics, factorial is often denoted with an exclamation ($/-!""#). The factorial of a whole number n 1 . This happens to be the number of distinct arrangements for n items. is defined as n! n n 1 n 2 Given six items, we compute 6! 6 5 4 3 2 1 720 possible arrangements.3.3. ACCUMULATING RESULTS: FACTORIAL Python pi e sin(x) cos(x) tan(x) asin(x) acos(x) atan(x) log(x) log10(x) exp(x) ceil(x) floor(x) Mathematics *! e sin x cos x tan x arcsinx arccosx arctanx ln x log 10 x e x x x 29 English An approximation of pi. An approximation of e. The sine of x. The cosine of x. The tangent of x. The inverse of sine x. The inverse of cosine x. The inverse of tangent x. The natural (base e) logarithm of x The common (base 10) logarithm of x. The exponential of x. The smallest whole number x The largest whole number x Table 3.2: Some math library functions. Let's write a program that will compute the factorial of a number entered by the user. The basic outline of our program follows an Input-Process-Output pattern. Input number to take factorial of, n Compute factorial of n, fact Output fact Obviously, the tricky part here is in the second step. How do we actually compute the factorial? Let's try one by hand to get an idea for the process. In computing the factorial of 6, we first multiply 6 5 30. Then we take that result and do another multipli- cation 30 4 120. This result is multiplied by three 120 3 360. Finally, this result is multiplied by 2 360 2 720. According to the definition, we then multiply this result by 1, but that won't change the final value of 720. Now let's try to think about the algorithm more generally. What is actually going on here? We are doing repeated multiplications, and as we go along, we keep track of the running product. This is a very common algorithmic pattern called an accumulator. We build up, or accumulate, a final value piece by piece. To accomplish this in a program, we will use an accumulator variable and a loop structure. The general pattern looks like this. Initialize the accumulator variable Loop until final result is reached update the value of accumulator variable Realizing this is the pattern that solves the factorial problem, we just need to fill in the details. We will be accumulating the factorial. Let's keep it in a variable called fact. Each time through the loop, we need to multiply fact by one of the factors n "!' n 1 .#$.!# )!&/'+ 1. It looks like we should use a for loop that iterates over this sequence of factors. For example, to compute the factorial of 6, we need a loop that works like this. fact = 1 for factor in [6,5,4,3,2,1]: fact = fact * factor Take a minute to trace through the execution of this loop and convince yourself that it works. When the loop body first executes, fact has the value 1 and factor is 6. So, the new value of fact is 1 6 6. The next time through the loop, factor will be 5, and fact is updated to 6 5 30. The pattern continues for each successive factor until the final result of 720 has been accumulated. The initial assignment of 1 to fact before the loop is essential to get the loop started. Each time through the loop body (including the first), the current value of fact is used to compute the next value. The30 CHAPTER 3. COMPUTING WITH NUMBERS initialization ensures that fact has a value on the very first iteration. Whenever you use the accumulator pattern, make sure you include the proper initialization. Forgetting it is a common mistake of beginning programmers. Of course, there are many other ways we could have written this loop. As you know from math class, multiplication is commutative and associative, so it really doesn't matter what order we do the multiplications in. We could just as easily go the other direction. You might also notice that including 1 in the list of factors is unnecessary, since multiplication by 1 does not change the result. Here is another version that computes the same result. fact = 1 for factor in [2,3,4,5,6]: fact = fact * factor Unfortunately, neither of these loops solves the original problem. We have hand-coded the list of factors to compute the factorial of six. What we really want is a program that can compute the factorial of any given input n. We need some way to generate an appropriate list from the value of n. Luckily, this is quite easy to do using the Python range function. Recall that range(n) produces a list of numbers starting with 0 and continuing up to, but not including, n. There are other variations of range that can be used to produce different sequences. With two parameters, range(start,n) produces a sequence that starts with the value start and continues up to, but not including, n. A third version range(start, n, step) is like the two parameter version, except that it uses step as the increment between numbers. Here are some examples. >>> range(10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> range(5,10) [5, 6, 7, 8, 9] >>> range(5, 10, 3) [5, 8] Given our input value n we have a couple of different range commands that produce an appropriate list of factors for computing the factorial of n. To generate them from smallest to largest (a la our second loop), we could use range(2,n+1). Notice how I used n+1 as the second parameter, since the range will go up to, but not including this value. We need the +1 to make sure that n itself is included as the last factor. Another possibility is to generate the factors in the other direction (a la our first loop) using the three- parameter version of range and a negative step to cause the counting to go backwards: range(n,1,-1). This one produces a list starting with n and counting down (step -1) to, but not including 1. Here then is one possible version of the factorial program. # factorial.py # Program to compute the factorial of a number # Illustrates for loop with an accumulator def main(): n = input("Please enter a whole number: ") fact = 1 for factor in range(n,1,-1): fact = fact * factor print "The factorial of", n, "is", fact main() Of course there are numerous other ways this program could have been written. I have already mentioned changing the order of factors. Another possibility is to initialize fact to n and then use factors starting at n 1 (as long as n 0). You might try out some of these variations and see which you like best.3.4. THE LIMITS OF INT 31 3.4 The Limits of Int So far, I have talked about numeric data types as representations of familiar numbers such as integers and decimal fractions. It is important to keep in mind, however, that these numeric types are just representations, and they do not always behave exactly like the numbers that they represent. We can see an example of this as we test out our new factorial program. >>> import factorial Please enter a whole number: 6 The factorial of 6 is 720 >>> factorial.main() Please enter a whole number: 10 The factorial of 10 is 3628800 >>> factorial.main() Please enter a whole number: 13 Traceback (innermost last): File "", line 1, in ? File "factorial.py", line 9, in main fact = fact * factor OverflowError: integer multiplication Everything seems fine until we try to compute the factorial of 13. When computing 13! the program prints out an OverflowError message. What is going on here? The problem is that this program is representing whole numbers using Python's int data type. Unfortu- nately, ints are not exactly like mathematical integers. There are infinitely many integers, but only a finite range of ints. Inside the computer, ints are stored in a fixed-sized binary representation. To make sense of this, we need to look at what's going on at the hardware level. Computer memory is composed of electrical ))+switches,."+ each of which can be in one of two possible states, basically on or off. Each switch represents a binary digit or bit of information. One bit can encode two possibilities, usually represented with the numerals 0 (for off) and 1 (for on). A sequence of bits can be used to represent more possibilities. With two bits, we can represent four things. bit 2 0 0 1 1 bit 1 0 1 0 1 Three bits allow us to represent eight different values by adding a zero or one to each of the four two-bit patterns. bit 3 bit 2 bit 1 0 0 0 0 0 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1 1 1 You can see the pattern here. Each extra bit doubles the number of distinct patterns. In general, n bits can represent 2 n different values. The number of bits that a particular computer uses to represent an int depends on the design of the CPU. Typical PCs today use 32 bits. That means there are 2 32 possible values. These values are centered at 0 toCHAPTER 3. COMPUTING WITH NUMBERS 32 32 represent a range of positive and negative integers. Now 2 2 2 31 . So, the range of integers that can be 31 31 represented in a 32 bit int value is 2 2 1. The reason for the 1 on the high end is to account for the representation of 0 in the top half of the range. Let's try out some expressions in Python to test this analysis. Remember that ** is the Python exponen- tiation operator. >>> 2 ** 30 1073741824 >>> 2 ** 31 Traceback (innermost last): File "", line 1, in ? OverflowError: integer pow() Python can calculate 2 30 , but /'/blows up.$( trying to compute 2 31 . You can see that the overflow happens somewhere between the 30th and 31st power of two. That is consistent with our analysis that the largest int is 2 31 1. Suppose we try to display the largest int. >>> 2 ** 31 - 1 Traceback (innermost last): File "", line 1, in ? OverflowError: integer pow() Our first try didn't work. Can you see why? Python evaluates this expression by first trying to calculate 2 ** 31. That calculation produces the error before Python has a chance to subtract one. We need to be a little cleverer and sneak up on the value from underneath. We can use the fact that 2 31 2 30 2 30 . Strategically subtracting one from each side gives us 2 31 1 2 30 1 2 30 . By subtracting one in the middle of the computation, we can ensure that the intermediate value never gets bigger than the final result. Here's what Python says: >>> 2 ** 30 - 1 + 2 ** 30 2147483647 By the way, this expression illustrates another way that Python ints differ from the integers that they represent. In normal arithmetic, there is no difference between 2 31 1 and 2 30 1 2 30 . They both represent the same value. In computer arithmetic, however, one is computable and the other is not! Representations of numbers do not always obey all the properties that we take for granted with numbers. Now that we have a numeric value, we can directly test our conjecture that this is the largest int. >>> 2147483647 2147483647 >>> 2147483648 OverflowError: integer literal too large There you have it. The largest int that can be represented in 32 bits is 2 !." 147 !') 483 .%+ 647. Now you know exactly why our program for factorial can't compute 13!. This value is larger than the limit of 2 "#+ 147 ,,$ 483 "#" 647. Naturally, the next step is to figure out a way around this limitation. 3.5 Handling Large Numbers: Long Ints As long as our factorial program relies on the int data type, we will not be able to find the factorial of larger numbers. We need to use another numeric type. You might first think of using a float instead. This does not really solve our problem. Here is an example run of a modified factorial program that initializes fact to the float 1 0.3.5. HANDLING LARGE NUMBERS: LONG INTS 33 Please enter a whole number. 15 The factorial of 15 is 1.307674368e+12 We do not get an overflow error, but we also do not get an exact answer. A very large (or very small) floating point value is printed out using exponential, or scientific, notation. The e+12 at the end means that the result is equal to 1 307674368 10 12 . You can think of the +12 at the end as a marker that shows where the decimal point should be placed. In this case, it must move 12 places to the right to get the actual value. However, there are only 9 digits to the right of the decimal, so we have %,"lost/"# the last three digits. Remember, floats are approximations. Using a float allows us to represent a much larger range of values, but the amount of precision is still fixed. In fact, a computer stores floating point numbers as a pair of fixed- length (binary) integers. One integer represents the string of digits in the value, and the second represents the exponent value that keeps track of where the whole part ends and the fractional part begins. Fortunately, Python provides a better solution for large, exact values in the form of a third numeric type long int. A long int is not a fixed size, but expands to accommodate whatever value it holds. The only limit is the amount of memory the computer has available to it. To get a long int, you put an "!!L!', suffix on a numeric literal. So, the literal 5 is an int representation of the number five, but 5L is a long int representation of the number five. Of course, for a number this small, there is no reason to use a long int. However, using a long int causes Python to use long int operations, and our value can grow to any size. Here are some examples that illustrate: >>> 2L 2L >>> 2L ** 31 2147483648L >>> type(100L) >>> 10000000000000000000000000000000000000L + 25 10000000000000000000000000000000000025L Notice how calculations involving a long int produce a long int result. Using long ints allows us to compute with really large numbers. We can modify our factorial program to use long int by simply initializing fact to 1L. Since we start with a long int, each successive multiplication will produce another long int. # factorial2.py def main(): n = input("Please enter a whole number: ") fact = 1L # Use a long int here for factor in range(n,0,-1): fact = fact * factor print "The factorial of", n, "is", fact Now we can take the factorial of arbitrarily large inputs. >>> import factorial2 Please enter a whole number: 13 The factorial of 13 is 6227020800 >>> factorial2.main() Please enter a whole number: 100 The factorial of 100 is 933262154439441526816992388562667004907159682 643816214685929638952175999932299156089414639761565182862536979208272 2375825118521091686400000000000000000000000034 CHAPTER 3. COMPUTING WITH NUMBERS If you have an older version of Python (prior to version 2.0), these answers will be printed with an )$,L+&. appended. This is Python's way of saying that the number is a long int. In newer versions, this artifact is automatically removed by the print statement. In the next chapter, you will learn how you could take care of getting the ')"L""% out yourself. Now we have a factorial program that can compute interestingly large results. Long ints are pretty cool; you might be tempted to use them all the time. The down-side of using long ints is that these representations are less efficient than the plain int data type. The operations needed to do arithmetic on ints is built into the CPU of the computer. When doing operations on long ints, Python has to employ algorithms that simulate long arithmetic using the computer's built-in fixed-length operations. As a result, long int arithmetic is much slower than int arithmetic. Unless you need very large values, ints are preferred. 3.6 Type Conversions Sometimes values of one data type need to be converted into another. You have seen that combining an int with an int produces an int, and combining a float with a float creates another float. But what happens if we write an expression that mixes an int with a float? For example, what should the value of x be after this assignment statement? x = 5.0 / 2 If this is floating point division, then the result should be the float value 2 5. If integer division is performed, the result is 2. Before reading ahead for the answer, take a minute to consider how you think Python should handle this situation. In order to make sense of the expression 5.0 / 2, Python must either change 5.0 to 5 and perform integer division or convert 2 to 2.0 and perform floating point division. In general, converting a float to an int is a dangerous step, because some information (the fractional part) will be lost. On the other hand, an int can be safely turned into a float just by adding a fractional part of 0. So, in mixed-typed expressions, Python will automatically convert ints to floats and perform floating point operations to produce a float result. Sometimes we may want to perform a type conversion ourselves. This is called an explicit type conver- sion. For example, suppose we are writing a program that finds the average of some numbers. Our program would first sum up the numbers and then divide by n, the count of how many numbers there are. The line of code to compute the average might look like this. average = sum / n Unfortunately, this line may not always produce the result we intend. Consider a specific example. The numbers to be averaged are the ints 4, 5, 6, 7. The sum variable will hold 22, also an int, and dividing by 4 gives the answer 5, not 5.5. Remember, an int divided by an int always produces an int. To solve this problem, we need to tell Python to convert one of the operands to a floating point value. average = float(sum) / n The float() function converts an int into a float. We only need to convert the numerator, because this produces a mixed-type expression, and Python will automatically convert the denominator. Notice that putting the float() around the entire expression would not work. average = float(sum/n) In this form, sum and n could both be ints causing Python to perform an integer division and then convert the resulting quotient to a float. Of course, this float would always end in 0, since it is being converted from an int. That is not what we want. Python also provides int() and long() functions that can be used to convert numbers into ints and longs, respectively. Here are a few examples.3.7. EXERCISES >>> 4 >>> 3 >>> 3L >>> 3.0 >>> 3 >>> 3 35 int(4.5) int(3.9) long(3.9) float(int(3.3)) int(float(3.3)) int(float(3)) As you can see, converting to an int or long int simply discards the fractional part of a float; the value is truncated, not rounded. If you want a rounded result, you can add 0.5 to the value before using int(), assuming the value is positive. A more general way of rounding off numbers is to use the built-in round function which rounds a float off to the nearest whole value. >>> round(3.14) 3.0 >>> round(-3.14) -3.0 >>> round(3.5) 4.0 >>> round(-3.5) -4.0 >>> int(round(-3.14)) -3 Notice that round returns a float. The last example shows how the result can then be turned into an int value, if necessary, by using int(). 3.7 Exercises 1. Show the result of evaluating each expression. Be sure that the value is in the proper form to indicate its type (int, long int, or float). If the expression is illegal, explain why. (a) 4.0 / 10.0 + 3.5 * 2 (b) 10 % 4 + 6 / 2 (c) abs(4 - 20 / 3) ** 3 (d) sqrt(4.5 - 5.0) + 7 * 3 (e) 3 * 10 / 3 + 10 % 3 (f) 3L ** 3 2. Translate each of the following mathematical expressions into an equivalent Python expression. You may assume that the math library has been imported (via import math). (a) 3 4 5 (b) (c) n n 1 2 4 #& r 2 2 (d) (e) r cos a y2 y1 x2 x1 2 r sinaCHAPTER 3. COMPUTING WITH NUMBERS 36 3. Show the list of numbers that would be generated by each of the following range expressions. (a) range(5) (b) range(3, 10) (c) range(4, 13, 3) (d) range(15, 5, -2) (e) range(5, 3) 4. Show the output that would be generated by each of the following program fragments. (a) for i in range(1, 11): print i*i (b) for i in [1,3,5,7,9]: print i, ":", i**3 print i (c) x = 2 y = 10 for j in range(0, y, x): print j, print x + y print "done" (d) ans = 0 for i in range(1, 11): ans = ans + i*i print i print ans 5. Write a program to calculate the volume and surface area of a sphere from its radius, given as input. Here are some formulas that might be useful: V 4 3 ($ r 3 A 4 ,) r 2 6. Write a program that calculates the cost per square inch of a circular pizza, given its diameter and price. A -. r 2 7. Write a program that determines the molecular weight of a hydrocarbon based on the number of hy- drogen, carbon, and oxygen atoms. You should use the following weights: Atom H C O Weight (grams / mole) 1.0079 12.011 15.9994 8. Write a program that determines the distance to a lighting strike based on the time elapsed between the flash and the sound of thunder. The speed of sound is approximately 1100 ft/sec and 1 mile is 5280 ft. 9. The Konditorei coffee shop sells coffee at $10.50 a pound plus the cost of shipping. Each order ships for $0.86 per pound + $1.50 fixed cost for overhead. Write a program that calculates the cost of an order. 10. Two points in a plane are specified using the coordinates (x1,y1) and (x2,y2). Write a program that y1 calculates the slope of a line through two (non-vertical) points entered by the user. m y2 x2 x1 11. Write a program that accepts two points (see previous problem) and determines the distance between x2 x1 2 y2 y1 2 them. d3.7. EXERCISES 37 12. The Gregorian Epact is the number of days between Jan. 1st and the previous 1st quarter moon phase. This value is used to figure out the date of Easter. It is calculated by these formulas (using int arith- 8 C 4 C 8C 13 25 11 year%19 %30 Write a program metic): C year 100 epact that prompts the user for a 4-digit year and then outputs the value of the epact. 13. Write a program to calculate the area of a triangle given the length of its three sides a, b, and c. s a 2 b c A s s a s b s c 14. Write a program to determine the length of a ladder required to reach a given height when leaned against a house. The height and angle of the ladder are given as inputs. len sin height angle 15. Write a program to find the sum of the first n natural numbers, where the value of n is provided by the user. 16. Write a program to find the sum of the squares for the first n natural numbers. 17. Write a program to sum a series of numbers entered by the user. The program should first prompt the user for how many numbers are to be summed. It should then input each of the numbers and print a total sum. 18. Write a program that finds the average of a series of numbers entered by the user. As in the previous problem, the program will first ask the user how many numbers there are. Note: the average should always be a float, even if the user inputs are all ints. 19. Write a program that approximates the value of "- by summing the terms of this series: 4 1 4 3 4 5 4 7 4 9 4 11 The program should prompt the user for n, the number of terms to sum and then output the sum of the first n terms of this series. 20. A Fibonacci sequence is a sequence of numbers where each successive number is the sum of the previous two. The classic Fibonacci sequence begins: 1, 1, 2, 3, 5, 8, 13, . Write a program that computes the nth Fibonacci number where n is a value input by the user. For example, if n = 6, then the result is 8. Note: Fibonacci numbers grow very rapidly; your program should be able to handle very large numbers. 21. You have seen that the math library contains a function that computes the square root of numbers. In this exercise, you are to write your own algorithm for computing square roots. One way to solve this problem is to use a guess-and-check approach. You first guess what the square root might be and then see how close your guess is. You can use this information to make another guess and continue guessing until you have found the square root (or a close approximation to it). One particularly good way of making guesses is to use Newton's method. Suppose x is the number we want the root of, and guess x guess guess 2 is the current guessed answer. The guess can be improved by using as the next guess. Write a program that implements Newton's method. The program should prompt the user for the value to find the square root of (x) and the number of times to improve the guess. Starting with a guess value of x/2, your program should loop the specified number of times applying Newton's method and report the final value of guess. You might also print out the value of math.sqrt(x) for comparison.38 CHAPTER 3. COMPUTING WITH NUMBERSChapter 4 Computing with Strings So far, we have been discussing programs designed to manipulate numbers. These days we know that com- puters are also useful for working with other kinds of data. In fact, the most common use for most personal computers is word processing. The data in this case is text. Text is represented in programs by the string data type, which is the subject of this chapter. 4.1 The String Data Type A string is a sequence of characters. In Chapter 2 you learned that a string literal is a sequence of characters in quotations. Python also allows strings to be delimited by single quotes (apostrophes). There's no difference.() just be sure to use a matching set. Strings can be stored in variables, just like numbers. Here are some examples illustrating these two forms of string literals. >>> str1 = "Hello" >>> str2 = .&*spam.+" >>> print str1, str2 Hello spam >>> type(str1) >>> type(str2) You already know how to print strings. Some programs also need to get string input from the user (e.g., a name). Getting string-valued input requires a bit of care. Remember that the input statement treats whatever the user types as an expression to be evaluated. Consider the following interaction. >>> firstName = input("Please Please enter your name: John Traceback (innermost last): File "", line 1, firstName = input("Please File "", line 0, in NameError: John enter your name: ") in ? enter your name: ") ? Something has gone wrong here. Can you see what the problem is? Remember, an input statement is just a delayed expression. When I entered the name, #+(John-/), this had the exact same effect as executing this assignment statement: firstName = John This statement says, *-*look up the value of the variable John and store that value in firstName./#" Since John was never given a value, Python cannot find any variable with that name and responds with a NameError. One way to fix this problem is to type quotes around a string input so that it evaluates as a string literal. 39CHAPTER 4. COMPUTING WITH STRINGS 40 >>> firstName = input("Please enter your name: ") Please enter your name: "John" >>> print "Hello", firstName Hello John This works, but it is not a very satisfactory solution. We shouldn't have to burden the users of our programs with details like typing quotes around their names. Python provides a better mechanism. The raw input function is exactly like input except it does not evaluate the expression that the user types. The input is simply handed to the program as a string of text. Revisiting our example, here is how it looks with raw input: >>> firstName = raw_input("Please enter your name: ") Please enter your name: John >>> print "Hello", firstName Hello John Notice that this example works as expected without having to type quotes around the input. If you want to get textual input from the user, raw input is the way to do it. So far, we have seen how to get strings as input, assign them to variables and print them out. That's enough to write a parrot program, but not to do any serious text-based computing. For that, we need some string operations. The rest of this section takes you on a tour of the more important Python string operations. In the following section, we'll put these ideas to work in some example programs. While the idea of numeric operations may be old hat to you from your math studies, you may not have thought about string operations before. What kinds of things can we do with strings? For starters, remember what a string is: a sequence of characters. One thing we might want to do is access the individual characters that make up the string. In Python, this can be done through the operation of indexing. We can think of the positions in a string as being numbered, starting from the left with 0. Figure 4.1 H 0 e l l 2 o 3 4 B 5 6 o 7 b 8 Figure 4.1: Indexing of the string /(.Hello Bob"') illustrates with the string *&'Hello Bob./'# Indexing is used in string expressions to access a specific character position in the string. The general form for indexing is []. The value of the expression determines which character is selected from the string. Here are some interactive indexing examples: >>> >>> */-H&(# >>> H l >>> >>> B greet = "Hello Bob" greet[0] print greet[0], greet[2], greet[4] o x = 8 print greet[x-2] Notice that, in a string of n characters, the last character is at position n 1, because the indexes start at 0. Indexing returns a string containing a single character from a larger string. It is also possible to access a contiguous sequence of characters or substring from a string. In Python, this is accomplished through an operation called slicing. You can think of slicing as a way of indexing a range of positions in the string. Slicing takes the form string [ start : end ]. Both start and end should be int-valued expressions. A slice produces the substring starting at the position given by start and running up to, but not including, position end. Continuing with our interactive example, here are some slices.4.2. SIMPLE STRING PROCESSING 41 >>> greet[0:3] '#&Hel&/, >>> greet[5:9] .*) Bob/!# >>> greet[:5] (/+Hello&!) >>> greet[5:] &#& Bob%", >>> greet[:] '++Hello Bob+!+ The last three examples show that if either expression is missing, the start and end of the string are the assumed defaults. The final expression actually hands back the entire string. Indexing and slicing are useful operations for chopping strings into smaller pieces. The string data type also supports operations for putting strings together. Two handy operators are concatenation (+) and repetition (*). Concatenation builds a string by ..-gluing+,, two strings together. Repetition builds a string by multiple concatenations of a string with itself. Another useful function is len, which tells how many characters are in a string. Here are some examples: >>> "spam" + "eggs" '"-spameggs'"- >>> "Spam" + "And" + "Eggs" )!%SpamAndEggs+', >>> 3 * "spam" +$.spamspamspam!(/ >>> "spam" * 5 %&"spamspamspamspamspam/"* >>> (3 * "spam") + ("eggs" * 5) "'(spamspamspameggseggseggseggseggs.)+ >>> len("spam") 4 >>> len("SpamAndEggs") 11 >>> These basic string operations are summarized in Table 4.1. Operator + * string [ ] len( string ) string [ : ] Meaning Concatenation Repetition Indexing length slicing Table 4.1: Python string operations 4.2 Simple String Processing Now that you have an idea what string operations can do, we're ready to write some programs. Our first example is a program to compute the usernames for a computer system. Many computer systems use a username and password combination to authenticate system users. The system administrator must assign a unique username to each user. Often, usernames are derived from the user's actual name. One scheme for generating usernames is to use the user's first initial followed by upCHAPTER 4. COMPUTING WITH STRINGS 42 to seven letters of the user's last name. Using this method, the username for Elmer Thudpucker would be #/-ethudpuc,/-. and John Smith would just be %-.jsmith.!), We want to write a program that reads a person's name and computes the corresponding username. Our program will follow the basic input-process-output pattern. The result is simple enough that we can skip the algorithm development and jump right to the code. The outline of the algorithm is included as comments in the final program. # username.py # Simple string processing program to generate usernames. def main(): print "This program generates computer usernames." print # get user's first and last names first = raw_input("Please enter your first name (all lowercase): ") last = raw_input("Please enter your last name (all lowercase): ") # concatenate first initial with 7 chars of the last name. uname = first[0] + last[:7] # output the username print "Your username is:", uname main() This program first uses raw input to get strings from the user. Then indexing, slicing, and concatenation are combined to produce the username. Here's an example run. This program generates computer usernames. Please enter your first name (all lowercase): elmer Please enter your last name (all lowercase): thudpucker Your username is: ethudpuc As you can see, computing with strings is very similar to computing with numbers. Here is another problem that we can solve with string operations. Suppose we want to print the abbrevia- tion of the month that corresponds to a given month number. The input to the program is an int that represents a month number (1,$(12), and the output is the abbreviation for the corresponding month. For example, if the input is 3, then the output should be Mar, for March. At first, it might seem that this program is beyond your current ability. Experienced programmers recog- nize that this is a decision problem. That is, we have to decide which of 12 different outputs is appropriate, based on the number given by the user. We will not cover decision structures until later; however, we can write the program now by some clever use of string slicing. The basic idea is to store all the month names in a big string. months = "JanFebMarAprMayJunJulAugSepOctNovDec" We can lookup a particular month by slicing out the appropriate substring. The trick is computing where to slice. Since each month is represented by three letters, if we knew where a given month started in the string, we could easily extract the abbreviation. monthAbbrev = months[pos:pos+3] This would get us the substring of length three that starts in the position indicated by pos. How do we compute this position? Let's try a few examples and see what we find. Remember that string indexing starts at 0.4.3. STRINGS AND SECRET CODES month Jan Feb Mar Apr 43 number 1 2 3 4 position 0 3 6 9 Of course, the positions all turn out to be multiples of 3. To get the correct multiple, we just subtract 1 from the month number and then multiply by 3. So for 1 we get 1 1 3 0 3 0 and for 12 we have 12 1 3 11 3 33 Now we're ready to code the program. Again, the final result is short and sweet; the comments document the algorithm we've developed. # month.py # A program to print the abbreviation of a month, given its number def main(): # months is used as a lookup table months = "JanFebMarAprMayJunJulAugSepOctNovDec" n = input("Enter a month number (1-12): ") # compute starting position of month n in months pos = (n-1) * 3 # Grab the appropriate slice from months monthAbbrev = months[pos:pos+3] # print the result print "The month abbreviation is", monthAbbrev + "." main() Notice the last line of this program uses string concatenation to put a period at the end of the month abbrevi- ation. Here is a sample of program output. Enter a month number (1-12): 4 The month abbreviation is Apr. 4.3 Strings and Secret Codes 4.3.1 String Representation Hopefully, you are starting to get the hang of computing with textual (string) data. However, you might still be wondering how computers actually manipulate strings. In the previous chapter, you saw how computers store numbers in binary notation (sequences of zeros and ones); the computer CPU contains circuitry to do arithmetic with these representations. Textual information is represented in exactly the same way. Under- neath, when the computer is manipulating text, it is really no different from number crunching. To understand this, you might think in terms of messages and secret codes. Consider the age-old grade school dilemma. You are sitting in class and want to pass a note to a friend across the room. Unfortunately, the note must pass through the hands, and in front of the curious eyes, of many classmates before it reaches its final destination. And, of course, there is always the risk that the note could fall into enemy hands (the teacher's). So you and your friend need to design a scheme for encoding the contents of your message. One approach is to simply turn the message into a sequence of numbers. You could choose a number to correspond to each letter of the alphabet and use the numbers in place of letters. Without too muchCHAPTER 4. COMPUTING WITH STRINGS 44 imagination, you might use the numbers 1-26 to represent the letters a#,/z. Instead of the word "!&sourpuss,+'. you would write %)%18, 14, 20, 17, 15, 20, 18, 18.$.- To those who don't know the code, this looks like a meaningless string of numbers. For you and your friend, however, it represents a word. This is how a computer represents strings. Each character is translated into a number, and the entire string is stored as a sequence of (binary) numbers in computer memory. It doesn't really matter what number is used to represent any given character as long as the computer is consistent about the encoding/decoding process. In the early days of computing, different designers and manufacturers used different encodings. You can imagine what a headache this was for people transferring data between different systems. Consider the situation that would result if, say, PCs and MacIntosh computers each used their own en- coding. If you type a term paper on a PC and save it as a text file, the characters in your paper are represented as a certain sequence of numbers. Then if the file was read into your instructor's MacIntosh computer, the numbers would be displayed on the screen as different characters from the ones you typed. The result would look like gibberish! To avoid this sort of problem, computer systems today use industry standard encodings. One important standard is called ASCII (American Standard Code for Information Interchange). ASCII uses the numbers 0 through 127 to represent the characters typically found on an (American) computer keyboard, as well as certain special values known as control codes that are used to coordinate the sending and receiving of information. For example, the capital letters A/!$Z are represented by the values 65,(!90, and the lowercase versions have codes 97"$-122. One problem with the ASCII encoding, as its name implies, is that it is American-centric. It does not have symbols that are needed in many other languages. Extended ASCII encodings have been developed by the International Standards Organization to remedy this situation. Most modern systems are moving to support of UniCode a much larger standard that includes support for the characters of all written languages. Newer versions of Python include support for UniCode as well as ASCII. Python provides a couple built-in functions that allow us to switch back and forth between characters and the numeric values used to represent them in strings. The ord function returns the numeric ('$-ordinal+')) code of a single-character string, while chr goes the other direction. Here are some interactive examples: >>> 97 >>> 65 >>> #)%a"#, >>> .-*Z,)* ord("a") ord("A") chr(97) chr(90) 4.3.2 Programming an Encoder Let's return to the note-passing example. Using the Python ord and chr functions, we can write some simple programs that automate the process of turning messages into strings of numbers and back again. The algorithm for encoding the message is simple. get the message to encode for each character in the message: print the letter number of the character Getting the message from the user is easy, a raw input will take care of that for us. message = raw_input("Please enter the message to encode: ") Implementing the loop requires a bit more effort. We need to do something for each character of the message. Recall that a for loop iterates over a sequence of objects. Since a string is a kind of sequence, we can just use a for loop to run-through all the characters of the message. for ch in message:4.3. STRINGS AND SECRET CODES 45 Finally, we need to convert each character to a number. The simplest approach is to use the ASCII number (provided by ord) for each character in the message. Here is the final program for encoding the message: # text2numbers.py # A program to convert a textual message into a sequence of # numbers, utlilizing the underlying ASCII encoding. def main(): print "This program converts a textual message into a sequence" print "of numbers representing the ASCII encoding of the message." print # Get the message to encode message = raw_input("Please enter the message to encode: ") print print "Here are the ASCII codes:" # Loop through the message and print out the ASCII values for ch in message: print ord(ch), # use comma to print all on one line. print main() We can use the program to encode important messages. This program converts a textual message into a sequence of numbers representing the ASCII encoding of the message. Please enter the message to encode: What a Sourpuss! Here are the ASCII codes: 87 104 97 116 32 97 32 83 111 117 114 112 117 115 115 33 One thing to notice about this result is that even the space character has a corresponding ASCII code. It is represented by the value 32. 4.3.3 Programming a Decoder Now that we have a program to turn a message into a sequence of numbers, it would be nice if our friend on the other end had a similar program to turn the numbers back into a readable message. Let's solve that problem next. Our decoder program will prompt the user for a sequence of numbers representing ASCII codes and then print out the text message corresponding to those codes. This program presents us with a couple of challenges; we'll address these as we go along. The overall outline of the decoder program looks very similar to the encoder program. One change in structure is that the decoding version will collect the characters of the message in a string and print out the entire message at the end of the program. To do this, we need to use an accumulator variable, a pattern we saw in the factorial program from the previous chapter. Here is the decoding algorithm: get the sequence of numbers to decode message = "" for each number in the input: convert the number to the appropriate character46 CHAPTER 4. COMPUTING WITH STRINGS add the character to the end of message print the message Before the loop, the accumulator variable message is initialized to be an empty string, that is a string that contains no characters (""). Each time through the loop a number from the input is converted into an appropriate character and appended to the end of the message constructed so far. The algorithm seems simple enough, but even the first step presents us with a problem. How exactly do we get the sequence of numbers to decode? We don't even know how many numbers there will be. To solve this problem, we are going to rely on some more string manipulation operations. First, we will read the entire sequence of numbers as a single string using raw input. Then we will split the big string into a sequence of smaller strings, each of which represents one of the numbers. Finally, we can iterate through the list of smaller strings, convert each into a number, and use that number to produce the corresponding ASCII character. Here is the complete algorithm: get the sequence of numbers as a string, inString split inString into a sequence of smaller strings message = "" for each of the smaller strings: change the string of digits into the number it represents append the ASCII character for that number to message print message This looks complicated, but Python provides some functions that do just what we need. We saw in Chapter 3 that Python provides a standard math library containing useful functions for com- puting with numbers. Similarly, the string library contains many functions that are useful in string- manipulation programs. For our decoder, we will make use of the split function. This function is used to split a string into a sequence of substrings. By default, it will split the string wherever a space occurs. Here's an example: >>> import string >>> string.split("Hello string library!") [!,)Hello%#!, (&/string('#, -&-library!*!%] You can see how split has turned the original string "Hello string library!" into a list of three strings: "Hello", "string" and "library!". By the way, the split function can be used to split a string at places other than spaces by supplying the character to split on as a second parameter. For example, if we have a string of numbers separated by commas, we could split on the commas. >>> string.split("32,24,25,57", ",") [##-32.+/, **$24,&,, &.!25.#*, '+!57&*.] Since our decoder program should accept the same format that was produced by the encoder program, namely a sequence of numbers with spaces between, the default version of split works nicely. >>> string.split("87 104 97 116 32 97 32 83 111 117 114 112 117 115 115 33") [)'$87&"$, "$-104/#), &$#97"//, -&-116%+(, /"#32'/$, %(+97)%$, ,'.32*-!, *#,83$$#, #)/111(%+, /+$117)"#, ,."114''., **%112)$", !%+117$(', '+'115&##, ().115$"', --%33/,+] Notice that the resulting list is not a sequence of numbers, it is a sequence of strings. It just so happens these strings contain only digits and could be interpreted as numbers. All that we need now is a way of converting a string containing digits into a Python number. One way to accomplish this is with the Python eval function. This function takes any string and evaluates it as if it were a Python expression. Here are some interactive examples of eval: >>> numStr = "500" >>> eval(numStr) 5004.3. STRINGS AND SECRET CODES 47 >>> eval("345.67") 345.67 >>> eval("3+4") 7 >>> x = 3.5 >>> y = 4.7 >>> eval("x * y") 16.45 >>> x = eval(raw_input("Enter a number ")) Enter a number 3.14 >>> print x 3.14 The last pair of statements shows that the eval of a raw input produces exactly what we would expect from an input expression. Remember, input evaluates what the user types, and eval evaluates whatever string it is given. Using split and eval we can write our decoder program. # numbers2text.py # A program to convert a sequence of ASCII numbers into # a string of text. import string # include string library for the split function. def main(): print "This program converts a sequence of ASCII numbers into" print "the string of text that it represents." print # Get the message to encode inString = raw_input("Please enter the ASCII-encoded message: ") # Loop through each substring and build ASCII message message = "" for numStr in string.split(inString): asciiNum = eval(numStr) # convert digit string to a number message = message + chr(asciiNum) # append character to message print "The decoded message is:", message main() Study this program a bit, and you should be able to understand exactly how it accomplishes its task. The heart of the program is the loop. for numStr in string.split(inString): asciiNum = eval(numStr) message = message + chr(asciiNum) The split function produces a sequence of strings, and numStr takes on each successive (sub)string in the sequence. I called the loop variable numStr to emphasize that its value is a string of digits that represents some number. Each time through the loop, the next substring is converted to a number by evaling it. This number is converted to the corresponding ASCII character via chr and appended to the end of the accumulator, message. When the loop is finished, every number in inString has been processed and message contains the decoded text. Here is an example of the program in action:CHAPTER 4. COMPUTING WITH STRINGS 48 >>> import numbers2text This program converts a sequence of ASCII numbers into the string of text that it represents. Please enter the ASCII-encoded message: 83 116 114 105 110 103 115 32 97 114 101 32 70 117 110 33 The decoded message is: Strings are Fun! 4.3.4 Other String Operations Now we have a couple programs that can encode and decode messages as sequences of ASCII values. These programs turned out to be quite simple due to the power both of Python's string data type and its built-in operations as well as extensions that can be found in the string library. Python is a very good language for writing programs that manipulate textual data. Table 4.2 lists some of the other useful functions of the string library. Note that many of these functions, like split, accept additional parameters to customize their operation. Python also has a number of other standard libraries for text-processing that are not covered here. You can consult the online documentation or a Python reference to find out more. Function capitalize(s) capwords(s) center(s, width) count(s, sub) find(s, sub) join(list) ljust(s, width) lower(s) lstrip(s) replace(s,oldsub,newsub) rfind(s, sub) rjust(s,width) rstrip(s) split(s) upper(s) Meaning Copy of s with only the first character capitalized Copy of s with first character of each word capitalized Center s in a field of given width Count the number of occurrences of sub in s Find the first position where sub occurs in s Concatenate list of strings into one large string Like center, but s is left-justified Copy of s in all lowercase characters Copy of s with leading whitespace removed Replace all occurrences of oldsub in s with newsub Like find, but returns the rightmost position Like center, but s is right-justified Copy of s with trailing whitespace removed Split s into a list of substrings (see text). Copy of s with all characters converted to upper case Table 4.2: Some components of the Python string library 4.3.5 From Encoding to Encryption We have looked at how computers represent strings as a sort of encoding problem. Each character in a string is represented by a number that is stored in the computer in a binary representation. You should realize that there is nothing really secret about this code at all. In fact, we are simply using an industry-standard mapping of characters into numbers. Anyone with a little knowledge of computer science would be able to crack our code with very little effort. The process of encoding information for the purpose of keeping it secret or transmitting it privately is called encryption. The study of encryption methods is an increasingly important subfield of mathematics and computer science known as cryptography. For example, if you shop over the Internet, it is important that your personal information such as social security number or credit card number is transmitted using encodings that keep it safe from potential eavesdroppers on the network. Our simple encoding/decoding programs use a very weak form of encryption known as a substitution cipher. Each character of the original message, called the plaintext, is replaced by a corresponding symbol (in our case a number) from a cipher alphabet. The resulting code is called the ciphertext .4.4. OUTPUT AS STRING MANIPULATION 49 Even if our cipher were not based on the well-known ASCII encoding, it would still be easy to discover the original message. Since each letter is always encoded by the same symbol, a code-breaker could use statistical information about the frequency of various letters and some simple trial and error testing to discover the original message. Such simple encryption methods may be sufficient for grade-school note passing, but they are certainly not up to the task of securing communication over global networks. Modern approaches to encryption start by translating a message into numbers, much like our encoding program. Then sophisticated mathematical algorithms are employed to transform these numbers into other numbers. Usually, the transformation is based on combining the message with some other special value called the key. In order to decrypt the message, the party on the receiving end needs to have the appropriate key so that the encoding can be reversed to recover the original message. Encryption approaches come in two flavors: private key and public key. In a private key system the same key is used for encrypting and decrypting messages. All parties that wish to communicate need to know the key, but it must be kept secret from the outside world. This is usual system that people think of when considering secret codes. In public key systems, there are separate but related keys for encrypting and decrypting. Knowing the encryption key does not allow you to decrypt messages or discover the decryption key. In a public key system, the encryption key can be made publicly available, while the decryption key is kept private. Anyone can safely send a message using the public key for encryption. Only the party holding the decryption key will be able to decipher it. For example, a secure web site can send your web browser its public key, and the browser can use it to encode your credit card information before sending it on the Internet. Then only the company that is requesting the information will be able to decrypt and read it using the proper private key. 4.4 Output as String Manipulation Even programs that we may not view as primarily doing text-manipulation often need to make use of string operations. For example, a program to do a financial analysis must produce a nicely formatted report of the results. Much of this report will be in the form of text that is used to label and explain numbers, charts, tables and figures. In this section, we'll look at techniques for generating nicely formatted text-based output. 4.4.1 Converting Numbers to Strings In the ASCII decoding program, we used the eval function to convert from a string data type into a numeric data type. Recall that eval evaluates a string as a Python expression. It is very general and can be used to turn strings into nearly any other Python data type. It is also possible to go the other direction and turn many Python data types into strings using the str function. Here are a couple of simple examples. >>> str(500) ---500)&( >>> value = 3.14 >>> str(value) ".-3.14$)% >>> print "The value is", str(value) + "." The value is 3.14. Notice particularly the last example. By turning value into a string, we can use string concatenation to put a period at the end of a sentence. If we didn't first turn value into a string, Python would interpret the + as a numerical operation and produce an error, because *//.(-) is not a number. Adding eval and str to the type-conversion operations discussed in Chapter 3, we now have a complete set of operations for converting values among various Python data types. Table 4.3 summarizes these five Python type conversion functions. One common reason for converting a number into a string is so that string operations can be used to control the way the value is printed. For example, in the factorial program from last chapter we saw that Python long ints have the letter +,"L!/* appended to them. In versions of Python prior to 2.0, the .-(L$$$ showed upCHAPTER 4. COMPUTING WITH STRINGS 50 Function float() int() long( str() eval() Meaning Convert expr to a floating point value. Convert expr to an integer value. Convert expr to a long integer value. Return a string representation of expr. Evaluate string as an expression. Table 4.3: Type Conversion Functions whenever a long int was printed. However, it is easy to remove this artifact using some straightforward string manipulation. factStr = str(fact) print factStr[0:len(factStr)-1] Can you see how this code turns the long int into a string and then uses slicing to remove the &(!L?#!. The print statement prints every position in the string up to, but not including, the final +"$L,(%, which is in position length - 1. As an aside, Python also allows sequences to be indexed from the back by using negative numbers as indexes. Thus -1 is the last position in a string, -2 is the second to last, etc. Using this convention, we can slice off the last character without first computing the length of the string. print str(fact)[:-1] This version uses str to turn fact into a string and then immediately slices it $'$in place+&! from the beginning (0 is the default start) up to, but not including, the last position. 4.4.2 String Formatting As you have seen, basic string operations can be used to build nicely formatted output. This technique is useful for simple formatting, but building up a complex output through slicing and concatenation of smaller strings can be tedious. Python provides a powerful string formatting operation that makes the job much easier. Let's start with a simple example. Here is a run of the change counting program from last chapter. Change Counter Please enter the count of each coin type. How many quarters do you have? 6 How many dimes do you have? 0 How many nickels do you have? 0 How many pennies do you have? 0 The total value of your change is 1.5 Notice that the final value is given as a fraction with only one decimal place. This looks funny, since we expect the output to be something like $1.50. We can fix this problem by changing the very last line of the program as follows. print "The total value of your change is $%0.2f" % (total) Now the program prints this message: The total value of your change is $1.50 Let's try to make some sense of this. The percent sign % is Python's string formatting operator. In general, the string formatting operator is used like this: % ()4.4. OUTPUT AS STRING MANIPULATION 51 Percent signs inside the template-string mark $!,slots-($ into which the values are inserted. There must be exactly one slot for each value. Each of the slots is described by a format specifier that tells Python how the value for that slot should appear. Returning to the example, the template contains a single specifier at the end: %0.2f. The value of total will be inserted into the template in place of the specifier. The specifier also tells Python that total is a floating point number that should be rounded to two decimal places. To understand the formatting, we need to look at the structure of the specifier. A formatting specifier has this general form: %. The specifier starts with a % and ends with a character that indicates the data type of the value being inserted. We will use three different format types: decimal, float, and string. Decimal mode is used to display ints as base-10 numbers. (Python allows ints to be printed using a number of different bases; we will only use the normal decimal representation.) Float and string formats are obviously used for the corresponding data types. The width and precision portions of a specifier are optional. If present, width tells how many spaces to use in displaying the value. If a value requires more room than is given in width, Python will just expand the width so that the value fits. You can use a 0 width to indicate ''!use as much space as needed.&*+ Precision is used with floating point values to indicate the desired number of digits after the decimal. The example specifier %0.2f tells Python to insert a floating point value using as much space as necessary and rounding it to two decimal places. The easiest way to get the hang of formatting is just to play around with some examples in the interactive environment. >>> "Hello %s %s, you may have won $%d!" % ("Mr.", "Smith", 10000) )'#Hello Mr. Smith, you may have already won $10000!""* >>> ',#This int, %5d, was placed in a field of width 5*+. % (7) &.%This int, 7, was placed in a field of width 5!!+ >>> !%#This int, %10d, was placed in a field of width 10%)+ % (7) (')This int, 7, was placed in a field of width 10/%# >>> *%!This float, %10.5f, has width 10 and precision 5.,&" % (3.1415926) &,%This float, 3.14159, has width 10 and precision 5./-/ >>> -+&This float, %0.5f, has width 0 and precision 5.#+& % (3.1415926) %$"This float, 3.14159, has width 0 and precision 5../" >>> "Compare %f and %0.20f" % (3.14, 3.14) !'!Compare 3.140000 and 3.14000000000000012434)-% A couple points are worth noting here. If the width in the specifier is larger than needed, the value is right-justified in the field by default. You can left-justify the value in the field by preceding the width with a minus sign (e.g., %-8.3f). The last example shows that if you print enough digits of a floating point number, you will almost always find a #')surprise.$*' The computer can't represent 3.14 exactly as a floating point number. The closest value it can represent is ever so slightly larger than 3.14. If not given an explicit precision, Python will print the number out to a few decimal places. The slight extra extra amount shows up if you print lots of digits. Generally, Python only displays a closely rounded version of a float. Using explicit formatting allows you to see the full result down to the last bit. 4.4.3 Better Change Counter Let's close our formatting discussion with one more example program. Given what you have learned about floating point numbers, you might be a little uneasy about using them to represent money.52 CHAPTER 4. COMPUTING WITH STRINGS Suppose you are writing a computer system for a bank. Your customers would not be too happy to learn that a check went through for an amount ,'#very close to $107.56."&( They want to know that the bank is keeping precise track of their money. Even though the amount of error in a given value is very small, the small errors can be compounded when doing lots of calculations, and the resulting error could add up to some real cash. That's not a satisfactory way of doing business. A better approach would be to make sure that our program used exact values to represent money. We can do that by keeping track of the money in cents and using an int (or long int) to store it. We can then convert this into dollars and cents in the output step. If total represents the value in cents, then we can get the number of dollars by total / 100 and the cents from total % 100. Both of these are integer calculations and, hence, will give us exact results. Here is the updated program: # change2.py # A program to calculate the value of some change in dollars # This version represents the total cash in cents. def main(): print "Change Counter" print print "Please enter the count of each coin type." quarters = input("Quarters: ") dimes = input("Dimes: ") nickels = input("Nickels: ") pennies = input("Pennies: ") total = quarters * 25 + dimes * 10 + nickels * 5 + pennies print print "The total value of your change is $%d.%02d" \ % (total/100, total%100) main() I have split the final print statement across two lines. Normally a statement ends at the end of the line. Sometimes it is nicer to break a long statement into smaller pieces. A backslash at the end of a line is one way to indicate that a statement is continued on the following line. Notice that the backslash must appear outside of the quotes; otherwise, it would be considered part of the string literal. The string formatting in the print statement contains two slots, one for dollars as an int and one for cents as an int. This example also illustrates one additional twist on format specifiers. The value of cents is printed with the specifier %02d. The zero in front of the width tells Python to pad the field (if necessary) with zeroes instead of spaces. This ensures that a value like 10 dollars and 5 cents prints as $10.05 rather than $10. 5. 4.5 File Processing I began the chapter with a reference to word-processing as an application of the string data type. One critical feature of any word processing program is the ability to store and retrieve documents as files on disk. In this section, we'll take a look at file input and output, which, as it turns out, is really just another form of string processing. 4.5.1 Multi-Line Strings Conceptually, a file is a sequence of data that is stored in secondary memory (usually on a disk drive). Files can contain any data type, but the easiest files to work with are those that contain text. Files of text have the advantage that they can be read and understood by humans, and they are easily created and edited using general-purpose text editors and word processors. In Python, text files can be very flexible, since it is easy to convert back and forth between strings and other types.4.5. FILE PROCESSING 53 You can think of a text file as a (possibly long) string that happens to be stored on disk. Of course, a typical file generally contains more than a single line of text. A special character or sequence of characters is used to mark the end of each line. There are numerous conventions for end-of-line markers. Python uses a single character called newline as a marker. You can think of newline as the character produced when you press the Enter key on your keyboard. Although a newline is a single character, it is represented in Python (and many other computer languages) using the special notation (## n%,-. Other special characters are represented using a similar notation (i.e., #(( t+'' for Tab ). Let's take a look at a concrete example. Suppose you type the following lines into a text editor exactly as shown here. Hello World Goodbye 32 When stored to a file, you get this sequence of characters. Hello\nWorld\n\nGoodbye 32\n Notice that the blank line becomes a bare newline in the resulting file/string. By the way, by embedding newline characters into output strings, you can produce multiple lines of output with a single print statement. Here is the example from above printed interactively. >>> print "Hello\nWorld\n\nGoodbye 32\n" Hello World Goodbye 32 >>> If you simply ask Python to evaluate a string containing newline characters, you will just get the embedded newline representation back again. >>>"Hello\nWorld\n\nGoodbye 32\n" -.%Hello\nWorld\n\nGoodbye 32\n-(" It's only when a string is printed that the special characters affect how the string is displayed. 4.5.2 File Processing The exact details of file-processing differ substantially among programming languages, but virtually all lan- guages share certain underlying file manipulation concepts. First, we need some way to associate a file on disk with a variable in a program. This process is called opening a file. Once a file has been opened, it is manipulated through the variable we assign to it. Second, we need a set of operations that can manipulate the file variable. At the very least, this includes operations that allow us to read the information from a file and write new information to a file. Typically, the reading and writing operations for text files are similar to the operations for text-based, interactive input and output. Finally, when we are finished with a file, it is closed. Closing a file makes sure that any bookkeeping that was necessary to maintain the correspondence between the file on disk and the file variable is finished up. For example, if you write information to a file variable, the changes might not show up on the disk version until the file has been closed. This idea of opening and closing files is closely related to how you might work with files in an application program like a word processor. However, the concepts are not exactly the same. When you open a file in a program like Microsoft Word, the file is actually read from the disk and stored into RAM. In programming54 CHAPTER 4. COMPUTING WITH STRINGS terminology, the file is opened for reading and the the contents of the file are then read into memory via file reading operations. At this point, the file is closed (again in the programming sense). As you )*/edit the file,"%) you are really making changes to data in memory, not the file itself. The changes will not show up in the file on the disk until you tell the application to ("'save#*$ it. Saving a file also involves a multi-step process. First, the original file on the disk is reopened, this time in a mode that allows it to store information*)'the file on disk is opened for writing. Doing so actually erases the old contents of the file. File writing operations are then used to copy the current contents of the in-memory version into the new file on the disk. From your perspective, it appears that you have edited an existing file. From the program's perspective, you have actually opened a file, read its contents into memory, closed the file, created a new file (having the same name), written the (modified) contents of memory into the new file, and closed the new file. Working with text files is easy in Python. The first step is to associate a file with a variable using the open function. = open(, ) Here name is a string that provides the name of the file on the disk. The mode parameter is either the string "r" or "w" depending on whether we intend to read from the file or write to the file. For example, to open a file on our disk called $.*numbers.dat(!# for reading, we could use a statement like the following. infile = open("numbers.dat", "r") Now we can use the variable infile to read the contents of numbers.dat from the disk. Python provides three related operations for reading information from a file: .read() .readline() .readlines() The read operation returns the entire contents of the file as a single string. If the file contains more than one line of text, the resulting string has embedded newline characters between the lines. Here's an example program that prints the contents of a file to the screen. # printfile.py # Prints a file to the screen. def main(): fname = raw_input("Enter filename: ") infile = open(fname,!+!r./.) data = infile.read() print data main() The program first prompts the user for a filename and then opens the file for reading through the variable infile. You could use any name for the variable, I used infile to emphasize that the file was being used for input. The entire contents of the file is then read as one large string and stored in the variable data. Printing data causes the contents to be displayed. The readline operation can be used to read one line from a file; that is, it reads all the characters up through the next newline character. Each time it is called, readline returns the next line from the file. This is analogous to raw input which reads characters interactively until the user hits the Enter key; each call to raw input get another line from the user. One thing to keep in mind, however, is that the string returned by readline will always end with a newline character, whereas raw input discards the newline character. As a quick example, this fragment of code prints out the first five lines of a file.4.5. FILE PROCESSING 55 infile = open(someFile, $$-r%$/) for i in range(5): line = infile.readline() print line[:-1] Notice the use of slicing to strip off the newline character at the end of the line. Since print automatically jumps to the next line (i.e., it outputs a newline), printing with the explicit newline at the end would put an extra blank line of output between the lines of the file. As an alternative to readline, when you want to loop through all the (remaining) lines of a file, you can use readlines. This operation returns a sequence of strings representing the lines of the file. Used with a for loop, it is a particularly handy way to process each line of a file. infile = open(someFile, ,&$r,%,) for line in infile.readlines(): # process the line here infile.close() Opening a file for writing prepares that file to receive data. If no file with the given name exists, a new file will be created. A word of warning: if a file with the given name does exist, Python will delete it and create a new, empty file. When writing to a file, make sure you do not clobber any files you will need later! Here is an example of opening a file for output. outfile = open("mydata.out", "w") We can put data into a file, using the write operation. .write() This is similar to print, except that write is a little less flexible. The write operation takes a single parameter, which must be a string, and writes that string to the file. If you want to start a new line in the file, you must explicitly provide the newline character. Here's a silly example that writes two lines to a file. outfile = open("example.out", $.-w'/') count = 1 outfile.write("This is the first line\n") count = count + 1 outfile.write("This is line number %d" % (count)) outfile.close() Notice the use of string formatting to write out the value of the variable count. If you want to output something that is not a string, you must first convert it; the string formatting operator is often a handy way to do this. This code will produce a file on disk called %#%example.out'+, containing the following two lines: This is the first line This is line number 2 If !+.example.out()& existed before executing this fragment, it's old contents were destroyed. 4.5.3 Example Program: Batch Usernames To see how all these pieces fit together, let's redo the username generation program. Our previous version created usernames interactively by having the user type in his or her name. If we were setting up accounts for a large number of users, the process would probably not be done interactively, but in batch mode. In batch processing, program input and output is done through files. Our new program is designed to process a file of names. Each line of the input file will contain the first and last names of a new user separated by one or more spaces. The program produces an output file containing a line for each generated username.CHAPTER 4. COMPUTING WITH STRINGS 56 # userfile.py # Program to create a file of usernames in batch mode. import string def main(): print "This program creates a file of usernames from a" print "file of names." # get the file names infileName = raw_input("What file are the names in? ") outfileName = raw_input("What file should the usernames go in? ") # open the files infile = open(infileName, &$)r+!-) outfile = open(outfileName, *,*w%(&) # process each line of the input file for line in infile.readlines(): # get the first and last names from line first, last = string.split(line) # create the username uname = string.lower(first[0]+last[:7]) # write it to the output file outfile.write(uname+"#%\n.*$) # close both files infile.close() outfile.close() print "Usernames have been written to", outfileName main() There are a few things worth noticing in this program. I have two files open at the same time, one for input (infile) and one for output (outfile). It's not unusual for a program to operate on several files simultaneously. Also, when creating the username, I used the lower function from the string library. This ensures that the username is all lower case, even if the input names are mixed case. Finally, you should also notice the line that writes the username to the file. outfile.write(uname+,&'\n!,)) Concatenating the newline character is necessary to indicate the end of line. Without this, all of the usernames would run together in one giant line. 4.5.4 Coming Attraction: Objects Have you noticed anything strange about the syntax of the file processing examples? To apply an operation to a file, we use dot notation. For example, to read from infile we type infile.read(). This is different from the normal function application that we have used before. Afterall, to take the absolute value of a variable x, we type abs(x), not x.abs(). In Python, a file is an example of an object. Objects combine both data and operations together. An object's operations, called methods, are invoked using the dot notation. That's why the syntax looks a bit different.4.6. EXERCISES 57 For completeness, I should mention that strings are also objects in Python. If you have a relatively new version of Python (2.0 or later), you can use string methods in place of the string library functions that we discussed earlier. For example, myString.split() is equivalent to string.split(myString) If this object stuff sounds confusing right now, don't worry; Chapter 5 is all about the power of objects (and pretty pictures, too). 4.6 Exercises 1. Given the initial statements: import string s1 = "spam" s2 = "ni!" Show the result of evaluating each of the following string expressions. (a) "The Knights who say, " + s2 (b) 3 * s1 + 2 * s2 (c) s1[1] (d) s1[1:3] (e) s1[2] + s2[:2] (f) s1 + s2[-1] (g) string.upper(s1) (h) string.ljust(string.upper(s2),4) * 3 2. Given the same initial statements as in the previous problem, show a Python expression that could construct each of the following results by performing string operations on s1 and s2. (a) "NI" (b) "ni!spamni!" (c) "Spam Ni! Spam Ni! Spam Ni!" (d) "span" (e) ["sp","m"] (f) "spm" 3. Show the output that would be generated by each of the following program fragments. (a) for ch in "aardvark": print ch (b) for w in string.split("Now is the winter of our discontent..."): print w (c) for w in string.split("Mississippi", "i"): print w,CHAPTER 4. COMPUTING WITH STRINGS 58 (d) msg = "" for s in string.split("secret","e"): msg = msg + s print msg (e) msg = "" for ch in "secret": msg = msg + chr(ord(ch)+1) print msg 4. Show the string that would result from each of the following string formatting operations. If the oper- ation is not legal, explain why. (a) "Looks like %s and %s for breakfast" % ("spam", "eggs") (b) "There is %d %s %d %s" % (1,"spam", 4, "you") (c) "Hello %s" % ("Suzie", "Programmer") (d) "%0.2f %0.2f" % (2.3, 2.3468) (e) "%7.5f %7.5f" % (2.3, 2.3468) (f) "Time left %02d:%05.2f" % (1, 37.374) (g) "%3d" % ("14") 5. Explain why public key encryption is more useful for securing communications on the Internet than private (shared) key encryption. 6. A certain CS professor gives 5-point quizzes that are graded on the scale 5-A, 4-B, 3-C, 2-D, 1-F, 0-F. Write a program that accepts a quiz score as an input and prints out the corresponding grade. 7. A certain CS professor gives 100-point exams that are graded on the scale 90$,+100:A, 80#++89:B, 70/"/ 79:C, 60/$/69:D, 60:F. Write a program that accepts an exam score as input and prints out the corre- sponding grade. 8. An acronym is a word formed by taking the first letters of the words in a phrase and making a word from them. For example, RAM is an acronym for $-#random access memory.-)* Write a program that allows the user to type in a phrase and outputs the acronym for that phrase. Note: the acronym should be all uppercase, even if the words in the phrase are not capitalized. 9. Numerologists claim to be able to determine a person's character traits based on the +"-numeric value/!. of a name. The value of a name is determined by summing up the values of the letters of the name where *,+a(%( is 1, !/+b+)' is 2, '(/c-(* is 3 etc., up to $+.z&!' being 26. For example, the name /+)Zelle$"! would have the value 26 5 12 12 5 60 (which happens to be a very auspicious number, by the way). Write a program that calculates the numeric value of a single name provided as input. 10. Expand your solution to the previous problem to allow the calculation of a complete name such as )+$John Marvin Zelle%+( or ,+(John Jacob Jingleheimer Smith...! The total value is just the sum of the numeric value for each name. 11. A Caesar cipher is a simple substitution cipher based on the idea of shifting each letter of the plaintext message a fixed number (called the key) of positions in the alphabet. For example, if the key value is 2, the word ,,!Sourpuss+)( would be encoded as %,*Uqwtrwuu.#"* The original message can be recovered by "(,reencoding*$- it using the negative of the key. Write a program that can encode and decode Caesar ciphers. The input to the program will be a string of plaintext and the value of the key. The output will be an encoded message where each character in the original message is replaced by shifting it key characters in the ASCII character set. For example, if ch is a character in the string and key is the amount to shift, then the character that replaces ch can be calculated as: chr(ord(ch) + key).4.6. EXERCISES 59 12. One problem with the previous exercise is that it does not deal with the case when we *,!drop off the end-!+ of the alphabet (or ASCII encodings). A true Caesar cipher does the shifting in a circular fashion where the next character after -$&z,*+ is $&'a$)'. Modify your solution to the previous problem to make it circular. You may assume that the input consists only of letters and spaces. 13. Write a program that counts the number of words in a sentence entered by the user. 14. Write a program that calculates the average word length in a sentence entered by the user. 15. Write an improved version of the Chaos program from Chapter 1 that allows a user to input two initial values and the number of iterations and then prints a nicely formatted table showing how the values change over time. For example, if the starting values were .25 and .26 with 10 iterations, the table might look like this: index 0.25 0.26 ---------------------------- 1 0.731250 0.750360 2 0.766441 0.730547 3 0.698135 0.767707 4 0.821896 0.695499 5 0.570894 0.825942 6 0.955399 0.560671 7 0.166187 0.960644 8 0.540418 0.147447 9 0.968629 0.490255 10 0.118509 0.974630 16. Write an improved version of the future value program from Chapter 2. Your program will prompt the user for the amount of the investment, the annualized interest rate, and the number of years of the investment. The program will then output a nicely formatted table that tracks the value of the investment year by year. Your output might look something like this: Years Value ---------------- 0 $2000.00 1 $2200.00 2 $2420.00 3 $2662.00 4 $2928.20 5 $3221.02 6 $3542.12 7 $3897.43 17. Redo any of the previous programming problems to make them batch oriented (using text files for input and output). 18. Word count. A common utility on Unix/Linux systems is a small program called +%#wc.-/+ This program analyzes a file to determine the number of lines, words, and characters contained therein. Write your own version of wc. The program should accept a file name as input and then print three numbers showing the count of lines, words, and characters in the file.60 CHAPTER 4. COMPUTING WITH STRINGSChapter 5 Objects and Graphics So far we have been writing programs that use the built-in Python data types for numbers and strings. We saw that each data type could represent a certain set of values, and each had a set of associated operations. Basically, we viewed the data as passive entities that were manipulated and combined via active operations. This is a traditional way to view computation. To build complex systems, however, it helps to take a richer view of the relationship between data and operations. Most modern computer programs are built using an object-oriented (OO) approach. Object orientation is not easily defined. It encompasses a number of principles for designing and implementing software, prin- ciples that we will return to numerous times throughout course of this book. This chapter provides a basic introduction to object concepts by way of some computer graphics. 5.1 The Object of Objects The basic idea of object-oriented development is to view a complex system as the interaction of simpler objects. The word objects is being used here in a specific technical sense. Part of the challenge of OO programming is figuring out the vocabulary. You can think of an OO object as a sort of active data type that combines both data and operations. To put it simply, objects know stuff (they contain data), and they can do stuff (they have operations). Objects interact by sending each other messages. A message is simply a request for an object to perform one of its operations. Consider a simple example. Suppose we want to develop a data processing system for a college or university. We will need to keep track of considerable information. For starters, we must keep records on the students who attend the school. Each student could be represented in the program as an object. A student object would contain certain data such as name, ID number, courses taken, campus address, home address, GPA, etc. Each student object would also be able to respond to certain requests. For example, to send out a mailing, we would need to print an address for each student. This task might be handled by a printCampusAddress operation. When a particular student object is sent the printCampusAddress message, it prints out its own address. To print out all the addresses, a program would loop through the collection of student objects and send each one in turn the printCampusAddress message. Objects may refer to other objects. In our example, each course in the college might also be represented by an object. Course objects would know things such as who the instructor is, what students are in the course, what the prerequisites are, and when and where the course meets. One example operation might be addStudent, which causes a student to be enrolled in the course. The student being enrolled would be represented by the appropriate student object. Instructors would be another kind of object, as well as rooms, and even times. You can see how successive refinement of these ideas could lead to a rather sophisticated model of the information structure of the college. As a beginning programmer, you're probably not yet ready to tackle a college information system. For now, we'll study objects in the context of some simple graphics programming. 61CHAPTER 5. OBJECTS AND GRAPHICS 62 5.2 Graphics Programming Modern computer applications are not limited to the sort of textual input and output that we have been using so far. Most of the applications that you are familiar with probably have a so-called Graphical User Interface (GUI) that provides visual elements like windows, icons (representative pictures), buttons and menus. Interactive graphics programming can be very complicated; entire textbooks are devoted to the intricacies of graphics and graphical interfaces. Industrial-strength GUI applications are usually developed using a dedicated graphics programming framework. Python comes with its own standard GUI module called Tkinter. As GUI frameworks go, Tkinter is one of the simplest to use, and Python is great language for developing real-world GUIs. Even so, taking the time to learn Tkinter would detract from the more fundamental task of learning the principles of programming and design that are the focus of this book. To make learning easier, I have written a graphics library (graphics.py) for use with this book. This library is freely available as a Python module file 1 and you are welcome to use it as you see fit. Using the library is as easy as placing a copy of the graphics.py file in the same folder as your graphics program(s). Alternatively, you can put graphics.py in the system directory where other Python libraries are stored so that it can be used from any folder on the system. The graphics library makes it easy to write simple graphics programs. As you do, you will be learning principles of object-oriented programming and computer graphics that can be applied in more sophisticated graphical programming environments. The details of the graphics module will be explored in later sec- tions. Here we'll concentrate on a basic hands-on introduction to whet your appetite. As usual, the best way to start learning new concepts is to roll up your sleeves and try out some examples. The first step is to import the graphics module. Assuming you have placed graphics.py in an appropriate place, you can import the graphics commands into an interactive Python session. >>> import graphics Next we need to create a place on the screen where the graphics will appear. That place is a graphics window or GraphWin, which is provided by the graphics module. >>> win = graphics.GraphWin() This command creates a new window on the screen. The window will have the title '!+Graphics Window.$*& The GraphWin may overlap your Python interpreter window, so you might have to resize the Python window to make both fully visible. Figure 5.1 shows an example screen view. The GraphWin is an object, and we have assigned it to the the variable called win. We can manipulate the window object through this variable, similar to the way that file objects are manipulated through file variables. For example, when we are finished with a window, we can destroy it. This is done by issuing the close command. >>> win.close() Typing this command causes the window to vanish from the screen. We will be working with quite a few commands from the graphics library, and it gets tedious having to type the graphics. notation every time we use one. Python has an alternative form of import that can help out. from graphics import * The from statement allows you to load specific definitions from a library module. You can either list the names of definitions to be imported or use an asterisk, as shown, to import everything defined in the module. The imported commands become directly available without having to preface them with the module name. After doing this import, we can create a GraphWin more simply. win = GraphWin() 1 See Appendix A for information on how to obtain the graphics library and other supporting materials for this book.5.2. GRAPHICS PROGRAMMING 63 Figure 5.1: Screen shot with a Python window and a GraphWin All of the rest of the graphics examples will assume that the entire graphics module has been imported using from. Let's try our hand at some drawing. A graphics window is actually a collection of tiny points called pixels (short for picture elements). By controlling the color of each pixel, we control what is displayed in the window. By default, a GraphWin is 200 pixels tall and 200 pixels wide. That means there are 40,000 pixels in the GraphWin. Drawing a picture by assigning a color to each individual pixel would be a daunting challenge. Instead, we will rely on a library of graphical objects. Each type of object does its own bookkeeping and knows how to draw itself into a GraphWin. The simplest object in the graphics module is a Point. In geometry, a point is a dimensionless location in space. A point is located by reference to a coordinate system. Our graphics object Point is similar; it can represent a location in a GraphWin. We define a point by supplying x and y coordinates x !+" y . The x value represents the horizontal location of the point, and the y value represents the vertical. Traditionally, graphics programmers locate the point 0 (/* 0 in the upper-left corner of the window. Thus x values increase from left to right, and y values increase from top to bottom. In the default 200 x 200 GraphWin, the lower-right corner has the coordinates 199 #,- 199 . Drawing a Point sets the color of the corresponding pixel in the GraphWin. The default color for drawing is black. Here is a sample interaction with Python illustrating the use of Points. >>> >>> 50 >>> 60 >>> >>> >>> >>> p = Point(50,60) p.getX() p.getY() win = GraphWin() p.draw(win) p2 = Point(140,100) p2.draw(win) The first line creates a Point located at 50 !/' 60 . After the Point has been created, its coordinate values can be accessed by the operations getX and getY. A Point is drawn into a window using the draw operation. In this example, two different point objects (p and p2) are created and drawn into the GraphWin called win. Figure 5.2 shows the resulting graphical output. In addition to points, the graphics library contains commands for drawing lines, circles, rectangles, ovals,CHAPTER 5. OBJECTS AND GRAPHICS 64 Figure 5.2: Graphics window with two points drawn. polygons and text. Each of these objects is created and drawn in a similar fashion. Here is a sample interaction to draw various shapes into a GraphWin. >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> #### Open a graphics window win = GraphWin(+$%Shapes*.-) #### Draw a red circle centered at point (100,100) with radius 30 center = Point(100,100) circ = Circle(center, 30) circ.setFill(!""red+.!) circ.draw(win) #### Put a textual label in the center of the circle label = Text(center, "Red Circle") label.draw(win) #### Draw a square using a Rectangle object rect = Rectangle(Point(30,30), Point(70,70)) rect.draw(win) #### Draw a line segment using a Line object line = Line(Point(20,30), Point(180, 165)) line.draw(win) #### Draw an oval using the Oval object oval = Oval(Point(20,150), Point(180,199)) oval.draw(win) Try to figure out what each of these statements does. If you type them in as shown, the final result will look like Figure 5.3. 5.3 Using Graphical Objects Some of the examples in the above interactions may look a bit strange to you. To really understand the graphics module, we need to take an object-oriented point of view. Recall, objects combine data with operations. Computation is performed by asking an object to carry out one of its operations. In order to make use of objects, you need to know how to create them and how to request operations. In the interactive examples above, we manipulated several different kinds of objects: GraphWin, Point, Circle, Oval, Line, Text, and Rectangle. These are examples of classes. Every object is an instance of some class, and the class describes the properties the instance will have. Borrowing a biological metaphor, when we say that Fido is a dog, we are actually saying that Fido is a specific individual in the larger class of all dogs. In OO terminology, Fido is an instance of the dog class.5.3. USING GRAPHICAL OBJECTS 65 Figure 5.3: Various shapes from the graphics module. Because Fido is an instance of this class, we expect certain things. Fido has four legs, a tail, a cold, wet nose and he barks. If Rex is a dog, we expect that he will have similar properties, even though Fido and Rex may differ in specific details such as size or color. The same ideas hold for our computational objects. We can create two separate instances of Point, say p and p2. Each of these points has an x and y value, and they both support the same set of operations like getX and draw. These properties hold because the objects are Points. However, different instances can vary in specific details such as the values of their coordinates. To create a new instance of a class, we use a special operation called a constructor. A call to a constructor is an expression that creates a brand new object. The general form is as follows. (, , ...) Here is the name of the class that we want to create a new instance of, e.g., Circle or Point. The expressions in the parentheses are any parameters that are required to initialize the object. The number and type of the parameters depends on the class. A Point requires two numeric values, while a GraphWin can be constructed without any parameters. Typically, a constructor is used on the right side of an assignment statement, and the resulting object is immediately assigned to a variable on the left side that is then used to manipulate the object. To take a concrete example, let's look at what happens when we create a graphical point. Here is a constructor statement from the interactive example above. p = Point(50,60) The constructor for the Point class requires two parameters giving the x and y coordinates for the new point. These values are stored as instance variables inside of the object. In this case, Python creates an instance of Point having an x value of 50 and a y value of 60. The resulting point is then assigned to the variable p. A conceptual diagram of the result is shown in Figure 5.4. Note that, in this diagram as well as similar ones later on, only the most salient details are shown. Points also contain other information such as their color and which window (if any) they are drawn in. Most of this information is set to default values when the Point is created. To perform an operation on an object, we send the object a message. The set of messages that an object responds to are called the methods of the object. You can think of methods as functions that live inside of the object. A method is invoked using dot-notation. .(, , ...) The number and type of the parameters is determined by the method being used. Some methods require no parameters at all. You can find numerous examples of method invocation in the interactive examples above. As examples of parameterless methods, consider these two expressions.CHAPTER 5. OBJECTS AND GRAPHICS 66 Point p: x: 50 y: 60 Figure 5.4: Conceptual picture of the result of p = Point(50,60). The variable p refers to a freshly created Point having the given coordinates. p.getX() p.getY() The getX and getY methods return the x and y values of a point, respectively. Methods such as these are sometimes called accessors, because they allow us to access information from the instance variables of the object. Other methods change the values of an object's instance variables, hence changing the state of the object. All of the graphical objects have a move method. Here is a specification: move(dx, dy): Moves the object dx units in the x direction and dy units in the y direction. To move the point p to the right 10 units, we could use this statement. p.move(10,0) This changes the x instance variable of p by adding 10 units. If the point is currently drawn in a GraphWin, move will also take care of erasing the old image and drawing it in its new position. Methods that change the state of an object are sometimes called mutators. The move method must be supplied with two simple numeric parameters indicating the distance to move the object along each dimension. Some methods require parameters that are themselves complex objects. For example, drawing a Circle into a GraphWin involves two objects. Let's examine a sequence of commands that does this. circ = Circle(Point(100,100), 30) win = GraphWin() circ.draw(win) The first line creates a Circle with a center located at the Point 100 -%( 100 and a radius of 30. Notice that we used the Point constructor to create a location for the first parameter to the Circle constructor. The second line creates a GraphWin. Do you see what is happening in the third line? This is a request for the Circle object circ to draw itself into the GraphWin object win. The visible effect of this statement is a circle in the GraphWin centered at 100 (/) 100 and having a radius of 30. Behind the scenes, a lot more is happening. Remember, the draw method lives inside the circ object. Using information about the center and radius of the circle from the instance variables, the draw method issues an appropriate sequence of low- level drawing commands (a sequence of method invocations) to the GraphWin. A conceptual picture of the interactions among the Point, Circle and GraphWin objects is shown in Figure 5.5. Fortunately, we don't usually have to worry about these kinds of details; they're all taken care of by the graphical objects. We just create objects, call the appropriate methods, and let them do the work. That's the power of object-oriented programming. There is one subtle -($gotcha'/$ that you need to keep in mind when using objects. It is possible for two different variables to refer to exactly the same object; changes made to the object through one variable will also be visible to the other. Suppose we are trying to write a sequence of code that draws a smiley face. We want to create two eyes that are 20 units apart. Here is a sequence of code intended to draw the eyes.5.3. USING GRAPHICAL OBJECTS 67 Circle circ: Point center: radius: draw( . . . 30 ) x: 100 y: 100 Low-level drawing commands GraphWin win: . . . Figure 5.5: Object interactions to draw a circle. ## Incorrect way to create two circles. leftEye = Circle(Point(80, 50), 5) leftEye.setFill((#'yellow%*.) leftEye.setOutline($,"red/&/) rightEye = leftEye rightEye.move(20,0) The basic idea is to create the left eye and then copy that into a right eye which is then moved over 20 units. This doesn't work. The problem here is that only one Circle object is created. The assignment rightEye = leftEye simply makes rightEye refer to the very same circle as leftEye. Figure 5.6 shows the situation. When the Circle is moved in the last line of code, both rightEye and leftEye refer to it in its new location on the right side. This situation where two variables refer to the same object is called aliasing, and it can sometimes produce rather unexpected results. leftEye: Circle Point center: radius: 10 x: 80 y: 50 rightEye: Figure 5.6: Variables leftEye and rightEye are aliases. One solution to this problem would be to create a separate circle for each eye. ## A correct way to create two circles. leftEye = Circle(Point(80, 50), 5) leftEye.setFill(,.!yellow%)&)CHAPTER 5. OBJECTS AND GRAPHICS 68 leftEye.setOutline(-&+red%&*) rightEye = Circle(Point(100, 50), 5) rightEye.setFill(.*)yellow"()) rightEye.setOutline(,/#red+,,) This will certainly work, but it's cumbersome. We had to write duplicated code for the two eyes. That's easy to do using a )'/cut and paste"&& approach, but it's not very elegant. If we decide to change the appearance of the eyes, we will have to be sure to make the changes in two places. The graphics library provides a better solution; all graphical objects support a clone method that makes a copy of the object. Using clone, we can rescue the original approach. ## Correct way to create two circles, using clone. leftEye = Circle(Point(80, 50), 5) leftEye.setFill(()+yellow!-$) leftEye.setOutline(&*!red(/-) rightEye = leftEye.clone() # rightEye is an exact copy of the left rightEye.move(20,0) Strategic use of cloning can make some graphics tasks much easier. 5.4 Graphing Future Value Now that you have some idea of how to use objects from the graphics module, we're ready to try some real graphics programming. One of the most important uses of graphics is providing a visual representation of data. They say a picture is worth a thousand words; it is almost certainly better than a thousand numbers. Few programs that manipulate numeric data couldn't be improved with a bit of graphical output. Remember the program in Chapter 2 that computed the future value of a ten year investment? Let's try our hand at creating a graphical summary. Programming with graphics requires careful planning. You'll probably want pencil and paper handy to draw some diagrams and scratch out calculations as we go along. As usual, we begin by considering the specification of exactly what the program will do. The original program futval.py had two inputs, the amount of money to be invested and the annual- ized rate of interest. Using these inputs, the program calculated the change in principal year by year for ten years using the formula principal principal 1 apr . It then printed out the final value of the principal. In the graphical version, the output will be a ten-year bar graph where the height of successive bars represents the value of the principal in successive years. Let's use a concrete example for illustration. Suppose we invest $2000 at 10% interest. This table shows the growth of the investment over a ten-year period: Years 0 1 2 3 4 5 6 7 8 9 10 Value $2,000.00 $2,200.00 $2,420.00 $2,662.00 $2,928.20 $3,221.02 $3,542.12 $3,897.43 $4,287.18 $4,715.90 $5,187.49 Our program will display this information in a bar graph. Figure 5.7 shows the data in graphical form. The graph actually contains eleven bars. The first bar shows the original value of the principal. For reference, let's number these bars according the the number of years of interest accrued, 0*%/10. Here is a rough design for the program.5.4. GRAPHING FUTURE VALUE 69 Figure 5.7: Bar graph showing growth of $2,000 at 10% interest Print an introduction Get value of principal and apr from user Create a GraphWin Draw scale labels on left side of window Draw bar at position 0 with height corresponding to principal For successive years 1 through 10 Calculate principal = principal * (1 + apr) Draw a bar for this year having a height corresponding to principal Wait for user to press Enter The pause created by the last step is necessary to keep the graphics window displayed so that we can interpret the results. Without such a pause, the program would end and the GraphWin would vanish with it. While this design gives us the broad brush strokes for our algorithm, there are some very important details that have been glossed over. We must decide exactly how big the graphics window will be and how we will position the objects that appear in this window. For example, what does it mean to draw, say, a bar for year five with height corresponding to $3,221.02? Let's start with the size of the GraphWin. Recall that the size of a window is given in terms of the number of pixels in each dimension. Computer screens are also measured in terms of pixels. The number of pixels or resolution of the screen is determined by the monitor and graphics card in the computer you use. The lowest resolution screen you are likely to encounter these days is a so-called standard VGA screen that is 640 x 480 pixels. Most screens are considerably larger. Let's make the GraphWin one quarter the size of a 640 x 480 screen, or 320 x 240. That should allow all users to see the graphical output as well as the textual output from our program. Given this analysis, we can flesh out a bit of our design. The third line of the design should now read: Create a 320 x 240 GraphWin titled #-'#*.Investment Growth Chart%,*(,! You may be wondering how this will translate into Python code. You have already seen that the GraphWin constructor allows an optional parameter to specify the title of the window. You may also supply width and height parameters to control the size of the window. Thus, the command to create the output window will be: win = GraphWin("Investment Growth Chart", 320, 240) Next we turn to the problem of printing labels along the left edge of our window. To simplify the problem, we will assume the graph is always scaled to a maximum of $10,000 with the five labels (%-0.0K%$& to /$*10.0K*"% as shown in the example window. The question is how should the labels be drawn? We will need some TextCHAPTER 5. OBJECTS AND GRAPHICS 70 objects. When creating Text, we specify the anchor point (the point the text is centered on) and the string to use as the label. The label strings are easy. Our longest label is five characters, and the labels should all line up on the right side of a column, so the shorter strings will be padded on the left with spaces. The placement of the labels is chosen with a bit of calculation and some trial and error. Playing with some interactive examples, it seems that a string of length five looks nicely positioned in the horizontal direction placing the center 20 pixels in from the left edge. This leaves just a bit of whitespace at the margin. In the vertical direction, we have just over 200 pixels to work with. A simple scaling would be to have 100 pixels represent $5,000. That means our five labels should be spaced 50 pixels apart. Using 200 pixels for the range 0"!+10,000 leaves 240 200 40 pixels to split between the top and bottom margins. We might want to leave a little more margin at the top to accommodate values that grow beyond $10,000. A little experimentation suggests that putting the '*/ 0.0K&%$ label 10 pixels from the bottom (position 230) seems to look nice. Elaborating our algorithm to include these details, the single step Draw scale labels on left side of window becomes a sequence of steps Draw Draw Draw Draw Draw label label label label label " 0.0K" " 2.5K" " 5.0K" " 7.5K" "10.0K" at at at at at (20, (20, (20, (20, (20, 230) 180) 130) 80) 30) The next step in the original design calls for drawing the bar that corresponds to the initial amount of the principal. It is easy to see where the lower left corner of this bar should be. The value of $0.0 is located vertically at pixel 230, and the labels are centered 20 pixels in from the left edge. Adding another 20 pixels gets us to the right edge of the labels. Thus the lower left corner of the 0th bar should be at location 40 ,'- 230 . Now we just need to figure out where the opposite (upper right) corner of the bar should be so that we can draw an appropriate rectangle. In the vertical direction, the height of the bar is determined by the value of principal. In drawing the scale, we determined that 100 pixels is equal to $5,000. This means that we have 100 5000 0 02 pixels to the dollar. This tells us, for example, that a principal of $2,000 should 40 pixels. In general, the y position of the upper-right corner will be produce a bar of height 2000 02 given by 230 principal 0 02 . (Remember that 230 is the 0 point, and the y coordinates decrease going up). How wide should the bar be? The window is 320 pixels wide, but 40 pixels are eaten up by the labels on the left. That leaves us with 280 pixels for 11 bars 280 11 25 4545. Let's just make each bar 25 pixels; that will give us a bit of margin on the right side. So, the right edge of our first bar will be at position 40 25 65. We can now fill the details for drawing the first bar into our algorithm. Draw a rectangle from (40, 230) to (65, 230 - principal * 0.02) At this point, we have made all the major decisions and calculations required to finish out the problem. All that remains is to percolate these details into the rest of the algorithm. Figure 5.8 shows the general layout of the window with some of the dimensions we have chosen. Let's figure out where the lower-left corner of each bar is going to be located. We chose a bar width of 25, so the bar for each successive year will start 25 pixels farther right than the previous year. We can use a variable year to represent the year number and calculate the x coordinate of the lower left corner as year 25 40. (The 40 leaves space on the left edge for the labels.) Of course, the y coordinate of this point is still 230 (the bottom of the graph). To find the upper-right corner of a bar, we add 25 (the width of the bar) to the x value of the lower-left corner. The y value of the upper right corner is determined from the (updated) value of principal exactly as we determined it for the first bar. Here is the refined algorithm. for year running from a value of 1 up through 10: Calculate principal = principal * (1 + apr)5.4. GRAPHING FUTURE VALUE 71 (0,0) 10.0K 7.5K 240 5.0K 2.5K 50 0.0K 10 (315,230) (40,230) (319,239) 40 25 320 Figure 5.8: Position of elements in future value bar graph. Calculate xll = 25 * year + 40 Calculate height = principal * 0.02 Draw a rectangle from (xll, 230) to (xll+25, 230 - height) The variable xll stands for x lower left"$!the x value of the lower left corner of the bar. Putting all of this together produces the detailed algorithm shown below. Print an introduction Get value of principal and apr from user Create a 320x240 GraphWin titled ++,#*(Investment Growth Chart/&,,,' Draw label " 0.0K" at (20,230) Draw label " 2.5K" at (20, 180) Draw label " 5.0K" at (20, 130) Draw label " 7.5K" at (20, 80) Draw label "10.0K" at (20, 30) Draw a rectangle from (40, 230) to (65, 230 - principal * 0.02) for year running from a value of 1 up through 10: Calculate principal = principal * (1 + apr) Calculate xll = 25 * year + 40 Draw a rectangle from (xll, 230) to (xll+25, 230 - principal * 0.02) Wait for user to press Enter Whew! That was a lot of work, but we are finally ready to translate this algorithm into actual Python code. The translation is straightforward using objects from the graphics module. Here's the program: # futval_graph.py from graphics import * def main(): # Introduction print "This program plots the growth of a 10-year investment." # Get principal and interest rate principal = input("Enter the initial principal: ") apr = input("Enter the annualized interest rate: ")CHAPTER 5. OBJECTS AND GRAPHICS 72 # Create a graphics window with labels on left edge win = GraphWin("Investment Growth Chart", 320, 240) win.setBackground("white") Text(Point(20, 230), '&/ 0.0K#/!).draw(win) Text(Point(20, 180), %&" 2.5K+#&).draw(win) Text(Point(20, 130), ..% 5.0K$(/).draw(win) Text(Point(20, 80), $#* 7.5k(-,).draw(win) Text(Point(20, 30), /,"10.0K+!.).draw(win) # Draw bar for initial principal height = principal * 0.02 bar = Rectangle(Point(40, 230), Point(65, 230-height)) bar.setFill("green") bar.setWidth(2) bar.draw(win) # Draw bars for successive years for year in range(1,11): # calculate value for the next year principal = principal * (1 + apr) # draw bar for this value xll = year * 25 + 40 height = principal * 0.02 bar = Rectangle(Point(xll, 230), Point(xll+25, 230-height)) bar.setFill("green") bar.setWidth(2) bar.draw(win) raw_input("Press to quit.") win.close() main() If you study this program carefully, you will see that I added a number of features to spruce it up a bit. All graphical objects support methods for changing color. I have set the background color of the window to white (by default it's gray). win.setBackground("white") I have also changed the color of the bar object. The following line asks the bar to color its interior green (because it's money, you know). bar.setFill("green") You can also change the color of a shape's outline using the setOutline method. In this case, I have chosen to leave the outline the default black so that the bars stand out from each other. To enhance this effect, this code makes the outline wider (two pixels instead of the default one). bar.setWidth(2) You might also have noted the economy of notation in drawing the labels. Since we don't ever change the labels, saving them into a variable is unnecessary. We can just create a Text object, tell it to draw itself, and be done with it. Here is an example. Text(Point(20,230), #'+ 0.0K"!").draw(win) Finally, take a close look at the use of the year variable in the loop.5.5. CHOOSING COORDINATES 73 for year in range(1,11): The expression range(1,11) produces a sequence of ints 1)%.10. The loop index variable year marches through this sequence on successive iterations of the loop. So, the first time through year is 1, then 2, then 3, etc., up to 10. The value of year is then used to compute the proper position of the lower left corner of each bar. xll = year * 25 + 40 I hope you are starting to get the hang of graphics programming. It's a bit strenuous, but very addictive. 5.5 Choosing Coordinates The lion's share of the work in designing the futval graph program was in determining the precise coor- dinates where things would be placed on the screen. Most graphics programming problems require some sort of a coordinate transformation to change values from a real-world problem into the window coordinates that get mapped onto the computer screen. In our example, the problem domain called for x values representing the year (0+/!10) and y values representing monetary amounts ($0(%'$10,000). We had to transform these val- ues to be represented in a 320 x 240 window. It's nice to work through an example or two to see how this transformation happens, but it makes for tedious programming. Coordinate transformation is an integral and well-studied component of computer graphics. It doesn't take too much mathematical savvy to see that the transformation process always follows the same general pattern. Anything that follows a pattern can be done automatically. In order to save you the trouble of having to explicitly convert back and forth between coordinate systems, the graphics module provides a simple mechanism to do it for you. When you create a GraphWin you can specify a coordinate system for the window using the setCoords method. The method requires four parameters specifying the coordinates of the lower-left and upper-right corners, respectively. You can then use this coordinate system to place graphical objects in the window. To take a simple example, suppose we just want to divide the window into nine equal squares, Tic-Tac- Toe fashion. This could be done without too much trouble using the default 200 x 200 window, but it would require a bit of arithmetic. The problem becomes trivial if we first change the coordinates of the window to run from 0 to 3 in both dimensions. # create a default 200x200 window win = GraphWin("Tic-Tac-Toe") # set coordinates to go from (0,0) in the lower left # to (3,3) in the upper right. win.setCoords(0.0, 0.0, 3.0, 3.0) # Draw vertical lines Line(Point(1,0), Point(1,3)).draw(win) Line(Point(2,0), Point(2,3)).draw(win) # Draw horizontal lines Line(Point(0,1), Point(3,1)).draw(win) Line(Point(0,2), Point(3,2)).draw(win) Another benefit of this approach is that the size of the window can be changed by simply changing the di- mensions used when the window is created (e.g. win = GraphWin("Tic-Tac-Toe", 300, 300)). Because the same coordinates span the window (due to setCoords) the objects will scale appropriately to the new window size. Using $(.raw#,! window coordinates would require changes in the definitions of the lines. We can apply this idea to simplify our graphing future value program. Basically, we want our graphics window to go from 0 through 10 (representing years) in the x dimension and from 0 to 10,000 (representing dollars) in the y dimension. We could create just such a window like this.CHAPTER 5. OBJECTS AND GRAPHICS 74 win = GraphWin("Investment Growth Chart", 320, 240) win.setCoords(0.0, 0.0, 10.0, 10000.0) Then creating a bar for any values of year and principal would be simple. Each bar starts at the given year and a baseline of 0 and grows to the next year and a height equal to principal. bar = Rectangle(Point(year, 0), Point(year+1, principal)) There is a small problem with this scheme. Can you see what I have forgotten? The bars will fill the entire window; we haven't left any room for labels or margins around the edges. This is easily fixed by expanding the coordinates of the window slightly. Since our bars start at 0, we can locate the left side labels at -1. We can add a bit of whitespace around the graph by expanding the coordinates slightly beyond that required for our graph. A little experimentation leads to this window definition: win = GraphWin("Investment Growth Chart", 320, 240) win.setCoords(-1.75,-200, 11.5, 10400) Here is the program again, using the alternative coordinate system: # futval_graph2.py from graphics import * def main(): # Introduction print "This program plots the growth of" print "a 10-year investment." # Get principal and interest rate principal = input("Enter the initial principal: ") apr = input("Enter the annualized interest rate: ") # Create a graphics window with labels on left edge win = GraphWin("Investment Growth Chart", 320, 240) win.setBackground("white") win.setCoords(-1.75,-200, 11.5, 10400) Text(Point(-1, 0), %$- 0.0K!*").draw(win) Text(Point(-1, 2500), )&) 2.5K./').draw(win) Text(Point(-1, 5000), "(' 5.0K*"#).draw(win) Text(Point(-1, 7500), )$) 7.5k)+-).draw(win) Text(Point(-1, 10000), //&10.0K%))).draw(win) # Draw bar for initial principal bar = Rectangle(Point(0, 0), Point(1, principal)) bar.setFill("green") bar.setWidth(2) bar.draw(win) # Draw a bar for each subsequent year for year in range(1, 11): principal = principal * (1 + apr) bar = Rectangle(Point(year, 0), Point(year+1, principal)) bar.setFill("green") bar.setWidth(2) bar.draw(win)5.6. INTERACTIVE GRAPHICS 75 raw_input("Press to quit.") main() Notice how the complex calculations have been eliminated. This version also makes it easy to change the size of the GraphWin. Changing the window size to 640 x 480 produces a larger, but correctly drawn bar graph. In the original program, all of the calculations would have to be redone to accommodate the new scaling factors in the larger window. Obviously, the second version of our program is much easier to develop and understand. When you are doing graphics programming, give some consideration to choosing a coordinate system that will make your task as simple as possible. 5.6 Interactive Graphics Graphical interfaces can be used for input as well as output. In a GUI environment, users typically interact with their applications by clicking on buttons, choosing items from menus, and typing information into on-screen text boxes. These applications use a technique called event-driven programming. Basically, the program draws a set of interface elements (often called widgets) on the screen, and then waits for the user to do something. When the user moves the mouse, clicks a button or types a key on the keyboard, this generates an event. Basically, an event is an object that encapsulates data about what just happened. The event object is then sent off to an appropriate part of the program to be processed. For example, a click on a button might produce a button event. This event would be passed to the button handling code, which would then perform the appropriate action corresponding to that button. Event-driven programming can be tricky for novice programmers, since it's hard to figure out &-$who's in charge*%- at any given moment. The graphics module hides the underlying event-handling mechanisms and provides two simple ways of getting user input in a GraphWin. 5.6.1 Getting Mouse Clicks We can get graphical information from the user via the getMouse method of the GraphWin class. When getMouse is invoked on a GraphWin, the program pauses and waits for the user to click the mouse somewhere in the graphics window. The spot where the user clicks is returned to the program as a Point. Here is a bit of code that reports the coordinates of ten successive mouse clicks. from graphics import * win = GraphWin("Click Me!") for i in range(10): p = win.getMouse() print "You clicked (%d, %d)" % (p.getX(), p.getY()) The value returned by getMouse() is a ready-made Point. We can use it like any other point using accessors such as getX and getY or other methods such as draw and move. Here is an example of an interactive program that allows the user to draw a triangle by clicking on three points in a graphics window. This example is completely graphical, making use of Text objects as prompts. No interaction with a Python text window is required. If you are programming in a Windows environment, you can name this program using a .pyw extension. Then when the program is run, it will not even display the Python shell window. # Program: triangle.pyw from graphics import * def main(): win = GraphWin("Draw a Triangle")CHAPTER 5. OBJECTS AND GRAPHICS 76 win.setCoords(0.0, 0.0, 10.0, 10.0) message = Text(Point(5, 0.5), "Click on three points") message.draw(win) # Get and draw three vertices of triangle p1 = win.getMouse() p1.draw(win) p2 = win.getMouse() p2.draw(win) p3 = win.getMouse() p3.draw(win) # Use Polygon object to draw the triangle triangle = Polygon(p1,p2,p3) triangle.setFill("peachpuff") triangle.setOutline("cyan") triangle.draw(win) # Wait for another click to exit message.setText("Click anywhere to quit.") win.getMouse() main() The three-click triangle illustrates a couple new features of the graphics module. There is no triangle class; however there is a general class Polygon that can be used for any multi-sided, closed shape. The constructor for Polygon accepts any number of points and creates a polygon by using line segments to connect the points in the order given and to connect the last point back to the first. A triangle is just a three-sided polygon. Once we have three Points p1, p2, and p3, creating the triangle is a snap. triangle = Polygon(p1, p2, p3) You should also study how the Text object is used to provide prompts. A single Text object is created and drawn near the beginning of the program. message = Text(Point(5, 0.5), "Click on three points") message.draw(win) To change the prompt, we don't need to create a new Text object, we can just change the text that is displayed. This is done near the end of the program with the setText method. message.setText("Click anywhere to quit.") As you can see, the getMouse method of GraphWin provides a simple way of interacting with the user in a graphics-oriented program. 5.6.2 Handling Textual Input In the triangle example, all of the input was provided through mouse clicks. The graphics module also includes a simple Entry object that can be used to get keyboard input in a GraphWin. An Entry object draws a box on the screen that can contain text. It understands setText and getText methods just like the Text object does. The difference is that the contents of an Entry can be edited by the user. Here's a version of the temperature conversion program from Chapter 2 with a graphical user interface: # convert_gui1.pyw # Program to convert Celsius to Fahrenheit using a simple # graphical interface.5.6. INTERACTIVE GRAPHICS 77 from graphics import * def main(): win = GraphWin("Celsius Converter", 300, 200) win.setCoords(0.0, 0.0, 3.0, 4.0) # Draw the interface Text(Point(1,3), " Celsius Temperature:").draw(win) Text(Point(1,1), "Fahrenheit Temperature:").draw(win) input = Entry(Point(2,3), 5) input.setText("0.0") input.draw(win) output = Text(Point(2,1),"") output.draw(win) button = Text(Point(1.5,2.0),"Convert It") button.draw(win) Rectangle(Point(1,1.5), Point(2,2.5)).draw(win) # wait for a mouse click win.getMouse() # convert input celsius = eval(input.getText()) fahrenheit = 9.0/5.0 * celsius + 32 # display output and change button output.setText("%0.1f" % fahrenheit) button.setText("Quit") # wait for click and then quit win.getMouse() win.close() main() When run, this produces a window with an entry box for typing in a Celsius temperature and a #.*button+$) for doing the conversion. The button is just for show. The program actually just pauses for a mouse click anywhere in the window. Figure 5.9 shows how the window looks when the program starts. Initially, the input entry box is set to contain the value 0.0. The user can delete this value and type in another temperature. The program pauses until the user clicks the mouse. Notice that the point where the user clicks is not even saved; the getMouse function is just used to pause the program until the user has a chance to enter a value in the input box. The program then processes the input in four steps. First, the text in the input box is converted into a number (via eval). This number is then converted to degrees Fahrenheit. Finally, the resulting number is turned back into a string (via the string formatting operator) for display in the output text area. Figure 5.10 shows how the window looks after the user has typed an input and clicked the mouse. Notice that the converted temperature shows up in the output area, and the label on the button has changed to "%"Quit//* to show that clicking again will exit the program. This example could be made much prettier using some of the options in the graphics library for changing the colors, sizes and line widths of the various widgets. The code for the program is deliberately Spartan to illustrate just the essential elements of GUI design. Although the basic tools getMouse and Entry do not provide a full-fledged GUI environment, we will see in later chapters how these simple mechanisms can support surprisingly rich interactions.78 CHAPTER 5. OBJECTS AND GRAPHICS Figure 5.9: Initial screen for graphical temperature converter Figure 5.10: Graphical temperature converter after user input.5.7. GRAPHICS MODULE REFERENCE 79 5.7 Graphics Module Reference The examples in this chapter have touched on most of the elements in the graphics module. This section provides a complete reference to the objects and functions provided in the graphics library. Experienced programmers use these sorts of guides to learn about new libraries. You will probably want to refer back to this section often when you are writing your own graphical programs. 5.7.1 GraphWin Objects A GraphWin object represents a window on the screen where graphical images may be drawn. A program may define any number of GraphWins. A GraphWin understands the following methods. GraphWin(title, width, height) Constructs a new graphics window for drawing on the screen. The parameters are optional, the default title is "/-Graphics Window,*(/ and the default size is 200 x 200. plot(x, y, color) Draws the pixel at x //& y in the window. Color is optional, black is the default. plotPixel(x, y, Color) Draws the pixel at the %$/raw%!$ position x (#) y ignoring any coordinate trans- formations set up by setCoords. setBackground(color) Sets the window background to the given color. The initial background is gray. See section 5.7.5 for information on specifying colors. close() Closes the on-screen window. getMouse() Pauses for the user to click a mouse in the window and returns where the mouse was clicked as a Point object. setCoords(xll, yll, xur, yur) Sets the coordinate system of the window. The lower left corner is xll %%. yll and the upper right corner is xur '(" yur . All subsequent drawing will be done with respect to the altered coordinate system (except for plotPixel). flush() Updates the appearance of the window to reflect all drawing operations that have been done so far. In normal operation, the window is only updated during /!.idle(!% periods. A sequence of drawing commands may end up appearing all at once. If you want to get an animation effect, flush should be called at appropriate places in the program to perform drawing operations incrementally. 5.7.2 Graphics Objects The module provides the following classes of drawable objects: Point, Line, Circle, Oval, Rectangle, Polygon, and Text. All objects are initially created unfilled with a black outline. All graphics objects sup- port the following generic set of methods. setFill(color) Sets the interior of the object to the given color. setOutline(color) Sets the outline of the object to the given color. setWidth(pixels) Sets the width of the outline of the object to this many pixels. draw(aGraphWin) Draws the object into the given GraphWin. undraw() Undraws the object from a graphics window. This produces an error if the object is not currently drawn. move(dx,dy) Moves the object dx units in the x direction and dy units in the y direction. If the object is currently drawn, the image is adjusted to the new position. clone() Returns a duplicate of the object. Clones are always created in an undrawn state. Other than that, they are identical to the cloned object.80 CHAPTER 5. OBJECTS AND GRAPHICS Point Methods Point(x,y) Constructs a point having the given coordinates. getX() Returns the x coordinate of a point. getY() Returns the y coordinate of a point. Line Methods Line(point1, point2) Constructs a line segment from point1 to point2. setArrow(string) Sets the arrowhead status of a line. Arrows may be drawn at either the first point, the last point, or both. Possible values of string are ))$first&$#, &#"last#.&, "#-both&*/, and &&%none$,$. The default setting is ,$$none*#+. getCenter() Returns a clone of the midpoint of the line segment. getP1(), getP2() Returns a clone of the corresponding endpoint of the segment. Circle Methods Circle(centerPoint, radius) Constructs a circle with given center point and radius. getCenter() Returns a clone of the center point of the circle. getRadius() Returns the radius of the circle. getP1(), getP2() Returns a clone of the corresponding corner of the circle's bounding box. These are opposite corner points of a square that circumscribes the circle. Rectangle Methods Rectangle(point1, point2) Constructs a rectangle having opposite corners at point1 and point2. getCenter() Returns a clone of the center point of the rectangle. getP1(), getP2() Returns a clone of corner points originally used to construct the rectangle. Oval Methods Oval(point1, point2) Constructs an oval in the bounding box determined by point1 and point2. getCenter() Returns a clone of the point at the center of the oval. getP1(), getP2 Return a clone of the corresponding point used to construct the oval. Polygon Methods Polygon(point1, point2, point3, ...) Constructs a polygon having the given points as ver- tices. getPoints() Returns a list containing clones of the points used to construct the polygon.5.7. GRAPHICS MODULE REFERENCE 81 Text Methods Text(anchorPoint, string) Constructs a text object that displays the given string centered at anchorPoint. The text is displayed horizontally. setText(string) Sets the text of the object to string. getText() Returns the current string. getAnchor() Returns a clone of the anchor point. setFace(family) Changes the font face to the given family. Possible values are: .,%helvetica))(, ,&"courier%)', **)times roman/'*, and .,'arial*'+. setSize(point) Changes the font size to the given point size. Sizes from 5 to 36 points are legal. setStyle(style) Changes font to the given style. Possible values are: -$&normal)-(, $$+bold($%, '/*italic/%#, and &&%bold italic.%#. 5.7.3 Entry Objects Objects of type Entry are displayed as text entry boxes that can be edited by the user of the program. Entry objects support the generic graphics methods move(), draw(graphwin), undraw(), setFill(color), and clone(). The Entry specific methods are given below. Entry(centerPoint, width) Constructs an Entry having the given center and width. The width is specified in number of characters of text that can be displayed. getAnchor() Returns a clone of the point where the entry box is centered. getText() Returns the string of text that is currently in the entry box. setText(string) Sets the text in the entry box to the given string. 5.7.4 Displaying Images The graphics module also provides minimal support for displaying certain image formats into a GraphWin. Most platforms will support JPEG, PPM and GIF images. Display is done with an Image object. Images support the generic methods move(dx,dy), draw(graphwin), undraw(), and clone(). Image specific methods are given below. Image(centerPoint, filename) Constructs an image from contents of the given file, centered at the given center point. getAnchor() Returns a clone of the point where the image is centered. 5.7.5 Generating Colors Colors are indicated by strings. Most normal colors such as /+'red'%-, )#(purple.#&, &&!green)&$, -,+cyan.,', etc. should be available. Many colors come in various shades, such as "",red1-(), ''$red2-%(,#-"red3*++, ')"red4."., which are increasingly darker shades of red. The graphics module also provides a function for mixing your own colors numerically. The function color rgb(red, green, blue) will return a string representing a color that is a mixture of the inten- sities of red, green and blue specified. These should be ints in the range 0#&*255. Thus color rgb(255, 0, 0) is a bright red, while color rgb(130, 0, 130) is a medium magenta.CHAPTER 5. OBJECTS AND GRAPHICS 82 5.8 Exercises 1. Pick an example of an interesting real-world object and describe it as a programming object by listing its data (attributes, what it %$-knows+,') and its methods (behaviors, what it can .!.do'(%). 2. Describe in your own words the object produced by each of the following operations from the graphics module. Be as precise as you can. Be sure to mention such things as the size, position, and appearance of the various objects. You may include a sketch if that helps. (a) Point(130,130) (b) c = Circle(Point(30,40),25) c.setFill(","blue"&.) c.setOutline(*!"red&.") (c) r = Rectangle(Point(20,20), Point(40,40)) r.setFill(color_rgb(0,255,150)) r.setWidth(3) (d) l = Line(Point(100,100), Point(100,200)) l.setOutline(.-#red4,//) l.setArrow(-+#first+&') (e) Oval(Point(50,50), Point(60,100)) (f) shape = Polygon(Point(5,5), Point(10,10), Point(5,10), Point(10,5)) shape.setFill("$'orange/+&) (g) t = Text(Point(100,100), "Hello World!") t.setFace("courier") t.setSize(16) t.setStyle("italic") 3. Describe what happens when the following interactive graphics program runs. from graphics import * def main(): win = GraphWin() shape = Circle(Point(50,50), 20) shape.setOutline("red") shape.setFill("red") shape.draw(win) for i in range(10): p = win.getMouse() c = shape.getCenter() dx = p.getX() - c.getX() dy = p.getY() - c.getY() shape.move(dx,dy) win.close() 4. Modify the program from the previous problem in the following ways: (a) Make it draw squares instead of circles. (b) Have each successive click draw an additional square on the screen (rather than moving the ex- isting one). (c) Print a message on the window when all the clicks is entered, and wait for a final click before closing the window.5.8. EXERCISES 83 5. An archery target consists of a central circle of yellow surrounded by concentric rings of red, blue, black and white. Each ring has the same +$#width,$%( which is the same as the radius of the yellow circle. Write a program that draws such a target. Hint: Objects drawn later will appear on top of objects drawn earlier. 6. Write a program that draws some sort of face. 7. Write a program that draws a winter scene with a Christmas tree and a snowman. 8. Write a program that draws 5 dice on the screen depicting a straight (1, 2, 3, 4, 5 or 2, 3, 4, 5, 6). 9. Modify the graphical future value program so that the input (principal and apr) also are done in a graphical fashion using Entry objects. 10. Circle Intersection. Write a program that computes the intersection of a circle with a horizontal line and displays the information textually and graphically. Input: Radius of the circle and the y-intercept of the line. Output: Draw a circle centered at 0 #// 0 with the given radius in a window with coordinates running from -10,-10 to 10,10. Draw a horizontal line across the window with the given y-intercept. Draw the two points of intersection in red. Print out the x values of the points of intersection. Formula: x r 2 y 2 11. Line Information. This program allows the user to draw a line segment and then displays some graphical and textual information about the line segment. Input: 2 mouse clicks for the end points of the line segment. Output: Draw the midpoint of the segment in cyan. Draw the line. Print the length and the slope of the line. dx x 2 x 1 dy y 2 y 1 slope dy dx length dx 2 dy 2 Formulas: 12. Rectangle Information. This program displays information about a rectangle drawn by the user. Input: 2 mouse clicks for the opposite corners of a rectangle. Output: Draw the rectangle. Print the perimeter and area of the rectangle. Formulas: length width area perimeter 2 length width 13. Triangle Information. Same as previous problem, but with 3 clicks for the verticies of a triangle. Formulas: For perimeter, see length from line problem. s s a s b s c where a +,. b ,*+ and c are the lengths of the sides and s area a b c 2CHAPTER 5. OBJECTS AND GRAPHICS 84 14. 5-click house. You are to write a program that allows the user to draw a simple house using five mouse-clicks. The first two clicks will be the opposite corners of the rectangular frame of the house. The third click will indicate the center of the top edge of a rectangular door. The door should have a total width that is 15 of the width of the house frame. The sides of the door should extend from the corners of the top down to the bottom of the frame. The fourth click will indicate the center of a square window. The window is half as wide as the door. The last click will indicate the peak of the roof. The edges of the roof will extend from the point at the peak to the corners of the top edge of the house frame. 5 2 3 1 4Chapter 6 Defining Functions The programs that we have written so far comprise a single function, usually called main. We have also been using pre-written functions and methods including built-in Python functions (e.g., abs), functions from the Python standard libraries (e.g., math.sqrt, string.split), and object methods from the graphics module (e.g., myPoint.getX()). Functions are an important tool for building sophisticated programs. This chapter covers the whys and hows of designing your own functions to make your programs easier to write and understand. 6.1 The Function of Functions In the previous chapter, we looked at a graphic solution to the future value problem. This program makes use of the graphics library to draw a bar chart showing the growth of an investment. Here is the program as we left it: # futval_graph2.py from graphics import * def main(): # Introduction print "This program plots the growth of a 10-year investment." # Get principal and interest rate principal = input("Enter the initial principal: ") apr = input("Enter the annualized interest rate: ") # Create a graphics window with labels on left edge win = GraphWin("Investment Growth Chart", 640, 480) win.setBackground("white") win.setCoords(-1.75,-200, 11.5, 10400) Text(Point(-1, 0), &-" 0.0K%*,).draw(win) Text(Point(-1, 2500), .%) 2.5K%-,).draw(win) Text(Point(-1, 5000), $,. 5.0K)/#).draw(win) Text(Point(-1, 7500), ##. 7.5k+,-).draw(win) Text(Point(-1, 10000), "'!10.0K'**).draw(win) # Draw bar for initial principal bar = Rectangle(Point(0, 0), Point(1, principal)) bar.setFill("green") bar.setWidth(2) bar.draw(win) 85CHAPTER 6. DEFINING FUNCTIONS 86 # Draw a bar for each subsequent year for year in range(1, 11): principal = principal * (1 + apr) bar = Rectangle(Point(year, 0), Point(year+1, principal)) bar.setFill("green") bar.setWidth(2) bar.draw(win) raw_input("Press to quit.") This is certainly a workable program, but there is a nagging issue of program style that really should be addressed. Notice that this program draws bars in two different places. The initial bar is drawn just before the loop, and the subsequent bars are drawn inside of the loop. Having similar code like this in two places has some drawbacks. Obviously, one issue is having to write the code twice. A more subtle problem is that the code has to be maintained in two different places. Should we decide to change the color or other facets of the bars, we would have to make sure these changes occurred in both places. Failing to keep related parts of the code in synch is a common problem in program maintenance. Functions can be used to reduce code duplication and make programs more understandable and easier to maintain. Before fixing up the future value program, let's take look at what functions have to offer. 6.2 Functions, Informally You can think of a function as a subprogram)/.a small program inside of a program. The basic idea of a function is that we write a sequence of statements and give that sequence a name. The instructions can then be executed at any point in the program by referring to the function name. The part of the program that creates a function is called a function definition. When a function is subsequently used in a program, we say that the definition is called or invoked. A single function definition may be called at many different points of a program. Let's take a concrete example. Suppose you want to write a program that prints out the lyrics to the *+)Happy Birthday-*% song. The standard lyrics look like this. Happy Happy Happy Happy birthday to you! birthday to you! birthday, dear . birthday to you! We're going to play with this example in the interactive Python environment. You might want to fire up Python and try some of this out for yourself. A simple approach to this problem is to use four print statements. Here's an interactive session that creates a program for singing Happy Birthday to Fred. >>> def main(): print "Happy print "Happy print "Happy print "Happy birthday to you!" birthday to you!" birthday, dear Fred." birthday to you!" We can then run this program to get our lyrics. >>> main() Happy birthday to you! Happy birthday to you! Happy birthday, dear Fred. Happy birthday to you!6.2. FUNCTIONS, INFORMALLY 87 Obviously, there is some duplicated code in this program. For such a simple program, that's not a big deal, but even here it's a bit annoying to keep retyping the same line. Let's introduce a function that prints the lyrics of the first, second, and fourth lines. >>> def happy(): print "Happy birthday to you!" We have defined a new function called happy. Here is an example of what it does. >>> happy() Happy birthday to you! Invoking the happy command causes Python to print a line of the song. Now we can redo the verse for Fred using happy. Let's call our new version singFred. >>> def singFred(): happy() happy() print "Happy birthday, dear Fred." happy() This version required much less typing, thanks to the happy command. Let's try printing the lyrics for Fred just to make sure it works. >>> singFred() Happy birthday to you! Happy birthday to you! Happy birthday, dear Fred. Happy birthday to you! So far, so good. Now suppose that it's also Lucy's birthday, and we want to sing a verse for Fred followed by a verse for Lucy. We've already got the verse for Fred; we can prepare one for Lucy as well. >>> def singLucy(): happy() happy() print "Happy birthday, dear Lucy." happy() Now we can write a main program that sings to both Fred and Lucy. >>> def main(): singFred() print singLucy() The bare print between the two function calls puts a space between the verses in our output. And here's the final product in action. >>> main() Happy birthday to you! Happy birthday to you! Happy birthday, dear Fred. Happy birthday to you! Happy Happy Happy Happy birthday to you! birthday to you! birthday, dear Lucy. birthday to you!CHAPTER 6. DEFINING FUNCTIONS 88 Well now, that certainly seems to work, and we've removed some of the duplication by defining the happy function. However, something still doesn't feel quite right. We have two functions, singFred and singLucy, that are almost identical. Following this approach, adding a verse for Elmer would have us create a singElmer function that looks just like those for Fred and Lucy. Can't we do something about the proliferation of verses? Notice that the only difference between singFred and singLucy is the name at the end of the third print statement. The verses are exactly the same except for this one changing part. We can collapse these two functions together by using a parameter. Let's write a generic function called sing. >>> def sing(person): happy() happy() print "Happy Birthday, dear", person + "." happy() This function makes use of a parameter named person. A parameter is a variable that is initialized when the function is called. We can use the sing function to print a verse for either Fred or Lucy. We just need to supply the name as a parameter when we invoke the function. >>> sing("Fred") Happy birthday to you! Happy birthday to you! Happy Birthday, dear Fred. Happy birthday to you! >>> sing("Lucy") Happy birthday to you! Happy birthday to you! Happy Birthday, dear Lucy. Happy birthday to you! Let's finish with a program that sings to all three of our birthday people. >>> def main(): sing("Fred") print sing("Lucy") print sing("Elmer") It doesn't get much easier than that. Here is the complete program as a module file. # happy.py def happy(): print "Happy Birthday to you!" def sing(person): happy() happy() print "Happy birthday, dear", person + "." happy() def main(): sing("Fred") print6.3. FUTURE VALUE WITH A FUNCTION 89 sing("Lucy") print sing("Elmer") 6.3 Future Value with a Function Now that you've seen how defining functions can help solve the code duplication problem, let's return to the future value graph. Recall the problem is that bars of the graph are printed at two different places in the program. The code just before the loop looks like this. # Draw bar for initial principal bar = Rectangle(Point(0, 0), Point(1, principal)) bar.setFill("green") bar.setWidth(2) bar.draw(win) And the code inside of the loop is as follows. bar = Rectangle(Point(year, 0), Point(year+1, principal)) bar.setFill("green") bar.setWidth(2) bar.draw(win) Let's try to combine these two into a single function that draws a bar on the screen. In order to draw the bar, we need some information. Specifically, we need to know what year the bar will be for, how tall the bar will be, and what window the bar will be drawn in. These three values will be supplied as parameters for the function. Here's the function definition. def drawBar(window, year, height): # Draw a bar in window for given year with given height bar = Rectangle(Point(year, 0), Point(year+1, height)) bar.setFill("green") bar.setWidth(2) bar.draw(window) To use this function, we just need to supply values for the three parameters. For example, if win is a GraphWin, we can draw a bar for year 0 and a principal of $2,000 by invoking drawBar like this. drawBar(win, 0, 2000) Incorporating the drawBar function, here is the latest version of our future value program. # futval_graph3.py from graphics import * def drawBar(window, year, height): # Draw a bar in window starting at year with given height bar = Rectangle(Point(year, 0), Point(year+1, height)) bar.setFill("green") bar.setWidth(2) bar.draw(window) def main(): # Introduction print "This program plots the growth of a 10-year investment."CHAPTER 6. DEFINING FUNCTIONS 90 # Get principal and interest rate principal = input("Enter the initial principal: ") apr = input("Enter the annualized interest rate: ") # Create a graphics window with labels on left edge win = GraphWin("Investment Growth Chart", 320, 240) win.setBackground("white") win.setCoords(-1.75,-200, 11.5, 10400) Text(Point(-1, 0), &&# 0.0K#)&).draw(win) Text(Point(-1, 2500), (#% 2.5K*%/).draw(win) Text(Point(-1, 5000), !'# 5.0K%!").draw(win) Text(Point(-1, 7500), &'+ 7.5k'!#).draw(win) Text(Point(-1, 10000), &"-10.0K./.).draw(win) # Draw bar for initial principal drawBar(win, 0, principal) # Draw a bar for each subsequent year for year in range(1, 11): principal = principal * (1 + apr) drawBar(win, year, principal) raw_input("Press to quit.") You can see how drawBar has eliminated the duplicated code. Should we wish to change the appearance of the bars in the graph, we only need to change the code in one spot, the definition of drawBar. Don't worry yet if you don't understand every detail of this example. You still have some things to learn about functions. 6.4 Functions and Parameters: The Gory Details You may be wondering about the choice of parameters for the drawBar function. Obviously, the year for which a bar is being drawn and the height of the bar are the changeable parts in the drawing of a bar. But, why is window also a parameter to this function? After all, we will be drawing all of the bars in the same window; it doesn't seem to change. The reason for making window a parameter has to do with the scope of variables in function definitions. Scope refers to the places in a program where a given variable may be referenced. Remember each function is its own little subprogram. The variables used inside of one function are local to that function, even if they happen to have the same name as variables that appear inside of another function. The only way for a function to see a variable from another function is for that variable to be passed as a parameter. Since the GraphWin (in the variable win) is created inside of main, it is not directly accessible in drawBar. However, the window parameter in drawBar gets assigned the value of win from main when drawBar is called. To see how this happens, we need to take a more detailed look at the function invocation process. A function definition looks like this. def (): The name of the function must be an identifier, and formal-parameters is a (possibly empty) list of variable names (also identifiers). The formal parameters, like all variables used in the function, are only accessible in the body of the function. Variables with identical names elswhere in the program are distinct from the formal parameters and variables inside of the function body. A function is called by using its name followed by a list of actual parameters or arguments. ()6.4. FUNCTIONS AND PARAMETERS: THE GORY DETAILS 91 When Python comes to a function call, it initiates a four-step process. 1. The calling program suspends at the point of the call. 2. The formal parameters of the function get assigned the values supplied by the actual parameters in the call. 3. The body of the function is executed. 4. Control returns to the point just after where the function was called. Returning to the Happy Birthday example, let's trace through the singing of two verses. Here is part of the body from main. sing("Fred") print sing("Lucy") ... When Python gets to sing("Fred"), execution of main is temporarily suspended. At this point, Python looks up the definition of sing and sees that it has a single formal parameter, person. The formal parameter is assigned the value of the actual, so it is as if we had executed this statement: person = "Fred" A snapshot of this situation is shown in Figure 6.1. Notice the variable person inside of sing has just been initialized. def def main(): person = "Fred" sing("Fred") print sing("Lucy") sing(person): happy() happy() print "Happy birthday, dear", person + "." happy() person: "Fred" Figure 6.1: Illustration of control transferring to sing. At this point, Python begins executing the body of sing. The first statement is another function call, this one to happy. Python suspends execution of sing and transfers control to the called function. The body of happy consists of a single print. This statement is executed, and then control returns to where it left off in sing. Figure 6.2 shows a snapshot of the execution so far. def def main(): person = "Fred" sing("Fred") print sing("Lucy") def happy(): sing(person): print "Happy Birthday to you!" happy() happy() print "Happy birthday, dear", person + "." happy() person: "Fred" Figure 6.2: Snaphot of completed call to happy. Execution continues in this manner with Python making two more side trips back to happy to complete the execution of sing. When Python get to the end of sing, control then returns to main and continues immediately after the function call. Figure 6.3 shows where we are at that point. Notice that the personCHAPTER 6. DEFINING FUNCTIONS 92 def def main(): person = "Fred" sing("Fred") print sing("Lucy") sing(person): happy() happy() print "Happy birthday, dear", person + "." happy() Figure 6.3: Snaphot of completed call to sing. variable in sing has disappeared. The memory occupied by local function variables is reclaimed when the function finishes. Local variables do not retain any values from one function execution to the next. The next statement to execute is the bare print statement in main. This produces a blank line in the output. Then Python encounters another call to sing. As before, control transfers to the function definition. This time the formal parameter is "Lucy". Figure 6.4 shows the situation as sing begins to execute for the second time. def def main(): ucy" L " sing("Fred") on = pers print sing("Lucy") sing(person): happy() happy() print "Happy birthday, dear", person + "." happy() person: "Lucy" Figure 6.4: Snaphot of second call to sing. Now we'll fast forward to the end. The function body of sing is executed for Lucy (with three side trips through happy) and control returns to main just after the point of the function call. Now we have reached the bottom of our code fragment, as illustrated by Figure 6.5. These three statements in main have caused sing to execute twice and happy to execute six times. Overall, nine total lines of output were generated. def def main(): y" "Luc sing("Fred") = on print pers sing("Lucy") sing(person): happy() happy() print "Happy birthday, dear", person + "." happy() Figure 6.5: Completion of second call to sing. Hopefully you're getting the hang of how function calls actually work. One point that this example did not address is the use of multiple parameters. When a function definition has several parameters, the actual parameters are matched up with the formal parameters by position. The first actual parameter is assigned to the first formal paramter, the second actual is assigned to the second formal, etc. As an example, look again at the use of the drawBar function from the future value program. Here is the call to draw the initial bar. drawBar(win, 0, principal) When Python transfers control to drawBar, these parameters are matched up to the formal parameters in the function heading. def drawBar(window, year, height):6.5. FUNCTIONS THAT RETURN VALUES 93 The net effect is as if the function body had been prefaced with three assignment statements. window = win year = 0 height = principal You must always be careful when calling a function that you get the actual parameters in the correct order to match the function definition. 6.5 Functions that Return Values You have seen that parameter passing provides a mechanism for initializing the variables in a function. In a way, parameters act as inputs to a function. We can call a function many times and get different results by changing the input parameters. Sometimes we also want to get information back out of a function. This is accomplished by having functions return a value to the caller. You have already seen numerous examples of this type of function. For example, consider this call to the sqrt function from the math library. discRt = math.sqrt(b*b - 4*a*c) Here the value of b*b - 4*a*c is the actual parameter of math.sqrt. This function call occurs on the right side of an assignment statement; that means it is an expression. The math.sqrt function must somehow produce a value that is then assigned to the variable discRt. Technically, we say that sqrt returns the square root of its argument. It's very easy to write functions that return values. Here's an example value-returning function that does the opposite of sqrt; it returns the square of its argument. def square(x): return x * x The body of this function consists of a single return statement. When Python encounters return, it exits the function and returns control to the point where the function was called. In addition, the value(s) provided in the return statement are sent back to the caller as an expression result. We can use our square function any place that an expression would be legal. Here are some interactive examples. >>> 9 >>> 16 >>> >>> >>> 25 >>> 34 square(3) print square(4) x = 5 y = square(x) print y print square(x) + square(3) Let's use the square function to write another function that finds the distance between two points. Given two points x 1 #,% y 1 and x 2 ,&- y 2 , the distance between them is calculated from the Pythagorean Theorem as x 2 x 1 2 y 2 y 1 2 . Here is a Python function to compute the distance between two Point objects. def distance(p1, p2): dist = math.sqrt(square(p2.getX() - p1.getX()) + square(p2.getY() - p1.getY()) return dist Using the distance function, we can augment the interactive triangle program from last chapter to calculate the perimeter of the triangle. Here's the complete program:CHAPTER 6. DEFINING FUNCTIONS 94 # Program: triangle2.py from graphics import * def square(x): return x * x def distance(p1, p2): dist = math.sqrt(square(p2.getX() - p1.getX()) + square(p2.getY() - p1.getY()) return dist def main(): win = GraphWin("Draw a Triangle") win.setCoords(0.0, 0.0, 10.0, 10.0) message = Text(Point(5, 0.5), "Click on three points") message.draw(win) # Get and draw three vertices of triangle p1 = win.getMouse() p1.draw(win) p2 = win.getMouse() p2.draw(win) p3 = win.getMouse() p3.draw(win) # Use Polygon object to draw the triangle triangle = Polygon(p1,p2,p3) triangle.setFill("peachpuff") triangle.setOutline("cyan") triangle.draw(win) # Calculate the perimeter of the triangle perim = distance(p1,p2) + distance(p2,p3) + distance(p3,p1) message.setText("The perimeter is: %0.2f" % perim) # Wait for another click to exit win.getMouse() You can see how distance is called three times in one line to compute the perimeter of the triangle. Using a function here saves quite a bit of tedious coding. Sometimes a function needs to return more than one value. This can be done by simply listing more than one expression in the return statement. As a silly example, here is a function that computes both the sum and the difference of two numbers. def sumDiff(x,y): sum = x + y diff = x - y return sum, diff As you can see, this return hands back two values. When calling this function, we would place it in a simultaneous assignment. num1, num2 = input("Please enter two numbers (num1, num2) ") s, d = sumDiff(num1, num2) print "The sum is", s, "and the difference is", d6.6. FUNCTIONS AND PROGRAM STRUCTURE 95 As with parameters, when multiple values are returned from a function, they are assigned to variables by position. In this example, s will get the first value listed in the return (sum), and d will get the second value (diff). That's just about all there is to know about functions in Python. There is one #.'gotcha'/$ to warn you about. Technically, all functions in Python return a value, regardless of whether or not the function actually contains a return statement. Functions without a return always hand back a special object, denoted None. This object is often used as a sort of default value for variables that don't currently hold anything useful. A common mistake that new (and not-so-new) programmers make is writing what should be a value-returning function but forgetting to include a return statement at the end. Suppose we forget to include the return statement at the end of the distance function. def distance(p1, p2): dist = math.sqrt(square(p2.getX() - p1.getX()) + square(p2.getY() - p1.getY()) Running the revised triangle program with this version of distance generates this Python error message. Traceback (innermost last): File "", line 1, in ? File "triangle2err.py", line 44, in ? main() File "triangle2err.py", line 37, in main perim = distance(p1,p2) + distance(p2,p3) + distance(p3,p1) TypeError: bad operand type(s) for + The problem here is that this version of distance does not return a number, but always hands back the value None. Addition is not defined for None, and so Python complains. If your value-returning functions are producing strange error messages, check to make sure you remembered to include the return. 6.6 Functions and Program Structure So far, we have been discussing functions as a mechanism for reducing code duplication, thus shortening and simplifying our programs. Surprisingly, functions are often used even when doing so actually makes the program longer. A second reason for using functions is to make programs more modular. As the algorithms that you design get more complex, it gets more and more difficult to make sense out of programs. Humans are pretty good at keeping track of eight to ten things at a time. When presented with an algorithm that is hundreds of lines long, even the best programmers will throw up their hands in bewilderment. One way to deal with this complexity is to break an algorithm into smaller subprograms, each of which makes sense on its own. I'll have a lot more to say about this later when we discuss program design in Chapter 9. For now, we'll just take a look at an example. Let's return to the future value problem one more time. Here is the main program as we left it: def main(): # Introduction print "This program plots the growth of a 10-year investment." # Get principal and interest rate principal = input("Enter the initial principal: ") apr = input("Enter the annualized interest rate: ") # Create a graphics window with labels on left edge win = GraphWin("Investment Growth Chart", 320, 240) win.setBackground("white") win.setCoords(-1.75,-200, 11.5, 10400)CHAPTER 6. DEFINING FUNCTIONS 96 Text(Point(-1, Text(Point(-1, Text(Point(-1, Text(Point(-1, Text(Point(-1, 0), -/" 0.0K!%").draw(win) 2500), )*) 2.5K$!)).draw(win) 5000), &-* 5.0K+#!).draw(win) 7500), )&- 7.5k-"#).draw(win) 10000), #+!10.0K!*().draw(win) # Draw bar for initial principal drawBar(win, 0, principal) # Draw a bar for each subsequent year for year in range(1, 11): principal = principal * (1 + apr) drawBar(win, year, principal) raw_input("Press to quit.") Although we have already shortened this algorithm through the use of the drawBar function, it is still long enough to make reading through it awkward. The comments help to explain things, but, not to put too fine a point on it, this function is just too long. One way to make the program more readable is to move some of the details into a separate function. For example, there are eight lines in the middle that simply create the window where the chart will be drawn. We could put these steps into a value returning function. def createLabeledWindow(): # Returns a GraphWin with title and labels drawn window = GraphWin("Investment Growth Chart", 320, 240) window.setBackground("white") window.setCoords(-1.75,-200, 11.5, 10400) Text(Point(-1, 0), $'' 0.0K&.+).draw(window) Text(Point(-1, 2500), '., 2.5K'#-).draw(window) Text(Point(-1, 5000), //" 5.0K""%).draw(window) Text(Point(-1, 7500), )+" 7.5k&/*).draw(window) Text(Point(-1, 10000), !)&10.0K(*/).draw(window) return window As its name implies, this function takes care of all the nitty-gritty details of drawing the initial window. It is a self-contained entity that performs this one well-defined task. Using our new function, the main algorithm seems much simpler. def main(): print "This program plots the growth of a 10-year investment." principal = input("Enter the initial principal: ") apr = input("Enter the annualized interest rate: ") win = createLabeledWindow() drawBar(win, 0, principal) for year in range(1, 11): principal = principal * (1 + apr) drawBar(win, year, principal) raw_input("Press to quit.") Notice that I have removed the comments; the intent of the algorithm is now clear. With suitably named functions, the code has become nearly self-documenting. Here is the final version of our future value program:6.7. EXERCISES 97 # futval_graph4.py from graphics import * def createLabeledWindow(): window = GraphWin("Investment Growth Chart", 320, 240) window.setBackground("white") window.setCoords(-1.75,-200, 11.5, 10400) Text(Point(-1, 0), /-- 0.0K'*$).draw(window) Text(Point(-1, 2500), """ 2.5K.($).draw(window) Text(Point(-1, 5000), #/+ 5.0K$($).draw(window) Text(Point(-1, 7500), /!. 7.5k!.&).draw(window) Text(Point(-1, 10000), +..10.0K!-().draw(window) return window def drawBar(window, year, height): bar = Rectangle(Point(year, 0), Point(year+1, height)) bar.setFill("green") bar.setWidth(2) bar.draw(window) def main(): print "This program plots the growth of a 10 year investment." principal = input("Enter the initial principal: ") apr = input("Enter the annualized interest rate: ") win = createLabeledWindow() drawBar(win, 0, principal) for year in range(1, 11): principal = principal * (1 + apr) drawBar(win, year, principal) raw_input("Press to quit.") win.close() Although this version is longer than the previous version, experienced programmers would find it much easier to understand. As you get used to reading and writing functions, you too will learn to appreciate the elegance of more modular code. 6.7 Exercises 1. In your own words, describe the two motivations for defining functions in your programs. 2. We have been thinking about computer programs as sequences of instructions where the computer methodically executes one instruction and then moves on to the next one. Do programs that contain functions fit this model? Explain your answer. 3. Parameters are an important concept in defining functions. (a) What is the purpose of parameters? (b) What is the difference between a formal parameter and an actual parameter? (c) In what ways are parameters similar to and different from ordinary variables?CHAPTER 6. DEFINING FUNCTIONS 98 4. Functions can be thought of as miniature (sub)programs inside of other programs. Like any other program, we can think of functions as having input and output to communicate with the main program. (a) How does a program provide '--input$$' to one of its functions? (b) How does a function provide ',(output)"( to the program? 5. Consider this very simple function: def cube(x): answer = x * x * x return answer (a) What does this function do? (b) Show how a program could use this function to print the value of y 3 , assuming y is a variable. (c) Here is a fragment of a program that uses this function: answer = 4 result = cube(3) print answer, result The output from this fragment is 4 27. Explain why the output is not 27 27, even though cube seems to change the value of answer to 27. 6. Write a program to print the lyrics of the song $+.Old MacDonald.&/- Your program should print the lyrics for five different animals, similar to the example verse below. Old MacDonald had a farm, Ee-igh, Ee-igh, Oh! And on that farm he had a cow, Ee-igh, Ee-igh, Oh! With a moo, moo here and a moo, moo there. Here a moo, there a moo, everywhere a moo, moo. Old MacDonald had a farm, Ee-igh, Ee-igh, Oh! 7. Write a program to print the lyrics for ten verses of *&-The Ants Go Marching./'# A couple sample verses are given below. You may choose your own activity for the little one in each verse, but be sure to choose something that makes the rhyme work (or almost work). The ants go marching one by one, hurrah! hurrah! The ants go marching one by one, hurrah! hurrah! The ants go marching one by one. The little one stops to suck his thumb And they all go marching down... In the gound... To get out.... Of the rain. Boom! Boom! Boom! The ants go marching two by two, hurrah! hurrah! The ants go marching two by two, hurrah! hurrah! The ants go marching two by two. The little one stops to tie his shoe And they all go marching down... In the ground... To get out... Of the rain. Boom! Boom! Boom!6.7. EXERCISES 99 8. Redo any of your favorite programming problems from previous chapters and use a function or two to encapsulate the calculations. For example, a program to compute the volume and surface area of a sphere could use functions sphereVol and sphereArea to do the calculations. 9. Redo some more problems....100 CHAPTER 6. DEFINING FUNCTIONSChapter 7 Control Structures, Part 1 So far, we have viewed computer programs as sequences of instructions that are followed one after the other. Sequencing is a fundamental concept of programming, but alone, it is not sufficient to solve every problem. Often it is necessary to alter the sequential flow of a program to suit the needs of a particular situation. This is done with special statements known as control structures. In this chapter, we'll take a look at decision structures, which are statements that allow a program to execute different sequences of instructions for different cases, effectively allowing the program to %"(choose$)& an appropriate course of action. 7.1 Simple Decisions 7.1.1 Example: Temperature Warnings Let's start by getting the computer to make a simple decision. For an easy example, we'll return to the Celsius to Fahrenheit temperature conversion program from Chapter 2. Remember, this was written by Suzie Programmer to help her figure out how to dress each morning in Europe. Here is the program as we left it: # convert.py # A program to convert Celsius temps to Fahrenheit # by: Suzie Programmer def main(): celsius = input("What is the Celsius temperature? ") fahrenheit = 9.0 / 5.0 * celsius + 32 print "The temperature is", fahrenheit, "degrees fahrenheit." main() This is a fine program as far as it goes, but we want to enhance it. Suzie Programmer is not a morning person, and even though she has a program to convert the temperatures, sometimes she does not pay very close attention to the results. Our enhancement to the program will ensure that when the temperatures are extreme, the program prints out a suitable warning so that Suzie takes notice. The first step is to fully specify the enhancement. An extreme temperature is either quite hot or quite cold. Let's say that any temperature over 90 degrees Fahrenheit deserves a heat warning, and a temperature under 30 degrees warrants a cold warning. With this specification in mind, we can design an enhanced algorithm. Input the temperature in degrees Celsius (call it celsius) Calculate fahrenheit as 9/5 celsius + 32 Output fahrenheit if fahrenheit > 90 print a heat warning if fahrenheit < 30 print a cold warning 101CHAPTER 7. CONTROL STRUCTURES, PART 1 102 This new design has two simple decisions at the end. The indentation indicates that a step should be performed only if the condition listed in the previous line is met. The idea here is that the decision introduces an alternative flow of control through the program. The exact set of steps taken by the algorithm will depend on the value of fahrenheit. Figure 7.1 is a flowchart showing the possible paths that can be taken through the algorithm. The diamond boxes show conditional decisions. If the condition is false, control passes to the next statement in the sequence (the one below). If the condition holds, however, control transfers to the instructions in the box to the right. Once these instructions are done, control then passes to the next statement. Input Celsius Temperature Farenheit = 9/5 * celsius + 32 Print Fahrenheit fahrenheit > 90? no fahrenheit < 30? no yes Print a Heat Warning yes Print a Cold Warning Figure 7.1: Flowchart of temperature conversion program with warnings. Here is how the new design translates into Python code: # convert2.py # A program to convert Celsius temps to Fahrenheit. # This version issues heat and cold warnings. def main(): celsius = input("What is the Celsius temperature? ") fahrenheit = 9.0 / 5.0 * celsius + 32 print "The temperature is", fahrenheit, "degrees fahrenheit." # Print warnings for extreme temps if fahrenheit > 90: print "It's really hot out there, be careful!" if fahrenheit < 30: print "Brrrrr. Be sure to dress warmly!" main() You can see that the Python if statement is used to implement the decision. The form of the if is very similar to the pseudo-code in the algorithm. if : 7.1. SIMPLE DECISIONS 103 The body is just a sequence of one or more statements indented under the if heading. In convert2.py there are two if statements, both of which have a single statement in the body. The semantics of the if should be clear from the example above. First, the condition in the heading is evaluated. If the condition is true, the sequence of statements in the body is executed, and then control passes to the next statement in the program. If the condition is false, the statements in the body are skipped. Figure 7.2 shows the semantics of the if as a flowchart. Notice that the body of the if either executes or true? yes no Figure 7.2: Control flow of simple if-statement not depending on the condition. In either case, control then passes to the next statement after the if. This is a one-way or simple decision. 7.1.2 Forming Simple Conditions One point that has not yet been discussed is exactly what a condition looks like. For the time being, our programs will use simple conditions that compare the values of two expressions. is short for relational operator. That's just a fancy name for the mathematical concepts like #/#less than/*- or '))equal to.&"# There are six relational operators in Python, shown in the following table. Python Mathematics ! Meaning Less than Less than or equal to Equal to Greater than or equal to Greater than Not equal to Notice especially the use of for equality. Since Python uses the sign to indicate an assignment state- ment, a different symbol is required for the concept of equality. A common mistake in Python programs is using in conditions, where a is required. Conditions may compare either numbers or strings. When comparing strings, the ordering is lexico- graphic. Basically, this means that strings are put in alphabetic order according to the underlying ASCII codes. So all upper-case letters come before lower case letters (e.g., %,-Bbbb##$ comes before ',*aaaa%+!, since .()B'#) precedes $%/a-,)). I should mention that conditions are actually a type of expression, called a Boolean expression, after George Boole, a 19th century English mathematician. When a Boolean expression is evaluated, it produces a value of either true (the condition holds) or false (it does not hold). In Python, conditions return int values. A true condition produces a 1, while a false condition produces a 0. Here are a few examples:CHAPTER 7. CONTROL STRUCTURES, PART 1 104 >>> 1 >>> 0 >>> 1 >>> 0 >>> 1 3 < 4 3 * 4 < 3 + 4 "hello" == "hello" "hello" < "hello" "Hello" < "hello" 7.1.3 Example: Conditional Program Execution Back in Chapter 1, I mentioned that there are several different ways of running Python programs. Some Python module files are designed to be run directly. These are usually referred to as **"programs*'* or ,,'scripts.*&! Other Python modules are designed primarily to be imported and used by other programs, these are often called --,libraries.'). Sometimes we want to create a sort of hybrid module that can be used both as a stand- alone program and as a library that can be imported by other programs. So far, all of our programs have had a line at the bottom to invoke the main function. main() As you know, this is what actually starts a program running. These programs are suitable for running directly. In a windowing environment, you might run a file by (double-)clicking its icon. Or you might type a command like python .py. Since Python evaluates the lines of a module during the import process, our current programs also run when they are imported into either an interactive Python session or into another Python program. Generally, it is nicer not to have modules run as they are imported. When testing a program interactively, the usual approach is to first import the module and then call its main (or some other function) each time we want to run it. In a program that can be either imported (without running) or run directly, the call to main at the bottom must be made conditional. A simple decision should do the trick. if : main() We just need to figure out a suitable condition. Whenever a module is imported, Python sets a special variable in the module called name to be the name of the imported module. Here is an example interaction showing what happens with the math library. >>> import math >>> math.__name__ ''-math'%/ You can see that, when imported, the name variable inside the math module is assigned the string //,math.,/. However, when Python code is being run directly (not imported), Python sets the value of name to be /&) main (!/. To see this in action, you just need to start Python and look at the value. >>> __name__ #,#__main__)-) So, if a module is imported, the code in that module will see a variable called name whose value is the name of the module. When a file is run directly, the code will see that name has the value )+- main ,"&. A program can determine how it is being used by inspecting this variable. Putting the pieces together, we can change the final lines of our programs to look like this:7.2. TWO-WAY DECISIONS 105 if __name__ == "!$__main__/.): main() This guarantees that main will automatically run when the program is invoked directly, but it will not run if the module is imported. You will see a line of code similar to this at the bottom of virtually every Python program. 7.2 Two-Way Decisions Now that we have a way to selectively execute certain statements in a program using decisions, it's time to go back and spruce up the quadratic equation solver from Chapter 3. Here is the program as we left it: # quadratic.py # A program that computes the real roots of a quadratic equation. # Illustrates use of the math library. # Note: this program crashes if the equation has no real roots. import math # Makes the math library available. def main(): print "This program finds the real solutions to a quadratic" print a, b, c = input("Please enter the coefficients (a, b, c): ") discRoot = math.sqrt(b * b - 4 * a * c) root1 = (-b + discRoot) / (2 * a) root2 = (-b - discRoot) / (2 * a) print print "The solutions are:", root1, root2 main() As noted in the comments, this program crashes when it is given coefficients of a quadratic equation that has no real roots. The problem with this code is that when b 2 4ac is less than 0, the program attempts to take the square root of a negative number. Since negative numbers do not have real roots, the math library reports an error. Here's an example. >>> import quadratic This program finds the real solutions to a quadratic Please enter the coefficients (a, b, c): 1,2,3 Traceback (innermost last): File "", line 1, in ? File "quadratic.py", line 21, in ? main() File "quadratic.py", line 14, in main discRoot = math.sqrt(b * b - 4 * a * c) OverflowError: math range error We can use a decision to check for this situation and make sure that the program can't crash. Here's a first attempt: # quadratic2.py import mathCHAPTER 7. CONTROL STRUCTURES, PART 1 106 def main(): print "This program finds the real solutions to a quadratic\n" a, b, c = input("Please enter the coefficients (a, b, c): ") dicrim = b * b - 4 * a * c if discrim >= 0: discRoot = math.sqrt(discrim) root1 = (-b + discRoot) / (2 * a) root2 = (-b - discRoot) / (2 * a) print "\nThe solutions are:", root1, root2 This version first computes the value of the discriminant (b 2 4ac) and then checks to make sure it is not negative. Only then does the program proceed to take the square root and calculate the solutions. This program will never attempt to call math.sqrt when discrim is negative. Incidently, you might also notice that I have replaced the bare print statements from the original version of the program with embedded newlines to put whitespace in the output; you hadn't yet learned about n the first time we encountered this program. Unfortunately, this updated version is not really a complete solution. Study the program for a moment. What happens when the equation has no real roots? According to the semantics for a simple if, when b*b - 4*a*c is less than zero, the program will simply skip the calculations and go to the next statement. Since there is no next statement, the program just quits. Here's what happens in an interactive session. >>> quadratic2.main() This program finds the real solutions to a quadratic Please enter the coefficients (a, b, c): 1,2,3 >>> This is almost worse than the previous version, because it does not give the user any indication of what went wrong; it just leaves them hanging. A better program would print a message telling the user that their particular equation has no real solutions. We could accomplish this by adding another simple decision at the end of the program. if discrim < 0: print "The equation has no real roots!" This will certainly solve our problem, but this solution just doesn't feel right. We have programmed a sequence of two decisions, but the two outcomes are mutually exclusive. If discrim >= 0 is true then discrim < 0 must be false and vice versa. We have two conditions in the program, but there is really only one decision to make. Based on the value of discrim The program should either print that there are no real roots or it should calculate and display the roots. This is an example of a two-way decision. Figure 7.3 illustrates the situation. In Python, a two-way decision can be implemented by attaching an else clause onto an if. The result is called an if-else statement. if : else: When the Python interpreter encounters this structure, it will first evaluate the condition. If the condition is true, the statements under the if are executed. If the condition is false, the statements under the else are executed. In either case, control then passes to the statement following the if-else. Using a two-way decision in the quadratic solver yields a more elegant solution.7.3. MULTI-WAY DECISIONS 107 no discrim < 0 ? yes Calculate roots Print "no roots" Figure 7.3: Quadratic solver as a two-way decision. # quadratic3.py import math def main(): print "This program finds the real solutions to a quadratic\n" a, b, c = input("Please enter the coefficients (a, b, c): ") discrim = b * b - 4 * a * c if discrim < 0: print "\nThe equation has no real roots!" else: discRoot = math.sqrt(b * b - 4 * a * c) root1 = (-b + discRoot) / (2 * a) root2 = (-b - discRoot) / (2 * a) print print "\nThe solutions are:", root1, root2 This program fits the bill nicely. Here is a sample session that runs the new program twice. >>> quadratic3.main() This program finds the real solutions to a quadratic Please enter the coefficients (a, b, c): 1,2,3 The equation has no real roots! >>> quadratic3.main() This program finds the real solutions to a quadratic Please enter the coefficients (a, b, c): 2,4,1 The solutions are: -0.292893218813 -1.70710678119 7.3 Multi-Way Decisions The newest version of the quadratic solver is certainly a big improvement, but it still has some quirks. Here is another example run.CHAPTER 7. CONTROL STRUCTURES, PART 1 108 >>> quadratic3.main() This program finds the real solutions to a quadratic Please enter the coefficients (a, b, c): 1, 2, 1 The solutions are: -1.0 -1.0 This is technically correct; the given coefficients produce an equation that has a double root at -1. However, the output might be confusing to some users. It looks like the program has mistakenly printed the same number twice. Perhaps the program should be a bit more informative to avoid confusion. The double-root situation occurs when discrim is exactly 0. In this case, discRoot is also 0, and both roots have the value 2a b . If we want to catch this special case, it looks like our program actually needs a three-way decision. Here's a quick sketch of the design. ... Check the value of discrim when < 0: handle the case of no roots when = 0: handle the case of a double root when > 0: handle the case of two distinct roots. One way to code this algorithm is to use two if-else statements. The body of an if or else clause can contain any legal Python statements, including other if or if-else statements. Putting one compound statement inside of another is called nesting. Here's a fragment of code that uses nesting to achieve a three-way decision : if discrim < 0: print "Equation has no real roots" else: if discrim == 0: root = -b / (2 * a) print "There is a double root at", root else: # Do stuff for two roots If you trace through this code carefully, you will see that there are exactly three possible paths. The sequencing is determined by the value of discrim. A flowchart of this solution is shown in Figure 7.4. You can see that the top-level structure is just an if-else. (Treat the dashed box as one big statement.) The dashed box contains the second if-else nested comfortably inside the else part of the top-level decision. Once again, we have a working solution, but the implementation doesn't feel quite right. We have finessed a three-way decision by using two two-way decisions. The resulting code does not reflect the true three-fold decision of the original problem. Imagine if we needed to make a five-way decision using this technique. The if-else structures would nest four levels deep, and the Python code would march off the right-hand edge of the page. There is another way to write multi-way decisions in Python that preserves the semantics of the nested structures but gives it a more appealing look. The idea is to combine an else followed immediately by an if into a single clause called an elif. if : elif : elif : ... else: 7.4. EXCEPTION HANDLING yes 109 discrim < 0 ? no Print "no roots" yes discrim == 0 ? Do Double Root no Do Unique Roots Figure 7.4: Three-way decision for quadratic solver using nested if-else. This form is used to set off any number of mutually exclusive code blocks. Python will evaluate each condi- tion in turn looking for the first one that is true. If a true condition is found, the statements indented under that condition are executed, and control passes to the next statement after the entire if-elif-else. If none of the conditions are true, the statements under the else are performed. The else clause is optional; if omitted, it is possible that no indented statement block will be executed. Using an if-elif-else to show the three-way decision in our quadratic solver yields a nicely finished program. # quadratic4.py import math def main(): print "This program finds the real solutions to a quadratic\n" a, b, c = input("Please enter the coefficients (a, b, c): ") discrim = b * b - 4 * a * c if discrim < 0: print "\nThe equation has no real roots!" elif discrim == 0: root = -b / (2 * a) print "\nThere is a double root at", root else: discRoot = math.sqrt(b * b - 4 * a * c) root1 = (-b + discRoot) / (2 * a) root2 = (-b - discRoot) / (2 * a) print "\nThe solutions are:", root1, root2 7.4 Exception Handling Our quadratic program uses decision structures to avoid taking the square root of a negative number and generating a run-time error. This is a common pattern in many programs: using decisions to protect againstCHAPTER 7. CONTROL STRUCTURES, PART 1 110 rare but possible errors. In the case of the quadratic solver, we checked the data before the call to the sqrt function. Sometimes functions themselves check for possible errors and return a special value to indicate that the operation was unsuccessful. For example, a different square root operation might return a negative number (say -1) to indicate an error. Since the square roots of real numbers are never negative, this value could be used to signal that an error had occurred. The program would check the result of the operation with a decision. discRt = otherSqrt(b*b - 4*a*c) if discRt < 0: print "No real roots." else: ... Sometimes programs become so peppered with decisions to check for special cases that the main algo- rithm for handling the run-of-the-mill cases seems completely lost. Programming language designers have come up with mechanisms for exception handling that help to solve this design problem. The idea of an exception handling mechanism is that the programmer can write code that catches and deals with errors that arise when the program is running. Rather than explicitly checking that each step in the algorithm was suc- cessful, a program with exception handling can in essence say &./Do these steps, and if any problem crops up, handle it this way.)$, We're not going to discuss all the details of the Python exception handling mechanism here, but I do want to give you a concrete example so you can see how exception handling works and understand programs that use it. In Python, exception handling is done with a special control structure that is similar to a decision. Let's start with a specific example and then take a look at the general approach. Here is a version of the quadratic program that uses Python's exception mechanism to catch potential errors in the math.sqrt function. # quadratic5.py import math def main(): print "This program finds the real solutions to a quadratic\n" try: a, b, c = input("Please enter the coefficients (a, b, c): ") discRoot = math.sqrt(b * b - 4 * a * c) root1 = (-b + discRoot) / (2 * a) root2 = (-b - discRoot) / (2 * a) print "\nThe solutions are:", root1, root2 except OverflowError: print "\nNo real roots" Notice that this is basically the very first version of the quadratic program with the addition of a try...except around the heart of the program. A try statement has the general form: try: except : When Python encounters a try statement, it attempts to execute the statements inside the body. If these statements execute without error, control then passes to the next statement after the try...except. If an error occurs somewhere in the body, Python looks for an except clause with a matching error type. If a suitable except is found, the handler code is executed. The original program without the exception-handling produced the following error.7.4. EXCEPTION HANDLING 111 Traceback (innermost last): File "", line 1, in ? File "quadratic.py", line 13, in ? discRoot = math.sqrt(b * b - 4 * a * c) OverflowError: math range error The last line of this error message indicates the type of error that was generated, namely an OverflowError. The updated version of the program provides an except clause to catch the OverflowError. Here is the error handling version in action: This program finds the real solutions to a quadratic Please enter the coefficients (a, b, c): 1,2,3 No real roots Instead of crashing, the exception handler catches the error and prints a message indicating that the equation does not have real roots. The nice thing about the try...except statement is that it can be used to catch any kind of error, even ones that might be difficult to test for, and hopefully, provide a graceful exit. For example, in the quadratic solver, there are lots of other things that could go wrong besides having a bad set of coefficients. If the user fails to type the correct number of inputs, the program generates a ValueError. If the user accidently types an identifier instead of a number, the program generates a NameError. If the user types in a valid Python expression that produces non-numeric results, the program generates a TypeError. A single try statement can have multiple except clauses to catch various possible classes of errors. Here's one last version of the program designed to robustly handle any possible errors in the input. # quadratic6.py import math def main(): print "This program finds the real solutions to a quadratic\n" try: a, b, c = input("Please enter the coefficients (a, b, c): ") discRoot = math.sqrt(b * b - 4 * a * c) root1 = (-b + discRoot) / (2 * a) root2 = (-b - discRoot) / (2 * a) print "\nThe solutions are:", root1, root2 except OverflowError: print "\nNo real roots" except ValueError: print "\nYou didn't give me three coefficients." except NameError: print "\nYou didn't enter three numbers" except TypeError: print "\nYour inputs were not all numbers" except: print "\nSomething went wrong, sorry!" The multiple excepts are similar to elifs. If an error occurs, Python will try each except in turn looking for one that matches the type of error. The bare except at the bottom acts like an else and will be used if none of the others match. If there is no default at the bottom and none of the except types match the error, then the program crashes and Python reports the error. You can see how the try...except statement allows us to write really bullet-proof programs. Whether you need to go to this much trouble depends on the type of program that you are writing. In your beginningCHAPTER 7. CONTROL STRUCTURES, PART 1 112 programs, you might not worry too much about bad input; however, professional quality software should do whatever is feasible to shield users from unexpected results. 7.5 Study in Design: Max of Three Now that we have decisions that can alter the control flow of a program, our algorithms are liberated from the monotony of step-by-step, strictly sequential processing. This is both a blessing and a curse. The positive side is that we can now develop more sophisticated algorithms, as we did for our quadratic solver. The negative side is that designing these more sophisticated algorithms is much harder. In this section, we'll step through the design of a more difficult decision problem to illustrate some of the challenge and excitement of the design process. Suppose we need an algorithm to find the largest of three numbers. This algorithm could be part of a larger problem such as determining grades or computing taxes, but we are not interested in the final details, just the crux of the problem. That is, how can a computer determine which of three user inputs is the largest? Here is a program outline. We need to fill in the missing part. def main(): x1, x2, x3 = input("Please enter three values: ") # missing code sets max to the value of the largest print "The largest value is", max Before reading the following analysis, you might want to try your hand at solving this problem. 7.5.1 Strategy 1: Compare Each to All Obviously, this program presents us with a decision problem. We need a sequence of statements that sets the value of max to the largest of the three inputs x1, x2, and x3. At first glance, this looks like a three-way decision; we need to execute one of the following assignments. max = x1 max = x2 max = x3 It would seem we just need to preface each one with the appropriate condition(s), so that it is executed only in the proper situation. Let's consider the first possibility, that x1 is the largest. To see that x1 is actually the largest, we just need to check that it is at least as large as the other two. Here is a first attempt: if x1 >= x2 >= x3: max = x1 Your first concern here should be whether this statement is syntactically correct. The condition x1 x2 x3 does not match the template for conditions shown above. Most computer languages would not accept this as a valid expression. It turns out that Python does allow this compound condition, and it behaves exactly like the mathematical relations x1 x2 x3. That is, the condition is true when x1 is at least as large as x2 and x2 is at least as large as x3. So, Python has no problem with this condition. Whenever you write a decision, you should ask yourself two crucial questions. First, when the condition is true, are you absolutely certain that executing the body of the decision is the right action to take? In this case, the condition clearly states that x1 is at least as large as x2 and x3, so assigning its value to max should be correct. Always pay particular attention to borderline values. Notice that our condition includes equal as well as greater. We should convince ourselves that this is correct. Suppose that x1, x2, and x3 are all the same; this condition will return true. That's OK because it doesn't matter which we choose, the first is at least as big as the others, and hence, the max.7.5. STUDY IN DESIGN: MAX OF THREE 113 The second question to ask is the converse of the first. Are we certain that this condition is true in all cases where x1 is the max? Unfortunately, our condition does not meet this test. Suppose the values are 5, 2, and 4. Clearly, x1 is the largest, but our condition returns false since the relationship 5 2 4 does not hold. We need to fix this. We want to ensure that x1 is the largest, but we don't care about the relative ordering of x2 and x3. What we really need is two separate tests to determine that x1 >= x2 and that x2 >= x3. Python allows us to test multiple conditions like this by combining them with the keyword and. We'll discuss the exact semantics of and in Chapter 8. Intuitively, the following condition seems to be what we are looking for: if x1 >= x2 and x1 >= x3: max = x1 # x1 is greater than each of the others To complete the program, we just need to implement analogous tests for the other possibilities. if x1 >= x2 and x1 >= x3: max = x1 elif x2 >= x1 and x2 >= x3: max = x2 else: max = x3 Summing up this approach, our algorithm is basically checking each possible value against all the others to determine if it is the largest. With just three values the result is quite simple. But how would this solution look if we were trying to find the max of five values? Then we would need four Boolean expressions, each consisting of four conditions anded together. The complex expressions result from the fact that each decision is designed to stand on its own; information from one test is ignored in the following. To see what I mean, look back at our simple max of three code. Suppose the first decision discovers that x1 is greater than x2, but not greater than x3. At this point, we know that x3 must be the max. Unfortunately, our code ignores this; Python will go ahead and evaluate the next expression, discover it to be false and finally execute the else. 7.5.2 Strategy 2: Decision Tree One way to avoid the redundant tests of the previous algorithm is to use a decision tree approach. Suppose we start with a simple test x1 x2. This knocks either x1 or x2 out of contention to be the max. If the condition is true, we just need to see which is larger, x1 or x3. Should the initial condition be false, the result boils down to a choice between x2 and x3. As you can see, the first decision (#)branches(+) into two possibilities, each of which is another decision. Hence the name, decision tree. Figure 7.5 shows the situation in a flowchart. This flowchart translates easily into nested if-else statements. if x1 >= x2: if x1 >= x3: max = x1 else: max = x3 else: if x2 >= x3: max = x2 else: max = x3 The strength of this approach is its efficiency. No matter what the ordering of the three values, this algorithm will make exactly two comparisons and assign the correct value to max. However, the structure of this approach is more complicated than the first, and it suffers a similar complexity explosion, should we try this design with more than three values. As a challenge, you might see if you can design a decision tree to find the max of four values. (You will need if-elses nested three levels deep leading to eight assignment statements.)CHAPTER 7. CONTROL STRUCTURES, PART 1 114 yes no x1 >= x2 yes max = x1 x1 >= x3 no max = x3 yes max = x2 x2 > =x3 no max = x3 Figure 7.5: Flowchart of the decision tree approach to max of three 7.5.3 Strategy 3: Sequential Processing So far, we have designed two very different algorithms, but neither one seems particularly elegant. Perhaps there is yet a third way. When designing an algorithm, a good starting place is to ask yourself how you would solve the problem if you were asked to do the job. For finding the max of three numbers, you probably don't have a very good intuition about the steps you go through. You'd just look at the numbers and know which is the largest. But what if you were handed a book containing hundreds of numbers in no particular order. How would you find the largest in this collection? When confronted with the larger problem, most people develop a simple strategy. Scan through the numbers until you find a big one, and put your finger on it. Continue scanning; if you find a number bigger than the one your finger is on, move your finger to the new one. When you get to the end of the list, your finger will remain on the largest value. In a nutshell, this strategy has us look through the list sequentially, keeping track of the largest number seen so far. A computer doesn't have fingers, but we can use a variable to keep track of the max so far. In fact, the easiest approach is just to use max to do this job. That way, when we get to the end, max automatically contains the value of the largest. A flowchart depicting this strategy for the max of three problem is shown in Figure 7.6. Here is the translation into Python code: max = x1 if x2 > max: max = x2 if x3 > max: max = x3 Clearly, the sequential approach is the best of our three algorithms. The code itself is quite simple, containing only two simple decisions, and the sequencing is easier to understand than the nesting used in the previous algorithm. Furthermore, the idea scales well to larger problems; adding a fourth item adds only one more statement. max = x1 if x2 > max: max = x2 if x3 > max: max = x3 if x4 > max: max = x47.5. STUDY IN DESIGN: MAX OF THREE 115 max = x1 x2 > max max = x2 x3 > max max = x3 Figure 7.6: Flowchart of a sequential approach to the max of three problem. It should not be surprising that the last solution scales to larger problems; we invented the algorithm by explicitly considering how to solve a more complex problem. In fact, you can see that the code is very repetitive. We can easily write a program that allows the user to find the largest of n numbers by folding our algorithm into a loop. Rather than having separate variables for x1, x2, x3, etc., we can just get the values one at a time and keep reusing a single variable x. Each time, we compare the newest x against the current value of max to see if it is larger. # program: maxn.py # Finds the maximum of a series of numbers def main(): n = input("How many numbers are there? ") # Set max to be the first value max = input("Enter a number >> ") # Now compare the n-1 successive values for i in range(n-1): x = input("Enter a number >> ") if x > max: max = x print "The largest value is", max main() This code uses a decision nested inside of a loop to get the job done. On each iteration of the loop, max contains the largest value seen so far.CHAPTER 7. CONTROL STRUCTURES, PART 1 116 7.5.4 Strategy 4: Use Python Before leaving this problem, I really should mention that none of the algorithm development we have so painstakingly pursued was necessary. Python actually has a built-in function called max that returns the largest of its parameters. Here is the simplest version of our program. def main(): x1, x2, x3 = input("Please enter three values: ") print "The largest value is", max(x1, x2, x3) Of course, this version didn't require any algorithm development at all, which rather defeats the point of the exercise! Sometimes Python is just too simple for our own good.... 7.5.5 Some Lessons The max of three problem is not particularly earth shattering, but the attempt to solve this problem has illustrated some important ideas in algorithm and program design. There is more than one way to do it. For any non-trivial computing problem, there are many ways to approach the problem. While this may seem obvious, many beginning programmers do not really take this point to heart. What does this mean for you? Don't rush to code up the first idea that pops into your head. Think about your design, ask yourself if there is a better way to approach the problem. Once you have written the code, ask yourself again if there might be a better way. Your first task is to find a correct algorithm. After that, strive for clarity, simplicity, efficiency, scalability and elegance. Good algorithms and programs are like poems of logic. They are a pleasure to read and maintain. Be the computer. Especially for beginning programmers, one of the best ways to formulate an algorithm is to simply ask yourself how you would solve the problem. There are other techniques for designing good algorithms (see Chapter 13); however, the straightforward approach is often simple, clear and efficient enough. Generality is good. We arrived at the best solution to the max of three problem by considering the more general max of n problem. It is not unusual that consideration of a more general problem can lead to a better solution for some special case. Don't be afraid to step back and think about the overarching problem. Similarly, when designing programs, you should always have an eye toward making your program more generally useful. If the max of n program is just as easy to write as max of three, you may as well write the more general program because it is more likely to be useful in other situations. That way you get the maximum utility from your programming effort. Don't reinvent the wheel. Our fourth solution was to use Python's max function. You may think that was cheating, but this example illustrates an important point. A lot of very smart programmers have designed countless good algorithms and programs. If the problem you are trying to solve seems to be one that lots of others must have encountered, you might begin by finding out if the problem has already been solved for you. As you are learning to program, designing from scratch is great experience. Truly expert programmers, however, know when to borrow. 7.6 Exercises 1. Explain the following patterns in your own words. (a) simple decision (b) two-way decision (c) multi-way decision 2. The following is a (silly) decision structure.7.6. EXERCISES 117 a, b, c = input(/%-Enter three numbers: "%*) if a > b: if b > c: print "Spam Please!" else: print "It's a late parrot!" elif b > c: print "Cheese Shoppe" if a >= c: print "Cheddar" elif a < b: print "Gouda" elif c == b: print "Swiss" else: print "Trees" if a == b: print "Chestnut" else: print "Larch" print "Done" Show the output that would result from each of the following possible inputs. (a) 3, 4, 5 (b) 3, 3, 3 (c) 5, 4, 3 (d) 3, 5, 2 (e) 5, 4, 7 (f) 3, 3, 2 3. Many companies pay time-and-a-half for any hours worked above 40 in a given week. Write a program to input the number of hours worked and the hourly rate and calculate the total wages for the week. 4. A certain CS professor gives 5-point quizzes that are graded on the scale 5-A, 4-B, 3-C, 2-D, 1-F, 0-F. Write a program that accepts a quiz score as an input and uses a decision structure to calculate the corresponding grade. 5. A certain CS professor gives 100-point exams that are graded on the scale 90"%,100:A, 80.!&89:B, 70$)& 79:C, 60(/$69:D, 60:F. Write a program that accepts an exam score as input and uses a decision struc- ture to calculate the corresponding grade. 6. A certain college classifies students according to credits earned. A student with less than 7 credits is a Freshman. At least 7 credits are required to be a Sophomore, 16 to be a Junior and 26 to be classified as a Senior. Write a program that calculates class standing from the number of credits earned. 7. The body mass index (BMI) is calculated as a person's weight (in pounds) times 720, divided by the square of the person's height (in inches). A BMI in the range 19(-)25, inclusive, is considered healthy. Write a program that calculates a person's BMI and prints a message telling whether they are above, within or below the healthy range. 8. The speeding ticket fine policy in Podunksville is $50 plus $5 for each mph over the limit plus a penalty of $200 for any speed over 90 mph. Write a program that accepts a speed limit and a clocked speed and either prints a message indicating the speed was legal or prints the amount of the fine, if the speed is illegal.CHAPTER 7. CONTROL STRUCTURES, PART 1 118 9. A babysitter charges $2.50 an hour until 9:00 PM when the rate drops to $1.75 an hour (the children are in bed). Write a program that accepts a starting time and ending time in hours and minutes and calculates the total babysitting bill. You may assume that the starting and ending times are in a single 24 hour period. Partial hours should be appropriately prorated. 10. A person is eligible to be a US senator if they are at least 30 years old and have been a US citizen for at least 9 years. To be a US representative these numbers are 25 and 7, respectively. Write a program that accepts a person's age and years of citizenship as input and outputs their eligibility for the Senate and House. 11. A formula for computing Easter in the years 1982'&-2048, inclusive, is as follows: let a year%19, b year%4, c year%7, d 19a 24 %30, e 2b 4c 6d 5 %7. The date of Easter is March 22 d e (which could be in April). Write a program that inputs a year, verifies that it is in the proper range and then prints out the date of Easter that year. 12. The formula for Easter in the previous problem works for every year in the range 1900*&'2099 except for 1954, 1981, 2049 and 2076. For these 4 years it produces a date that is one week too late. Modify the above program to work for the entire range 1900$-)2099. 13. A year is a leap year if it is divisible by 4, unless it is a century year that is not divisible by 400. (1800 and 1900 are not leap years while 1600 and 2000 are.) Write a program that calculates whether a year is a leap year. 14. Write a program that accepts a date in the form month/day/year and outputs whether or not the date is valid. For example 5/24/1962 is valid, but 9/31/2000 is not. (September has only 30 days.) 15. The days of the year are often numbered from 1 to through 365 (or 366). This number can be computed in three steps using int arithmetic: (a) dayNum 31 month 1 day (b) if the month is after February subtract 4month 23 10 (c) if it's a leap year and after February 29, add 1 Write a program that accepts a date as month/day/year, verifies that it is a valid date (see previous problem) and then calculates the corresponding day number. 16. Take a favorite programming problem from a previous chapter and add decisions and/or exception handling as required to make it truly robust (will not crash on any inputs). 17. Archery Scorer. Write a program that draws an archery target (see exercises from Chapter 5) and allows the user to click 5 times to represent arrows shot at the target. Using 5-band scoring, a bulls-eye (yellow) is worth 9 points and each successive ring is worth 2 fewer points down to 1 for white. The program should output a score for each click and keep track of a running sum for the entire series.Chapter 8 Control Structures, Part 2 In Chapter 7, we looked in detail at the Python if statement and its use in implementing design patterns such as one-way, two-way and multi-way decisions. In this chapter, we'll wrap up our tour of control structures with a detailed look at loops and Boolean expressions. 8.1 For Loops: A Quick Review You already know that the Python for statement provides a kind of loop. It allows us to iterate through a sequence of values. for in : The loop index variable var takes on each successive value in the sequence, and the statements in the body of the loop are executed once for each value. Suppose we want to write a program that can compute the average of a series of numbers entered by the user. To make the program general, it should work for any size set of numbers. You know that an average is calculated by summing up the numbers and dividing by the count of how many numbers there are. We don't need to keep track of all the numbers that have been entered; we just need a running sum so that we can calculate the average at the end. This problem description should start some bells ringing in your head. It suggests the use of some design patterns you have seen before. We are dealing with a series of numbers$$-that will be handled by some form of loop. If there are n numbers, the loop should execute n times; we can use the counted loop pattern. We also need a running sum; that calls for a loop accumulator. Putting the two ideas together, we can generate a design for this problem. Input the count of the numbers, n Initialize sum to 0 Loop n times Input a number, x Add x to sum Output average as sum / n Hopefully, you see both the counted loop and accumulator patterns integrated into this design. We can translate this design almost directly into a Python implementation. # average1.py def main(): n = input("How many numbers do you have? ") sum = 0.0 for i in range(n): 119CHAPTER 8. CONTROL STRUCTURES, PART 2 120 x = input("Enter a number >> ") sum = sum + x print "\nThe average of the numbers is", sum / n The running sum starts at 0, and each number is added in turn. Notice that sum is initialized to a float 0.0. This ensures that the division sum / n on the last line returns a float even if all the input values were ints. Here is the program in action. How many numbers do you have? 5 Enter a number >> 32 Enter a number >> 45 Enter a number >> 34 Enter a number >> 76 Enter a number >> 45 The average of the numbers is 46.4 Well, that wasn't too bad. Knowing a couple of common patterns, counted loop and accumulator, got us to a working program with minimal difficulty in design and implementation. Hopefully, you can see the worth of committing these sorts of programming clich (&es to memory. 8.2 Indefinite Loops Our averaging program is certainly functional, but it doesn't have the best user interface. It begins by asking the user how many numbers there are. For a handful of numbers this is OK, but what if I have a whole page of numbers to average? It might be a significant burden to go through and count them up. It would be much nicer if the computer could take care of counting the numbers for us. Unfortunately, as you no doubt recall, the for loop is a definite loop, and that means the number of iterations is determined when the loop starts. We can't use a definite loop unless we know the number of iterations ahead of time, and we can't know how many iterations this loop needs until all of the numbers have been entered. We seem to be stuck. The solution to this dilemma lies in another kind of loop, the indefinite or conditional loop. An indefinite loop keeps iterating until certain conditions are met. There is no guarantee ahead of time regarding how many times the loop will go around. In Python, an indefinite loop is implemented using a while statement. Syntactically, the while is very simple. while : Here condition is a Boolean expression, just like in if statements. The body is, as usual, a sequence of one or more statements. The semantics of while is straightforward. The body of the loop executes repeatedly as long as the condition remains true. When the condition is false, the loop terminates. Figure 8.1 shows a flowchart for the while. Notice that the condition is always tested at the top of the loop, before the loop body is executed. This kind of structure is called a pre-test loop. If the loop condition is initially false, the loop body will not execute at all. Here is an example of a simple while loop that counts from 0 to 10: i = 0 while i <= 10: print i i = i + 1 This code will have the same output as if we had written a for loop like this:8.3. COMMON LOOP PATTERNS 121 ? no yes Figure 8.1: Flowchart of a while loop. for i in range(11): print i Notice that the while version requires us to take care of initializing i before the loop and incrementing i at the bottom of the loop body. In the for loop, the loop variable is handled automatically. The simplicity of the while statement makes it both powerful and dangerous. Because it is less rigid, it is more versatile; it can do more than just iterate through sequences. But it is also a common source of errors. Suppose we forget to increment i at the bottom of the loop body in the counting example. i = 0 while i <= 10: print i What will the output from this program be? When Python gets to the loop, i will be 0, which is less than 10, so the loop body executes, printing a 0. Now control returns to the condition; i is still 0, so the loop body executes again, printing a 0. Now control returns to the condition; i is still 0, so the loop body executes again, printing a 0.... You get the picture. This is an example of an infinite loop. Usually, infinite loops are a bad thing. Clearly this version of the program does nothing useful. That reminds me, did you hear about the computer scientist who died of exhaustion while washing his hair? The instructions on the bottle said: "*'Lather. Rinse. Repeat.&&- As a beginning programmer, it would surprising if you did not accidently write a few programs with infinite loops-+!it's a rite of passage for programmers. Even more experienced programmers have been known to do this from time to time. Usually, you can break out of a loop by pressing Ctrl -c (holding down the Ctrl key and pressing .(/c-$.). If your loop is really tight, this might not work, and you'll have to resort to more drastic means (such as Ctrl - Alt - Delete on a PC). If all else fails, there is always the trusty reset button on your computer. The best idea is to avoid writing infinite loops in the first place. 8.3 Common Loop Patterns 8.3.1 Interactive Loops One good use of the indefinite loop is to write interactive loops. The idea behind an interactive loop is that it allows the user to repeat certain portions of a program on demand. Let's take a look at this loop pattern in the context of our number averaging problem.CHAPTER 8. CONTROL STRUCTURES, PART 2 122 Recall that the previous version of the program forced the user to count up how many numbers there were to be averaged. We want to modify the program so that it keeps track of how many numbers there are. We can do this with another accumulator, call it count, that starts at zero and increases by 1 each time through the loop. To allow the user to stop at any time, each iteration of the loop will ask whether there is more data to process. The general pattern for an interactive loop looks like this: set moredata to "yes" while moredata is "yes" get the next data item process the item ask user if there is moredata Combining the interactive loop pattern with accumulators for the sum and count yields this algorithm for the averaging program. initialize sum to 0.0 initialize count to 0 set moredata to "yes" while moredata is "yes" input a number, x add x to sum add 1 to count ask user if there is moredata output sum / count Notice how the two accumulators are interleaved into the basic structure of the interactive loop. Here is the corresponding Python program: # average2.py def main(): sum = 0.0 count = 0 moredata = "yes" while moredata[0] == "y": x = input("Enter a number >> ") sum = sum + x count = count + 1 moredata = raw_input("Do you have more numbers (yes or no)? ") print "\nThe average of the numbers is", sum / count Notice this program uses string indexing (moredata[0]) to look just at the first letter of the user's input. This allows for varied responses such as ",-yes,(!' %/*y,)(& ---yeah,#.' etc. All that matters is that the first letter is a !,-y.-!/ Also note the use of raw input to get this value. Remember you should use raw input to get string data. Here is sample output from this program. Enter a number >> 32 Do you have more numbers Enter a number >> 45 Do you have more numbers Enter a number >> 34 Do you have more numbers Enter a number >> 76 Do you have more numbers Enter a number >> 45 Do you have more numbers (yes or no)? yes (yes or no)? y (yes or no)? y (yes or no)? y (yes or no)? nope8.3. COMMON LOOP PATTERNS 123 The average of the numbers is 46.5 In this version, the user doesn't have to count the data values, but the interface is still not good. The user will almost certainly be annoyed by the constant prodding for more data. The interactive loop has many good applications; this is not one of them. 8.3.2 Sentinel Loops A better solution to the number averaging problem is to employ a pattern commonly known as a sentinel loop. A sentinel loop continues to process data until reaching a special value that signals the end. The special value is called the sentinel. Any value may be chosen for the sentinel. The only restriction is that it be distinguishable from actual data values. The sentinel is not processed as part of the data. Here is a general pattern for designing sentinel loops: get the first data item while item is not the sentinel process the item get the next data item Notice how this pattern avoids processing the sentinel item. The first item is retrieved before the loop starts. This is sometimes called the priming read, as it gets the process started. If the first item is the sentinel, the loop immediately terminates and no data is processed. Otherwise, the item is processed and the next one is read. The loop test at the top ensures this next item is not the sentinel before processing it. When the sentinel is reached, the loop terminates. We can apply the sentinel pattern to our number averaging problem. The first step is to pick a sentinel. Suppose we are using the program to average exam scores. In that case, we can safely assume that no score will be below 0. The user can enter a negative number to signal the end of the data. Combining the sentinel loop with the two accumulators from the interactive loop version yields this program. # average3.py def main(): sum = 0.0 count = 0 x = input("Enter a number (negative to quit) >> ") while x >= 0: sum = sum + x count = count + 1 x = input("Enter a number (negative to quit) >> ") print "\nThe average of the numbers is", sum / count I have changed the prompt so that the user knows how to signal the end of the data. Notice that the prompt is identical at the priming read and the bottom of the loop body. Now we have a useful form of the program. Here it is in action: Enter Enter Enter Enter Enter Enter a a a a a a number number number number number number (negative (negative (negative (negative (negative (negative to to to to to to quit) quit) quit) quit) quit) quit) >> >> >> >> >> >> 32 45 34 76 45 -1 The average of the numbers is 46.4 This version provides the ease of use of the interactive loop without the hassle of having to type &.(yes%$+ all the time. The sentinel loop is a very handy pattern for solving all sorts of data processing problems. It's another clich +%e that you should commit to memory.CHAPTER 8. CONTROL STRUCTURES, PART 2 124 This sentinel loop solution is quite good, but there is still a limitation. The program can't be used to average a set of numbers containing negative as well as positive values. Let's see if we can't generalize the program a bit. What we need is a sentinel value that is distinct from any possible valid number, positive or negative. Of course, this is impossible as long as we restrict ourselves to working with numbers. No matter what number or range of numbers we pick as a sentinel, it is always possible that some data set may contain such a number. In order to have a truly unique sentinel, we need to broaden the possible inputs. Suppose that we get the input from the user as a string. We can have a distinctive, non-numeric string that indicates the end of the input; all others would be converted into numbers and treated as data. One simple solution is to have the sentinel value be an empty string. Remember, an empty string is represented in Python as "" (quotes with no space between). If the user types a blank line in response to a raw input (just hits Enter ), Python returns an empty string. We can use this as a simple way to terminate input. The design looks like this: Initialize sum to 0.0 Initialize count to 0 Input data item as a string, xStr while xStr is not empty Convert xStr to a number, x Add x to sum Add 1 to count Input next data item as a string, xStr Output sum / count Comparing this to the previous algorithm, you can see that converting the string to a number has been added to the processing section of the sentinel loop. Translating into Python yields this program: # average4.py def main(): sum = 0.0 count = 0 xStr = raw_input("Enter a number ( to quit) >> ") while xStr != "": x = eval(xStr) sum = sum + x count = count + 1 xStr = raw_input("Enter a number ( to quit) >> ") print "\nThe average of the numbers is", sum / count This code makes use of eval (from Chapter 4) to convert the input string into a number. Here is an example run, showing that it is now possible to average arbitrary sets of numbers: Enter Enter Enter Enter Enter Enter Enter a a a a a a a number number number number number number number ( ( ( ( ( ( ( to to to to to to to quit) quit) quit) quit) quit) quit) quit) >> >> >> >> >> >> >> 34 23 0 -25 -34.4 22.7 The average of the numbers is 3.38333333333 We finally have an excellent solution to our original problem. You should study this solution so that you can incorporate these techniques into your own programs.8.3. COMMON LOOP PATTERNS 125 8.3.3 File Loops One disadvantage of all the averaging programs presented so far is that they are interactive. Imagine you are trying to average 87 numbers and you happen to make a typo near the end. With our interactive program, you will need to start all over again. A better approach to the problem might be to type all of the numbers into a file. The data in the file can be perused and edited before sending it to a program that generates a report. This file-oriented approach is typically used for data processing applications. Back in Chapter 4, we looked at reading data from files using the Python readlines method and a for loop. We can apply this technique directly to the number averaging problem. Assuming that the numbers are typed into a file one per line, we can compute the average with this program. # average5.py def main(): fileName = raw_input("What file are the numbers in? ") infile = open(fileName,.($r/.() sum = 0.0 count = 0 for line in infile.readlines(): sum = sum + eval(line) count = count + 1 print "\nThe average of the numbers is", sum / count In this code, readlines reads the entire file into memory as a sequence of strings. The loop variable line then iterates through this sequence; each line is converted to a number and added to the running sum. One potential problem with this kind of file processing loop is that the entire contents of the file are first read into main memory via the readlines method. As you know from Chapter 1, secondary memory where files reside is usually much larger than the primary memory. It's possible that a large data file may not fit into memory all at one time. In that case, this approach to file processing will be very inefficient and, perhaps, not work at all. With very large data files, it is better to read and process small sections of the file at a time. In the case of text files, a simple approach is to process the file one line at a time. This is easily accomplished by a sentintel loop and the Python readline method on files. Recall, the readline method gets the next line from a file as a string. When we come to the end of the file, readline returns an empty string, which we can use as a sentinel value. Here is a general pattern for an end-of-file loop in Python. line = infile.readline() while line != "": # process line line = infile.readline() At first glance, you may be concerned that this loop stops prematurely if it encounters an empty line in the file. This is not the case. Remember, a blank line in a text file contains a single newline character (" n"), and the readline method includes the newline character in its return value. Since " n" != "", the loop will continue. Here is the code that results from applying the end-of-file sentinel loop to our number averaging problem. # average6.py def main(): fileName = raw_input("What file are the numbers in? ") infile = open(fileName,&!"r-&.) sum = 0.0 count = 0CHAPTER 8. CONTROL STRUCTURES, PART 2 126 line = infile.readline() while line != "": sum = sum + eval(line) count = count + 1 line = infile.readline() print "\nThe average of the numbers is", sum / count Obviously, this version is not quite as concise as the version using readlines and a for loop. If the file sizes are known to be relatively modest, that approach is probably better. When file sizes are quite large, however, an end-of-file loop is invaluable. 8.3.4 Nested Loops In the last chapter, you saw how control structures such as decisions and loops could be nested inside one another to produce sophisticated algorithms. One particularly useful, but somewhat tricky technique is the nesting of loops. Let's take a look at an example program. How about one last version of our number averaging problem? Honest, I promise this is the last time I'll use this example. 1 Suppose we modify the specification of our file averaging problem slightly. This time, instead of typing the numbers into the file one-per-line, we'll allow any number of values on a line. When multiple values appear on a line, they will be separated by commas. At the top level, the basic algorithm will be some sort of file-processing loop that computes a running sum and count. For practice, let's use an end-of-file loop. Here is the code comprising the top-level loop. sum = 0.0 count = 0 line = infile.readline() while line != "": # update sum and count for values in line line = infile.readline() print "\nThe average of the numbers is", sum / count Now we need to figure out how to update the sum and count in the body of the loop. Since each individual line of the file contains one or more numbers separated by commas, we can split the line into substrings, each of which represents a number. Then we need to loop through these substrings, convert each to a number, and add it to sum. We also need to add 1 to count for each number. Here is a code fragment that processes a line: for xStr in string.split(line,","): sum = sum + eval(xStr) count = count +1 Notice that the iteration of the for loop in this fragment is controlled by the value of line, which just happens to be the loop-control variable for the file-processing loop we outlined above. Knitting these two loops together, here is our program. # average7.py import string def main(): fileName = raw_input("What file are the numbers in? ") infile = open(fileName,**(r!"/) sum = 0.0 count = 0 line = infile.readline() while line != "": 1 until Chapter 11...8.4. COMPUTING WITH BOOLEANS 127 # update sum and count for values in line for xStr in string.split(line): sum = sum + eval(xStr) count = count + 1 line = infile.readline() print "\nThe average of the numbers is", sum / count As you can see, the loop that processes the numbers in a line is indented inside of the file processing loop. The outer while loop iterates once for each line of the file. On each iteration of the outer loop, the inner for loop iterates as many times as there are numbers on that line. When the inner loop finishes, the next line of the file is read, and the outer loop goes through its next iteration. The individual fragments of this problem are not complex when taken separately, but the final result is fairly intricate. The best way to design nested loops is to follow the process we did here. First design the outer loop without worrying about what goes inside. Then design what goes inside, ignoring the outer loop(s). Finally, put the pieces together, taking care to preserve the nesting. If the individual loops are correct, the nested result will work just fine; trust it. With a little practice, you'll be implementing double-, even triple- nested loops with ease. 8.4 Computing with Booleans We now have two control structures, if and while, that use conditions, which are Boolean expressions. Conceptually, a Boolean expression evaluates to one of two values: false or true. In Python, these values are represented by the ints 0 and 1. So far, we have used simple Boolean expressions that compare two values (e.g., while x >= 0). 8.4.1 Boolean Operators Sometimes the simple conditions that we have been using do not seem expressive enough. For example, suppose you need to determine whether two point objects are in the same position$&%that is, they have equal x coordinates and equal y coordinates. One way of handling this would be a nested decision. if p1.getX() == p2.getX(): if p1.getY() == p2.getY(): # points are the same else: # points are different else: # points are different You can see how awkward this is. Instead of working around this problem with a decision structure, another approach would be to construct a more complex expression using Boolean operations. Like most programming languages, Python provides three Boolean operators: and, or and not. Let's take a look at these three operators and then see how they can be used to simplify our problem. The Boolean operators and and or are used to combine two Boolean expressions and produce a Boolean result. and or The and of two expressions is true exactly when both of the expressions are true. We can represent this definition in a truth table.CHAPTER 8. CONTROL STRUCTURES, PART 2 128 P T T F F Q T F T F P and Q T F F F In this table, P and Q represent smaller Boolean expressions. Since each expression has two possible values, there are four possible combinations of values, each shown as one row in the table. The last column gives the value of P and Q for each possible combination. By definition, the and is true only in the case where both P and Q are true. The or of two expressions is true when either expression is true. Here is the truth table defining or: P T T F F Q T F T F P or Q T T T F The only time the or is false is when both expressions are false. Notice especially that or is true when both expressions are true. This is the mathematical definition of or, but the word $%"or*$- is sometimes used in an exclusive sense in everyday English. If your mom said that you could have cake or cookies for dessert, she would probably scold you for taking both. The not operator computes the opposite of a Boolean expression. It is a unary operator, meaning that it operates on a single expression. The truth table is very simple. P T F not P F T Using Boolean operators, it is possible to build arbitrarily complex Boolean expressions. As with arith- metic operators, the exact meaning of a complex expression depends on the precedence rules for the operators. Consider this expression. a or not b and c How should this be evaluated? Python follows a standard convention that the order of precedence is not, followed by and, followed by or. So the expression would be equivalent to this parenthesized version. (a or ((not b) and c)) Unlike arithmetic, however, most people don't tend to know or remember the precedence rules for Booleans. I suggest that you always parenthesize your complex expressions to prevent confusion. Now that we have some Boolean operators, we are ready to return to our example problem. To test for the co-location of two points, we could use an and operation. if p1.getX() == p2.getX() and p2.getY() == p1.getY(): # points are the same else: # points are different Here the entire expression will only be true when both of the simple conditions are true. This ensures that both the x and y coordinates have to match for the points to be the same. Obviously, this is much simpler and clearer than the nested ifs from the previous version. Let's look at a slightly more complex example. In the next chapter, we will develop a simulation for the game of racquetball. Part of the simulation will need to determine when a game has ended. Suppose that scoreA and scoreB represent the scores of two racquetball players. The game is over as soon as either of the players has reached 15 points. Here is a Boolean expression that is true when the game is over:8.4. COMPUTING WITH BOOLEANS 129 scoreA == 15 or scoreB == 15 When either score reaches 15, one of the two simple conditions becomes true, and, by definition of or, the entire Boolean expression is true. As long as both conditions remain false (neither player has reached 15) the entire expression is false. Our simulation will need a loop that continues as long as the game is not over. We can construct an appropriate loop condition by taking the negation of the game-over condition: while not (scoreA == 15 or scoreB == 15): # continue playing We can also construct more complex Boolean expressions that reflect different possible stopping condi- tions. Some racquetball players play shutouts (sometimes called a skunk). For these players, a game also ends when one of the players reaches 7 and the other has not yet scored a point. For brevity, I'll use a for scoreA and b for scoreB. Here is an expression for game-over when shutouts are included: a == 15 or b == 15 or (a == 7 and b == 0) or (b == 7 and a == 0) Do you see how I have added two more situations to the original condition? The new parts reflect the two possible ways a shutout can occur, and each requires checking both scores. The result is a fairly complex expression. While we're at it, let's try one more example. Suppose we were writing a simulation for volleyball, rather than racquetball. Volleyball does not have shutouts, but it requires a team to win by at least two points. If the score is 15 to 14, or even 21 to 20, the game continues. Let's write a condition that computes when a volleyball game is over. Here's one approach. (a >= 15 and a - b >= 2) or (b >= 15 and b - a >= 2) Do you see how this expression works? It basically says the game is over when team A has won (scored at least 15 and leading by at least 2) or when team B has won. Here is another way to do it. (a >= 15 or b >= 15) and abs(a - b) >= 2 This version is a bit more succinct. It states that the game is over when one of the teams has reached a winning total and the difference in the scores is at least 2. Remember that abs returns the absolute value of an expression. 8.4.2 Boolean Algebra All decisions in computer programs boil down to appropriate Boolean expressions. The ability to formulate, manipulate and reason with these expressions is an important skill for programmers and computer scientists. Boolean expressions obey certain algebraic laws similar to those that apply to numeric operations. These laws are called Boolean logic or Boolean algebra. Let's look at a few examples. The following table shows some rules of algebra with their correlates in Boolean algebra. Algebra a 0 0 a 1 a a 0 a Boolean algebra a and false == false a and true == a a or false == a From these examples, you can see that and has similarities to multiplication, and or has similarities to addition; while 0 and 1 correspond to false and true. Here are some other interesting properties of Boolean operations. Anything ored with true is just true. a or true == true Both and and or distribute over each other.CHAPTER 8. CONTROL STRUCTURES, PART 2 130 a or (b and c) == (a or b) and (a or c) a and (b or c) == (a and b) or (a and c) A double negative cancels out. not(not a) == a The next two identities are known as DeMorgan's laws. not(a or b) == (not a) and (not b) not(a and b) == (not a) or (not b) Notice how the operator changes between and and or when the not is pushed into an expression. One application of Boolean algebra is the analysis and simplification of Boolean expressions inside of programs. For example, let's go back to the racquetball game one more time. Above, we developed a loop condition for continuing the game that looked like this: while not (scoreA == 15 or scoreB == 15): # continue playing You can read this condition as something like: While it is not the case that player A has 15 or player B has 15, continue playing. We're pretty sure that's correct, but negating complex conditions like this can be somewhat awkward, to say the least. Using a little Boolean algebra, we can transform this result. Applying DeMorgan's law, we know that the expression is equivalent to this. (not scoreA == 15) and (not scoreB == 15) Remember, we have to change the or to and when %&(distributing)%/ the not. This condition is no better than the first, but we can go one step farther by pushing the nots into the conditions themselves. while scoreA != 15 and scoreB != 15: # continue playing Now we have a version that is much easier to understand. This reads simply as while player A has not reached 15 and player B has not reached 15, continue playing. This particular example illustrates a generally useful approach to loop conditions. Sometimes it's easier to figure out when a loop should stop, rather than when the loop should continue. In that case, simply write the loop termination condition and then put a not in front of it. An application or two of DeMorgan's laws can then get you to a simpler but equivalent version suitable for use in a while statement. 8.5 Other Common Structures Taken together, the decision structure (if) along with a pre-test loop (while) provide a complete set of control structures. This means that every algorithm can be expressed using just these. Once you've mastered the while and the if, there is no algorithm that you cannot write, in principle. However, for certain kinds of problems, alternative structures can sometimes be convenient. This section outlines some of those alternatives. 8.5.1 Post-Test Loop Suppose you are writing an input algorithm that is supposed to get a nonnegative number from the user. If the user types an incorrect input, the program asks for another value. It continues to reprompt until the user enters a valid value. This process is called input validation. Well-engineered programs validate inputs whenever possible. Here is a simple algorithm. repeat get a number from the user until number is >= 08.5. OTHER COMMON STRUCTURES 131 The idea here is that the loop keeps getting inputs until the value is acceptable. The flowchart depicting this design in shown in Figure 8.2. Notice how this algorithm contains a loop where the condition test comes after the loop body. This is a post-test loop. A post-test loop must always execute the body of the loop at least once. Get a number number < 0 ? yes no Figure 8.2: Flowchart of a post-test loop. Unlike some other languages, Python does not have a statement that directly implements a post-test loop. However, this algorithm can be implemented with a while by %+&seeding,!& the loop condition for the first iteration. number = -1 # Start with an illegal value to get into the loop. while number < 0: number = input("Enter a positive number: ") This forces the loop body to execute at least once and is equivalent to the post-test algorithm. You might notice that this is similar to the structure given earlier for the interactive loop pattern. Interactive loops are naturally suited to a post-test implementation. Some programmers prefer to simulate a post-test loop more directly by using a Python break statement. Executing break causes Python to immediately exit the enclosing loop. Often a break statement is used to leave what looks syntactically like an infinite loop. Here is the same algorithm implemented with a break. while 1: number = input("Enter a positive number: ") if x >= 0: break # Exit loop if number is valid. The first line may look a bit strange to you. Remember that conditions in Python evaluate to either a 0 for false or a 1 for true. The heading while 1 appears to be an infinite loop, since the expression always evaluates to 1 (i.e., it is always true). However, when the value of x is nonnegative, the break statement executes, which terminates the loop. Notice the break is placed on the same line as the if. This is legal when the body of the if only contains one statement. It's common to see a one-line if-break combination used as a loop exit. Even this small example can be improved. It would be nice if the program issued a warning explaining why the input was invalid. In the while version of the post-test loop, this is a bit awkward. We need to add an if so that the warning is not displayed for valid inputs.132 CHAPTER 8. CONTROL STRUCTURES, PART 2 number = -1 # Start with an illegal value to get into the loop. while number < 0: number = input("Enter a positive number: ") if number < 0: print "The number you entered was not positive" See how the validity check gets repeated in two places? Adding a warning to the version using break only requires adding an else to the existing if. while 1: number = input("Enter a positive number: ") if x >= 0: break # Exit loop if number is valid. else: print "The number you entered was not positive" 8.5.2 Loop and a Half Some programmers would solve the warning problem from the previous section using a slightly different style. while 1: number = input("Enter a positive number: ") if x >= 0: break # Loop exit print "The number you entered was not positive" Here the loop exit is actually in the middle of the loop body. This is called a loop and a half. Some purists frown on exits in the midst of a loop like this, but the pattern can be quite handy. The loop and a half is an elegant way to avoid the priming read in a sentinel loop. Here is the general pattern of a sentinel loop implemented as a loop and a half. while 1: Get next data item if the item is the sentinel: break process the item Figure 8.3 shows a flowchart of this approach to sentinel loops. You can see that this implementation is faithful to the first rule of sentinel loops: avoid processing the sentinel value. The choice of whether to use break statements or not is largely a matter of taste. Either style is accept- able. One temptation that should generally be avoided is peppering the body of a loop with multiple break statements. The logic of a loop is easily lost when there are multiple exits. However, there are times when even this rule should be broken to provide the most elegant solution to a problem. 8.5.3 Boolean Expressions as Decisions So far, we have talked about Boolean expressions only within the context of other control structures. Some- times, Boolean expressions themselves can act as control structures. In fact, Boolean expressions are so flexible in Python that they can sometimes lead to subtle programming errors. Consider writing an interactive loop that keeps going as long as the user response starts with a %%#y.*/, To allow the user to type either an upper or lower case response, you could use a loop like this: while response[0] == "y" or response[0] == "Y": You must be be careful not to abbreviate this condition as you might think of it in English: )'%While the first letter is )**y%%' or &."Y#*(#$$. The following form does not work. while response[0] == "y" or "Y":8.5. OTHER COMMON STRUCTURES 133 Get next Data item Item is the sentinel ? yes no Process the item Figure 8.3: Loop-and-a-half implementation of sentinel loop pattern In fact, this is an infinite loop. Understanding why this condition is always true requires digging into some idiosyncrasies of Python Boolean expressions. You already know that Python does not have any special Boolean type. We have been using the int values 0 and 1 to represent the Boolean values false and true, respectively. The Python condition operators (i.e., ==) always evaluate to either 0 or 1. However, Python is actually very flexible about what can be a Boolean expression. Any built-in type can be interpreted as a Boolean. For numbers (ints, floats and long ints) a zero value is considered as false, anything other than zero is taken as true. Other types can also be used in Boolean expressions. For example, Python interprets an empty string as false and any nonempty string as true. The flexibility of Python Booleans extends to the Boolean operators. Although the main use of these operators is forming Boolean expressions, they have operational definitions that make them useful for other purposes as well. This table summarizes the behavior of these operators. operator x and y x or y not x operational definition If x is false, return x. Otherwise, return y. If x is false, return y. Otherwise, return x. If x is false, return 1. Otherwise, return 0. The definition of not is straightforward. It might take a bit of thinking to convince yourself that these descriptions of and and or faithfully reflect the truth tables you saw at the beginning of the chapter. Consider the expression x and y. In order for this to be true, both expressions x and y must be true. As soon as one of them is discovered to be false, the party is over. Python looks at the expressions left-to-right. If x is false, Python should return a false result. Whatever the false value of x was, that is what is returned. If x turns out to be true, then the truth or falsity of the whole expression turns on the result of y. Simply returning y guarantees that if y is true, the whole result is true, and if y is false, the whole result is false. Similar reasoning can be used to show that the description of or is faithful to the logical definition of or given in the truth table. These operational definitions show that Python's Boolean operators are short-circuit operators. That means that a true or false value is returned as soon as the result is known. In an and where the first expression is false and in an or where the first expression is true, Python will not even evaluate the second expression. Now let's take a look at our infinite loop problem.134 CHAPTER 8. CONTROL STRUCTURES, PART 2 response[0] == "y" or "Y" Treated as a Boolean expression, this will always evaluate to true. The first thing to notice is that the Boolean operator is combining two expressions; the first is a simple condition, and the second is a string. Here is an equivalent parenthesized version: (response[0] == "y") or ("Y"): By the operational description of or, this expression returns either 1 (returned by == when response[0] is "(.y'+&) or "Y" (when response[0] is not a $!*y-*!). Either of these results is interpreted by Python as true. A more logic-oriented way to think about this is to simply look at the second expression. It is a nonempty string, so Python will always interpret it as true. Since at least one of the two expressions is always true, the or of the expressions must always be true as well. So, the strange behavior of this example is due to some quirks in the definitions of the Boolean operators. This is one of the few places where the design of Python has a potential pitfall for the beginning programmer. You may wonder about the wisdom of this design, yet the flexibility of Python allows for certain succinct programming idioms that many programmers find useful. Let's look at an example. Frequently, programs prompt users for information but offer a default value for the response. The default value, sometimes listed in square brackets, is used if the user simply hits the Enter key. Here is an example code fragment: ans = raw_input("What flavor do you want [vanilla]: ") if ans != "": flavor = ans else: flavor = "vanilla" Exploiting the fact that the string in ans can be treated as a Boolean, the condition in this code can be simplified as follows. ans = raw_input("What flavor do you want [vanilla]: ") if ans: flavor = ans else: flavor = "vanilla" Here a Boolean condition is being used to decide how to set a string variable. If the user just hits Enter , ans will be an empty string, which Python interprets as false. In this case, the empty string will be replaced by "vanilla" in the else clause. The same idea can be succinctly coded by treating the strings themselves as Booleans and using an or. ans = raw_input("What flavor do you want [vanilla]: ") flavor = ans or "vanilla" The operational definition of or guarantees that this is equivalent to the if-else version. Remember, any nonempty answer is interpreted as *+'true.&+( In fact, this task can easily be accomplished in a single line of code. flavor = raw_input("What flavor do you want [vanilla]: ") or "vanilla" I don't know whether it's really worthwhile to save a few lines of code using Boolean operators this way. If you like this style, by all means, feel free to use it. Just make sure that your code doesn't get so tricky that others (or you) have trouble understanding it. 8.6 Exercises 1. Compare and contrast the following pairs of terms.8.6. EXERCISES 135 (a) Definite loop vs. Indefinite loop (b) For loop vs. While loop (c) Interactive loop vs. Sentinel loop (d) Sentinel loop vs. End-of-file loop 2. Give a truth table that shows the (Boolean) value of each of the following Boolean expressions, for every possible combination of (/-input&,# values. Hint: including columns for '.-intermediate&(( expressions is helpful. (a) not (P and Q) (b) (not P) and Q (c) (not P) or (not Q) (d) (P and Q) or R (e) (P or R) and (Q or R) 3. Write a while loop fragment that calculates the following values. (a) Sum of the first n counting numbers: 1 (b) Sum of the first n odd numbers: 1 3 2 5 3 2n n 1 (c) Sum of a series of numbers entered by the user until the value 999 is entered. Note: 999 should not be part of the sum. (d) The number of times a whole number n can be divided by 2 (using integer division) before reach- ing 1 (i.e., log 2 n). 4. The Fibonacci sequence starts 1 )*/ 1 .(" 2 $$" 3 .!$ 5 .". 8 -() . Each number in the sequence (after the first two) is the sum of the previous two. Write a program that computes and outputs the nth Fibonacci number, where n is a value entered by the user. 5. The National Weather Service computes the windchill index using the following formula. 35 75 V 0 16 35 74 0 6215T 0 4275T V 0 16 Where T is the temperature in degrees Fahrenheit, and V is the wind speed in miles per hour. Write a program that prints a nicely formatted table of windchill values. Rows should represent wind speed for 0 to 50 in 5 mph increments, and the columns represent temperatures from -20 to +60 in 10-degree increments. 6. Write a program that uses a while loop to determine how long it takes for an investment to double at a given interest rate. The input will be an annualized interest rate, and the output is the number of years it takes an investment to double. Note: the amount of the initial investment does not matter; you can use $1. 7. The Syracuse (also called Collatz or Hailstone) sequence is generated by starting with a natural number and repeatedly applying the following function until reaching 1. syr x x 2 if x is even 3x 1 if x is odd For example, the Syracuse sequence starting with 5 is: 5 %// 16 ",+ 8 ,#! 4 ))) 2 !)* 1. It is an open question in mathe- matics whether this sequence will always go to 1 for every possible starting value. Write a program that gets a starting value from the user and then prints the Syracuse sequence for that starting value.CHAPTER 8. CONTROL STRUCTURES, PART 2 136 8. A postive whole number n 2 is prime if no number between 2 and n (inclusive) evenly divides n. Write a program that accepts a value of n as input and determines if the value is prime. If n is not prime, your program should quit as soon as it finds a value that evenly divides n. 9. Modify the previous program to find every prime number less than or equal to n. 10. The greatest common divisor (GCD) of two values can be computed using Euclid's algorithm. Starting with the values m and n, we repeatedly apply the formula: n, m = m, n%m until m is 0. At that point, n is the GCD of the original m and n. Write a program that finds the GCD of two numbers using this algorithm. 11. Write a program that computes the fuel efficiency of a multi-leg journey. The program will first prompt for the starting odometer reading and then get information about a series of legs. For each leg, the user enters the current odometer reading and the amount of gas used (separated by a space). The user signals the end of the trip with a blank line. The program should print out the miles per gallon achieved on each leg and the total MPG for the trip. 12. Modify the previous program to get its input from a file. 13. Heating and cooling degree-days are measures used by utility companies to estimate energy require- ments. If the average temperature for a day is below 60, then the number of degrees below 60 is added to the heating degree-days. If the temperature is above 80, the amount over 80 is added to the cooling degree-days. Write a program that accepts a sequence of average daily temps and computes the running total of cooling and heating degree-days. The program should print these two totals after all the data has been processed. 14. Modify the previous program to get its input from a file. 15. Write a program that graphically plots a regression line, that is, the line with the best fit through a collection of points. First ask the user to specify the data points by clicking on them in a graphics window. To find the end of input, place a small rectangle labelled '("Done(+, in the lower left corner of the window; the program will stop gathering points when the user clicks inside that rectangle. The regression line is the line with the following equation: y "! y where m x -, x '." x i y i n $- x */ y $!) x 2 i n /+ x 2 m %* x is the mean of the x-values and +% y is the mean of the y-values. As the user clicks on points, the program should draw them in the graphics window and keep track of the count of input values and the running sum of x, y, x 2 and xy values. When the user clicks inside the #+,Done#%* rectangle, the program then computes value of y (using the equations above) correponding to the x values at the left and right edges of the window to compute the endpoints of the regression line spanning the window. After the line is drawn, the program will pause for another mouse click before closing the window and quitting.Chapter 9 Simulation and Design You may not realize it, but you have reached a significant milestone in the journey to becoming a computer scientist. You now have all the tools to write programs that solve interesting problems. By interesting, I mean problems that would be difficult or impossible to solve without the ability to write and implement computer algorithms. You are probably not yet ready to write the next great killer application, but you can do some nontrivial computing. One particularly powerful technique for solving real-world problems is simulation. Computers can model real-world processes to provide otherwise unobtainable information. Computer simulation is used every day to perform myriad tasks such as predicting the weather, designing aircraft, creating special effects for movies, and entertaining video game players, to name just a few. Most of these applications require extremely complex programs, but even relatively modest simulations can sometimes shed light on knotty problems. In this chapter we are going to develop a simple simulation of the game of racquetball. Along the way, you will learn some important design and implementation strategies that will help you in tackling your own problems. 9.1 Simulating Racquetball 9.1.1 A Simulation Problem Suzie Programmer's friend, Denny Dibblebit, plays racquetball. Over years of playing, he has noticed a strange quirk in the game. He often competes with players who are just a little bit better than he is. In the process, he always seems to get thumped, losing the vast majority of matches. This has led him to question what is going on. On the surface, one would think that players who are slightly better should win slightly more often, but against Denny, they seem to win the lion's share. One obvious possibility is that Denny Dibblebit's problem is in his head. Maybe his mental game isn't up to par with his physical skills. Or perhaps the other players are really much better than he is, and he just refuses to see it. One day, Denny was discussing racquetball with Suzie, when she suggested another possibility. Maybe it is the nature of the game itself that small differences in ability lead to lopsided matches on the court. Denny was intrigued by the idea; he didn't want to to waste money on an expensive sports psychologist if it wasn't going to help. But how could he figure out if the problem was mental or just part of the game? Suzie suggested she could write a computer program to simulate certain aspects of racquetball. Using the simulation, they could let the computer model thousands of games between players of differing skill levels. Since there would not be any mental aspects involved, the simulation would show whether Denny is losing more than his share of matches. Let's write our own racquetball simulation and see what Suzie and Denny discovered. 137CHAPTER 9. SIMULATION AND DESIGN 138 9.1.2 Program Specification Racquetball is a sport played between two players using racquets to strike a ball in a four-walled court. It has aspects similar to many other ball and racquet games such as tennis, volleyball, badminton, squash, table tennis, etc. We don't need to understand all the rules of racquetball to write the program, just the basic outline of the game. To start the game, one of the players puts the ball into play*."this is called serving. The players then alternate hitting the ball to keep it in play; this is a rally. The rally ends when one of the players fails to hit a legal shot. The player who misses the shot loses the rally. If the loser is the player who served, then service passes to the other player. If the server wins the rally, a point is awarded. Players can only score points during their service. The first player to reach 15 points wins the game. In our simulation, the ability-level of the players will be represented by the probability that the player wins the rally when he or she serves. Thus, players with a 0.6 probability win a point on 60% of their serves. The program will prompt the user to enter the service probability for both players and then simulate multiple games of racquetball using those probabilities. The program will then print a summary of the results. Here is a detailed specification: Input The program first prompts for and gets the service probabilities of the two players (called -**Player A-'& and +-'Player B&"(). Then the program prompts for and gets the number of games to be simulated. Output The program will provide a series of initial prompts such as the following: What is the prob. player A wins a serve? What is the prob. player B wins a serve? How many games to simulate? The program will print out a nicely formatted report showing the number of games simulated and the number of wins and winning percentage for each player. Here is an example: Games Simulated: 500 Wins for A: 268 (53.6%) Wins for B: 232 (46.4%) Notes: All inputs are assumed to be legal numeric values, no error or validity checking is required. In each simulated game, player A serves first. 9.2 Random Numbers Our simulation program will have to deal with uncertain events. When we say that a player wins 50% of the serves, that does not mean that every other serve is a winner. It's more like a coin toss. Overall, we expect that half the time the coin will come up heads and half the time it will come up tails, but there is nothing to prevent a run of five tails in a row. Similarly, our racquetball player should win or lose rallies randomly. The service probability provides a likelihood that a given serve will be won, but there is no set pattern. Many simulations share this property of requiring events to occur with a certain likelihood. A driving simulation must model the unpredictability of other drivers; a bank simulation has to deal with the random arrival of customers. These sorts of simulations are sometimes called Monte Carlo algorithms, because the results depend on &&(chance.,* probabilities. Of course, you know that there is nothing random about computers; they are instruction-following machines. How can computer programs model seemingly random happenings? Simulating randomness is a well-studied problem in computer science. Remember the chaos program from Chapter 1? The numbers produced by that program seemed to jump around randomly between zero and one. This apparent randomness came from repeatedly applying a function to generate a sequence of numbers. A similar approach can be used to generate random (actually pseudorandom) numbers. A pseudorandom number generator works by starting with some seed value. This value is fed to a function to produce a *&&random./( number. The next time a random number is needed, the current value is fed back into9.2. RANDOM NUMBERS 139 the function to produce a new number. With a carefully chosen function, the resulting sequence of values looks essentially random. Of course, if you start the process over again with the same seed value, you end up with exactly the same sequence of numbers. It's all determined by the generating function and the value of the seed. Python provides a library module that contains a number of useful functions for generating pseudorandom numbers. The functions in this module derive an initial seed value from the date and time when the module is loaded, so you get a different seed value each time the program is run. This means that you will also get a unique sequence of pseudorandom values. The two functions of greatest interest to us are randrange and random. The randrange function is used to select a pseudorandom int from a given range. It can be used with one, two or three parameters to specify a range exactly as with the range function. For example, randrange(1,6) returns some number from the range [1,2,3,4,5], and randrange(5,105,5) returns a multiple of 5 between 5 and 100, inclusive. (Remember, ranges go up to, but not including, the stopping value.) Each call to randrange generates a new pseudorandom int. Here is an interactive session that shows randrange in action. >>> >>> 3 >>> 3 >>> 5 >>> 5 >>> 5 >>> 1 >>> 5 >>> 4 >>> 2 from random import randrange randrange(1,6) randrange(1,6) randrange(1,6) randrange(1,6) randrange(1,6) randrange(1,6) randrange(1,6) randrange(1,6) randrange(1,6) Notice it took ten calls to randrange to eventually generate every number in the range 1"#-5. The value 5 came up almost half of the time. This shows the probabilistic nature of random numbers. Over the long haul, this function produces a uniform distribution, which means that all values will appear an (approximately) equal number of times. The random function can be used to generate pseudorandom floating point values. It requires no param- eters and returns values uniformly distributed between 0 and 1 (including 0, but excluding 1). Here are some interactive examples. >>> from random import random >>> random() 0.545146406725 >>> random() 0.221621655814 >>> random() 0.928877335157 >>> random() 0.258660828538 >>> random() 0.859346793436CHAPTER 9. SIMULATION AND DESIGN 140 The name of the module (random) is the same as the name of the function, which gives rise to the funny- looking import line. Our racquetball simulation can make use of the random function to determine whether or not a player wins a serve. Let's look at a specific example. Suppose a player's service probability is 0.70. This means that they should win 70% of their serves. You can imagine a decision in the program something like this: if : score = score + 1 We need to insert a probabilistic condition that will succeed 70% of the time. Suppose we generate a random value between 0 and 1. Exactly 70% of the interval 0 1 is to the left of 0.7. So 70% of the time the random number will be 0.7, and it will be 0.7 the other 30% of the time. (The goes on the upper end, because the random generator can produce a 0, but never a 1.) In general, if prob represents the probability that the player wins a serve, the condition random() < prob will succeed with just the right probability. Here is how the decision will look: if random() < prob: score = score + 1 9.3 Top-Down Design Now you have the complete specification for our simulation and the necessary knowledge of random numbers to get the job done. Go ahead and take a few minutes to write up the program; I'll wait. OK, seriously, this is a more complicated program than you've probably attempted so far. You may not even know where to begin. If you're going to make it through with minimal frustration, you'll need a systematic approach. One proven technique for tackling complex problems is called top-down design. The basic idea is to start with the general problem and try to express a solution in terms of smaller problems. Then each of the smaller problems is attacked in turn using the same technique. Eventually the problems get so small that they are trivial to solve. Then you just put all the pieces back together and, voil`a, you've got a program. 9.3.1 Top-Level Design Top-down design is easier to illustrate than it is to define. Let's give it a try on our racquetball simulation and see where it takes us. As always, a good start is to study the program specification. In very broad brush strokes, this program follows the basic Input-Process-Output pattern. We need to get the simulation inputs from the user, simulate a bunch of games, and print out a report. Here is a basic algorithm. Print an Introduction Get the inputs: probA, probB, n Simulate n games of racquetball using probA and probB Print a report on the wins for playerA and playerB Now that we've got an algorithm, we're ready to write a program. I know what you're thinking: this design is too high-level; you don't have any idea yet how it's all going to work. That's OK. Whatever we don't know how to do, we'll just ignore for now. Imagine that all of the components you need to implement the algorithm have already been written for you. Your job is to finish this top-level algorithm using those components. First we have to print an introduction. I think I know how to do this. It just requires a few print statements, but I don't really want to bother with it right now. It seems an unimportant part of the algorithm. I'll procrastinate and pretend that someone else will do it for me. Here's the beginning of the program. def main(): printInstructions()9.3. TOP-DOWN DESIGN 141 Do you see how this works. I'm just assuming there is a printInstructions function that takes care of printing the instructions. That step was easy! Let's move on. Next, I need to get some inputs from the user. I also know how to do that+,+I just need a few input statements. Again, that doesn't seem very interesting, and I feel like putting off the details. Let's assume that a component already exists to solve that problem. We'll call the function getInputs. The point of this function is to get values for variables probA, probB and n. The function must return these values for the main program to use. Here is our program so far: def main(): printInstructions() probA, probB, n = getInputs() We're making progress, let's move on to the next line. Here we've hit the crux of the problem. We need to simulate n games of racquetball using the values of probA, and probB. This time, I really don't have a very good idea how that will even be accomplished. Let's procrastinate again and push the details off into a function. (Maybe we can get someone else to write that part for us later.) But what should we put into main? Let's call our function simNGames. We need to figure out what the call of this function looks like. Suppose you were asking a friend to actually carry out a simulation of n games. What information would you have to give him? Your friend would need to know how many games he was supposed to simulate and what the values of probA and probB should be for those simulations. These three values will, in a sense, be inputs to the function. What information do you need to get back from your friend? Well, in order to finish out the program (print a report) you need to know how many games were won by player A and how many games were won by Player B. These must be outputs from the simNGames function. Remember in the discussion of functions in Chapter 6, I said that parameters were used as function inputs, and return values serve as function outputs. Given this analysis, we now know how the next step of the algorithm can be coded. def main(): printInstructions() probA, probB, n = getInputs() winsA, winsB = simNGames(n, probA, probB) Are you getting the hang of this? The last step is to print a report. If you told your friend to type up the report, you would have to tell him how many wins there were for each player; these values are inputs to the function. Here's the complete program. def main(): printInstructions() probA, probB, n = getInputs() winsA, winsB = simNGames(n, probA, probB) printSummary(winsA, winsB) That wasn't very hard. The main function is only five lines long, and the program looks like a more precise formulation of the rough algorithm. 9.3.2 Separation of Concerns Of course, the main function alone won't do very much; we've put off all of the interesting details. In fact, you may think that we have not yet accomplished anything at all, but that is far from true. We have broken the original problem into four independent tasks: printInstructions, getInputs, simNGames and printSummary. Further, we have specified the name, parameters and expected return values of the functions that perform these tasks. This information is called the interface or signature of a function. Having signatures allows us to tackle pieces independently. For the purposes of main, we don't care how simNGames does its job. The only concern is that, when given the number of games to simulate and the twoCHAPTER 9. SIMULATION AND DESIGN 142 probabilities, it must hand back the correct number of wins for each player. The main function only cares what each (sub-)function does. Our work so far can be represented as a structure chart (also called a module hierarchy chart). Figure 9.1 illustrates. Each component in the design is a rectangle. A line connecting two rectangles indicates that the one above uses the one below. The arrows and annotations show the interfaces between the components in terms of information flow. main winsA winsB probA probB n printIntro getInputs probA probB n winsA winsB simNGames printSummary Figure 9.1: First-level structure chart for racquetball simulation. At each level of a design, the interface tells us which details of the lower level are important. Anything else can be ignored (for the moment). The general process of determining the important characteristics of something and ignoring other details is called abstraction. Abstraction is the fundamental tool of design. You might view the entire process of top-down design as a systematic method for discovering useful abstractions. 9.3.3 Second-Level Design Now all we need to do is repeat the design process for each of the remaining components. Let's take them in order. The printIntro function should print an introduction to the program. Let's compose a suitable sequence of print statements. def printIntro(): print "This program simulates a game of racquetball between two" print $!%players called "A" and "B". The abilities of each player is.&$ print "indicated by a probability (a number between 0 and 1) that" print "the player wins the point when serving. Player A always" print "has the first serve." Notice the second line. I wanted to put double quotes around +!(A*.) and )$&B,")# so the entire string is enclosed in apostrophes. This function comprises only primitive Python instructions. Since we didn't introduce any new functions, there is no change to our structure chart. Now let's tackle getInputs. We need to prompt for and get three values, which are returned to the main program. Again, this is simple to code. def getInputs(): # RETURNS the three simulation parameters probA, probB and n a = input("What is the prob. player A wins a serve? ") b = input("What is the prob. player B wins a serve? ") n = input("How many games to simulate? ") return a, b, n Notice that I have taken some shortcuts with the variable names. Remember, variables inside of a function are local to that function. This function is so short, it's very easy to see what the three values represent. The main concern here is to make sure the values are returned in the correct order to match with the interface we established between getInputs and main.9.3. TOP-DOWN DESIGN 143 9.3.4 Designing simNGames Now that we are getting some experience with the top-down design technique, we are ready to try our hand at the real problem, simNGames. This one requires a bit more thought. The basic idea is to simulate n games and keep track of how many wins there are for each player. Well, +*(simulate n games((( sounds like a counted loop, and tracking wins sounds like the job for a couple of accumulators. Using our familiar patterns, we can piece together an algorithm. Initialize winsA and winsB to 0 loop n times simulate a game if playerA wins Add one to winsA else Add one to winsB It's a pretty rough design, but then so was our top-level algorithm. We'll fill in the details by turning it into Python code. Remember, we already have the signature for our function. def simNGames(n, probA, probB): # Simulates n games and returns winsA and winsB We'll add to this by initializing the two accumulator variables and adding the counted loop heading. def simNGames(n, probA, probB): # Simulates n games and returns winsA and winsB winsA = 0 winsB = 0 for i in range(n): The next step in the algorithm calls for simulating a game of racquetball. I'm not quite sure how to do that, so as usual, I'll put off the details. Let's just assume there's a function called simOneGame to take care of this. We need to figure out what the interface for this function will be. The inputs for the function seem straightforward. In order to accurately simulate a game, we need to know what the probabilities are for each player. But what should the output be? In the next step of the algorithm, we need to know who won the game. How do you know who won? Generally, you look at the final score. Let's have simOneGame return the final scores for the two players. We can update our structure chart to reflect these decisions. The result is shown in Figure 9.2. Translating this structure into code yields this nearly completed function: def simNGames(n, probA, probB): # Simulates n games and returns winsA and winsB winsA = 0 winsB = 0 for i in range(n): scoreA, scoreB = simOneGame(probA, probB) Finally, we need to check the scores to see who won and update the appropriate accumulator. Here is the result. def simNGames(n, probA, probB): winsA = winsB = 0 for i in range(n): scoreA, scoreB = simOneGame(probA, probB) if scoreA > scoreB: winsA = winsA + 1CHAPTER 9. SIMULATION AND DESIGN 144 main winsA winsB probA probB n printIntro getInputs probA probB n winsA winsB simNGames printSummary probA probB scoreA scoreB simOneGame Figure 9.2: Level 2 structure chart for racquetball simulation. else: winsB = winsB + 1 return winsA, winsB 9.3.5 Third-Level Design Everything seems to be coming together nicely. Let's keep working on the guts of the simulation. The next obvious point of attack is simOneGame. Here's where we actually have to code up the logic of the racquetball rules. Players keep doing rallies until the game is over. That suggests some kind of indefinite loop structure; we don't know how many rallies it will take before one of the players gets to 15. The loop just keeps going until the game is over. Along the way, we need to keep track of the score(s), and we also need to know who is currently serving. The scores will probably just be a couple of int-valued accumulators, but how do we keep track of who's serving? It's either player A or player B. One approach is to use a string variable that stores either "A" or "B". It's also an accumulator of sorts, but to update its value, we just switch it from one value to the other. That's enough analysis to put together a rough algorithm. Let's try this: Initialize scores to 0 Set serving to "A" Loop while game is not over: Simulate one serve of whichever player is serving update the status of the game Return scores It's a start, at least. Clearly there's still some work to be done on this one. We can quickly fill in the first couple steps of the algorithm to get the following. def simOneGame(probA, probB): scoreA = 0 scoreB = 0 serving = "A" while : The question at this point is exactly what the condition will be. We need to keep looping as long as the game is not over. We should be able to tell if the game is over by looking at the scores. We discussed a9.3. TOP-DOWN DESIGN 145 number of possibilities for this condition in the previous chapter, some of which were fairly complex. Let's hide the details in another function, gameOver, that looks at the scores and returns true (1) if the game is over, and false (0) if it is not. That gets us on to the rest of the loop for now. Figure 9.3 shows the structure chart with our new function. The code for simOneGame now looks like this: def simOneGame(probA, probB): scoreA = 0 scoreB = 0 serving = "A" while not gameOver(scoreA, scoreB): main winsA winsB probA probB n printIntro getInputs probA probB n winsA winsB simNGames printSummary probA probB scoreA scoreB simOneGame scoreA scoreB true|false gameOver Figure 9.3: Level 3 structure chart for racquetball simulation. Inside the loop, we need to do a single serve. Remember, we are going to compare a random number to a probability in order to determine if the server wins the point (random() < prob). The correct probability to use is determined by the value of serving. We will need a decision based on this value. If A is serving, then we need to use A's probability, and, based on the result of the serve, update either A's score or change the service to B. Here is the code: if serving == "A": if random() < probA: # A wins the serve scoreA = scoreA + 1 else: # A loses the serve serving = "B" Of course, if A is not serving, we need to do the same thing, only for B. We just need to attach a mirror image else clause. if serving == "A": if random() < probA: scoreA = scoreA + 1 # A wins the serveCHAPTER 9. SIMULATION AND DESIGN 146 else: serving = "B" else: if random() < probB: scoreB = scoreB + 1 else: serving = "A" # A loses serve # B wins the serve # B loses the serve That pretty much completes the function. It got a bit complicated, but seems to reflect the rules of the simulation as they were laid out. Putting the function together, here is the result. def simOneGame(probA, probB): scoreA = 0 scoreB = 0 serving = "A" while gameNotOver(scoreA, scoreB): if serving == "A": if random() < probA: scoreA = scoreA + 1 else: serving = "B" else: if random() < probB: scoreB = scoreB + 1 else: serving = "A" return scoreA, scoreB 9.3.6 Finishing Up Whew! We have just one more troublesome function left, gameOver. Here is what we know about it so far. def gameOver(a,b): # a and b represent scores for a racquetball game # RETURNS true if the game is over, false otherwise. According to the rules for our simulation, a game is over when either player reaches a total of 15. We can check this with a simple Boolean condition. def gameOver(a,b): # a and b represent scores for a racquetball game # RETURNS true if the game is over, false otherwise. return a==15 or b==15 Notice how this function directly computes and returns the Boolean result all in one step. We've done it! Except for printSummary, the program is complete. Let's fill in the missing details and call it a wrap. Here is the complete program from start to finish: # rball.py from random import random def main(): printIntro() probA, probB, n = getInputs() winsA, winsB = simNGames(n, probA, probB) printSummary(winsA, winsB)9.3. TOP-DOWN DESIGN 147 def printIntro(): print "This program simulates a game of racquetball between two" print !)"players called "A" and "B". The abilities of each player is%+- print "indicated by a probability (a number between 0 and 1) that" print "the player wins the point when serving. Player A always" print "has the first serve." def getInputs(): # Returns the three simulation parameters a = input("What is the prob. player A wins a serve? ") b = input("What is the prob. player B wins a serve? ") n = input("How many games to simulate? ") return a, b, n def simNGames(n, probA, probB): # Simulates n games of racquetball between players whose # abilities are represented by the probability of winning a serve. # RETURNS number of wins for A and B winsA = winsB = 0 for i in range(n): scoreA, scoreB = simOneGame(probA, probB) if scoreA > scoreB: winsA = winsA + 1 else: winsB = winsB + 1 return winsA, winsB def simOneGame(probA, probB): # Simulates a single game or racquetball between players whose # abilities are represented by the probability of winning a serve. # RETURNS final scores for A and B serving = "A" scoreA = 0 scoreB = 0 while not gameOver(scoreA, scoreB): if serving == "A": if random() < probA: scoreA = scoreA + 1 else: serving = "B" else: if random() < probB: scoreB = scoreB + 1 else: serving = "A" return scoreA, scoreB def gameOver(a, b): # a and b represent scores for a racquetball game # RETURNS true if the game is over, false otherwise. return a==15 or b==15 def printSummary(winsA, winsB): # Prints a summary of wins for each player.CHAPTER 9. SIMULATION AND DESIGN 148 n = winsA + winsB print "\nGames simulated:", n print "Wins for A: %d (%0.1f%%)" % (winsA, float(winsA)/n*100) print "Wins for B: %d (%0.1f%%)" % (winsB, float(winsB)/n*100) if __name__ == /..__main__+$+: main() You might take notice of the string formatting in printSummary. Since a percent sign normally marks the beginning of a slot specifier, to get a normal percent sign at the end, I had to use a double percent %%. 9.3.7 Summary of the Design Process You have just seen an example of top-down design in action. Now you can really see why it's called top-down design. We started at the highest level of our structure chart and worked our way down. At each level, we began with a general algorithm and then gradually refined it into precise code. This approach is sometimes called step-wise refinement. The whole process can be summarized in four steps: 1. Express the algorithm as a series of smaller problems. 2. Develop an interface for each of the small problems. 3. Detail the algorithm by expressing it in terms of its interfaces with the smaller problems. 4. Repeat the process for each smaller problem. Top-down design is an invaluable tool for developing complex algorithms. The process may seem easy, since I've walked you through it step-by-step. When you first try it out for yourself, though, things probably won't go quite so smoothly. Stay with it&'+the more you do it, the easier it will get. Initially, you may think writing all of those functions is a lot of trouble. The truth is, developing any sophisticated system is virtually impossible without a modular approach. Keep at it, and soon expressing your own programs in terms of cooperating functions will become second nature. 9.4 Bottom-Up Implementation Now that we've got a program in hand, your inclination might be to run off, type the whole thing in, and give it a try. If you do that, the result will probably be disappointment and frustration. Even though we have been very careful in our design, there is no guarantee that we haven't introduced some silly errors. Even if the code is flawless, you'll probably make some mistakes when you enter it. Just as designing a program one piece at a time is easier than trying to tackle the whole problem at once, implementation is best approached in small doses. 9.4.1 Unit Testing A good way to approach the implementation of a modest size program is to start at the lower levels of the structure chart and work your way up, testing each component as you complete it. Looking back at the structure chart for our simulation, we could start with the gameOver function. Once this function is typed into a module file, we can immediately import the file and test it. Here is a sample session testing out just this function. >>> >>> 0 >>> 0 >>> 1 import rball rball1.gameOver(0,0) rball1.gameOver(5,10) rball1.gameOver(15,3)9.4. BOTTOM-UP IMPLEMENTATION 149 >>> rball1.gameOver(3,15) 1 I have selected test data that exercise all the important cases for the function. The first time it is called, the score will be 0 to 0. The function correctly responds with 0 (false); the game is not over. As the game progresses, the function will be called with intermediate scores. The second example shows that the function again responded that the game is still in progress. The last two examples show that the function correctly identifies that the game is over when either player reaches 15. Having confidence that gameOver is functioning correctly, now we can go back and implement the simOneGame function. This function has some probabilistic behavior, so I'm not sure exactly what the output will be. The best we can do in testing it is to see that it behaves reasonably. Here is a sample session. >>> import rball >>> rball1.simOneGame(.5,.5) (13, 15) >>> rball1.simOneGame(.5,.5) (15, 11) >>> rball1.simOneGame(.3,.3) (15, 11) >>> rball1.simOneGame(.3,.3) (11, 15) >>> rball1.simOneGame(.4,.9) (4, 15) >>> rball1.simOneGame(.4,.9) (1, 15) >>> rball1.simOneGame(.9,.4) (15, 3) >>> rball1.simOneGame(.9,.4) (15, 0) >>> rball1.simOneGame(.4,.6) (9, 15) >>> rball1.simOneGame(.4,.6) (6, 15) Notice that when the probabilities are equal, the scores are close. When the probabilities are farther apart, the game is a rout. That squares with how we think this function should behave. We can continue this piecewise implementation, testing out each component as we add it into the code. Software engineers call this process unit testing. Testing each function independently makes it easier to spot errors. By the time you get around to testing the entire program, chances are that everything will work smoothly. Separating concerns through a modular design makes it possible to design sophisticated programs. Sep- arating concerns through unit testing makes it possible to implement and debug sophisticated programs. Try these techniques for yourself, and you'll see that you are getting your programs working with less overall effort and far less frustration. 9.4.2 Simulation Results Finally, we can take a look at Denny Dibblebit's question. Is it the nature of racquetball that small differences in ability lead to large differences in the outcome? Suppose Denny wins about 60% of his serves and his opponent is 5% better. How often should Denny win the game? Here's an example run where Denny's opponent always serves first. This program simulates a game of racquetball between two players called "A" and "B". The abilities of each player is indicated by a probability (a number between 0 and 1) that the player wins the point when serving. Player A always150 CHAPTER 9. SIMULATION AND DESIGN has the first serve. What is the prob. player A wins a serve? .65 What is the prob. player B wins a serve? .6 How many games to simulate? 5000 Games simulated: 5000 Wins for A: 3360 (67.2%) Wins for B: 1640 (32.8%) Even though there is only a small difference in ability, Denny should win only about one in three games. His chances of winning a three- or five-game match are pretty slim. Apparently Denny is winning his share. He should skip the shrink and work harder on his game. Speaking of matches, expanding this program to compute the probability of winning multi-game matches would be a great exercise. Why don't you give it a try? 9.5 Other Design Techniques Top-down design is a very powerful technique for program design, but it is not the only way to go about creating a program. Sometimes you may get stuck at a step and not know how to go about refining it. Or the original specification might be so complicated that refining it level-by-level is just too daunting. 9.5.1 Prototyping and Spiral Development Another approach to design is to start with a simple version of a program or program component and then try to gradually add features until it meets the full specification. The initial stripped-down version is called a prototype. Prototyping often leads to a sort of spiral development process. Rather than taking the entire problem and proceeding through specification, design, implementation and testing, we first design, implement and test a prototype. Then new features are designed, implemented and tested. We make many mini-cycles through the development process as the prototype is incrementally expanded into the final program. As an example, consider how we might have approached the racquetball simulation. The very essence of the problem is simulating a game of racquetball. We might have started with just the simOneGame function. Simplifying even further, our prototype could assume that each player has a 50-50 chance of winning any given point and just play a series of 30 rallies. That leaves the crux of the problem, which is handling the awarding of points and change of service. Here is an example prototype: from random import random def simOneGame(): scoreA = 0 scoreB = 0 serving = "A" for i in range(30): if serving == "A": if random() < .5: scoreA = scoreA + 1 else: serving = "B" else: if random() < .5: scoreB = scoreB + 1 else: serving = "A" print scoreA, scoreB9.5. OTHER DESIGN TECHNIQUES 151 if __name__ == %!)__main__&)$: simOneGame() You can see that I have added a print statement at the bottom of the loop. Printing out the scores as we go along allows us to see that the prototype is playing a game. Here is some example output. 1 0 1 0 1 0 2 0 2 0 2 1 2 1 3 1 3 1 3 1 3 1 3 2 ... 7 6 7 7 7 8 It's is not pretty, but it shows that we have gotten the scoring and change of service working. We could then work on augmenting the program in phases. Here's a project plan. Phase 1 Initial prototype. Play 30 rallies where the server always has a 50% chance of winning. Print out the scores after each rally. Phase 2 Add two parameters to represent different probabilities for the two players. Phase 3 Play the game until one of the players reaches 15 points. At this point, we have a working simulation of a single game. Phase 4 Expand to play multiple games. The output is the count of games won by each player. Phase 5 Build the complete program. Add interactive inputs and a nicely formatted report of the results. Spiral development is particularly useful when dealing with new or unfamiliar features or technologies. It's helpful to +"$get your hands dirty%%- with a quick prototype just to see what you can do. As a novice programmer, everything may seem new to you, so prototyping might prove useful. If full-blown top-down design does not seem to be working for you, try some spiral development. 9.5.2 The Art of Design It is important to note that spiral development is not an alternative to top-down design. Rather, they are com- plementary approaches. When designing the prototype, you will still use top-down techniques. In Chapter 12, you will see yet another approach called object-oriented design. There is no -$,one true way,+, of design. The truth is that good design is as much a creative process as a science. Designs can be meticulously analyzed after the fact, but there are no hard and fast rules for producing a design. The best software designers seem to employ a variety of techniques. You can learn about techniques by reading books like this one, but books can't teach how and when to apply them. That you have to learn for yourself through experience. In design as in almost anything, the key to success is practice.CHAPTER 9. SIMULATION AND DESIGN 152 9.6 Exercises 1. Draw the top levels of a structure chart for a program having the following main function. def main(): printIntro() length, width = getDimensions() amtNeeded = computeAmount(length,width) printReport(length, width, amtNeeded) 2. Write an expression using either random or randrange to calculate the following. A random int in the range 0-/'10 A random float in the range -0.5"&&0.5 A random number representing the roll of a six-sided die A random number representing the sum resulting from rolling two six-sided dice A random float in the range -10.0')!10.0 3. Revise the racquetball simulation so that it keeps track of results for best of n game matches. 4. Revise the racquetball simulation to take shutouts into account. Your updated version should report for both players the number of wins, percentage of wins, number of shutouts, and percentage of wins that are shutouts. 5. Design and implement a simulation of the game of volleyball. Normal volleyball is played like rac- quetball, in that a team can only score points when it is serving. Games are played to 15, but must be won by at least two points. 6. College volleyball is now played using rally scoring. In this system, the team that wins a rally is awarded a point, even if they were not the serving team. Games are played to a score of 21. Design and implement a simulation of volleyball using rally scoring. 7. Design and implement a system that compares regular volleyball games to those using rally scoring. Your program should be able to investigate whether rally scoring magnifies, reduces, or has no effect on the relative advantage enjoyed by the better team. 8. Design and implement a simulation of some other racquet sport (e.g. tennis or table tennis). 9. Craps is a dice game played at many casinos. A player rolls a pair of normal six-sided dice. If the initial roll is 2, 3 or 12, the player loses. If the roll is 7 or 11, the player wins. Any other initial roll causes the player to "&'roll for point.%+' That is, the player keeps rolling the dice until either rolling a 7 or re-rolling the value of the initial roll. If the player re-rolls the initial value before rolling a 7, it's a win. Rolling a 7 first is a loss. Write a program to simulate multiple games of craps and estimate the probability that the player wins. For example, if the player wins 249 out of 500 games, then the estimated probability of winning is 249 500 0 498 10. Blackjack (twenty-one) is a casino game played with cards. The goal of the game to draw cards that total as close to 21 points as possible without going over. All face cards count as 10 points, aces count as 1 or 11, and all other cards count their numeric value. The game is played against a dealer. The player tries to get closer to 21 (without going over) than the dealer. If the dealer busts (goes over 21) the player automatically wins (provided the player had not already busted). The dealer must always take cards according to a fixed set of rules. The dealer takes cards until he or she achieves a total of at least 17. If the dealer's hand contains an ace, it will be counted as 11 when that results in a total between 17 and 21 inclusive; otherwise, the ace is counted as 1.9.6. EXERCISES 153 Write a program that simulates multiple games of blackjack and estimates the probability that the dealer will bust. 11. A blackjack dealer always starts with one card showing. It would be useful for a player to know the dealer's bust probability (see previous problem) for each possible starting value. Write a simulation program that runs multiple hands of blackjack for each possible starting value (ace%!(10) and estimates the probability that the dealer busts for each starting value. 12. Monte Carlo techniques can be used to estimate the value of pi. Suppose you have a round dart board that just fits inside of a square cabinet. If you throw darts randomly, the proportion that hit the dart board vs. those that hit the cabinet (in the corners not covered by the board) will be determined by the relative area of the dart board and the cabinet. If n is the total number of darts randomly thrown (that land within the confines of the cabinet), and h is the number that hit the board, it is easy to show that )# 4 h n Write a program that accepts the !$(number of darts%"% as an input and then performs a simulation to estimate !- . Hint: you can use 2*random() - 1 to generate the x and y coordinates of a random point inside a 2x2 square centered at 0 (+* 0 . The point lies inside the inscribed circle if x 2 y 2 1. 13. Write a program that performs a simulation to estimate the probability of rolling five-of-a-kind in a single roll of five six-sided dice. 14. A random walk is a particular kind of probablistic simulation that models certain statistical systems such as the Brownian motion of molecules. You can think of a one-dimensional random walk in terms of coin flipping. Suppose you are standing on a very long straight sidewalk that extends both in front of and behind you. You flip a coin. If it comes up heads, you take a step forward; tails means to take a step backward. Suppose you take a random walk of n steps. On average, how many steps away from the starting point will you end up? Write a program to help you investigate this question. 15. Suppose you are doing a random walk (see previous problem) on the blocks of a city street. At each ,#!step-(- you choose to walk one block (at random) either forward, backward, left or right. In n steps, how far do you expect to be from your starting point? Write a program to help answer this question. 16. Write a graphical program to trace a random walk (see previous two problems) in two dimensions. In this simulation you should allow the step to be taken in any direction. You can generate a random direction as an angle off of the x axis. angle = random() * 2 * math.pi The new x and y positions are then given by these formulas. x = x + cos(angle) y = y + sin(angle) The program should take the number of steps as an input. Start your walker at the center of a 100x100 grid and draw a line that traces the walk as it progresses. 17. (Advanced) Here is a puzzle problem that can be solved with either some fancy analytic geometry (calculus) or a (relatively) simple simulation. Suppose you are located at the exact center of a cube. If you could look all around you in every direction, each wall of the cube would occupy 6 1 of your field of vision. Suppose you move toward one of the walls so that you are now half-way between it and the center of the cube. What fraction of your field of vision is now taken up by the closest wall? Hint: use a Monte Carlo simulation that repeatedly ,(.looks-.% in a random direction and counts how many times it sees the wall.154 CHAPTER 9. SIMULATION AND DESIGNChapter 10 Defining Classes In the last three chapters, we have developed techniques for structuring the computations of a program. In the next few chapters, we will take a look at techniques for structuring the data that our programs use. You already know that objects are one important tool for managing complex data. So far, our programs have made use of objects created from pre-defined classes such as Circle. In this chapter, you will learn how to write your own classes so that you can create novel objects. 10.1 Quick Review of Objects Remember back in Chapter 5, I defined an object as an active data type that knows stuff and can do stuff. More precisely, an object consists of 1. A collection of related information. 2. A set of operations to manipulate that information. The information is stored inside the object in instance variables. The operations, called methods, are func- tions that )!$live.!+ inside the object. Collectively, the instance variables and methods are called the attributes of an object. To take a now familiar example, a Circle object will have instance variables such as center, which remembers the center point of the circle, and radius, which stores the length of the circle's radius. The methods of the circle will need this data to perform actions. The draw method examines the center and radius to decide which pixels in a window should be colored. The move method will change the value of center to reflect the new position of the circle. Recall that every object is said to be an instance of some class. The class of the object determines what attributes the object will have. Basically a class is a description of what its instances will know and do. New objects are created from a class by invoking a constructor. You can think of the class itself as a sort of factory for stamping out new instances. Consider making a new circle object: myCircle = Circle(Point(0,0), 20) Circle, the name of the class, is used to invoke the constructor. This statement creates a new Circle instance and stores a reference to it in the variable myCircle. The parameters to the constructor are used to initialize some of the instance variables (namely center and radius) inside of myCircle. Once the instance has been created, it is manipulated by calling on its methods: myCircle.draw(win) myCircle.move(dx, dy) ... 155CHAPTER 10. DEFINING CLASSES 156 10.2 Example Program: Cannonball Before launching into a detailed discussion of how to write your own classes, let's take a short detour to see how useful new classes can be. 10.2.1 Program Specification Suppose we want to write a program that simulates the flight of a cannonball (or any other projectile such as a bullet, baseball or shot put). We are particularly interested in finding out how far the cannonball will travel when fired at various launch angles and initial velocities. The input to the program will be the launch angle (in degrees), the initial velocity (in meters per second) and the initial height (in meters). The output will be the distance that the projectile travels before striking the ground (in meters). If we ignore the effects of wind resistance and assume that the cannon ball stays close to earth's surface (i.e., we're not trying to put it into orbit), this is a relatively simple classical physics problem. The acceleration of gravity near the earth's surface is about 9.8 meters per second per second. That means if an object is thrown upward at a speed of 20 meters per second, after one second has passed, its upward speed will have slowed to 20 9 8 10 2 meters per second. After another second, the speed will be only 0 4 meters per second, and shortly thereafter it will start coming back down. For those who know a little bit of calculus, it's not hard to derive a formula that gives the position of our cannonball at any given moment in its flight. Rather than take the calculus approach, however, our program will use simulation to track the cannonball moment by moment. Using just a bit of simple trigonometry to get started, along with the obvious relationship that the distance an object travels in a given amount of time is equal to its rate times the amount of time (d rt), we can solve this problem algorithmically. 10.2.2 Designing the Program Let's start by desigining an algorithm for this problem. Given the problem statement, it's clear that we need to consider the flight of the cannonball in two dimensions: height, so we know when it hits the ground, and distance, to keep track of how far it goes. We can think of the position of the cannonball as a point x .#+ y in a 2D graph where the value of y gives the height and the value of x gives the distance from the starting point. Our simulation will have to update the position of the cannonball to account for its flight. Suppose the ball starts at position 0 "*( 0 , and we want to check its position, say, every tenth of a second. In that interval, it will have moved some distance upward (positive y) and some distance forward (positive x). The exact distance in each dimension is determined by its velocity in that direction. Separating out the x and y components of the velocity makes the problem easier. Since we are ignoring wind resistance, the x velocity remains constant for the entire flight. However, the y velocity changes over time due to the influence of gravity. In fact, the y velocity will start out being positive and then become negative as the cannonball starts back down. Given this analysis, it's pretty clear what our simulation will have to do. Here is a rough outline: Input the simulation parameters: angle, velocity, height, interval. Calculate the initial position of the cannonball: xpos, ypos Calculate the initial velocities of the cannonball: xvel, yvel While the cannonball is still flying: update the values of xpos, ypos, and yvel for interval seconds further into the flight Output the distance traveled as xpos Let's turn this into a program using stepwise refinement. The first line of the algorithm is straightforward. We just need an appropriate sequence of input state- ments. Here's a start: def main(): angle = input("Enter the launch angle (in degrees): ") vel = input("Enter the initial velocity (in meters/sec): ")10.2. EXAMPLE PROGRAM: CANNONBALL 157 h0 = input("Enter the initial height (in meters): ") time = input("Enter the time interval between position calculations: ") Calculating the initial position for the cannonball is also easy. It will start at distance 0 and height h0. We just need a couple assignment statements. xpos = 0.0 ypos = h0 Next we need to calculate the x and y components of the initial velocity. We'll need a little high-school trigonometry. (See, they told you you'd use that some day.) If we consider the initial velocity as consisting of some amount of change in y and and some amount of change in x, then these three components (velocity, x- velocity and y-velocity) form a right triangle. Figure 10.1 illustrates the situation. If we know the magnitude of the velocity and the launch angle (labeled theta, because the Greek letter *& is often used as the measure of angles), we can easily calculate the magnitude of xvel by the equation xvel velocity costheta. A similar formula (using sintheta) provides yvel. ty oci l e v theta yvel = velocity * sin(theta) xvel = velocity * cos(theta) Figure 10.1: Finding the x and y components of velocity. Even if you don't completely understand the trigonometry, the important thing is that we can translate these formulas into Python code. There's still one subtle issue to consider. Our input angle is in degrees, and the Python math library uses radian measures. We'll have to /(% convert our angle before applying the formulas. angle There are 2 ") radians in a circle (360 degrees); so theta !$ 180 . These three formulas give us the code for computing the initial velocities: theta = math.pi * angle / 180.0 xvel = velocity * math.cos(theta) yvel = velocity * math.sin(theta) That brings us to the main loop in our program. We want to keep updating the position and velocity of the cannonball until it reaches the ground. We can do this by examining the value of ypos. while ypos >= 0.0: I used as the relationship so that we can start with the cannon ball on the ground (= 0) and still get the loop going. The loop will quit as soon as the value of ypos dips just below 0, indicating the cannonball has embedded itself slightly in the ground. Now we arrive at the crux of the simulation. Each time we go through the loop, we want to update the state of the cannonball to move it time seconds farther in its flight. Let's start by considering movement in the horizontal direction. Since our specification says that we can ignore wind resistance, the horizontal speed of the cannonball will remain constant and is given by the value of xvel. As a concrete example, suppose the ball is traveling at 30 meters per second and is currently 50 meters from the firing point. In another second, it will go 30 more meters and be 80 meters from the firing point. If the interval is only 0.1 second (rather than a full second), then the cannonball will only fly another 0 !&$ 1 ."# 30 $&!(/! 3 meters and be at a distance of 53 meters. You can see that the new distance traveled is always given by time * xvel. To update the horizontal position, we need just one statement. xpos = xpos + time * xvelCHAPTER 10. DEFINING CLASSES 158 The situation for the vertical component is slightly more complicated, since gravity causes the y-velocity to change over time. Each second, yvel must decrease by 9.8 meters per second, the acceleration of gravity. In 0.1 seconds the velocity will decrease by 0 1 9 8 0 98 meters per second. The new velocity at the end of the interval is calculated as yvel1 = yvel - time * 9.8 To calculate how far the cannonball travels during this interval, we need to know its average vertical velocity. Since the acceleration due to gravity is constant, the average velocity will just be the average of the starting and ending velocities: (yvel+yvel1)/2.0. Multiplying this average velocity by the amount of time in the interval gives us the change in height. Here is the completed loop: while yvel >= 0.0: xpos = xpos + time * xvel yvel1 = yvel - time * 9.8 ypos = ypos + time * (yvel + yvel1)/2.0 yvel = yvel1 Notice how the velocity at the end of the time interval is first stored in the temporary variable yvel1. This is done to preserve the initial yvel so that the average velocity can be computed from the two values. Finally, the value of yvel is assigned its value at the end of the loop. This represents the correct vertical velocity of the cannonball at the end of the interval. The last step of our program simply outputs the distance traveled. Adding this step gives us the complete program. # cball1.py from math import pi, sin, cos def main(): angle = input("Enter the launch angle (in degrees): ") vel = input("Enter the initial velocity (in meters/sec): ") h0 = input("Enter the initial height (in meters): ") time = input("Enter the time interval between position calculations: ") # convert angle to radians theta = (angle * pi)/180.0 # set the intial position and velocities in x and y directions xpos = 0 ypos = h0 xvel = vel * cos(theta) yvel = vel * sin(theta) # loop until the ball hits the ground while ypos >= 0: # calculate position and velocity in time seconds xpos = xpos + time * xvel yvel1 = yvel - time * 9.8 ypos = ypos + time * (yvel + yvel1)/2.0 yvel = yvel1 print "\nDistance traveled: %0.1f meters." % (xpos)10.3. DEFINING NEW CLASSES 159 10.2.3 Modularizing the Program You may have noticed during the design discussion that I employed stepwise refinement (top-down design) to develop the program, but I did not divide the program into separate functions. We are going to modularize the program in two different ways. First, we'll use functions (a la top-down design). While the final program is not too long, it is fairly complex for its length. One cause of the complexity is that it uses ten variables, and that is a lot for the reader to keep track of. Let's try dividing the program into functional pieces to see if that helps. Here's a version of the main algorithm using helper functions: def main(): angle, vel, h0, time = getInputs() xpos, ypos = 0, h0 xvel, yvel = getXYComponents(velocity, angle) while ypos >= 0: xpos, ypos, yvel = updateCannonBall(time, xpos, ypos, xvel, yvel) print "\nDistance traveled: %0.1f meters." % (xpos) It should be obvious what each of these functions does based on their names and the original program code. You might take a couple of minutes to code up the three helper functions. This second version of the main algorithm is certainly more concise. The number of variables has been reduced to eight, since theta and yvel1 have been eliminated from the main algorithm. Do you see where they went? The value of theta is only needed locally inside of getXYComponents. Similarly, yvel1 is now local to updateCannonBall. Being able to hide some of the intermediate variables is a major benefit of the separation of concerns provided by top-down design. Even this version seems overly complicated. Look especially at the loop. Keeping track of the state of the cannonball requires four pieces of information, three of which must change from moment to moment. All four variables along with the value of time are needed to compute the new values of the three that change. That results in an ugly function call having five parameters and three return values. An explosion of parameters is often an indication that there might be a better way to organize a program. Let's try another approach. The original problem specification itself suggests a better way to look at the variables in our program. There is a single real-world cannonball object, but describing it in the current program requires four pieces of information: xpos, ypos, xvel and yvel. Suppose we had a Projectile class that +%(understood-*# the physics of objects like cannonballs. Using such a class, we could express the main algorithm in terms of creating and updating a suitable object stored in a single variable. Using this object-based approach, we might write main like this. def main(): angle, vel, h0, time = getInputs() cball = Projectile(angle, vel, h0) while cball.getY() >= 0: cball.update(time) print "\nDistance traveled: %0.1f meters." % (cball.getX()) Obviously, this is a much simpler and more direct expression of the algorithm. The initial values of angle, vel and h0 are used as parameters to create a Projectile called cball. Each time through the loop, cball is asked to update its state to account for time. We can get the position of cball at any moment by using its getX and getY methods. To make this work, we just need to define a suitable Projectile class that implements the methods update, getX, and getY. 10.3 Defining New Classes Before designing a Projectile class, let's take an even simpler example to examine the basic ideas.CHAPTER 10. DEFINING CLASSES 160 10.3.1 Example: Multi-Sided Dice You know that a normal die (the singular of dice) is a cube and each face shows a number from one to six. Some games employ nonstandard dice that may have fewer (e.g., four) or more (e.g., thirteen) sides. Let's design a general class MSDie to model multi-sided dice. 1 We could use such an object in any number of simulation or game programs. Each MSDie object will know two things. 1. How many sides it has. 2. Its current value. When a new MSDie is created, we specify how many sides it will have, n. We can then operate on the die through three provided methods: roll, to set the die to a random value between 1 and n, inclusive; setValue, to set the die to a specific value (i.e., cheat); and getValue, to see what the current value is. Here is an interactive example showing what our class will do: >>> >>> 1 >>> >>> 4 >>> >>> 1 >>> >>> 12 >>> >>> 8 die1 = MSDie(6) die1.getValue() die1.roll() die1.getValue() die2 = MSDie(13) die2.getValue() die2.roll() die2.getValue() die2.setValue(8) die2.getValue() Do you see how this might be useful? I can define any number of dice having arbitrary numbers of sides. Each die can be rolled independently and will always produce a random value in the proper range determined by the number of sides. Using our object-oriented terminology, we create a die by invoking the MSDie constructor and providing the number of sides as a parameter. Our die object will keep track of this number internally using an instance variable. Another instance variable will be used to store the current value of the die. Initially, the value of the die will be set to be 1, since that is a legal value for any die. The value can be changed by the roll and setRoll methods and returned from the getValue method. Writing a definition for the MSDie class is really quite simple. A class is a collection of methods, and methods are just functions. Here is the class definition for MSDie: # msdie.py Class definition for an n-sided die. # from random import randrange class MSDie: def __init__(self, sides): self.sides = sides self.value = 1 1 Obviously, the name MSDie is short for !#$Multi-Sided Die/-" and is not intended as a comment on any software giant, real or fictional, which may or may not employ unfair monopolostic trade practices to the detriment of consumers.10.3. DEFINING NEW CLASSES 161 def roll(self): self.value = randrange(1,self.sides+1) def getValue(self): return self.value def setValue(self, value): self.value = value As you can see, a class definition has a simple form: class : Each method definition looks like a normal function definition. Placing the function inside a class makes it a method of that class, rather than a stand-alone function. Let's take a look at the three methods defined in this class. You'll notice that each method has a first parameter named self. The first parameter of a method is special.%&it always contains a reference to the object on which the method is acting. As usual, you can use any name you want for this parameter, but the traditional name is self, so that is what I will always use. An example might be helpful in making sense of self. Suppose we have a main function that executes die1.setValue(8). A method invocation is a function call. Just as in normal function calls, Python executes a four-step sequence: 1. The calling program (main) suspends at the point of the method application. Python locates the approriate method defintion inside the class of the object to which the method is being applied. In this case, control is transferring to the setValue method in the MSDie class, since die1 is an instance of MSDie. 2. The formal parameters of the method get assigned the values supplied by the actual parameters of the call. In the case of a method call, the first formal parameter corresponds to the object. In our example, it is as if the following assignments are done before executing the method body: self = die1 value = 8 3. The body of the method is executed. 4. Control returns to the point just after where the method was called, in this case, the statement immedi- ately following die1.setValue(8). Figure 10.2 illustrates the method-calling sequence for this example. Notice how the method is called with one parameter (the value), but the method definition has two parameters, due to self. Generally speaking, we would say setValue requires one parameter. The self parameter in the definition is a bookkeeping detail. Some languages do this implicitly; Python requires us to add the extra parameter. To avoid confusion, I will always refer to the first formal parameter of a method as the self parameter and any others as normal parameters. So, I would say setValue uses one normal parameter. class MSDie: def main(): ... die1 = MSDie(12) self=die1; value=8 def setValue(self,value): die1.setValue(8) self.value = value print die1.getValue() Figure 10.2: Flow of control in call: die1.setValue(8).CHAPTER 10. DEFINING CLASSES 162 OK, so self is a parameter that represents an object. But what exactly can we do with it? The main thing to remember is that objects contain their own data. Instance variables provide storage locations inside of an object. Just as with regular variables, instance variables are accessed by name. We can use our familiar dot notation: object . instance-var . Look at the definition of setValue; self.value refers to the instance variable value that is stored inside the object. Each instance of a class has its own instance variables, so each MSDie object has its very own value. Certain methods in a class have special meaning to Python. These methods have names that begin and end with two underscores. The special method init is the object constructor. Python calls this method to initialize a new MSDie. The role of init is to provide initial values for the instance variables of an object. From outside the class, the constructor is referred to by the class name. die1 = MSDie(6) When executing this statement, Python creates a new MSDie and executes init on that object. The net result is that die1.sides is set to 6 and die1.value is set to 1. The power of instance variables is that we can use them to remember the state of a particular object, and this information then gets passed around the program as part of the object. The values of instance variables can be referred to again in other methods or even in successive calls to the same method. This is different from regular local function variables whose values disappear once the function terminates. Here is a simple illustration: >>> >>> 1 >>> >>> 8 die1 = Die(13) print die1.getValue() die1.setValue(8) print die1.getValue() The call to the constructor sets the instance variable die1.value to 1. The next line prints out this value. The value set by the constructor persists as part of the object, even though the constructor is over and done with. Similarly, executing die1.setValue(8) changes the object by setting its value to 8. When the object is asked for its value the next time, it responds with 8. That's just about all there is to know about defining new classes in Python. Now it's time to put this new knowledge to use. 10.3.2 Example: The Projectile Class Returning to the cannonball example, we want a class that can represent projectiles. This class will need a contructor to initialize instance variables, an update method to change the state of the projectile, and getX and getY methods so that we can find the current position. Let's start with the constructor. In the main program, we will create a cannonball from the initial angle, velocity and height. cball = Projectile(angle, vel, h0) The Projectile class must have an init method that uses these values to initialize the instance variables of cball. But what should the instance variables be? Of course, they will be the four pieces of information that characterize the flight of the cannonball: xpos, ypos, xvel and yvel. We will calculate these values using the same formulas that were in the original program. Here is how our class looks with the constructor: class Projectile: def __init__(self, angle, velocity, height): self.xpos = 0.0 self.ypos = height10.3. DEFINING NEW CLASSES 163 theta = math.pi * angle / 180.0 self.xvel = velocity * math.cos(theta) self.yvel = velocity * math.sin(theta) Notice how we have created four instance variables inside the object using the self dot notation. The value of theta is not needed after init terminates, so it is just a normal (local) function variable. The methods for accessing the position of our projectiles are straightforward; the current position is given by the instance variables xpos and ypos. We just need a couple methods that return these values. def getX(self): return self.xpos def getY(self): return self.ypos Finally, we come to the update method. This method takes a single normal parameter that represents an interval of time. We need to update the state of the projectile to account for the passage of that much time. Here's the code: def update(self, time): self.xpos = self.xpos + time * self.xvel yvel1 = self.yvel - time * 9.8 self.ypos = self.ypos + time * (self.yvel + yvel1)/2.0 self.yvel = yvel1 Basically, this is the same code that we used in the original program updated to use and modify instance variables. Notice the use of yvel1 as a temporary (ordinary) variable. This new value is saved by storing it into the object in the last line of the method. That completes our projectile class. We now have a complete object-based solution to the cannonball problem. # cball3.py from math import pi, sin, cos class Projectile: def __init__(self, angle, velocity, height): self.xpos = 0.0 self.ypos = height theta = pi * angle / 180.0 self.xvel = velocity * cos(theta) self.yvel = velocity * sin(theta) def update(self, time): self.xpos = self.xpos + time * self.xvel yvel1 = self.yvel - 9.8 * time self.ypos = self.ypos + time * (self.yvel + yvel1) / 2.0 self.yvel = yvel1 def getY(self): return self.ypos def getX(self): return self.xpos def getInputs():CHAPTER 10. DEFINING CLASSES 164 a = input("Enter v = input("Enter h = input("Enter t = input("Enter return a,v,h,t the the the the launch angle (in degrees): ") initial velocity (in meters/sec): ") initial height (in meters): ") time interval between position calculations: ") def main(): angle, vel, h0, time = getInputs() cball = Projectile(angle, vel, h0) while cball.getY() >= 0: cball.update(time) print "\nDistance traveled: %0.1f meters." % (cball.getX()) 10.4 Objects and Encapsulation 10.4.1 Encapsulating Useful Abstractions Hopefully, you can see how defining new classes can be a good way to modularize a program. Once we identify some objects that might be useful in solving a particular problem, we can write an algorithm as if we had those objects available and push the implementation details into a suitable class definition. This gives us the same kind of separation of concerns that we had using functions in top-down design. The main program only has to worry about what objects can do, not about how they are implemented. Computer scientists call this separation of concerns encapsulation. The implementation details of an object are encapsulated in the class defintion, which insulates the rest of the program from having to deal with them. This is another application of abstraction (ignoring irrelevant details), which is the essence of good design. I should mention that encapsulation is only a programming convention in Python. It is not enforced by the language, per se. In our Projectile class we included two short methods, getX and getY, that simply returned the values of instance variables xpos and ypos, respectively. Strictly speaking, these methods are not absolutely necessary. In Python, you can access the instance variables of any object with the regular dot notation. For example, we could test the constructor for the Projectile class interactively by creating an object and then directly inspecting the values of the instance variables. >>> c = Projectile(60, 50, 20) >>> c.xpos 0.0 >>> c.ypos 20 >>> c.xvel 25.0 >>> c.yvel 43.301270 Accessing the instance variables of an object like this is very handy for testing purposes, but it is generally considered poor practice to this in programs. One of the main reasons for using objects is to insulate programs that use those objects from the internal details of how they are implemented. References to instance variables should remain inside the class definition with the rest of the implementation details. From outside the class, our interaction with an object should take place using the interface provided by its methods. As you design classes of your own, you should strive to provide a complete set of methods to make your class useful. That way other programs do not need to know about or manipulate internal details like instance variables. 10.4.2 Putting Classes in Modules Often a well-defined class or set of classes provide(s) useful abstractions that can be leveraged in many different programs. We might want to turn our projectile class into its own module file so that it can be used10.4. OBJECTS AND ENCAPSULATION 165 in other programs. In doing so, it would be a good idea to add documentation that describes how the class can be used so that programmers who want to use the module don't have to study the code to figure out (or remember) what the class and its methods do. You are already familiar with one way of documenting programs, namely comments. It's always a good idea to provide comments explaining the contents of a module and its uses. In fact, comments of this sort are so important that Python incorporates a special kind of commenting convention called a docstring. You can insert a plain string literal as the first line of a module, class or function to document that component. The advantage of docstrings is that, while ordinary comments are simply ignored by Python, docstrings are actually carried along during execution in a special attribute called doc . These strings can be examined dynamically. Most of the Python library modules have extensive docstrings that you can use to get help on using the module or its contents. For example, if you can't remember how to use the randrange function, you can print its docstring like this: >>> import random >>> print random.randrange.__doc__ Choose a random item from range(start, stop[, step]). Here is a version of our Projectile class as a module file with docstrings included: # projectile.py """projectile.py Provides a simple class for modeling the flight of projectiles.""" from math import pi, sin, cos class Projectile: """Simulates the flight of simple projectiles near the earth's surface, ignoring wind resistance. Tracking is done in two dimensions, height (y) and distance (x).""" def __init__(self, angle, velocity, height): """Create a projectile with given launch angle, initial velocity and height.""" self.xpos = 0.0 self.ypos = height theta = pi * angle / 180.0 self.xvel = velocity * cos(theta) self.yvel = velocity * sin(theta) def update(self, time): """Update the state of this projectile to move it time seconds farther into its flight""" self.xpos = self.xpos + time * self.xvel yvel1 = self.yvel - 9.8 * time self.ypos = self.ypos + time * (self.yvel + yvel1) / 2.0 self.yvel = yvel1 def getY(self): "Returns the y position (height) of this projectile." return self.ypos def getX(self):CHAPTER 10. DEFINING CLASSES 166 "Returns the x position (distance) of this projectile." return self.xpos You might notice that many of the docstrings in this code are enclosed in triple quotes ('%.+%$&#,). This is a third way that Python allows string literals to be delimited. Triple quoting allows us to directly type multi-line strings. Here is an example of how the docstrings appear when they are printed. >>> print projectile.Projectile.__doc__ Simulates the flight of simple projectiles near the earth's surface, ignoring wind resistance. Tracking is done in two dimensions, height (y) and distance (x). Our main program could now simply import this module in order to solve the original problem. # cball4.py from projectile import Projectile def getInputs(): a = input("Enter v = input("Enter h = input("Enter t = input("Enter return a,v,h,t the the the the launch angle (in degrees): ") initial velocity (in meters/sec): ") initial height (in meters): ") time interval between position calculations: ") def main(): angle, vel, h0, time = getInputs() cball = Projectile(angle, vel, h0) while cball.getY() >= 0: cball.update(time) print "\nDistance traveled: %0.1f meters." % (cball.getX()) In this version, details of projectile motion are now hidden in the projectile module file. 10.5 Widget Objects One very common use of objects is in the design of graphical user interfaces (GUIs). Back in Chapter 5, we talked about GUIs being composed of visual interface objects called widgets. The Entry object defined in our graphics library is one example of a widget. Now that we know how to define new classes, we can create our own custom widgets. 10.5.1 Example Program: Dice Roller Let's try our hand at building a couple useful widgets. As an example application, consider a program that rolls a pair of standard (six-sided) dice. The program will display the dice graphically and provide two buttons, one for rolling the dice and one for quitting the program. Figure 10.3 shows a snapshot of the user interface. You can see that this program has two kinds of widgets: buttons and dice. We can start by developing suitable classes. The two buttons will be instances of a Button class, and the class that provides a graphical view of the value of a die will be DieView. 10.5.2 Building Buttons Buttons, of course, are standard elements of virtually every GUI these days. Modern buttons are very so- phisticated, usually having a 3-dimensional look and feel. Our simple graphics package does not have the machinery to produce buttons that appear to depress as they are clicked. The best we can do is find out where10.5. WIDGET OBJECTS 167 Figure 10.3: Snapshot of dice roller in action. the mouse was clicked after the click has already completed. Nevertheless, we can make a useful, if less pretty, button class. Our buttons will be rectangular regions in a graphics window where user clicks can influence the behavior of the running application. We will need to create buttons and determine when they have been clicked. In addition, it is also nice to be able to activate and deactivate individual buttons. That way, our applications can signal which options are available to the user at any given moment. Typically, an inactive button is grayed-out to show that it is not available. Summarizing this description, our buttons will support the following methods: constructor Create a button in a window. We will have to specify the window in which the button will be displayed, the location/size of the button, and the label that will be on the button. activate Set the state of the button to active. deactivate Set the state of the button to inactive. clicked Indicate if the button was clicked. If the button is active, this method will determine if the point clicked is inside the button region. The point will have to be sent as a parameter to the method. getLabel Returns the label string of the button. This is provided so that we can identify a particular button. In order to support these operations, our buttons will need a number of instance variables. For example, the button itself will be drawn as a rectangle with some text centered in it. Invoking the activate and deactivate methods will change the appearance of the button. Saving the Rectangle and Text objects as instance variables will allow us to change the width of the outline and the color of the label. We might start by implementing the various methods to see what other instance variables might be needed. Once we have identified the relevant variables, we can write a constructor that initializes these values. Let's start with the activate method. We can signal that the button is active by making the outline thicker and making the label text black. Here is the code (remember the self parameter refers to the button object): def activate(self): "Sets this button to (&"active!&(." self.label.setFill(!($black!&-) self.rect.setWidth(2) self.active = 1CHAPTER 10. DEFINING CLASSES 168 As I mentioned above, in order for this code to work, our constructor will have to initialize self.label as an approprate Text object and self.rect as a Rectangle object. In addition, the self.active instance variable stores a Boolean value (1 for true, 0 for false) to remember whether or not the button is currently active. Our deactivate method will do the inverse of activate. It looks like this: def deactivate(self): "Sets this button to #-!inactive-,%." self.label.setFill(."!darkgrey%(%) self.rect.setWidth(1) self.active = 0 Of course, the main point of a button is being able to determine if it has been clicked. Let's try to write the clicked method. As you know, the graphics package provides a getMouse method that returns the point where the mouse was clicked. If an application needs to get a button click, it will first have to call getMouse and then see which active button (if any) the point is inside of. We could imagine the button processing code looking something like the following: pt = win.getMouse() if button1.clicked(pt): # Do button1 stuff elif button2.clicked(pt): # Do button2 stuff elif button2.clicked(pt) # Do button3 stuff ... The main job of the clicked method is to determine whether a given point is inside the rectangular button. The point is inside the rectangle if its x and y coordinates lie between the extreme x and y values of the rectangle. This would be easiest to figure out if we just assume that the button object has instance variables that record the min and max values of x and y. Assuming the existence of instance variables xmin, xmax, ymin, and ymax, we can implement the clicked method with a single Boolean expression. def clicked(self, p): "RETURNS true if button is active and p is inside" return self.active and \ self.xmin <= p.getX() <= self.xmax and \ self.ymin <= p.getY() <= self.ymax Here we have a single large Boolean expression composed by anding together three simpler expressions; all three must be true for the function to return a true value. Recall that the backslash at the end of a line is used to extend a statement over multiple lines. The first of the three subexpressions simply retrieves the value of the instance variable self.active. This ensures that only active buttons will report that they have been clicked. If self.active is false, then clicked will return false. The second two subexpressions are compound conditions to check that the x and y values of the point fall between the edges of the button rectangle. (Remember, x <= y <= z means the same as the mathematical expression x y z (section 7.5.1)). Now that we have the basic operations of the button ironed out, we just need a constructor to get all the instance variables properly initialized. It's not hard, but it is a bit tedious. Here is the complete class with a suitable constructor. # button.py from graphics import * class Button:10.5. WIDGET OBJECTS 169 """A button is a labeled rectangle in a window. It is activated or deactivated with the activate() and deactivate() methods. The clicked(p) method returns true if the button is active and p is inside it.""" def __init__(self, win, center, width, height, label): """ Creates a rectangular button, eg: qb = Button(myWin, Point(30,25), 20, 10, *#)Quit-/$) """ w,h = width/2.0, height/2.0 x,y = center.getX(), center.getY() self.xmax, self.xmin = x+w, x-w self.ymax, self.ymin = y+h, y-h p1 = Point(self.xmin, self.ymin) p2 = Point(self.xmax, self.ymax) self.rect = Rectangle(p1,p2) self.rect.setFill(!(!lightgray)(&) self.rect.draw(win) self.label = Text(center, label) self.label.draw(win) self.deactivate() def clicked(self, p): "RETURNS true if button active and p is inside" return self.active and \ self.xmin <= p.getX() <= self.xmax and \ self.ymin <= p.getY() <= self.ymax def getLabel(self): "RETURNS the label string of this button." return self.label.getText() def activate(self): "Sets this button to )/.active,/+." self.label.setFill(")(black)'$) self.rect.setWidth(2) self.active = 1 def deactivate(self): "Sets this button to -,,inactive-($." self.label.setFill("".darkgrey*&-) self.rect.setWidth(1) self.active = 0 You should study the constructor in this class to make sure you understand all of the instance variables and how they are initialized. A button is positioned by providing a center point, width and height. Other instance variables are calculated from these parameters. 10.5.3 Building Dice Now we'll turn our attention to the DieView class. The purpose of this class is to display the value of a die in a graphical fashion. The face of the die will be a square (via Rectangle) and the pips will be circles. Our DieView will have the following interface: constructor Create a die in a window. We will have to specify the window, the center point of the die, andCHAPTER 10. DEFINING CLASSES 170 the size of the die as parameters. setValue Change the view to show a given value. The value to display will be passed as a parameter. Obviously, the heart of DieView is turning various pips %&,on&'$ and +/+off!+" to indicate the current value of the die. One simple approach is to pre-place circles in all the possible locations where a pip might be and then turn them on or off by changing their colors. Using the standard position of pips on a die, we will need seven circles: three down the left edge, three down the right edge, and one in the center. The constructor will create the background square and the seven circles. The setValue method will set the colors of the circles based on the value of the die. Without further ado, here is the code for our DieView class. The comments will help you to follow how it works. class DieView: """ DieView is a widget that displays a graphical representation of a standard six-sided die.""" def __init__(self, win, center, size): """Create a view of a die, e.g.: d1 = GDie(myWin, Point(40,50), 20) creates a die centered at (40,50) having sides of length 20.""" # first define some standard values self.win = win # save this for drawing pips later self.background = "white" # color of die face self.foreground = "black" # color of the pips self.psize = 0.1 * size # radius of each pip hsize = size / 2.0 # half the size of the die offset = 0.6 * hsize # distance from center to outer pips # create a square for the face cx, cy = center.getX(), center.getY() p1 = Point(cx-hsize, cy-hsize) p2 = Point(cx+hsize, cy+hsize) rect = Rectangle(p1,p2) rect.draw(win) rect.setFill(self.background) # Create 7 circles for standard pip locations self.pip1 = self.__makePip(cx-offset, cy-offset) self.pip2 = self.__makePip(cx-offset, cy) self.pip3 = self.__makePip(cx-offset, cy+offset) self.pip4 = self.__makePip(cx, cy) self.pip5 = self.__makePip(cx+offset, cy-offset) self.pip6 = self.__makePip(cx+offset, cy) self.pip7 = self.__makePip(cx+offset, cy+offset) # Draw an initial value self.setValue(1) def __makePip(self, x, y): "Internal helper method to draw a pip at (x,y)" pip = Circle(Point(x,y), self.psize) pip.setFill(self.background)10.5. WIDGET OBJECTS 171 pip.setOutline(self.background) pip.draw(self.win) return pip def setValue(self, value): "Set this die to display value." # turn all pips off self.pip1.setFill(self.background) self.pip2.setFill(self.background) self.pip3.setFill(self.background) self.pip4.setFill(self.background) self.pip5.setFill(self.background) self.pip6.setFill(self.background) self.pip7.setFill(self.background) # turn correct pips on if value == 1: self.pip4.setFill(self.foreground) elif value == 2: self.pip1.setFill(self.foreground) self.pip7.setFill(self.foreground) elif value == 3: self.pip1.setFill(self.foreground) self.pip7.setFill(self.foreground) self.pip4.setFill(self.foreground) elif value == 4: self.pip1.setFill(self.foreground) self.pip3.setFill(self.foreground) self.pip5.setFill(self.foreground) self.pip7.setFill(self.foreground) elif value == 5: self.pip1.setFill(self.foreground) self.pip3.setFill(self.foreground) self.pip4.setFill(self.foreground) self.pip5.setFill(self.foreground) self.pip7.setFill(self.foreground) else: self.pip1.setFill(self.foreground) self.pip2.setFill(self.foreground) self.pip3.setFill(self.foreground) self.pip5.setFill(self.foreground) self.pip6.setFill(self.foreground) self.pip7.setFill(self.foreground) There are a couple of things worth noticing in this code. First, in the constructor, I have defined a set of values that determine various aspects of the die such as its color and the size of the pips. Calculating these values in the constructor and then using them in other places allows us to easily tweak the appearance of the die without having to search through the code to find all the places where those values are used. I actually figured out the specific calculations (such as the pip size being one-tenth of the die size) through a process of trial and error. Another important thing to notice is that I have added an extra method makePip that was not part of the original specification. This method is just a helper function that executes the four lines of code nec- essary to draw each of the seven pips. Since this is a function that is only useful within the DieView class, it is appropriate to make it a class method. Inside the constructor, it is invoked by lines such as: self. makePip(cx, cy). Method names beginning with a single or double underscore are used inCHAPTER 10. DEFINING CLASSES 172 Python to indicate that a method is +$#private#,) to the class and not intended for use by outside programs. 10.5.4 The Main Program Now we are ready to write our main program. The Button and Dieview classes are imported from their respective modules. Here is the program that uses our new widgets. # roller.py # Graphics program to roll a pair of dice. Uses custom widgets # Button and DieView. from random import randrange from graphics import GraphWin, Point from button import Button from dieview import DieView def main(): # create the application window win = GraphWin("Dice Roller") win.setCoords(0, 0, 10, 10) win.setBackground("green2") # Draw the interface widgets die1 = DieView(win, Point(3,7), 2) die2 = DieView(win, Point(7,7), 2) rollButton = Button(win, Point(5,4.5), 6, 1, "Roll Dice") rollButton.activate() quitButton = Button(win, Point(5,1), 2, 1, "Quit") # Event loop pt = win.getMouse() while not quitButton.clicked(pt): if rollButton.clicked(pt): value1 = randrange(1,7) die1.setValue(value1) value2 = randrange(1,7) die2.setValue(value2) quitButton.activate() pt = win.getMouse() # close up shop win.close() Notice that near the top of the program I have built the visual interface by creating the two DieViews and two Buttons. To demonstrate the activation feature of buttons, the roll button is initially active, but the quit button is left deactivated. The quit button is activated inside the event loop below when the roll button is clicked. This approach forces the user to roll the dice at least once before quitting. The heart of the program is the event loop. It is just a sentinel loop that gets mouse clicks and processes them until the user successfully clicks the quit button. The if inside the loop ensures that the rolling of the dice only happens when the roll button is clicked. Clicking a point that is not inside either button causes the loop to iterate, but nothing is actually done.10.6. EXERCISES 173 10.6 Exercises 1. Explain the similarities and differences between instance variables and -)%regular)&% function variables. 2. Show the output that would result from the following nonsense program. class Bozo: def __init__(self, value): print "Creating a Bozo from:", value self.value = 2 * value def clown(self, x): print "Clowning:", x print x * self.value return x + self.value def main(): print "Clowning around now." c1 = Bozo(3) c2 = Bozo(4) print c1.clown(3) print c2.clown(c1.clown(2)) main() 3. Use the Button class discussed in this chapter to build a GUI for one (or more) of your projects from previous chapters. 4. Write a modified Button class that creates circular buttons. 5. Write a shell game program using several different shapes of buttons. The program should draw at least three shapes on the screen and -.&pick#), one of them at random. The user tries to guess which of the shapes is the special one (by clicking in it). The user wins if he/she picks the right one. 6. Write a set of classes corresponding to the geometric solids: cube, rectangular prism (brick), sphere and cylinder. Each class should have a constructor that allows for the creation of different sized objects (e.g., a cube is specified by the length of its side) and methods that return the surface area and volume of the solid. 7. Extend the previous problem to include a method, inside, that determines whether a particular point lies within the solid. The method should accept three numbers representing the x, y and z coordinates of a point and return true if the point is inside the object and false otherwise. You may assume that the objects are always centered at 0 /%) 0 ,*/ 0 . 8. Here is a simple class that draws a (grim) face in a graphics window. # face.py from graphics import * class Face: def __init__(self, window, center, size): eyeSize = 0.15 * size eyeOff = size / 3.0 mouthSize = 0.8 * sizeCHAPTER 10. DEFINING CLASSES 174 mouthOff = size / 2.0 self.head = Circle(center, size) self.head.draw(window) self.leftEye = Circle(center, eyeSize) self.leftEye.move(-eyeOff, -eyeOff) self.rightEye = Circle(center, eyeSize) self.rightEye.move(eyeOff, -eyeOff) self.leftEye.draw(window) self.rightEye.draw(window) p1 = center.clone() p1.move(-mouthSize/2, mouthOff) p2 = center.clone() p2.move(mouthSize/2, mouthOff) self.mouth = Line(p1,p2) self.mouth.draw(window) (a) Use this class to write a program that draws three faces in a window. (b) Add and test a move method so that faces can be moved like other graphics objects. (c) Add and test a flinch method that causes a face's eyes to close. Also add and test an unflinch to open them back up again. (d) Write a complete program that draws a single face in a window and then animates it '&.bouncing around."-" Start the face at a random location in the window and use a loop that moves it a small increment in the x and y directions. When the face hits the edge of the window, it should flinch and bounce off. You can do the bounce by simply reversing the sign of the x increment or the y increment depending on whether the face hit a side- or top-/bottom-edge respectively. Write the animation program to run for a certain (fixed) number of steps. Note: You will need to flush the graphics window at the bottom of the loop to achieve the effect of animation. 9. Create a Tracker class that displays a circle in a graphics window to show the current location of an object. Here is a quick specification of the class: class Tracker: def __init__(self, window, objToTrack): # window is a graphWin and objToTrack is an object whose # position is to be shown in the window. objToTrack must be # an object that has getX() and getY() methods that report its # current position. # Creates a Tracker object and draws a circle in window at the # current position of objToTrack. def update(): # Moves the circle in the window to the current position of the # object being tracked. Use your new Tracker class in conjunction with the Projectile class to write a program that graphically depicts the flight of a cannonball. 10. Add a Target class to the cannonball program from the previous problem. A target should be a rectangle placed at a random position in the window. Allow the user to keep firing until they hit the target.10.6. EXERCISES 175 11. Redo the regression problem from Chapter 8 using a Regression class. Your new class will keep track of the various quantities that are needed to compute a line of regresion (the running sums of x, y, x 2 and xy)The regression class should have the following methods. init Creates a new regression object to which points can be added. addPoint Adds a point to the regression object. predict Accepts a value of x as a parameter, and returns the value of the corresponding y on the line of best fit. Note: Your class might also use some internal helper methods to do such things as compute the slope of the regression line. 12. Implement a card class that represents a playing card. Use your class to write a program that $)%deals&#" a random hand of cards and displays them in a graphics window. You should be able to find a freely available set of card images to display your cards by searching on the Internet.176 CHAPTER 10. DEFINING CLASSESChapter 11 Data Collections As you saw in the last chapter, classes are one mechanism for structuring the data in our programs. Classes alone, however, are not enough to satisfy all of our data-handling needs. If you think about the kinds of data that most real-world programs manipulate, you will quickly realize that many programs deal with large collections of similar information. A few examples of the collections that you might find in a modern program include Words in a document. Students in a course. Data from an experiment. Customers of a business. Graphics objects drawn on the screen. Cards in a deck. In this chapter, you will learn techniques for writing programs that manipulate collections like these. 11.1 Example Problem: Simple Statistics Back in Chapter 8, we wrote a simple but useful program to compute the mean (average) of a set of numbers entered by the user. Just to refresh your memory (as if you could forget it), here is the program again: # average4.py def main(): sum = 0.0 count = 0 xStr = raw_input("Enter a number ( to quit) >> ") while xStr != "": x = eval(xStr) sum = sum + x count = count + 1 xStr = raw_input("Enter a number ( to quit) >> ") print "\nThe average of the numbers is", sum / count main() This program allows the user to enter a sequence of numbers, but the program itself does not keep track of what numbers were entered. Instead, it just keeps a summary of the numbers in the form of a running sum. That's all that's needed to compute the mean. 177CHAPTER 11. DATA COLLECTIONS 178 Suppose we want to extend this program so that it computes not only the mean, but also the median and standard deviation of the data. You are probably familiar with the concept of a median. This is the value that splits the data set into equal-sized parts. For the data 2, 4, 6, 9, 13, the median value is 6, since there are two values greater than 6 and two that are smaller. To calculate the median, we need to store all the numbers and put them in order so that we can identify the middle value. The standard deviation is a measure of how spread out the data is relative to the mean. If the data is tightly clustered around the mean, then the standard deviation is small. When the data is more spread out, the standard deviation is larger. The standard deviation provides a yardstick for determining how exceptional a value is. For example, some teachers define an &("A"(/ as any score that is at least two standard deviations above the mean. The standard deviation, s, is defined as &,- $. x x i 2 n 1 s In this formula &' x is the mean, x i represents the ith data value and n is the number of data values. The formula looks complicated, but it is not hard to compute. The expression &+ x x i 2 is the square of the /",deviation'() of an individual item from the mean. The numerator of the fraction is the sum of the deviations (squared) across all the data values. Let's take a simple example. If we again use the values: 2, 4, 6, 9, and 13, the mean of this data ( ," x) is 6.8. So the numerator of the fraction is computed as 2 6 8 2 2 6 8 4 2 6 8 6 2 6 8 9 2 6 8 13 149 6 Finishing out the calculation gives us s 149 6 5 1 37 4 6 11 The standard deviation is about 6.1. You can see how the first step of this calculation uses both the mean (which can't be computed until all of the numbers have been entered) and each individual value as well. Again, this requires some method to remember all of the individual values that were entered. 11.2 Applying Lists In order to complete our enhanced statistics program, we need a way to store and manipulate an entire collection of numbers. We can't just use a bunch of independent variables, because we don't know how many numbers there will be. What we need is some way of combining an entire collection of values into one object. Actually, we've already done something like this, but we haven't discussed the details. Take a look at these interactive examples. >>> range(10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> string.split("This is an ex-parrot!") ['%,This'*), !#.is(+', (*%an$((, #%.ex-parrot!('%] Both of these familiar functions return a collection of values denoted by the enclosing square brackets. As you know, these collections are called lists. 11.2.1 Lists are Sequences Lists are ordered sequences of items. The ideas and notations that we use for manipulating lists are borrowed from mathematics. Mathematicians sometimes give an entire sequence of items a single name. For instance, a sequence of n numbers might just be called S: S s 0 !$# s 1 /,' s 2 !*+ s 3 s n 111.2. APPLYING LISTS 179 When they want to refer to specific values in the sequence, these values are denoted by subscripts. In this example, the first item in the sequence is denoted with the subscript 0, s 0 . By using numbers as subscripts, mathematicians are able to succinctly summarize computations over items in the sequence using subscript variables. For example, the sum of the sequence is written using standard summation notation as n 1 $!, s i i 0 . A similar idea can be applied to computer programs. We can use a single variable to represent an entire sequence, and the individual items in the sequence can be accessed through subscripting. Well, almost; We don't have a way of typing subscripts, but we can use indexing instead. Suppose that our sequence is stored in a variable called s. We could write a loop to calculate the sum of the items in the sequence like this: sum = 0 for i in range(n): sum = sum + s[i] To make this work, s must be the right kind of object. In Python, we can use a list; other languages have arrays, which are similar. A list or array is a sequence of items where the entire sequence is referred to by a single name (in this case, s) and individual items can be selected by indexing (e.g., s[i]). 11.2.2 Lists vs. Strings You may notice the similarity between lists and strings. In Python strings and lists are both sequences that can be indexed. In fact, all of the built-in string operations that we discussed in Chapter 4 are sequence operations and can also be applied to lists. To jog your memory, here's a summary. Operator seq + seq seq * int-expr seq [ ] len( seq ) seq [ : ] for var in seq : Meaning Concatenation Repetition Indexing Length Slicing Iteration The last line shows that a for loop can be used to iterate through the items in a sequence. The summation example above can be coded more simply like this: sum = 0 for num in s: sum = sum + s Lists differ from strings in a couple important ways. First, the items in a list can be any data type, including instances of programmer-defined classes. Strings, obviously, are always sequences of characters. Second, lists are mutable. That means that the contents of a list can be modified. Strings cannot be changed ,"&in place."!" Here is an example interaction that illustrates the difference: >>> myList = [34, 26, 15, 10] >>> myList[2] 15 >>> myList[2] = 0 >>> myList [34, 26, 0, 10] >>> myString = "Hello World" >>> myString[2]CHAPTER 11. DATA COLLECTIONS 180 )(+l%$* >>> myString[2] = ,&/z"%- Traceback (innermost last): File "", line 1, in ? TypeError: object doesn't support item assignment The first line creates a list of four numbers. Indexing position 2 returns the value 15 (as usual, indexes start at 0). The next command assigns the value 0 to the item in position 2. After the assignment, evaluating the list shows that the new value has replaced the old. Attempting a similar operation on a string produces an error. Strings are not mutable; lists are. 11.2.3 List Operations Arrays in other programming languages are generally fixed size. When you create an array, you have to specify how many items it will hold. If you don't know how many items you will have, then you have to allocate a large array, just in case, and keep track of how many -,!slots!+% you actually fill. Arrays are also usually homogeneous. That means they are restricted to holding objects of a single data type. You can have an array of ints or an array of strings but can not mix strings and ints in a single array. In contrast, Python lists are dynamic. They can grow and shrink on demand. They are also heterogeneous. You can mix arbitrary data types in a single list. In a nutshell, Python lists are mutable sequences of arbitrary objects. As you know, lists can be created by listing items inside square brackets. odds = [1, 3, 5, 7, 9] food = ["spam", "eggs", "back bacon"] silly = [1, "spam", 4, "U"] empty = [] In the last example, empty is a list containing no items at all'$.an empty list. A list of identical items can be created using the repetition operator. This example creates a list containing 50 zeroes: zeroes = [0] * 50 Typically, lists are built up one piece at a time using the append method. Here is a fragment of code that fills a list with positive numbers typed by the user: nums = [] x = input("''Enter a number: .$/) while x >= 0: nums.append(x) x = input("Enter a number: ") In essence, nums is being used as an accumulator. The accumulator starts out empty, and each time through the loop a new value is tacked on. The append method is just one example of a number of useful list-specific methods. The following table briefly summarizes of what you can do to a list: Method .append(x) +,/,%//)#),//-$ .sort() -+'%#")),/"$&'. .reverse() %#"+&.*,',&++,/ .index(x) &!",&$&.$"--,*$ .insert(i,x) ".-$"+"+'//$"*! .count(x) "(!*+&.(,)"!&-+ "'!&""!*%(!$##% .remove(x) .pop(i) x in list #+,+%/(*'$$(,!- Meaning Add element x to end of list. Sort the list. A comparison function may be passed as parameter. Reverses the list. Returns index of first occurrence of x. Insert x into list at index i. (Same as list[i:i] = [x]) Returns the number of occurrences of x in list. Deletes the first occurrence of x in list. Deletes the ith element of the list and returns its value. Checks to see if x is in the list (returns a Boolean).11.3. STATISTICS WITH LISTS 181 We have seen how lists can grow by appending new items. Lists can also shrink when items are deleted. Individual items or entire slices can be removed from a list using the del operator. >>> myList [34, 26, 0, 10] >>> del myList[1] >>> myList [34, 0, 10] >>> del myList[1:3] >>> myList [34] As you can see, Python lists provide a very flexible mechanism for handling indefinitely large sequences of data. Using lists is easy if you keep these basic principles in mind: A list is a sequence of items stored as a single object. Items in a list can be accessed by indexing, and sublists can be accessed by slicing. Lists are mutable; individual items or entire slices can be replaced through assignment statements. Lists will grow and shrink as needed. 11.3 Statistics with Lists Now that you know more about lists, we are ready to solve our little statistics problem. Recall that we are trying to develop a program that can compute the mean, median and standard deviation of a sequence of numbers entered by the user. One obvious way to approach this problem is to store the numbers in a list. We can write a series of functions.)+mean, stdDev and median&/,that take a list of numbers and calculate the corresponding statistics. Let's start by using lists to rewrite our original program that only computes the mean. First, we need a function that gets the numbers from the user. Let's call it getNumbers. This function will implement the basic sentinel loop from our original program to input a sequence of numbers. We will use an initially empty list as an accumulator to collect the numbers. The list will then be returned from the function. Here's the code for getNumbers: def getNumbers(): nums = [] # start with an empty list # sentinel loop to get numbers xStr = raw_input("Enter a number ( to quit) >> ") while xStr != "": x = eval(xStr) nums.append(x) # add this value to the list xStr = raw_input("Enter a number ( to quit) >> ") return nums Using this function, we can get a list of numbers from the user with a single line of code. data = getNumbers() Next, let's implement a function that computes the mean of the numbers in a list. This function takes a list of numbers as a parameter and returns the mean. We will use a loop to go through the list and compute the sum. def mean(nums): sum = 0.0CHAPTER 11. DATA COLLECTIONS 182 for num in nums: sum = sum + num return sum / len(nums) Notice how the average is computed and returned in the last line of this function. The len operation returns the length of a list; we don't need a separate loop accumulator to determine how many numbers there are. With these two functions, our original program to average a series of numbers can now be done in two simple lines: def main(): data = getNumbers() print /'-The mean is$&-, mean(data) Next, let's tackle the standard deviation function, stdDev. In order use the standard deviation formula discussed above, we first need to compute the mean. We have a design choice here. The value of the mean can either be calculated inside of stdDev or passed to the function as a parameter. Which way should we do it? On the one hand, calculating the mean inside of stdDev seems cleaner, as it makes the interface to the function simpler. To get the standard deviation of a set of numbers, we just call stdDev and pass it the list of numbers. This is exactly analogous to how mean (and median below) works. On the other hand, programs that need to compute the standard deviation will almost certainly need to compute the mean as well. Computing it again inside of stdDev results in the calculations being done twice. If our data set is large, this seems inefficient. Since our program is going to output both the mean and the standard deviation, let's have the main program compute the mean and pass it as a parameter to stdDev. Other options are explored in the exercises at the end of the chapter. Here is the code to compute the standard deviation using the mean (xbar) as a parameter: def stdDev(nums, xbar): sumDevSq = 0.0 for num in nums: dev = xbar - num sumDevSq = sumDevSq + dev * dev return sqrt(sumDevSq/(len(nums)-1)) Notice how the summation from the standard deviation formula is computed using a loop with an accumulator. The variable sumDevSq stores the running sum of the squares of the deviations. Once this sum has been computed, the last line of the function calculates the rest of the formula. Finally, we come to the median function. This one is a little bit trickier, as we do not have a formula to calculate the median. We need an algorithm that picks out the middle value. The first step is to arrange the numbers in increasing order. Whatever value ends up in the middle of the pack is, by definition, the median. There is just one small complication. If we have an even number of values, there is no exact middle number. In that case, the median is determined by averaging the two middle values. So the median of 3, 5, 6 and 9 is 5 6 2 5 5. In pseudocode our median algorithm looks like this. sort the numbers into ascending order if the size of data is odd: median = the middle value else: median = the average of the two middle values return median This algorithm translates almost directly into Python code. We can take advantage of the sort method to put the list in order. To test whether the size is even, we need to see if it is divisible by two. This is a perfect application of the remainder operation. The size is even if size % 2 == 0, that is, dividing by 2 leaves a remainder of 0. With these insights, we are ready to write the code.11.3. STATISTICS WITH LISTS 183 def median(nums): nums.sort() size = len(nums) midPos = size / 2 if size % 2 == 0: median = (nums[midPos] + nums[midPos-1]) / 2.0 else: median = nums[midPos] return median You should study this code carefully to be sure you understand how it selects the correct median from the sorted list. The middle position of the list is calculated using integer division as size / 2. If size is 3, then midPos is 1 (2 goes into 3 just one time). This is the correct middle position, since the three values in the list will have the indexes 0, 1, 2. Now suppose size is 4. In this case, midPos will be 2, and the four values will be in locations 0, 1, 2, 3. The correct median is found by averaging the values at midPos (2) and midPos-1 (1). Now that we have all the basic functions, finishing out the program is a cinch. def main(): print ')'This program computes mean, median and standard deviation.##) data = getNumbers() xbar = mean(data) std = stdDev(data, xbar) med = median(data) print /-.\nThe mean is&&/, xbar print ,!#The standard deviation is,$+, std print )"$The median is+(*, med Many computational tasks from assigning grades to monitoring flight systems on the space shuttle require some sort of statisitical analysis. By using the if name == .'" main +*' technique, we can make our code useful as a stand-alone program and as a general statistical library module. Here's the complete program: # stats.py from math import sqrt def getNumbers(): nums = [] # start with an empty list # sentinel loop to get numbers xStr = raw_input("Enter a number ( to quit) >> ") while xStr != "": x = eval(xStr) nums.append(x) # add this value to the list xStr = raw_input("Enter a number ( to quit) >> ") return nums def mean(nums): sum = 0.0 for num in nums: sum = sum + num return sum / len(nums)CHAPTER 11. DATA COLLECTIONS 184 def stdDev(nums, xbar): sumDevSq = 0.0 for num in nums: dev = num - xbar sumDevSq = sumDevSq + dev * dev return sqrt(sumDevSq/(len(nums)-1)) def median(nums): nums.sort() size = len(nums) midPos = size / 2 if size % 2 == 0: median = (nums[midPos] + nums[midPos-1]) / 2.0 else: median = nums[midPos] return median def main(): print /('This program computes mean, median and standard deviation..** data = getNumbers() xbar = mean(data) std = stdDev(data, xbar) med = median(data) print '!.\nThe mean is,-+, xbar print ,*,The standard deviation is&-/, std print !-)The median is,(!, med if __name__ == &&.__main__'((: main() 11.4 Combining Lists and Classes In the last chapter, we saw how classes could be used to structure data by combining several instance variables together into a single object. Lists and classes used together are powerful tools for structuring the data in our programs. Remember the DieView class from last chapter? In order to display the six possible values of a die, each DieView object keeps track of seven circles representing the position of pips on the face of a die. In the previous version, we saved these circles using instance variables, pip1, pip2, pip3, etc. Let's consider how the code looks using a collection of circle objects stored as a list. The basic idea is to replace our seven instance variables with a single list called pips. Our first problem is to to create a suitable list. This will be done in the constructor for the DieView class. In our previous version, the pips were created with this sequence of statements inside of init : self.pip1 self.pip2 self.pip3 self.pip4 self.pip5 self.pip6 self.pip7 = = = = = = = self.__makePip(cx-offset, self.__makePip(cx-offset, self.__makePip(cx-offset, self.__makePip(cx, cy) self.__makePip(cx+offset, self.__makePip(cx+offset, self.__makePip(cx+offset, cy-offset) cy) cy+offset) cy-offset) cy) cy+offset) Recall that makePip is a local method of the DieView class that creates a circle centered at the postion given by its parameters.11.4. COMBINING LISTS AND CLASSES 185 We want to replace these lines with code to create a list of pips. One approach would be to start with an empty list of pips and build up the final list one pip at a time. pips = [] pips.append(self.__makePip(cx-offset, pips.append(self.__makePip(cx-offset, pips.append(self.__makePip(cx-offset, pips.append(self.__makePip(cx, cy)) pips.append(self.__makePip(cx+offset, pips.append(self.__makePip(cx+offset, pips.append(self.__makePip(cx+offset, self.pips = pips cy-offset)) cy)) cy+offset)) cy-offset)) cy)) cy+offset)) An even more straightforward approach is to create the list directly, enclosing the calls to inside list construction brackets, like this: self.pips = [ self.__makePip(cx-offset, self.__makePip(cx-offset, self.__makePip(cx-offset, self.__makePip(cx, cy)), self.__makePip(cx+offset, self.__makePip(cx+offset, self.__makePip(cx+offset, ] makePip cy-offset)), cy)), cy+offset)), cy-offset)), cy)), cy+offset)) Notice how I have formatted this statement. Rather than making one giant line, I put one list element on each line. Python is smart enough to know that the end of the statement has not been reached until it finds the matching square bracket. Listing complex objects one per line like this makes it much easier to see what is happening. Just make sure to include the commas at the end of intermediate lines to separate the items of the list. The advantage of a pip list is that it is much easier to perform actions on the entire set. For example, we can blank out the die by setting all of the pips to have the same color as the background. for pip in self.pips: pip.setFill(self.background) See how these two lines of code loop through the entire collection of pips to change their color? This required seven lines of code in the previous version using separate instance variables. Similarly, we can turn a set of pips back on by indexing the appropriate spot in the pips list. In the original program, pips 1, 4, and 7 were turned on for the value 3. self.pip1.setFill(self.foreground) self.pip4.setFill(self.foreground) self.pip7.setFill(self.foreground) In the new version, this corresponds to pips in positions 0, 3 and 6, since the pips list is indexed starting at 0. A parallel approach could accomplish this task with these three lines of code: self.pips[0].setFill(self.foreground) self.pips[3].setFill(self.foreground) self.pips[6].setFill(self.foreground) Doing it this way makes explicit the correspondence between the individual instance variables used in the first version and the list elements used in the second version. By subscripting the list, we can get at the individual pip objects, just as if they were separate variables. However, this code does not really take advantage of the new representation. Here is an easier way to turn on the same three pips:186 CHAPTER 11. DATA COLLECTIONS for i in [0,3,6]: self.pips[i].setFill(self.foreground) Using an index variable in a loop, we can turn all three pips on using the same line of code. The second approach considerably shortens the code needed in the setValue method of the DieView class. Here is the updated algorithm: Loop through pips and turn all off Determine the list of pip indexes to turn on Loop through the list of indexes and turn on those pips. We could implement this algorthim using a multi-way selection followed by a loop. for pip in self.pips: self.pip.setFill(self.background) if value == 1: on = [3] elif value == 2: on = [0,6] elif value == 3: on = [0,3,6] elif value == 4: on = [0,2,4,6] elif value == 5: on = [0,2,3,4,6] else: on = [0,1,2,4,5,6] for i in on: self.pips[i].setFill(self.foreground) The version without lists required 36 lines of code to accomplish the same task. But we can do even better than this. Notice that this code still uses the if-elif structure to determine which pips should be turned on. The correct list of indexes is determined by value; we can make this decision table-driven instead. The idea is to use a list where each item in the list is itself a list of pip indexes. For example, the item in position 3 should be the list [0,3,6], since these are the pips that must be turned on to show a value of 3. Here is how a table-driven approach can be coded: onTable = [ [], [3], [2,4], [2,3,4], [0,2,4,6], [0,2,3,4,6], [0,1,2,4,5,6] ] for pip in self.pips: self.pip.setFill(self.background) on = onTable[value] for i in on: self.pips[i].setFill(self.foreground) I have called the table of pip indexes onTable. Notice that I padded the table by placing an empty list in the first position. If value is 0, the DieView will be blank. Now we have reduced our 36 lines of code to seven. In addition, this version is much easier to modify; if you want to change which pips are displayed for various values, you simply modify the entries in onTable. There is one last issue to address. The onTable will remain unchanged throughout the life of any particular DieView. Rather than (re)creating this table each time a new value is displayed, it would be better to create the table in the constructor and save it in an instance variable. Putting the definition of onTable into init yields this nicely completed class:11.4. COMBINING LISTS AND CLASSES 187 class DieView: """ DieView is a widget that displays a graphical representation of a standard six-sided die.""" def __init__(self, win, center, size): """Create a view of a die, e.g.: d1 = GDie(myWin, Point(40,50), 20) creates a die centered at (40,50) having sides of length 20.""" # first define some standard values self.win = win self.background = "white" # color of die face self.foreground = "black" # color of the pips self.psize = 0.1 * size # radius of each pip hsize = size / 2.0 # half of size offset = 0.6 * hsize # distance from center to outer pips # create a square for the face cx, cy = center.getX(), center.getY() p1 = Point(cx-hsize, cy-hsize) p2 = Point(cx+hsize, cy+hsize) rect = Rectangle(p1,p2) rect.draw(win) rect.setFill(self.background) # Create 7 circles for standard pip locations self.pips = [ self.__makePip(cx-offset, cy-offset), self.__makePip(cx-offset, cy), self.__makePip(cx-offset, cy+offset), self.__makePip(cx, cy), self.__makePip(cx+offset, cy-offset), self.__makePip(cx+offset, cy), self.__makePip(cx+offset, cy+offset) ] # Create a table for which pips are on for each value self.onTable = [ [], [3], [2,4], [2,3,4], [0,2,4,6], [0,2,3,4,6], [0,1,2,4,5,6] ] self.setValue(1) def __makePip(self, x, y): """Internal helper method to draw a pip at (x,y)""" pip = Circle(Point(x,y), self.psize) pip.setFill(self.background) pip.setOutline(self.background) pip.draw(self.win) return pip def setValue(self, value): """ Set this die to display value.""" # Turn all the pips off for pip in self.pips: pip.setFill(self.background)CHAPTER 11. DATA COLLECTIONS 188 # Turn the appropriate pips back on for i in self.onTable[value]: self.pips[i].setFill(self.foreground) 11.5 Case Study: Python Calculator The reworked DieView class shows how lists can be used effectively as instance variables of objects. Inter- estingly, our pips list and onTable list contain circles and lists, respectively, which are themselves objects. Maintaining lists of objects can be a powerful technique for organizing a program. We can go one step further and view a program itself as a collection of data structures (collections and objects) and a set of algorithms that operate on those data structures. Now, if a program contains data and operations, then one natural way to organize the program it to treat the entire application itself as an object. 11.5.1 A Calculator as an Object As an example, we'll develop a program that implements a simple Python calculator. Our calculator will have buttons for the ten digits (0&.'9), a decimal point $,(.-$!, four operations ($'-+.#., "!+-)((, "$/*)",, '/$/&,#), and a few special keys: ,!&C+%! to clear the display, +(( -!'" to backspace over characters in the display, and ./"=*(" to do the calculation. We'll take a very simple approach to performing calculations. As buttons are clicked, the corresponding characters will show up in the display, allowing the user to create a formula. When the )),=)"- key is pressed, the formula will be evaluated and the resulting value shown in the display. Figure 11.1 shows a snapshot of the calculator in action. Figure 11.1: Python calculator in action. Basically, we can divide the functioning of the calculator into two parts: creating the interface and inter- acting with the user. The user interface in this case consists of a display widget and a bunch of buttons. We can keep track of these GUI widgets with instance variables. The user interaction can be managed by a set of methods that manipulate the widgets. To implement this division of labor, we will create a Calculator class that represents the calculator in our program. The constructor for the class will create the initial interface. We will make the calculator respond to user interaction by invoking a special run method. 11.5.2 Constructing the Interface Let's take a detailed look at the constructor for the Calculator class. First, we'll need to create a graphics window to draw the interface.11.5. CASE STUDY: PYTHON CALCULATOR 189 def __init__(self): # create the window for the calculator win = GraphWin("Calculator") win.setCoords(0,0,6,7) win.setBackground("slategray") self.win = win The coordinates for the window were chosen to simplify the layout of the buttons. In the last line, the window object is tucked into an instance variable so that other methods can refer to it. The next step is to create the buttons. We will reuse the button class from last chapter. Since there are a lot of similar buttons, we will use a list to store them. Here is the code that creates the button list: # create list of buttons # start with all the standard sized buttons # bSpecs gives center coords and label of buttons bSpecs = [(2,1,,(-0!%)), (3,1,*(!./$&), (1,2,!&-1/&+), (2,2,!%+2*&#), (3,2,+.$3..,), (4,2,,+,+!#!), (5,2,)$)-**,), (1,3,/*)4$!/), (2,3,/"+5*#!), (3,3,$%.6.#%), (4,3,+./*'.)), (5,3,'/,/'!&), (1,4,#'(7,*(), (2,4,#!!8-"*), (3,4,--.9#+)), (4,4,.&.<--,%),(5,4,-++C)"+)] self.buttons = [] for cx,cy,label in bSpecs: self.buttons.append(Button(self.win,Point(cx,cy),.75,.75,label)) # create the larger ',"=./# button self.buttons.append(Button(self.win, Point(4.5,1), 1.75, .75, "=")) # activate all buttons for b in self.buttons: b.activate() Study this code carefully. A button is normally specified by providing a center point, width, height and label. Typing out calls to the Button constructor with all this information for each button would be tedious. Rather than creating the buttons directly, this code first creates a list of button specifications, bSpecs. This list of specifications is then used to create the buttons. Each specification is a tuple consisting of the x and y coordinates of the center of the button and its label. A tuple looks like a list except that it is enclosed in round parentheses () instead of square brackets []. A tuple is just another kind of sequence in Python. Tuples are like lists except that tuples are immutable-)$the items can't be changed. If the contents of a sequence won't be changed after it is created, using a tuple is more efficient than using a list. The next step is to iterate through the specification list and create a corresponding button for each entry. Take a look at the loop heading: for (cx,cy,label) in bSpecs: According to the definition of a for loop, the tuple (cx,cy,label) will be assigned each successive item in the list bSpecs. Put another way, conceptually, each iteration of the loop starts with an assignment. (cx,cy,label) = Of course, each item in bSpecs is also a tuple. When a tuple of variables is used on the left side of an assignment, the corresponding components of the tuple on the right side are unpacked into the variables on the left side. In fact, this is how Python actually implements all simultaneous assignments. The first time through the loop, it is as if we had done this simultaneous assignment: cx, cy, label = 2, 1, "0" Each time through the loop, another tuple from bSpecs is unpacked into the variables in the loop heading. The values are then used to create a Button that is appended to the list of buttons. After all of the standard-sized buttons have been created, the larger = button is created and tacked onto the list.CHAPTER 11. DATA COLLECTIONS 190 self.buttons.append(Button(self.win, Point(4.5,1), 1.75, .75, "=")) I could have written a line like this for each of the previous buttons, but I think you can see the appeal of the specification-list/loop approach for creating the seventeen similar buttons. In contrast to the buttons, creating the calculator display is quite simple. The display will just be a rectangle with some text centered on it. We need to save the text object as an instance variable so that its contents can be accessed and changed during processing of button clicks. Here is the code that creates the display: bg = Rectangle(Point(.5,5.5), Point(5.5,6.5)) bg.setFill(&&,white'*)) bg.draw(self.win) text = Text(Point(3,6), "") text.draw(self.win) text.setFace("courier") text.setStyle("bold") text.setSize(16) self.display = text 11.5.3 Processing Buttons Now that we have an interface drawn, we need a method that actually gets the calculator running. Our calculator will use a classic event loop that waits for a button to be clicked and then processes that button. Let's encapsulate this in a method called run. def run(self): while 1: key = self.getKeyPress() self.processKey(key) Notice that this is an infinite loop. To quit the program, the user will have to &$%kill&#* the calculator window. All that's left is to implement the getKeyPress and processKey methods. Getting key presses is easy; we continue getting mouse clicks until one of those mouse clicks is on a button. To determine whether a button has been clicked, we loop through the list of buttons and check each one. The result is a nested loop. def getKeyPress(self): # Waits for a button to be clicked # RETURNS the label of the button the was clicked. while 1: # loop for each mouse click p = self.win.getMouse() for b in self.buttons: # loop for each button if b.clicked(p): return b.getLabel() # method exit You can see how having the buttons in a list is a big win here. We can use a for loop to look at each button in turn. If the clicked point p turns out to be in one of the buttons, the label of that button is returned, providing an exit from the otherwise infinite while loop. The last step is to update the display of the calculator according to which button was clicked. This is accomplished in processKey. Basically, this is a multi-way decision that checks the key label and takes the appropriate action. A digit or operator is simply appended to the display. If key contains the label of the button, and text contains the current contents of the display, the appropriate line of code looks like this: self.display.setText(text+key)11.5. CASE STUDY: PYTHON CALCULATOR 191 The clear key blanks the display. self.display.setText("") The backspace strips off one character. self.display.setText(text[:-1]) Finally, the equal key causes the expression in the display to be evaluated and the result displayed. try: result = eval(text) except: result = $'.ERROR/,! self.display.setText(str(result)) The try-except here is necessary to catch run-time errors caused by entries that are not legal Python expressions. If an error occurs, the calculator will display ERROR rather than causing the program to crash. Here is the complete program. # calc.pyw -- A four function calculator using Python arithmetic. from graphics import * from buttons import Button class Calculator: # This class implements a simple calculator GUI def __init__(self): # create the window for the calculator win = GraphWin("calculator") win.setCoords(0,0,6,7) win.setBackground("slategray") self.win = win # Now create the widgets self.__createButtons() self.__createDisplay() def __createButtons(self): # create list of buttons # buttonSpec gives center, width and label of a button buttonSpecs = [(2,1,.75,*%)0%#,), (3,1,.75,-!#.!&+), (4.5,1,2,+#(=')"), (1,2,.75,(&-1,+'), (1,3,.75,-'%4+&!), (1,4,.75,#!-7,,/), (2,2,.75,(*(2#"-), (2,3,.75,-$'5&"/), (2,4,.75,.)&8#%(), (3,2,.75,)#&3&%,), (3,3,.75,&/$6#+'), (3,4,.75,#*(9*$)), (4,2,.75,.",+,.&), (4,3,.75,+$.*('$), (4,4,.75,!'-<--,)), (5,2,.75,)*(-$,*), (5,3,.75,+&,/*''), (5,4,.75,$,(C'(-) ] yinc = .75/2.0 # half of a "standard" button height self.buttons = [] for cx,cy,bwidth,label in buttonSpecs: xinc = bwidth/2.0 p1 = Point(cx-xinc, cy-yinc) p2 = Point(cx+xinc, cy+yinc) b = Button(self.win, p1, p2, label) b.activate() self.buttons.append(b)CHAPTER 11. DATA COLLECTIONS 192 def __createDisplay(self): bg = Rectangle(Point(.5,5.5), Point(5.5,6.5)) bg.setFill(,$"white+#/) bg.draw(self.win) text = Text(Point(3,6), "") text.draw(self.win) text.setFace("courier") text.setStyle("bold") text.setSize(16) self.display = text def getKeyPress(self): # Waits for a button to be clicked and returns the label of # the button the was clicked. while 1: p = self.win.getMouse() for b in self.buttons: if b.clicked(p): return b.getLabel() # method exit def processKey(self, key): # Updates the display of the calculator for press of this key text = self.display.getText() if key == &*-C*(*: self.display.setText("") elif key == *(+<-*,': # Backspace, slice off the last character. self.display.setText(text[:-1]) elif key == +.%=.(+: # Evaluate the expresssion and display the result. # the try...except mechanism "catches" errors in the # formula being evaluated. try: result = eval(text) except: result = &#(ERROR%"/ self.display.setText(str(result)) else: # Normal key press, append it to the end of the display self.display.setText(text+key) def run(self): # Infinite #-#event loop+/- to process key presses. while 1: key = self.getKeyPress() self.processKey(key) # This runs the program. if __name__ == "__main__": # First create a calculator object theCalc = Calculator() # Now call the calculator's run method. theCalc.run()11.6. NON-SEQUENTIAL COLLECTIONS 193 Notice especially the very end of the program. To run the application, we create an instance of the Calculator class and then call its run method. 11.6 Non-Sequential Collections Python provides another built-in data type for collections, called a dictionary. While dictionaries are incred- ibly useful, they are not as common in other languages as lists (arrays). The example programs in the rest of the book will not use dictionaries, so you can skip the rest of this section if you've learned all you want to about collections for the moment. 11.6.1 Dictionary Basics Lists allow us to store and retrieve items from sequential collections. When we want to access an item in the collection, we look it up by index",'its position in the collection. Many applications require a more flexible way to look up information. For example, we might want to retrieve information about a student or employee based on their social security number. In programming terminology, this is a key-value pair. We access the value (student information) associated with a particular key (social security number). If you think a bit, you can come up with lots of other examples of useful key-value pairs: names and phone numbers, usernames and passwords, zip codes and shipping costs, state names and capitals, sales items and quantity in stock, etc. A collection that allows us to look up information associated with arbitrary keys is called a mapping. Python dictionaries are mappings. Some other programming languages provide similar structures called hashes or associative arrays. A dictionary can be created in Python by listing key-value pairs inside of curly braces. Here is a simple dictionary that stores some fictional usernames and passwords. >>> passwd = {"guido":"superprogrammer", "turing":"genius", "bill":"monopoly"} Notice that keys and values are joined with a .-.:/(', and commas are used to separate the pairs. The main use for a dictionary is to look up the value associated with a particular key. This is done through indexing notation. >>> passwd["guido"] *"*superprogrammer)$( >>> passwd["bill"] -#'monopoly((* In general, [] returns the object associated with the given key. Dictionaries are mutable; the value associated with a key can be changed through assignment. >>> passwd["bill"] = "bluescreen" >>> passwd {',#turing'&/: !('genius-)*, %/(bill#&': !(/bluescreen*!#, )%%guido!,,: "$!superprogrammer$"-} In this example, you can see that the value associated with *"&bill(/& has changed to #+%bluescreen"%+. Also notice that the dictionary prints out in a different order from how it was originally created. This is not a mistake. Mappings are inherently unordered. Internally, Python stores dictionaries in a way that makes key lookup very efficient. When a dictionary is printed out, the order of keys will look essentially random. If you want to keep a collection of items in a certain order, you need a sequence, not a mapping. To summarize, dictionaries are mutable collections that implement a mapping from keys to values. Our password example showed a dictionary having strings as both keys and values. In general, keys can be any immutable type, and values can be any type at all, including programmer-defined classes. Python dictionaries are very efficient and can routinely store even hundreds of thousands of items.CHAPTER 11. DATA COLLECTIONS 194 11.6.2 Dictionary Operations Like lists, Python dictionaries support a number of handy built-in operations. You have already seen how dictionaries can be defined by explicitly listing the key-value pairs in curly braces. You can also extend a dictionary by adding new entries. Suppose a new user is added to our password system. We can expand the dictionary by assigning a password for the new username. >>> passwd[(&#newuser.'*] = !!.ImANewbie&!" >>> passwd {)),turing!,%: .-'genius$&-, )//bill.!$: !/!bluescreen*-(, \ +%/newuser!*': "+/ImANewbie&+#, *)/guido(,%: ,-$superprogrammer+#+} In fact, a common method for building dictionaries is to start with an empty collection and add the key- value pairs one at a time. Suppose that usernames and passwords were stored in a file called passwords, where each line of the file contains a username and password with a space between. We could easily create the passwd dictionary from the file. passwd = {} for line in open('#*passwords'$.,&.$r%.%).readlines(): user, pass = string.split(line) passwd[user] = pass To manipulate the contents of a dictionary, Python provides the following methods. Method dict .has key( key ) dict .keys() dict .values() dict .items() del dict [ key ] dict .clear() Meaning Returns true if dictionary contains the specified key, false if it doesn't. Returns a list of the keys. Returns a list of the values. Returns a list of tuples (key,value) representing the key-value pairs. Delete the specified entry. Delete all entries. These methods are mostly self-explanatory. For illustration, here is an interactive session using our pass- word dictionary: >>> passwd.keys() [+'/turing"$+, $.$bill$-(, (*%newuser$%', .#!guido!*+] >>> passwd.values() [.')genius)$., .&%bluescreen-(), /#.ImANewbie$&+, #&,superprogrammer.!)] >>> passwd.items() [(,.+turing$.,, .%$genius.-.), (,+$bill(!#, (/&bluescreen&*#), (),/newuser%"/, --&ImANewbie!($), \ (,&/guido'+-, %'/superprogrammer)'/)] >>> passwd.has_key(#./bill/,)) 1 >>> passwd.has_key(#%-fred)!.) 0 >>> passwd.clear() >>> passwd {} 11.6.3 Example Program: Word Frequency Let's write a program that analyzes text documents and counts how many times each word appears in the document. This kind of analysis is sometimes used as a crude measure of the style similarity between two documents and is also used by automatic indexing and archiving programs (such as Internet search engines).11.6. NON-SEQUENTIAL COLLECTIONS 195 At the highest level, this is just a multi-accumulator problem. We need a count for each word that appears in the document. We can use a loop that iterates through each word in the document and adds one to the appropriate count. The only catch is that we will need hundreds or thousands of accumulators, one for each unique word in the document. This is where a (Python) dictionary comes in handy. We will use a dictionary where the keys are strings representing words in the document and the values are ints that count of how many times the word appears. Let's call our dictionary counts. To update the count for a particular word, w, we just need a line of code something like this: counts[w] = counts[w] + 1 This says to set the count associated with word w to be one more than the current count for w. There is one small complication with using a dictionary here. The first time we encounter a word, it will not yet be in counts. Attempting to access a non-existent key produces a run-time KeyError. To guard against this, we need a decision in our algorithm. if w is already in counts: add one to the count for w else: set count for w to 1 This decision ensures that the first time a word is encountered, it will be entered into the dictionary with a count of 1. One way to implement this decision is to use the has key method for dictionaries. if counts.has_key(w): counts[w] = counts[w] + 1 else: counts[w] = 1 Another approach is to use a try-except to catch the error. try: counts[w] = counts[w] + 1 except KeyError: counts[w] = 1 This is a common pattern in programs that use dictionaries, and both of these coding styles are used. The dictionary updating code will form the heart of our program. We just need to fill in the parts around it. The first task is to split our text document into a sequence of words. In the process, we will also convert all the text to lowercase (so occurrences of )&$Foo&&+ match !))foo!!$) and eliminate punctuation (so %%#foo,$%) matches $('foo*.*). Here's the code to do that: fname = raw_input("File to analyze: ") # read file as one long string text = open(fname,*(&r'"').read() # convert all letters to lower case text = string.lower(text) # replace each punctuation character with a space for ch in -"$!"#$%&()*+,-./:;<=>?@[\\]('_!*'{|} )#').: text = string.replace(text, ch, +&! '*)) # split string at whitespace to form a list of words words = string.split(text) Now we can easily loop through the words to build the counts dictionary.CHAPTER 11. DATA COLLECTIONS 196 counts = {} for w in words: try: counts[w] = counts[w] + 1 except KeyError: counts[w] = 1 Our last step is to print a report that summarizes the contents of counts. One approach might be to print out the list of words and their associated counts in alphabetical order. Here's how that could be done: # get list of words that appear in document uniqueWords = counts.keys() # put list of words in alphabetical order uniqueWords.sort() # print words and associated counts for w in uniqueWords: print w, counts[w] For a large document, however, this is unlikely to be useful. There will be far too many words, most of which only appear a few times. A more interesting analysis is to print out the counts for the n most frequent words in the document. In order to do that, we will need to create a list that is sorted by counts (most to fewest) and then select the first n items in the list. We can start by getting a list of key-value pairs using the items method for dictionaries. items = counts.items() Here items will be a list of tuples (e.g., [(")'foo+(",5), (%/'bar$(%,7), (()/spam///,376), ]). If we simply sort this list (items.sort()) Python will put them in a standard order. Unfortunately, when Python compares tuples, it orders them by components, left to right. Since the first component of each pair is the word, items.sort() will put this list in alphabetical order, which is not what we want. In order to put our pair list in the proper order, we need to investigate the sorting method for lists a bit more carefully. When we first covered the sort method, I mentioned that it can take a comparison function as an optional parameter. We can use this feature to tell Python how to sort the list of pairs. If no comparison function is given, Python orders a list according to the the built-in function cmp. This function accepts two values as parameters and returns -1, 0 or 1, corresponding to the relative ordering of the parameters. Thus, cmp(a,b) returns -1 if a precedes b, 0 if they are the same, and 1 if a follows b. Here are a few examples. >>> -1 >>> -1 >>> 1 >>> 0 cmp(1,2) cmp("a","b") cmp(3,1) cmp(3.1,3.1) To sort our list of items, we need a comparison function that takes two items (i.e., word-count pairs) and returns either -1, 0 or 1, giving the relative order in which we want those two items to appear in the sorted list. Here is the code for a suitable comparison function: def compareItems((w1,c1), (w2,c2)): if c1 > c2: return - 1 elif c1 == c2:11.6. NON-SEQUENTIAL COLLECTIONS 197 return cmp(w1, w2) else: return 1 This function accepts two parameters, each of which is a tuple of two values. Notice I have taken advan- tage of Python's automatic tuple unpacking and written each parameter as a pair of variables. Take a look at the decision structure. If the count in the first item is greater than the count in the second item, then the first item should precede the second in the sorted list (since we want the most frequent words at the front of the list) and the function returns -1. If the counts are equal, then we let Python compare the two word strings with cmp. This ensures that groups of words with the same frequency will appear in alphabetical order relative to each other. Finally, the else handles the case when the second count is larger; the function returns a 1 to indicate that the first parameter should follow the second. With this comparison function, it is now a simple matter to sort our items into the correct order. items.sort(compareItems) Notice here I have used just the name of the function as the parameter to sort. When a function name is used like this (without any trailing parentheses), it tells Python that the function object itself is being referred to. As you know, a function name followed by parentheses tells Python to call the function. In this case, we are not calling the function, but rather sending the function object to the sort method to let it do the calling. Now that our items are sorted in order from most to least frequent, we are ready to print a report of the n most frequent words. Here's a loop that does the trick: for i in range(n): print "%-10s%5d" % items[i] Notice especially the formatted print statement. It prints a string, left-justified in ten spaces followed by an int right-justified in five spaces. Normally, we would supply a pair of values to fill in the slots (e.g., print "%-10s%5d" % (word, count)). In this case, however, items[i] is a pair, so Python can extract the two values that it needs. That about does it. Here is the complete program: # wordfreq.py import string def compareItems((w1,c1), (w2,c2)): if c1 > c2: return - 1 elif c1 == c2: return cmp(w1, w2) else: return 1 def main(): print "This program analyzes word frequency in a file" print "and prints a report on the n most frequent words.\n" # get the sequence of words from the file fname = raw_input("File to analyze: ") text = open(fname,).)r$%)).read() text = string.lower(text) for ch in "(.!"#$%&()*+,-./:;<=>?@[\\])._))'{|} )/"!(: text = string.replace(text, ch, -(' (/#) words = string.split(text) # construct a dictionary of word countsCHAPTER 11. DATA COLLECTIONS 198 counts = {} for w in words: try: counts[w] = counts[w] + 1 except KeyError: counts[w] = 1 # output analysis of n most frequent words. n = input("Output analysis of how many words? ") items = counts.items() items.sort(compareItems) for i in range(n): print "%-10s%5d" % items[i] if __name__ == /!#__main__&'": main() Just for fun, here's the result of running this program to find the twenty most frequent words in the book you're reading right now. This program analyzes word frequency in a file and prints a report on the n most frequent words. File to analyze: book.txt Output analysis of how many words? 20 the a of to is that and in we this for you program be it are as can will an 6428 2845 2622 2468 1936 1332 1259 1240 1030 985 719 702 684 670 618 612 607 583 480 470 11.7 Exercises 1. Given the initial statements import string s1 = [2,1,4,3] s2 = [/%"c**+,+))a$.),$%#b#-*] show the result of evaluating each of the following sequence expressions:11.7. EXERCISES 199 (a) s1 + s2 (b) 3 * s1 + 2 * s2 (c) s1[1] (d) s1[1:3] (e) s1 + s2[-1] 2. Given the same initial statements as in the previous problem, show the values of s1 and s2 after executing each of the following statements. Treat each part independently (i.e., assume that s1 and s2 start with their original values each time). (a) s1.remove(2) (b) s1.sort().reverse() (c) s1.append([s2.index(#")b)*!)]) (d) s2.pop(s1.pop(2)) (e) s2.insert(s1[0], +!!d%'') 3. Modify the statistics package from the chapter so that client programs have more flexibility in com- puting the mean and/or standard deviation. Specifically, redesign the library to have the following functions: mean(nums) Returns the mean of numbers in nums. stdDev(nums) Returns the standard deviation of nums. meanStdDev(nums) Returns both the mean and standard deviation of nums. 4. Most languages do not have the flexible built-in list (array) operations that Python has. Write an algo- rithm for each of the following Python operations and test your algorithm by writing it up in a suitable function. For example, as a function, reverse(myList) should do the same as myList.reverse(). Obviously, you are not allowed to use the corresponding Python method to implement your function. (a) count(myList, x) (like myList.count(x)) (b) isin(myList, x) (like x in myList)) (c) index(myList, x) (like myList.index(x)) (d) reverse(myList) (like myList.reverse()) (e) sort(myList) (like myList.sort()) 5. Write and test a function shuffle(myList) that scrambles a list into a random order, like shuffling a deck of cards. 6. The Sieve of Eratosthenes is an elegant algorithm for finding all of the prime numbers up to some limit n. The basic idea is to first create a list of numbers from 2 to n. The first number is removed from the list, and announced as a prime number, and all multiples of this number up to n are removed from the list. This process continues until the list is empty. For example, if we wished to find all the primes up to 10, the list would originally contain 2, 3, 4, 5, 6, 7, 8, 9, 10. The 2 is removed and announced to be prime. Then 4, 6, 8 and 10 are removed, since they are multiples of 2. That leaves 3, 5, 7, 9. Repeating the process, 3 is announced as prime and removed, and 9 is removed because it is a multiple of 3. That leaves 5 and 7. The algorithm continues by announcing that 5 is prime and removing it from the list. Finally, 7 is announced and removed, and we're done. Write a program that prompts a user for n and then uses the sieve algorithm to find all the primes less than or equal to n. 7. Create and test a Set class to represent a classical set. Your sets should support the following methods:CHAPTER 11. DATA COLLECTIONS 200 Set(elements) Create a set (elements is the initial list of items in the set). addElement(x) Adds x to the set. deleteElement(x) Removes x from the set. member(x) Returns true if x is in the set. intersection(set2) Returns a new set containing just those elements that are common to this set and set2. union(set2) Returns a new set containing all of elements that are in this set, set2, or both. subtract(set2) Returns a new set containing all the elements of this set that are not in set2.Chapter 12 Object-Oriented Design Now that you know some data structuring techniques, it's time to stretch your wings and really put those tools to work. Most modern computer applications are designed using a data-centered view of computing. This so-called object-oriented design (OOD) process is a powerful complement to top-down design for the development of reliable, cost-effective software systems. In this chapter, we will look at the basic principles of OOD and apply them in a couple case studies. 12.1 The Process of OOD The essence of design is describing a system in terms of magical black boxes and their interfaces. Each component provides a set of services through its interface. Other components are users or clients of the services. A client only needs to understand the interface of a service; the details of how that service is implemented are not important. In fact, the internal details may change radically and not affect the client at all. Similarly, the component providing the service does not have to consider how the service might be used. The black box just has to make sure that the service is faithfully delivered. This separation of concerns is what makes the design of complex systems possible. In top-down design, functions serve the role of our magical black boxes. A client program can use a function as long as it understands what the function does. The details of how the task is accomplished are encapsulated in the function definition. In object-oriented design, the black boxes are objects. The magic behind objects lies in class definitions. Once a suitable class definition has been written, we can completely ignore how the class works and just rely on the external interface'-,the methods. This is what allows you to draw circles in graphics windows without so much as a glance at the code in the graphics module. All the nitty-gritty details are encapsulated in the class definitions for GraphWin and Circle. If we can break a large problem into a set of cooperating classes, we drastically reduce the complexity that must be considered to understand any given part of the program. Each class stands on its own. Object- oriented design is the process of finding and defining a useful set of classes for a given problem. Like all design, it is part art and part science. There are many different approaches to OOD, each with its own special techniques, notations, gurus and textbooks. I can't pretend to teach you all about OOD in one short chapter. On the other hand, I'm not convinced that reading many thick volumes will help much either. The best way to learn about design is to do it. The more you design, the better you will get. Just to get you started, here are some intuitive guidelines for object-oriented design. 1. Look for object candidates. Your goal is to define a set of objects that will be helpful in solving the problem. Start with a careful consideration of the problem statement. Objects are usually described by nouns. You might underline all of the nouns in the problem statement and consider them one by one. Which of them will actually be represented in the program? Which of them have ('-interesting*)- behavior? Things that can be represented as primitive data types (numbers or strings) are probably not 201202 CHAPTER 12. OBJECT-ORIENTED DESIGN important candidates for objects. Things that seem to involve a grouping of related data items (e.g., coordinates of a point or personal data about an employee) probably are. 2. Identify instance variables. Once you have uncovered some possible objects, think about the informa- tion that each object will need to do its job. What kinds of values will the instance variables have? Some object attributes will have primitive values; others might themselves be complex types that sug- gest other useful objects/classes. Strive to find good $$+home$'' classes for all the data in your program. 3. Think about interfaces. When you have identified a potential object/class and some associated data, think about what operations would be required for objects of that class to be useful. You might start by considering the verbs in the problem statement. Verbs are used to describe actions-!$what must be done. List the methods that the class will require. Remember that all manipulation of the object's data should be done through the methods you provide. 4. Refine the nontrivial methods. Some methods will look like they can be accomplished with a couple lines of code. Other methods will require considerable work to develop an algorithm. Use top-down design and stepwise refinement to flesh out the details of the more difficult methods. As you go along, you may very well discover that some new interactions with other classes are needed, and this might force you to add new methods to other classes. Sometimes you may discover a need for a brand-new kind of object that calls for the definition of another class. 5. Design iteratively. As you work through the design, you will bounce back and forth between designing new classes and adding methods to existing classes. Work on whatever seems to be demanding your attention. No one designs a program top to bottom in a linear, systematic fashion. Make progress wherever it seems progress needs to be made. 6. Try out alternatives. Don't be afraid to scrap an approach that doesn't seem to be working or to follow an idea and see where it leads. Good design involves a lot of trial and error. When you look at the programs of others, you are seeing finished work, not the process they went through to get there. If a program is well designed, it probably is not the result of a first try. Fred Brooks , a legendary software engineer, coined the maxim: /#%Plan to throw one away.(#& Often you won't really know how a system should be built until you've already built it the wrong way. 7. Keep it simple. At each step in the design, try to find the simplest approach that will solve the problem at hand. Don't design in extra complexity until it is clear that a more complex approach is needed. The next sections will walk you through a couple case studies that illustrate aspects of OOD. Once you thoroughly understand these examples, you will be ready to tackle your own programs and refine your design skills. 12.2 Case Study: Racquetball Simulation For our first case study, let's return to the racquetball simulation from Chapter 9. You might want to go back and review the program that we developed the first time around using top-down design. The crux of the problem is to simulate multiple games of racquetball where the ability of the two op- ponents is represented by the probability that they win a point when they are serving. The inputs to the simulation are the probability for player A, the probability for player B, and the number of games to simu- late. The output is a nicely formatted summary of the results. In the original version of the program, we ended a game when one of the players reached a total of 15 points. This time around, let's also consider shutouts. If one player gets to 7 before the other player has scored a point, the game ends. Our simulation should keep track of both the number of wins for each player and the number of wins that are shutouts.12.2. CASE STUDY: RACQUETBALL SIMULATION 203 12.2.1 Candidate Objects and Methods Our first task is to find a set of objects that could be useful in solving this problem. We need to simulate a series of racquetball games between two players and record some statistics about the series of games. This short description already suggests one way of dividing up the work in the program. We need to do two basic things: simulate a game and keep track of some statistics. Let's tackle simulation of the game first. We can use an object to represent a single game of racquetball. A game will have to keep track of information about two players. When we create a new game, we will specify the skill levels of the players. This suggests a class, let's call it RBallGame, with a constructor that requires parameters for the probabilities of the two players. What does our program need to do with a game? Obviously, it needs to play it. Let's give our class a play method that simulates the game until it is over. We could create and play a racquetball game with two lines of code. theGame = RBallGame(probA, probB) theGame.play() To play lots of games, we just need to put a loop around this code. That's all we really need in RBallGame to write the main program. Let's turn our attention to collecting statistics about the games. Obviously, we will have to keep track of at least four counts in order to print a summary of our simulations: wins for A, wins for B, shutouts for A, and shutouts for B. We will also print out the number of games simulated, but this can be calculated by adding the wins for A and B. Here we have four related pieces of information. Rather than treating them independently, let's group them into a single object. This object will be an instance of a class called SimStats. A SimStats object will keep track of all the information about a series of games. We have already analyzed the four crucial pieces of information. Now we have to decide what operations will be useful. For starters, we need a constructor that initializes all of the counts to 0. We also need a way of updating the counts as each new game is simulated. Let's give our object an update method. The update of the statistics will be based on the outcome of a game. We will have to send some information to the statistics object so that the update can be done appropriately. An easy approach would be to just send the entire game and let update extract whatever information it needs. Finally, when all of the games have been simulated, we need to print out a report of the results. This suggests a printReport method that prints out a nice report of the accumulated statistics. We have now done enough design that we can actually write the main function for our program. Most of the details have been pushed off into the definition of our two classes. def main(): printIntro() probA, probB, n = getInputs() # Play the games stats = SimStats() for i in range(n): theGame = RBallGame(probA, probB) # create a new game theGame.play() # play it stats.update(theGame) # get info about completed game # Print the results stats.printReport() I have also used a couple helper functions to print an introduction and get the inputs. You should have no trouble writing these functions. Now we have to flesh out the details of our two classes. The SimStats class looks pretty easy/&'let's tackle that one first. 12.2.2 Implementing SimStats The constructor for SimStats just needs to initialize the four counts to 0. Here is an obvious approach:CHAPTER 12. OBJECT-ORIENTED DESIGN 204 class SimStats: def __init__(self): self.winsA = 0 self.winsB = 0 self.shutsA = 0 self.shutsB = 0 Now let's take a look at the update method. It takes a game as a normal parameter and must update the four counts accordingly. The heading of the method will look like this: def update(self, aGame): But how exactly do we know what to do? We need to know the final score of the game, but this information resides inside of aGame. Remember, we are not allowed to directly access the instance variables of aGame. We don't even know yet what those instance variables will be. Our analysis suggests the need for a new method in the RBallGame class. We need to extend the interface so that aGame has a way of reporting the final score. Let's call the new method getScores and have it return the score for player A and the score for player B. Now the algorithm for update is straightforward. def update(self, aGame): a, b = aGame.getScores() # A won the game if a > b: self.winsA = self.winsA + 1 if b == 0: self.shutsA = self.shutsA + 1 else: # B won the game self.winsB = self.winsB + 1 if a == 0: self.shutsB = self.shutsB + 1 We can complete the SimStats class by writing a method to print out the results. Our printReport method will generate a table that shows the wins, win percentage, shutouts and shutout percentage for each player. Here is a sample output: Summary of 500 games: wins (% total) shutouts (% wins) -------------------------------------------- Player A: 411 82.2% 60 14.6% Player B: 89 17.8% 7 7.9% It is easy to print out the headings for this table, but the formatting of the lines takes a little more care. We want to get the columns lined up nicely, and we must avoid division by zero in calculating the shutout percentage for a player who didn't get any wins. Let's write the basic method but procrastinate a bit and push off the details of formatting the line into another method, printLine. The printLine method will need the player label (A or B), number of wins and shutouts, and the total number of games (for calculation of percentages). def printReport(self): # Print a nicely formatted report n = self.winsA + self.winsB print "Summary of", n , "games:" print print " wins (% total) shutouts (% wins) " print "--------------------------------------------" self.printLine("A", self.winsA, self.shutsA, n) self.printLine("B", self.winsB, self.shutsB, n)12.2. CASE STUDY: RACQUETBALL SIMULATION 205 To finish out the class, we implement the printLine method. This method will make heavy use of string formatting. A good start is to define a template for the information that will appear in each line. def printLine(self, label, wins, shuts, n): template = "Player %s: %4d %5.1f%% %11d %s" if wins == 0: # Avoid division by zero! shutStr = "-----" else: shutStr = "%4.1f%%" % (float(shuts)/wins*100) print template % (label, wins, float(wins)/n*100, shuts, shutStr) Notice how the shutout percentage is handled. The main template includes it as a string, and the if statement takes care of formatting this piece to prevent division by zero. 12.2.3 Implementing RBallGame Now that we have wrapped up the SimStats class, we need to turn our attention to RBallGame. Summa- rizing what we have decided so far, this class needs a constructor that accepts two probabilities as parameters, a play method that plays the game, and a getScores method that reports the scores. What will a racquetball game need to know? To actually play the game, we have to remember the probability for each player, the score for each player, and which player is serving. If you think about this carefully, you will see that probability and score are properties related to particular players, while the server is a property of the game between the two players. That suggests that we might simply consider that a game needs to know who the players are and which is serving. The players themselves can be objects that know their probability and score. Thinking about the RBallGame class this way leads us to design some new objects. If the players are objects, then we will need another class to define their behavior. Let's name that class Player. A Player object will keep track of its probability and current score. When a Player is first created the probability will be supplied as a parameter, but the score will just start out at 0. We'll flesh out the design of Player class methods as we work on RBallGame. We are now in a position to define the constructor for RBallGame. The game will need instance variables for the two players and another variable to keep track of which player is serving. class RBallGame: def __init__(self, probA, probB): self.playerA = Player(probA) self.playerB = Player(probB) self.server = self.PlayerA # Player A always serves first Sometimes it helps to draw a picture to see the relationships among the objects that we are creating. Suppose we create an instance of RBallGame like this: theGame = RBallGame(.6,.5) Figure 12.1 shows an abstract picture of the objects created by this statement and their inter-relationships. OK, now that we can create an RBallGame, we need to figure out how to play it. Going back to the dicussion of racquetball from Chapter 9, we need an algorithm that continues to serve rallies and either award points or change the server as appropriate until the game is over. We can translate this loose algorithm almost directly into our object-based code. First, we need a loop that continues as long as the game is not over. Obviously, the decision of whether the game has ended or not can only be made by looking at the game object itself. Let's just assume that an appropriate isOver method can be written. The beginning of our play method can make use of this (yet-to-be-written) method. def play(self): while not self.isOver():CHAPTER 12. OBJECT-ORIENTED DESIGN 206 Player RBallGame playerA: prob: 0.6 score: 0 playerB: server: Player prob: 0.5 score: 0 Figure 12.1: Abstract view of RBallGame object. Inside of the loop, we need to have the serving player serve and, based on the result, decide what to do. This suggests that Player objects should have a method that performs a serve. After all, whether the serve is won or not depends on the probability that is stored inside of each player object. We'll just ask the server if the serve is won or lost. if self.server.winsServe(): Based on this result, we either award a point or change the server. To award a point, we need to change a player's score. This again requires the player do something, namely increment the score. Changing servers, on the other hand, is done at the game level, since this information is kept in the server instance variable of RBallGame. Putting it all together, here is our play method: def play(self): while not self.isOver(): if self.server.winsServe(): self.server.incScore() else: self.changeServer() As long as you remember that self is an RBallGame, this code should be clear. While the game is not over, if the server wins a serve, award a point to the server, otherwise change the server. Of course the price we pay for this simple algorithm is that we now have two new methods (isOver and changeServer) that need to be implemented in the RBallGame class and two more (winsServer and incScore) for the Player class. Before attacking the new methods of the RBallGame class, let's go back and finish up the other top-level method of the class, namely getScores. This one just returns the scores of the two players. Of course, we run into the same problem again. It is the player objects that actually know the scores, so we will need a method that asks a player to return its score. def getScore(self): return self.playerA.getScore(), self.playerB.getScore() This adds one more method to be implemented in the Player class. Make sure you put that on our list to complete later. To finish out the RBallGame class, we need to write the isOver and changeServer methods. Given what we have developed already and our previous version of this program, these methods are straightforward. I'll leave those as an exercise for you at the moment. If you're looking for my solutions, skip to the complete code at the end of this section.12.2. CASE STUDY: RACQUETBALL SIMULATION 207 12.2.4 Implementing Player In developing the RBallGame class, we discovered the need for a Player class that encapsulates the service probability and current score for a player. The Player class needs a suitable constructor and methods for winsServe, incScore and getScore. If you are getting the hang of this object-oriented approach, you should have no trouble coming up with a constructor. We just need to initialize the instance variables. The player's probability will be passed as a parameter, and the score starts at 0. def __init__(self, prob): # Create a player with this probability self.prob = prob self.score = 0 The other methods for our Player class are even simpler. To see if a player wins a serve, we compare the probability to a random number between 0 and 1. def winsServe(self): return random() <= self.prob To give a player a point, we simply add one to the score. def incScore(self): self.score = self.score + 1 The final method just returns the value of the score. def getScore(self): return self.score Initially, you may think that it's silly to create a class with a bunch of one- or two-line methods. Actually, it's quite common for a well-modularized, objected-oriented program to have lots of trivial methods. The point of design is to break a problem down into simpler pieces. If those pieces are so simple that their implementation is obvious, that gives us confidence that we must have gotten it right. 12.2.5 The Complete Program That pretty much wraps up our object-oriented version of the racquetball simulation. The complete program follows. You should read through it and make sure you understand exactly what each class does and how it does it. If you have questions about any parts, go back to the discussion above to figure it out. # objrball.py -- Simulation of a racquet game. # Illustrates design with objects. from random import random class Player: # A Player keeps track of service probability and score def __init__(self, prob): # Create a player with this probability self.prob = prob self.score = 0 def winsServe(self): # RETURNS a Boolean that is true with probability self.prob return random() <= self.probCHAPTER 12. OBJECT-ORIENTED DESIGN 208 def incScore(self): # Add a point to this player's score self.score = self.score + 1 def getScore(self): # RETURNS this player's current score return self.score class RBallGame: # A RBallGame represents a game in progress. A game has two players # and keeps track of which one is currently serving. def __init__(self, probA, probB): # Create a new game having players with the given probs. self.playerA = Player(probA) self.playerB = Player(probB) self.server = self.playerA # Player A always serves first def play(self): # Play the game to completion while not self.isOver(): if self.server.winsServe(): self.server.incScore() else: self.changeServer() def isOver(self): # RETURNS game is finished (i.e. one of the players has won). a,b = self.getScores() return a == 15 or b == 15 or \ (a == 7 and b == 0) or (b==7 and a == 0) def changeServer(self): # Switch which player is serving if self.server == self.playerA: self.server = self.playerB else: self.server = self.playerA def getScores(self): # RETURNS the current scores of player A and player B return self.playerA.getScore(), self.playerB.getScore() class SimStats: # SimStats handles accumulation of statistics across multiple # (completed) games. This version tracks the wins and shutouts for # each player. def __init__(self): # Create a new accumulator for a series of games self.winsA = 0 self.winsB = 0 self.shutsA = 0 self.shutsB = 012.2. CASE STUDY: RACQUETBALL SIMULATION 209 def update(self, aGame): # Determine the outcome of aGame and update statistics a, b = aGame.getScores() if a > b: # A won the game self.winsA = self.winsA + 1 if b == 0: self.shutsA = self.shutsA + 1 else: # B won the game self.winsB = self.winsB + 1 if a == 0: self.shutsB = self.shutsB + 1 def printReport(self): # Print a nicely formatted report n = self.winsA + self.winsB print "Summary of", n , "games:" print print " wins (% total) shutouts (% wins) " print "--------------------------------------------" self.printLine("A", self.winsA, self.shutsA, n) self.printLine("B", self.winsB, self.shutsB, n) def printLine(self, label, wins, shuts, n): template = "Player %s: %4d %5.1f%% %11d %s" if wins == 0: # Avoid division by zero! shutStr = "-----" else: shutStr = "%4.1f%%" % (float(shuts)/wins*100) print template % (label, wins, float(wins)/n*100, shuts, shutStr) def printIntro(): print "This program simulates games of racquetball between two" print /*-players called "A" and "B". The ability of each player is.(* print "indicated by a probability (a number between 0 and 1) that" print "the player wins the point when serving. Player A always" print "has the first serve.\n" def getInputs(): # Returns the three simulation parameters a = input("What is the prob. player A wins a serve? ") b = input("What is the prob. player B wins a serve? ") n = input("How many games to simulate? ") return a, b, n def main(): printIntro() probA, probB, n = getInputs() # Play the games stats = SimStats() for i in range(n): theGame = RBallGame(probA, probB) # create a new gameCHAPTER 12. OBJECT-ORIENTED DESIGN 210 theGame.play() stats.update(theGame) # play it # get info about completed game # Print the results stats.printReport() main() raw_input("\nPress to quit") 12.3 Case Study: Dice Poker Back in Chapter 10, I suggested that objects are particularly useful for the design of graphical user interfaces. I want to finish up this chapter by looking at a graphical application using some of the widgets that we developed in previous chapters. 12.3.1 Program Specification Our goal is to write a game program that allows a user to play video poker using dice. The program will display a hand consisting of five dice. The basic set of rules is as follows: The player starts with $100. Each round costs $10 to play. This amount is subtracted from the user's money at the start of the round. The player initially rolls a completely random hand (i.e., all five dice are rolled). The player gets two chances to enhance the hand by rerolling some or all of the dice. At the end of the hand, the player's money is updated according to the following payout schedule: Hand Two Pairs Three of a Kind Full House (A Pair and a Three of a Kind) Four of a Kind Straight (1"!(5 or 2&*!6) Five of a Kind Pay 5 8 12 15 20 30 Ultimately, we want this program to present a nice graphical interface. Our interaction will be through mouse clicks. The interface should have the following characteristics: The current score (amount of money) is constantly displayed. The program automatically terminates if the player goes broke. The player may choose to quit at appropriate points during play. The interface will present visual cues to indicate what is going on at any given moment and what the valid user responses are. 12.3.2 Identifying Candidate Objects Our first step is to analyze the program description and identify some objects that will be useful in attacking this problem. This is a game involving dice and money. Are either of these good candidates for objects? Both the money and an individual die can be simply represented as numbers. By themselves, they do not seem to be good object candidates. However, the game uses five dice, and this sounds like a collection. We will need to be able to roll all the dice or a selection of dice as well as analyze the collection to see what it scores. We can encapsulate the information about the dice in a Dice class. Here are a few obvious operations that this class will have to implement.12.3. CASE STUDY: DICE POKER 211 constructor Create the initial collection. rollAll Assign random values to each of the five dice. roll Assign a random value to some subset of the dice, while maintaining the current value of others. values Return the current values of the five dice. score Return the score for the dice. We can also think of the entire program as an object. Let's call the class PokerApp. A PokerApp object will keep track of the current amount of money, the dice, the number of rolls, etc. It will implement a run method that we use to get things started and also some helper methods that are used to implement run. We won't know exactly what methods are needed until we design the main algorithm. Up to this point, I have concentrated on the actual game that we are implementing. Another component to this program will be the user interface. One good way to break down the complexity of a more sophisticated program is to separate the user interface from the main guts of the program. This is often called the model- view approach. Our program implements some model (in this case, it models a poker game), and the interface is a view of the current state of the model. One way of separating out the interface is to encapsulate the decisions about the interface in a separate interface object. An advantage of this approach is that we can change the look and feel of the program simply by substituting a different interface object. For example, we might have a text-based version of a progam and a graphical version. Let's assume that our program will make use of an interface object, call it a PokerInterface. It's not clear yet exactly what behaviors we will need from this class, but as we refine the PokerApp class, we will need to get information from the user and also display information. These will correspond to methods implemented by the PokerInterface class. 12.3.3 Implementing the Model So far, we have a pretty good picture of what the Dice class will do and a starting point for implementing the PokerApp class. We could proceed by working on either of these classes. We won't really be able to try out the PokerApp class until we have dice, so let's start with the lower-level Dice class. Implementing Dice The Dice class implements a collection of dice, which are just changing numbers. The obvious representa- tion is to use a list of five ints. Our constructor needs to create a list and assign some initial values. class Dice: def __init__(self): self.dice = [0]*5 self.rollAll() This code first creates a list of five zeroes. These need to be set to some random values. Since we are going to implement a rollAll function anyway, calling it here saves duplicating that code. We need methods to roll selected dice and also to roll all of the dice. Since the latter is a special case of the former, let's turn our attention to the roll function, which rolls a subset. We can specify which dice to roll by passing a list of indexes. For example, roll([0,3,4]) would roll the dice in positions 0, 3 and 4 of the dice list. We just need a loop that goes through the parameter and generates a new random value for each position. def roll(self, which): for pos in which: self.dice[pos] = randrange(1,7) Next, we can use roll to implement rollAll as follows:CHAPTER 12. OBJECT-ORIENTED DESIGN 212 def rollAll(self): self.roll(range(5)) I used range(5) to generate a list of all the indexes. The values function is used to return the values of the dice so that they can be displayed. Another one-liner suffices. def values(self): return self.dice[:] Notice that I created a copy of the dice list by slicing it. That way, if a Dice client modifies the list that it gets back from values, it will not affect the original copy stored in the Dice object. This defensive programming prevents other parts of the code from accidently messing with our object. Finally, we come to the score method. This is the function that will determine the worth of the current dice. We need to examine the values and determine whether we have any of the patterns that lead to a payoff, namely Five of a Kind, Four of a Kind, Full House, Three of a Kind, Two Pairs, or Straight. Our function will need some way to indicate what the payoff is. Let's return a string labeling what the hand is and an int that gives the payoff amount. We can think of this function as a multi-way decision. We simply need to check for each possible hand. If we do so in a sensible order, we can guarantee giving the correct payout. For example, a full house also contains a three of a kind. We need to check for the full house before checking for three of a kind, since the full house is more valuable. One simple way of checking the hand is to generate a list of the counts of each value. That is, counts[i] will be the number of times that the value i occurs in dice. If the dice are: [3,2,5,2,3] then the count list would be [0,0,2,2,0,1,0]. Notice that counts[0] will always be zero, since dice values are in the range 1#%$6. Checking for various hands can then be done by looking for various values in counts. For example, if counts contains a 3 and a 2, the hand contains a triple and a pair, and hence, is a full house. Here's the code: def score(self): # Create the counts list counts = [0] * 7 for value in self.dice: counts[value] = counts[value] + 1 # score the hand if 5 in counts: return "Five of a Kind", 30 elif 4 in counts: return "Four of a Kind", 15 elif (3 in counts) and (2 in counts): return "Full House", 12 elif 3 in counts: return "Three of a Kind", 8 elif not (2 in counts) and (counts[1]==0 or counts[6] == 0): return "Straight", 20 elif counts.count(2) == 2: return "Two Pairs", 5 else: return "Garbage", 0 The only tricky part is the testing for straights. Since we have already checked for 5, 4 and 3 of a kind, checking that there are no pairs not 2 in counts guarantees that the dice show five distinct values. If there is no 6, then the values must be 1%!"5; likewise, no 1 means the values must be 2#-"6. At this point, we could try out the Dice class to make sure that it is working correctly. Here is a short interaction showing some of what the class can do:12.3. CASE STUDY: DICE POKER 213 >>> from dice import Dice >>> d = Dice() >>> d.values() [6, 3, 3, 6, 5] >>> d.score() (-,%Two Pairs),., 5) >>> d.roll([4]) >>> d.values() [6, 3, 3, 6, 4] >>> d.roll([4]) >>> d.values() [6, 3, 3, 6, 3] >>> d.score() (/+$Full House&#&, 12) We would want to be sure that each kind of hand scores properly. Implementing PokerApp Now we are ready to turn our attention to the task of actually implementing the poker game. We can use top-down design to flesh out the details and also suggest what methods will have to be implemented in the PokerInterface class. Initially, we know that the PokerApp will need to keep track of the dice, the amount of money, and some user interface. Let's initialize these values in the constructor. class PokerApp: def __init__(self): self.dice = Dice() self.money = 100 self.interface = PokerInterface() To run the program, we will create an instance of this class and call its run method. Basically, the program will loop, allowing the user to continue playing hands until he or she is either out of money or chooses to quit. Since it costs $10 to play a hand, we can continue as long as self.money >= 10. Determining whether the user actually wants to play another hand must come from the user interface. Here is one way we might code the run method: def run(self): while self.money >= 10 and self.interface.wantToPlay(): self.playRound() self.interface.close() Notice the call to interface.close at the bottom. This will allow us to do any necessary cleaning up such as printing a final message for the user or closing a graphics window. Most of the work of the program has now been pushed into the playRound method. Let's continue the top-down process by focusing our attention here. Each round will consist of a series of rolls. Based on these rolls, the program will have to adjust the player's score. def playRound(self): self.money = self.money - 10 self.interface.setMoney(self.money) self.doRolls() result, score = self.dice.score() self.interface.showResult(result, score) self.money = self.money + score self.interface.setMoney(self.money)CHAPTER 12. OBJECT-ORIENTED DESIGN 214 This code only really handles the scoring aspect of a round. Anytime new information must be shown to the user, a suitable method from interface is invoked. The $10 fee to play a round is first deducted and the interface is updated with the new amount of money remaining. The program then processes a series of rolls (doRolls), shows the user the result, and updates the amount of money accordingly. Finally, we are down to the nitty-gritty details of implementing the dice rolling process. Initially, all of the dice will be rolled. Then we need a loop that continues rolling user-selected dice until either the user chooses to quit rolling or the limit of three rolls is reached. Let's use a local variable rolls to keep track of how many times the dice have been rolled. Obviously, displaying the dice and getting the the list of dice to roll must come from interaction with the user through interface. def doRolls(self): self.dice.rollAll() roll = 1 self.interface.setDice(self.dice.values()) toRoll = self.interface.chooseDice() while roll < 3 and toRoll != []: self.dice.roll(toRoll) roll = roll + 1 self.interface.setDice(self.dice.values()) if roll < 3: toRoll = self.interface.chooseDice() At this point, we have completed the basic functions of our interactive poker program. That is, we have a model of the process for playing poker. We can't really test out this program yet, however, because we don't have a user interface. 12.3.4 A Text-Based UI In designing PokerApp we have also developed a specification for a generic PokerInterface class. Our interface must support the methods for displaying information: setMoney, setDice, and showResult. It must also have methods that allow for input from the user: wantToPlay, and chooseDice. These methods can be implemented in many different ways, producing programs that look quite different even though the underlying model, PokerApp, remains the same. Usually, graphical interfaces are much more complicated to design and build than text-based ones. If we are in a hurry to get our application running, we might first try building a simple text-based interface. We can use this for testing and debugging of the model without all the extra complication of a full-blown GUI. First, let's tweak our PokerApp class a bit so that the user interface is supplied as a parameter to the constructor. class PokerApp: def __init__(self, interface): self.dice = Dice() self.money = 100 self.interface = interface Then we can easily create versions of the poker program using different interfaces. Now let's consider a bare-bones interface to test out the poker program. Our text-based version will not present a finished application, but rather, give us a minimalist interface solely to get the program running. Each of the necessary methods can be given a trivial implementation. Here is a complete TextInterface class using this approach: # file: textpoker.py class TextInterface: def __init__(self): print "Welcome to video poker."12.3. CASE STUDY: DICE POKER 215 def setMoney(self, amt): print "You currently have $%d." % (amt) def setDice(self, values): print "Dice:", values def wantToPlay(self): ans = raw_input("Do you wish to try your luck? ") return ans[0] in "yY" def close(self): print "\nThanks for playing!" def showResult(self, msg, score): print "%s. You win $%d." % (msg, score) def chooseDice(self): return input("Enter list of which to change ([] to stop) ") Using this interface, we can test out our PokerApp program to see if we have implemented a correct model. Here is a complete program making use of the modules that we have developed: # textpoker.py -- video dice poker using a text-based interface. from pokerapp import PokerApp from textinter import TextInterface inter = TextInterface() app = PokerApp(inter) app.run() Basically, all this program does is create a text-based interface and then build a PokerApp using this inter- face and start it running. Running this program, we get a rough but useable interaction. Welcome to video poker. Do you wish to try your luck? You currently have $90. Dice: [6, 4, 4, 2, 4] Enter list of which to change Dice: [1, 4, 4, 2, 2] Enter list of which to change Dice: [2, 4, 4, 2, 2] Full House. You win $12. You currently have $102. Do you wish to try your luck? You currently have $92. Dice: [5, 6, 4, 4, 5] Enter list of which to change Dice: [5, 5, 4, 4, 5] Enter list of which to change Full House. You win $12. You currently have $104. Do you wish to try your luck? You currently have $94. y ([] to stop) [0,4] ([] to stop) [0] y ([] to stop) [1] ([] to stop) [] y216 CHAPTER 12. OBJECT-ORIENTED DESIGN Dice: [3, 2, 1, 1, 1] Enter list of which to change ([] to stop) [0,1] Dice: [5, 6, 1, 1, 1] Enter list of which to change ([] to stop) [0,1] Dice: [1, 5, 1, 1, 1] Four of a Kind. You win $15. You currently have $109. Do you wish to try your luck? n Thanks for playing! You can see how this interface provides just enough so that we can test out the model. In fact, we've got a game that's already quite a bit of fun to play! 12.3.5 Developing a GUI Now that we have a working program, let's turn our attention to a nicer graphical interface. Our first step must be to decide exactly how we want our interface to look and function. The interface will have to support the various methods found in the text-based version and will also probably have some additional helper methods. Designing the Interaction Let's start with the basic methods that must be supported and decide exactly how interaction with the user will occur. Clearly, in a graphical interface, the faces of the dice and the current score should be continuously displayed. The setDice and setMoney methods will be used to change those displays. That leaves one output method, showResult, that we need to accommodate. One common way to handle this sort of tranisent information is with a message at the bottom of the window. This is sometimes called a status bar. To get information from the user, we will make use of buttons. In wantToPlay, the user will have to decide between either rolling the dice or quitting. We could include *(-Roll Dice+)% and #.'Quit*(+ buttons for this choice. That leaves us with figuring out how the user should choose dice. To implement chooseDice, we could provide a button for each die and have the user click the buttons for the dice they want to roll. When the user is done choosing the dice, they could click the %$+Roll Dice&&! button again to roll the selected dice. Elaborating on this idea, it would be nice if we allowed the user to change his or her mind while selecting the dice. Perhaps clicking the button of a currently selected die would cause it to become unselected. The clicking of the button will serve as a sort of toggle that selects/unselects a particular die. The user commits to a certain selection by clicking on '*,Roll Dice.##/ Our vision for chooseDice suggests a couple of tweaks for the interface. First, we should have some way of showing the user which dice are currently selected. There are lots of ways we could do this. One simple approach would be to change the color of the die. Let's ""+gray out,.% the pips on the dice selected for rolling. Second, we need a good way for the user to indicate that they wish to stop rolling. That is, they would like the dice scored just as they stand. We could handle this by having them click the "/,Roll Dice'*, button when no dice are selected, hence asking the program to roll no dice. Another approach would be to provide a separate button to click that causes the dice to be scored. The latter approach seems a bit more intuitive/informative. Let's add a +$"Score"%! button to the interface. Now we have a basic idea of how the interface will function. We still need to figure out how it will look. What is the exact layout of the widgets? Figure 12.2 is a sample of how the interface might look. I'm sure those of you with a more artisitic eye can come up with a more pleasing interface, but we'll use this one as our working design. Managing the Widgets The graphical interface that we are developing makes use of buttons and dice. Our intent is to resue the Button and DieView classes for these widgets that were developed in previous chapters. The Button class can be used as is, and since we have quite a number of buttons to manage, we can use a list of Buttons, similar to the approach we used in the calculator program from Chapter 11.12.3. CASE STUDY: DICE POKER 217 Figure 12.2: GUI interface for video dice poker. Unlike the buttons in the calculator program, the buttons of our poker interface will not be active all of the time. For example, the dice buttons will only be active when the user is actually in the process of choosing dice. When user input is required, the valid buttons for that interaction will be set active and the others will be inactive. To implement this behavior, we can add a helper method called choose to the PokerInterface class. The choose method takes a list of button labels as a parameter, activates them, and then waits for the user to click one of them. The return value of the function is the label of the button that was clicked. We can call the choose method whenever we need input from the user. For example, if we are waiting for the user to choose either the %)$Roll Dice'.! or '&*Quit+/$ button, we would use a sequence of code like this: choice = self.choose(["Roll Dice", "Quit"]) if choice == "Roll Dice": ... Assuming the buttons are stored in an instance variable called buttons, here is one possible implemen- tation of choose: def choose(self, choices): buttons = self.buttons # activate choice buttons, deactivate others for b in buttons: if b.getLabel() in choices: b.activate() else: b.deactivate() # get mouse clicks until an active button is clicked while 1: p = self.win.getMouse() for b in buttons:CHAPTER 12. OBJECT-ORIENTED DESIGN 218 if b.clicked(p): return b.getLabel() # function exit here. The other widgets in our interface will be our DieView that we developed in the last two chapters. Basically, we will use the same class as before, but we need to add just a bit of new functionality. As discussed above, we want to change the color of a die to indicate whether it is selected for rerolling. You might want to go back and review the DieView class. Remember, the class contructor draws a square and seven circles to represent the positions where the pips of various values will appear. The setValue method turns on the appropriate pips to display a given value. To refresh your memory a bit, here is the setValue method as we left it: def setValue(self, value): # Turn all the pips off for pip in self.pips: pip.setFill(self.background) # Turn the appropriate pips back on for i in self.onTable[value]: self.pips[i].setFill(self.foreground) We need to modify the DieView class by adding a setColor method. This method will be used to change the color that is used for drawing the pips. As you can see in the code for setValue, the color of the pips is determined by the value of the instance variable foreground. Of course, changing the value of foregroud will not actually change the appearance of the die until it is redrawn using the new color. The algorithm for setColor seems straightforward. We need two steps: change foreground to the new color redraw the current value of the die Unfortunately, the second step presents a slight snag. We already have code that draws a value, namely setValue. But setValue requires us to send the value as a parameter, and the current version of DieView does not store this value anywhere. Once the proper pips have been turned on, the actual value is discarded. In order to implement setColor, we need to tweak setValue so that it remembers the current value. Then setColor can redraw the die using its current value. The change to setValue is easy; we just need to add a single line. self.value = value This line stores the value parameter in an instance variable called value. With the modified version of setValue, implementing setColor is a breeze. def setColor(self, color): self.foreground = color self.setValue(self.value) Notice how the last line simply calls setValue to (re)draw the die, passing along the value that was saved from the last time setValue was called. Creating the Interface Now that we have our widgets under control, we are ready to actually implement our GUI poker interface. The constructor will create all of our widgets, setting up the interface for later interactions. class GraphicsInterface: def __init__(self): self.win = GraphWin("Dice Poker", 600, 400) self.win.setBackground("green3")12.3. CASE STUDY: DICE POKER 219 banner = Text(Point(300,30), "Python Poker Parlor") banner.setSize(24) banner.setFill("yellow2") banner.setStyle("bold") banner.draw(self.win) self.msg = Text(Point(300,380), "Welcome to the Dice Table") self.msg.setSize(18) self.msg.draw(self.win) self.createDice(Point(300,100), 75) self.buttons = [] self.addDiceButtons(Point(300,170), 75, 30) b = Button(self.win, Point(300, 230), 400, 40, "Roll Dice") self.buttons.append(b) b = Button(self.win, Point(300, 280), 150, 40, "Score") self.buttons.append(b) b = Button(self.win, Point(570,375), 40, 30, "Quit") self.buttons.append(b) self.money = Text(Point(300,325), "$100") self.money.setSize(18) self.money.draw(self.win) You should compare this code to Figure 12.2 to make sure you understand how the elements of the interface are created and positioned. I hope you noticed that I pushed the creation of the dice and their associated buttons into a couple of helper methods. Here are the necessary definitions: def createDice(self, center, size): center.move(-3*size,0) self.dice = [] for i in range(5): view = DieView(self.win, center, size) self.dice.append(view) center.move(1.5*size,0) def addDiceButtons(self, center, width, height): center.move(-3*width, 0) for i in range(1,6): label = "Die %d" % (i) b = Button(self.win, center, width, height, label) self.buttons.append(b) center.move(1.5*width, 0) These two methods are similar in that they employ a loop to draw five similar widgets. In both cases, a Point variable, center, is used to calculate the correct position of the next widget. Implementing the Interaction You might be a little scared at this point that the constructor for our GUI interface was so complex. Even simple graphical interfaces involve many independent components. Getting them all set up and initialized is often the most tedious part of coding the interface. Now that we have that part out of the way, actually writing the code that handles the interaction will not be too hard, provided we attack it one piece at a time. Let's start with the simple output methods setMoney and showResult. These two methods display some text in our interface window. Since our constructor took care of creating and positioning the relevant Text objects, all our methods have to do is call the setText methods for the appropriate objects. def setMoney(self, amt):220 CHAPTER 12. OBJECT-ORIENTED DESIGN self.money.setText("$%d" % (amt)) def showResult(self, msg, score): if score > 0: text = "%s! You win $%d" % (msg, score) else: text = "You rolled %s" % (msg) self.msg.setText(text) In a similar spirit, the output method setDice must make a call to the setValue method of the approriate DieView objects in dice. We can do this with a for loop. def setDice(self, values): for i in range(5): self.dice[i].setValue(values[i]) Take a good look at the line in the loop body. It sets the ith die to show the ith value. As you can see, once the interface has been constructed, making it functional is not overly difficult. Our output methods are completed with just a few lines of code. The input methods are only slightly more complicated. The wantToPlay method will wait for the user to click either *!&Roll Dice,-+ or !&#Quit.$$$ We can use our choose helper method to do this. def wantToPlay(self): ans = self.choose(["Roll Dice", "Quit"]) self.msg.setText("") return ans == "Roll Dice" After waiting for the user to click an appropriate button, this method then clears out any message, such as the previous results, by setting the msg text to the empty string. The method then returns a Boolean value by examining the label returned by choose. That brings us to the chooseDice method. Here we must implement a more extensive user interaction. The chooseDice method returns a list of the indexes of the dice that the user wishes to roll. In our GUI, the user will choose dice by clicking on corresponding buttons. We need to maintain a list of which dice have been chosen. Each time a die button is clicked, that die is either chosen (its index is appended to the list) or unchosen (its index is removed from the list). In addition, the color of the corresponding DieView reflects the status of the die. The interaction ends when the user clicks either the roll button or the score button. If the roll button is clicked, the method returns the list of currently chosen indexes. If the score button is clicked, the function returns an empty list to signal that the player is done rolling. Here is one way to implement the choosing of dice. The comments in this code explain the algorithm: def chooseDice(self): # choices is a list of the indexes of the selected dice choices = [] # No dice chosen yet while 1: # wait for user to click a valid button b = self.choose(["Die 1", "Die 2", "Die 3", "Die 4", "Die 5", "Roll Dice", "Score"]) if b[0] == "D": # User clicked a die button i = eval(b[4]) - 1 # Translate label to die index if i in choices: # Currently selected, unselect it choices.remove(i) self.dice[i].setColor("black") else: # Currently unselected, select it choices.append(i)12.4. OO CONCEPTS 221 self.dice[i].setColor("gray") else: # User clicked Roll or Score for d in self.dice: # Revert appearance of all dice d.setColor("black") if b == "Score": # Score clicked, ignore choices return [] elif choices != []: # Don't accept Roll unless some return choices # dice are actually selected That about wraps up our program. The only missing piece of our interface class is the close method. To close up the graphical version, we just need to close the graphics window. def close(self): self.win.close() Finally, we need a few lines to actually get our graphical poker playing program started. This code is exactly like the start code for the textual version, except that we use a GraphicsInterface in place of the TextInterface. inter = GraphicsInterface() app = PokerApp(inter) app.run() We now have a complete, useable video dice poker game. Of course, our game is lacking a lot of bells and whistles such as printing a nice introduction, providing help with the rules, and keeping track of high scores. I have tried to keep this example relatively simple, while still illustrating important issues in the design of GUIs using objects. Improvements are left as exercises for you. Have fun with them! 12.4 OO Concepts My goal for the racquetball and video poker case studies was to give you a taste for what OOD is all about. Actually, what you've seen is only a distillation of the design process for these two programs. Basically, I have walked you through the algorithms and rationale for two completed designs. I did not document every single decision, false start and detour along the way. Doing so would have at least tripled the size of this (already long) chapter. You will learn best by making your own decisions and discovering your own mistakes, not by reading about mine. Still, these smallish examples illustrate much of the power and allure of the object-oriented approach. Hopefully you can see why OO techniques are becoming standard practice in software development. The bottom-line is that the OO approach helps us to produce complex software that is more reliable and cost- effective. However, I still have not defined exactly what counts as objected-oriented development. Most OO gurus talk about three features that together make development truly object-oriented: encapsu- lation, polymorphism and inheritance. I don't want to belabor these concepts too much, but your introduction to object-oriented design and programming would not be complete without at least some understanding of what is meant by these terms. 12.4.1 Encapsulation I have already mentioned the term encapsulation in previous discussion of objects. As you know, objects know stuff and do stuff. They combine data and operations. This process of packaging some data along with the set of operations that can be performed on the data is called encapsulation. Encapsulation is one of the major attractions of using objects. It provides a convenient way to compose complex problems that corresponds to our intuitive view of how the world works. We naturally think of the world around us as consisiting of interacting objects. Each object has its own identity, and knowing what kind of object it is allows us to understand its nature and capabilities. I look out my window and I see houses, cars and trees, not a swarming mass of countless molecules or atoms.222 CHAPTER 12. OBJECT-ORIENTED DESIGN From a design standpoint, encapsulation also provides a critical service of separating the concerns of //(what!(, vs. !$-how.+++ The actual implementation of an object is independent of its use. The implementation can change, but as long as the interface is preserved, other components that rely on the object will not break. Encapsulation allows us to isolate major design decisions, especially ones that are subject to change. Another advantage of encapsulation is that it supports code reuse. It allows us to package up general components that can be used from one program to the next. The DieView class and Button classes are good examples of reuseable components. Encapsulation is probably the chief benefit of using objects, but alone, it only makes a system object- based. To be truly objected-oriented, the approach must also have the characteristics of polymorphism and inheritance. 12.4.2 Polymorphism Literally, the word polymorphism means +)(many forms.,$( When used in object-oriented literature, this refers to the fact that what an object does in response to a message (a method call) depends on the type or class of the object. Our poker program illustrated one aspect of polymorphism. The PokerApp class was used both with a TextInterface and a GraphicsInterface. There were two different forms of interface, and the PokerApp class could function quite well with either. When the PokerApp called the showDice method, for example, the TextInterface showed the dice one way and the GraphicsInterface did it an- other. In our poker example, we used either the text interface or the graphics interface. The remarkable thing about polymorphism, however, is that a given line in a program may invoke a completely different method from one moment to the next. As a simple example, suppose you had a list of graphics objects to draw on the screen. The list might contain a mixture of Circle, Rectangle, Polygon, etc. You could draw all the items in a list with this simple code: for obj in objects: obj.draw(win) Now ask yourself, what operation does this loop actually execute? When obj is a circle, it executes the draw method from the circle class. When obj is a rectangle, it is the draw method from the rectangle class, etc. Polymorphism gives object-oriented systems the flexibility for each object to perform an action just the way that it should be performed for that object. Before object orientation, this kind of flexibility was much harder to achieve. 12.4.3 Inheritance The third important property for object-oriented approaches, inheritance, is one that we have not yet used. The idea behind inheritance is that a new class can be defined to borrow behavior from another class. The new class (the one doing the borrowing) is called a subclass, and the existing class (the one being borrowed from) is its superclass. For example, if we are building a system to keep track of employees, we might have a general class Employee that contains the general information that is common to all employees. One example attribute would be a homeAddress method that returns the home address of an employee. Within the class of all employees, we might distinguish between SalariedEmployee and HourlyEmployee. We could make these subclasses of Employee, so they would share methods like homeAddress. However, each subclass would have its own monthlyPay function, since pay is computed differently for these different classes of employees. Inheritance provides two benefits. One is that we can structure the classes of a system to avoid duplication of operations. We don't have to write a separate homeAddress method for the HourlyEmployee and SalariedEmployee classes. A closely related benefit is that new classes can often be based on existing classes, promoting code reuse.12.5. EXERCISES 223 We could have used inheritance to build our poker program. When we first wrote the DieView class, it did not provide a way of changing the appearance of the die. We solved this problem by modifying the original class definition. An alternative would have been to leave the original class unchanged and create a new subclass ColorDieView. A ColorDieView is just like a DieView except that it contains an additional method that allows us to change its color. Here is how it would look in Python: class ColorDieView(DieView): def setValue(self, value): self.value = value DieView.setValue(self, value) def setColor(self, color): self.foreground = color self.setValue(self.value) The first line of this definition says that we are defining a new class ColorDieView that is based on (i.e., a subclass of) DieView. Inside the new class, we define two methods. The second method, setColor, adds the new operation. Of course, in order to make setColor work, we also need to modify the setValue operation slightly. The setValue method in ColorDieView redefines or overrides the definition of setValue that was provided in the DieView class. The setValue method in the new class first stores the value and then relies on the setValue method of the superclass DieView to actually draw the pips. Notice especially how the call to the method from the superclass is made. The normal approach self.setValue(value) would re- fer to the setValue method of the ColorDieView class, since self is an instance of ColorDieView. In order to call the original setValue method from the superclass, it is necessary to put the class name where the object would normally go. DieView.setValue(self,value) The actual object to which the method is applied is then sent as the first parameter. 12.5 Exercises 1. In your own words, describe the process of OOD. 2. In your own words, define encapsulation, polymorphism and inheritance. 3. Add bells and whistles to the Poker Dice game. 4. Redo any of the design problems from Chapter 9 using OO techniques. 5. Find the rules to an interesting dice game and write an interactive program to play it. Some examples are Craps, Yacht, Greed and Skunk. 6. Write a program that deals four bridge hands, counts how many points they have and gives opening bids. 7. Find a simple card game that you like and implement an interactive program to play that game. Some possibilities are War, Blackjack, various solitaire games, and Crazy Eights. 8. Write an interactive program for a board game. Some examples are Othello(Reversi), Connect Four, Battleship, Sorry!, and Parcheesi.224 CHAPTER 12. OBJECT-ORIENTED DESIGNChapter 13 Algorithm Analysis and Design If you have worked your way through to this point in the book, you are well on the way to becoming a programmer. Way back in Chapter 1, I discussed the relationship between programming and the study of computer science. Now that you have some programming skills, you are ready to start considering the broader issues in the field. Here we will take up one of the central issues, namely the design and analysis of algorithms. 13.1 Searching Let's begin by considering a very common and well-studied programming problem: search. Search is the process of looking for a particular value in a collection. For example, a program that maintains the member- ship list for a club might need to look up the information about a particular member. This involves some form of search process. 13.1.1 A Simple Searching Problem To make the discussion of searching algorithms as simple as possible, let's boil the problem down to its essential essence. Here is the specification of a simple searching function: def search(x, # nums is # RETURNS # x is nums): a list of numbers and x is a number the position in the list where x occurs or -1 if not in the list. Here are a couple interactive examples that illustrate its behavior: >>> search(4, [3, 1, 4, 2, 5]) 2 >>> search(7, [3, 1, 4, 2, 5]) -1 In the first example, the function returns the index where 4 appears in the list. In the second example, the return value -1 indicates that 7 is not in the list. You may recall from our discussion of list operations that Python actually provides a number of built-in search-related methods. For example, we can test to see if a value appears in a sequence using in. if x in nums: # do something If we want to know the position of x in a list, the index method fills the bill nicely. 225CHAPTER 13. ALGORITHM ANALYSIS AND DESIGN 226 >>> nums = [3,1,4,2,5] >>> nums.index(4) 2 In fact, the only difference between our search function and index is that the latter raises an exception if the target value does not appear in the list. We could implement the search function using index by simply catching the exception and returning -1 for that case. def search(x, nums): try: return nums.index(x) except: return -1 This approach begs the question, however. The real issue is how does Python actually search the list? What is the algorithm? 13.1.2 Strategy 1: Linear Search Let's try our hand at developing a search algorithm using a simple !./be the computer-*$ strategy. Suppose that I gave you a page full of numbers in no particular order and asked whether the number 13 is in the list. How would you solve this problem? If you are like most people, you would simply scan down the list comparing each value to 13. When you see 13 in the list, you quit and tell me that you found it. If you get to the very end of the list without seeing 13, then you tell me it's not there. This strategy is called a linear search. You are searching through the list of items one by one until the target value is found. This algorithm translates directly into simple code. def search(x, nums): for i in range(len(nums)): if nums[i] == x: # item found, return the index value return i return -1 # loop finished, item was not in list This algorithm was not hard to develop, and it will work very nicely for modest-sized lists. For an unordered list, this algorithm is as good as any. The Python in and index operators both implement linear searching algorithms. If we have a very large collection of data, we might want to organize it in some way so that we don't have to look at every single item to determine where, or if, a particular value appears in the list. Suppose that the list is stored in sorted order (lowest to highest). As soon as we encounter a value that is greater than the target value, we can quit the linear search without looking at the rest of the list. On average, that saves us about half of the work. But, if the list is sorted, we can do even better than this. 13.1.3 Strategy 2: Binary Search When a list is ordered, there is a much better searching strategy, one that you probably already know. Have you ever played the number guessing game? I pick a number between 1 and 100, and you try to guess what it is. Each time you guess, I will tell you if your guess is correct, too high, or too low. What is your stategy? If you play this game with a very young child, they might well adopt a strategy of simply guessing numbers at random. An older child might employ a systematic approach corresponding to linear search, guessing 1 (#* 2 *&- 3 .-$ 4 until the mystery value is found. Of course, virtually any adult will first guess 50. If told that the number is higher, then the range of possible values is 50--$100. The next logical guess is 75. Each time we guess the middle of the remaining range to try to narrow down the possible range. This strategy is called a binary search. Binary means two, and at each step, we are dividing the possible range into two parts. We can employ a binary search strategy to look through a sorted list. The basic idea is that we use two variables to keep track of the endpoints of the range in the list where the item could be. Initially, the target13.1. SEARCHING 227 could be anywhere in the list, so we start with variables low and high set to the first and last positions of the list, respectively. The heart of the algorithm is a loop that looks at the item in the middle of the remaining range to compare it to x. If x is smaller than the middle item, then we move top, so that the search is narrowed to the lower half. If x is larger, then we move low, and the search is narrowed to the upper half. The loop terminates when x is found or there are no longer any more places to look (i.e., low > high). Here is the code. def search(x, nums): low = 0 high = len(nums) - 1 while low <= high: mid = (low + high) / 2 item = nums[mid] if x == item : return mid elif x < item: high = mid - 1 else: low = mid + 1 return -1 # There is still a range to search # position of middle item # Found it! Return the index # x is in lower half of range # move top marker down # x is in upper half # move bottom marker up # no range left to search, x is not there This algorithm is quite a bit more sophisticated than the simple linear search. You might want to trace through a couple of example searches to convince yourself that it actually works. 13.1.4 Comparing Algorithms So far, we have developed two solutions to our simple searching problem. Which one is better? Well, that depends on what exactly we mean by better. The linear search algorithm is much easier to understand and implement. On the other hand, we expect that the binary seach is more efficient, because it doesn't have to look at every value in the list. Intuitively, then, we might expect the linear search to be a better choice for small lists and binary search a better choice for larger lists. How could we actually confirm such intuitions? One approach would be to do an empirical test. We could simply code up both algorithms and try them out on various sized lists to see how long the search takes. These algorithms are both quite short, so it would not be difficult to run a few experiments. When I tested the algorithms on my particular computer (a somewhat dated laptop), linear search was faster for lists of length 10 or less, and there was no significant difference in the range of length 10!&'1000. After that, binary search was a clear winner. For a list of a million elements, linear search averaged 2.5 seconds to find a random value, whereas binary search averaged only 0.0003 seconds. The empirical analysis has confirmed our intuition, but these are results from one particular machine under specific circumstances (amount of memory, processor speed, current load, etc.). How can we be sure that the results will always be the same? Another approach is to analyze our algorithms abstractly to see how efficient they are. Other factors being equal, we expect the algorithm with the fewest number of %!)steps!$# to be the more efficient. But how do we count the number of steps? For example, the number of times that either algorithm goes through its main loop will depend on the particular inputs. We have already guessed that the advantage of binary search increases as the size of the list increases. Computer scientists attack these problems by analyzing the number of steps that an algorithm will take relative to the size or difficulty of the specific problem instance being solved. For searching, the difficulty is determined by the size of the collection. Obviously, it takes more steps to find a number in a collection of a million than it does in a collection of ten. The pertinent question is how many steps are needed to find a value in a list of size n. We are particularly interested in what happens as n gets very large. Let's consider the linear search first. If we have a list of ten items, the most work our algorithm might have to do is to look at each item in turn. The loop will iterate at most ten times. Suppose the list is twice as big. Then we might have to look at twice as many items. If the list is three times as large, it will take threeCHAPTER 13. ALGORITHM ANALYSIS AND DESIGN 228 times as long, etc. In general, the amount of time required is linearly related to the size of the list n. This is what computer scientists call a linear time algorithm. Now you really know why it's called a linear search. What about the binary search? Let's start by considering a concrete example. Suppose the list contains sixteen items. Each time through the loop, the remaining range is cut in half. After one pass, there are eight items left to consider. The next time through there will be four, then two, and finally one. How many times will the loop execute? It depends on how many times we can halve the range before running out of data. This table might help you to sort things out: List size 1 2 4 8 16 Halvings 0 1 2 3 4 Can you see the pattern here? Each extra iteration of the loop doubles the size of the list. If the binary search loops i times, it can find a single value in a list of size 2 i . Each time through the loop, it looks at one value (the middle) in the list. To see how many items are examined in a list of size n, we need to solve this relationship: n 2 i for i. In this formula, i is just an exponent with a base of 2. Using the appropriate logarithm gives us this relationship: i log 2 n. If you are not entirely comfortable with logarithms, just remember that this value is the number of times that a collection of size n can be cut in half. OK, so what does this bit of math tell us? Binary search is an example of a log time algorithm. The amount of time it takes to solve a given problem grows as the log of the problem size. In the case of binary search, each additional iteration doubles the size of the problem that we can solve. You might not appreciate just how efficient binary search really is. Let me try to put it in perspective. Suppose you have a New York City phone book with, say, twelve million names listed in alphabetical order. You walk up to a typical New Yorker on the street and make the following proposition (assuming their number is listed): I'm going to try guessing your name. Each time I guess a name, you tell me if your name comes alphabetically before or after the name I guess. How many guesses will you need? Our analysis above shows the answer to this question is log 2 12 //& 000 .'+ 000. If you don't have a calculator handy, here is a quick way to estimate the result. 2 10 1024 or roughly 1000, and 1000x1000 1 .$- 000 "$/ 000. That means that 2 10 x2 10 2 20 1 #%, 000 (*) 000. That is, 2 20 is approximately one million. So, searching a million items requires only 20 guesses. Continuting on, we need 21 guesses for two million, 22 for four million, 23 for eight million, and 24 guesses to search among sixteen million names. We can figure out the name of a total stranger in New York City using only 24 guesses! By comparison, a linear search would require (on average) 6 million guesses. Binary search is a phenomenally good algorithm! I said earlier that Python uses a linear search algorithm to implement its built-in searching methods. If a binary search is so much better, why doesn't Python use it? The reason is that the binary search is less general; in order to work, the list must be in order. If you want to use binary search on an unordered list, the first thing you have to do is put it in order or sort it. This is another well-studied problem in computer science, and one that we should look at. Before we turn to sorting, however, we need to generalize the algorithm design technique that we used to develop the binary search. 13.2 Recursive Problem-Solving Remember the basic idea behind the binary search algorithm was to sucessively divide the problem in half. This is sometimes referred to as a #..divide and conquer&'$ approach to algorithm design, and it often leads to very efficient algorithms. One interesting aspect of divide and conquer alorithms is that the original problem divides into subprob- lems that are just smaller versions of the original. To see what I mean, think about the binary search again. Initially, the range to search is the entire list. Our first step is to look at the middle item in the list. Should the middle item turn out to be the target, then we are finished. If it is not the target, we continue by performing binary search on either the top-half or the bottom half of the list. Using this insight, we might express the binary search algorithm in another way.13.2. RECURSIVE PROBLEM-SOLVING 229 Algorithm: binarySearch -- search for x in range nums[low] to nums[high] mid = (low + high) / 2 if low > high x is not in nums elif x < nums[mid] perform binary search for x in range nums[low] to nums[mid-1] else perform binary search for x in range nums[mid+1] to nums[high] Rather than using a loop, this defintion of the binary search seems to refer to itself. What is going on here? Can we actually make sense of such a thing? 13.2.1 Recursive Definitions A description of something that refers to itself is called a recursive definition. In our last formulation, the binary search algorithm makes use of its own description. A -++call-+. to binary search /#.recurs&!, inside of the definition(&!hence, the label recursive definition. At first glance, you might think recursive definitions are just nonsense. Surely you have had a teacher who insisted that you can't use a word inside of its own definition? That's called a circular definition and is usually not worth much credit on an exam. In mathematics, however, certain recursive definitions are used all the time. As long as we excercise some care in the formulation and use of recursive definitions, they can be quite handy and surprisingly powerful. Let's look at a simple example to gain some insight and then apply those ideas to binary search. The classic recursive example in mathematics is the definition of factorial. Back in Chapter 3, we defined the factorial of a value like this: n! n n 1 n 2 1 For example, we can compute 5! 5 4 3 2 1 Recall that we implemented a program to compute factorials using a simple loop that accumulates the factorial product. Looking at the calculation of 5!, you will notice something interesting. If we remove the 5 from the front, what remains is a calculation of 4!. In general, n! n n 1 !. In fact, this relation gives us another way of expressing what is meant by factorial in general. Here is a recursive definition: n! 1 n n if n 0 1 ! otherwise This definition says that the factorial of 0 is, by definition, 1, while the factorial of any other number is defined to be that number times the factorial of one less than that number. Even though this definition is recursive, it is not circular. In fact, it provides a very simple method of calculating a factorial. Consider the value of 4!. By definition we have 4! 4 4 1 ! 4 3! But what is 3!? To find out, we apply the definition again. 4! 4 3! 4 3 3 1 ! 4 3 2! Now, of course, we have to expand 2!, which requires 1!, which requires 0!. Since 0! is simply 1, that's the end of it. 4! 4 3! 4 3 2! 4 3 2 1! 4 3 2 1 0! 4 3 2 1 1 24 You can see that the recursive definition is not circular because each application causes us to request the factorial of a smaller number. Eventually we get down to 0, which doesn't require another application of the definition. This is called a base case for the recursion. When the recursion bottoms out, we get a closed expression that can be directly computed. All good recursive definitions have these key characteristics:CHAPTER 13. ALGORITHM ANALYSIS AND DESIGN 230 1. There are one or more base cases for which no recursion is required. 2. When the definition is recursively applied, it is always applied to a smaller case. 3. All chains of recursion eventually end up at one of the base cases. 13.2.2 Recursive Functions You already know that the factorial can be computed using a loop with an accumulator. That implementation has a natural correspondence to the original definition of factorial. Can we also implement a version of factorial that follows the recursive definition? If we write factorial as a separate function, the recursive definition translates directly into code. def fact(n): if n == 0: return 1L else: return n * fact(n-1) Do you see how the definition that refers to itself turns into a function that calls itself? The function first checks to see if we are at a the base case n == 0 and, if so, returns 1 (note the use of a long int constant since factorials grow rapidly). If we are not yet at the base case, the function returns the result of multiplying n by the factorial of n-1. The latter is calculated by a recursive call to fact(n-1). I think you will agree that this is a reasonable translation of the recursive definition. The really cool part is that it actually works! We can use this recursive function to compute factorial values. >>> from recfact import fact >>> fact(4) 24 >>> fact(10) 3628800 Some beginning programmers are surprised by this result, but it follows naturally from the semantics for functions that we discussed way back in Chapter 6. Remember that each call to a function starts that function anew. That means it gets its own copy of any local values, including the values of the parameters. Figure 13.1 shows the sequence of recursive calls that computes 2!. Note especially how each return value is multiplied by a value of n appropriate for each function invocation. The values of n are stored on the way down the chain and then used on the way back up as the function calls return. n = 2 fact(2) def fact(n): if n == 0: 1 return 1 n = 2 else: return n * fact(n%(+1) n: 2 def fact(n): if n == 0: 0 return 1 n = 1 else: return n * fact(n+"/1) n: 1 1 def fact(n): if n == 0: return 1 else: return n * fact(n()*1) n: 0 Figure 13.1: Recursive computation of 2! 13.2.3 Recursive Search Now that we have a technique for implementing recursive definitions, we are ready to go back and look again at binary search as a recursive process. The basic idea was to look at the middle value and then recursively search either the lower half or the upper half of the array. The base cases for the recursion are the conditions when we can stop, namely when the target value is found, or we run out of places to look. The recursive calls13.3. SORTING ALGORITHMS 231 will cut the size of the problem in half each time. In order to do this, we need to specify the range of locations in the list that are still (,/in play&)& for each recursive call. We can do this by passing the values of low and high along with the list. Each invocation will search the list between the low and high indexes. Here is a direct implementation of the recursive algorithm using these ideas: def recBinSearch(x, nums, low, high): if low > high: # No place left to look, return -1 return -1 mid = (low + high) / 2 item = nums[mid] if item == x: # Found it! Return the index return mid elif x < item: # Look in lower half return recBinSearch(x, nums, low, mid-1) else: # Look in upper half return recBinSearch(x, nums, mid+1, high) We can then implement our original search function using a suitable call to the recursive binary search, telling it to start the search between 0 and len(nums)-1 def search(x, nums): return recBinSearch(x, nums, 0, len(nums)-1) Of course, as in the case of factorial, we already implemented this algorithm using a loop, and there is no compelling reason to use a recursive implementation. In fact, the looping version is probably a bit faster because calling functions is generally slower than iterating a loop. The recursive version, however, makes the divide-and-conquer structure of binary search much more obvious. Below, we will see examples where recursive divide-and-conquer approaches provide a natural solution to some problems where loops are awkward. 13.3 Sorting Algorithms The sorting problem provides a nice testbed for the algorithm design techniques we have been discussing. Recall, the basic sorting problem is to take a list and rearrange it so that the values are in increasing (actually, nondecreasing) order. 13.3.1 Naive Sorting: Selection Sort Let's start with a simple !-"be the computer,+$ approach to sorting. Suppose you have a stack of index cards, each with a number on it. The stack has been shuffled, and you need to put the cards back in order. How would you accomplish this task? There are any number of good systematic approaches. One simple method is to look through the deck to find the smallest value and then place that value at the front of the stack (or perhaps in a separate stack). Then you could go through and find the smallest of the remaining cards and put it next in line, etc. Of course, this means that you'll also need an algorithm for finding the smallest remaining value. You can use the same approach we used for finding the max of a list (see Chapter 6). As you go through, you keep track of the smallest value seen so far, updating that value whenever you find a smaller one. The algorithm I just described is called selection sort. Basically, the algorithm consists of a loop and each time through the loop, we select the smallest of the remaining elements and move it into its proper position. Applying this idea to a list, we proceed by finding the smallest value in the list and putting it into the 0th position. Then we find the smallest remaining value (from positions 1**&(n-1)) and put it in position 1. Next the smallest value from positions 2/'.(n-1) goes into position 2, etc. When we get to the end of the list, everything will be in its proper place. There is one subtlety in implementing this algorithm. When we place a value into its proper position, we need to make sure that we do not accidently lose the value that was originally stored in that position. For example, if the smallest item is in position 10, moving it into position 0 involves an assignment.CHAPTER 13. ALGORITHM ANALYSIS AND DESIGN 232 nums[0] = nums[10] But this wipes out the value currently in nums[0]; it really needs to be moved to another location in the list. A simple way to save the value is to swap it with the one that we are moving. Using simultaneous assignment, the statement nums[0], nums[10] = nums[10], nums[0] places the value from position 10 at the front of the list, but preserves the original first value by stashing it into location 10. Using this idea, it is a simple matter to write a selection sort in Python. I will use a variable called bottom to keep track of which position in the list we are currently filling, and the variable mp will be used to track the location of the smallest remaining value. The comments in this code explain this implementation of selection sort: def selSort(nums): # sort nums into ascending order n = len(nums) # For each position in the list (except the very last) for bottom in range(n-1): # find the smallest item in nums[bottom]..nums[n-1] mp = bottom for i in range(bottom+1,n): if nums[i] < nums[mp]: mp = i # initially bottom is smallest so far # look at each position # this one is smaller # remember its index # swap smallest item to the bottom lst[bottom], lst[mp] = lst[mp], lst[bottom] One thing to notice about this algorithm is the accumulator for finding the minimum value. Rather than actually storing the minimum seen so far, mp just remembers the position of the minimum. A new value is tested by comparing the item in position i to the item in position mp. You should also notice that bottom stops at the second to last item in the list. Once all of the items up to the last have been put in the proper place, the last item has to be the largest, so there is no need to bother looking at it. The selection sort algorithm is easy to write and works well for moderate-sized lists, but it is not a very efficient sorting algorithm. We'll come back and analyze it after we've developed another algorithm. 13.3.2 Divide and Conquer: Merge Sort As discussed above, one technique that often works for developing efficient algorithms is the divide-and- conquer approach. Suppose a friend and I were working together trying to put our deck of cards in order. We could divide the problem up by splitting the deck of cards in half with one of us sorting each of the halves. Then we just need to figure out a way of combining the two sorted stacks. The process of combining two sorted lists into a single sorted result is called merging. If you think about it, merging is pretty simple. Since our two stacks are sorted, each has its smallest value on top. Whichever of the top values is the smallest will be the first item in the merged list. Once the smaller value is removed, we can look at the tops of the stacks again, and whichever top card is smaller will be the next item in the list. We just continue this process of placing the smaller of the two top values into the big list until one of the stacks runs out. At that point, we finish out the list with the cards from the remaining stack. Here is a Python implementation of the merge process. In this code, lst1 and lst2 are the smaller lists and lst3 is the larger list where the results are placed. In order for the merging process to work, the length of lst3 must be equal to the sum of the lengths of lst1 and lst2. You should be able to follow this code by studying the accompanying comments:13.3. SORTING ALGORITHMS 233 def merge(lst1, lst2, lst3): # merge sorted lists lst1 and lst2 into lst3 # these indexes keep track of current position in each list i1, i2, i3 = 0, 0, 0 # all start at the front n1, n2 = len(lst1), len(lst2) # Loop while both lst1 and lst2 while i1 < n1 and i2 < n2: if lst1[i1] < lst2[i2]: lst3[i3] = lst1[i1] i1 = i1 + 1 else: lst3[i3] = lst2[i2] i2 = i2 + 1 i3 = i3 + 1 have more items # top of lst1 is smaller # copy it into current spot in lst3 # top of lst2 is smaller # copy it into current spot in lst3 # item added to lst3, update position # Here either lst1 or lst2 is done. One of the following loops will # execute to finish up the merge. # Copy remaining items (if any) from lst1 while i1 < n1: lst3[i3] = lst1[i1] i1 = i1 + 1 i3 = i3 + 1 # Copy remaining items (if any) from lst2 while i2 < n2: lst3[i3] = lst2[i2] i2 = i2 + 1 i3 = i3 + 1 With this merging algorithm in hand, it's easy to see the general structure for a divide-and-conquer sorting algorithm. Algorithm: mergeSort nums split nums into two halves sort the first half sort the second half merge the two sorted halves back into nums Looking at the steps in this algorithm, the first and last parts look easy. We can use slicing to split the list, and we can use the merge function that we just wrote to put the pieces back together. But how do we sort the two halves? Well, let's think about it. We are trying to sort a list, and our algorithm requires us to sort two smaller lists. This sounds like a perfect place to use recursion. Maybe we can use mergeSort itself to sort the two lists. Let's go back to our recursion guidelines and see if we can develop a proper recursive algorithm. In order for recursion to work, we need to find at least one base case that does not require a recursive call, and we also have to make sure that recursive calls are always made on smaller versions of the original problem. The recursion in our mergeSort will always occur on a list that is half as large as the original, so the latter property is automatically met. Eventually, our lists will be very small, containing only a single item. Fortunately, a list with just one item is already sorted! Voil ,/a, we have a base case. When the length of the list is less than 2, we do nothing, leaving the list unchanged. Given our analysis, we can update the mergeSort algorithm to make it properly recursive.CHAPTER 13. ALGORITHM ANALYSIS AND DESIGN 234 if len(nums) > 1: split nums into two halves mergeSort the first half mergeSort the second half merge the two sorted halves back into nums We can translate this algorithm directly into Python code. def mergeSort(nums): # Put items of nums in ascending order n = len(nums) # Do nothing if nums contains 0 or 1 items if n > 1: # split into two sublists m = n / 2 nums1, nums2 = nums[:m], nums[m:] # recursively sort each piece mergeSort(nums1) mergeSort(nums2) # merge the sorted pieces back into original list merge(nums1, nums2, nums) I know this use of recursion may still seem a bit mysterious to you. You might try tracing this algorithm with a small list (say eight elements), just to convince yourself that it really works. In general, though, tracing through recursive algorithms can be tedious and often not very enlightening. Recursion is closely related to mathematical induction, and it requires something of a leap of faith before it becomes comfortable. As long as you follow the rules and make sure that every recursive chain of calls eventually reaches a base case, your algorithms will work. You just have to trust and let go of the grungy details. Let Python worry about that for you! 13.3.3 Comparing Sorts Now that we have developed two sorting algorithms, which one should we use? Before we actually try them out, let's do some analysis. As in the searching problem, the difficulty of sorting a list depends on the size of the list. We need to figure out how many steps each of our sorting algorithms requires as a function of the size of the list to be sorted. Take a look back at the algorithm for selection sort. Remember, this algorithm works by first finding the smallest item, then finding the smallest of the remaining elements, and so on. Suppose we start with a list of size n. In order to find the smallest value, the algorithm has to inspect each of the n items. The next time around the outer loop, it has to find the smallest of the remaining n 1 items. The third time around, there are n 2 items of interest. This process continues until there is only one item left to place. Thus, the total number of iterations of the inner loop for the selection sort can be computed as the sum of a decreasing sequence. n n 1 n 2 n 3 1 In other words, the time required by selection sort to sort a list of n items in proportional to the sum of the first n whole numbers. There is a well-known formula for this sum, but even if you do not know the formula, it is easy to derive. If you add the first and last numbers in the series you get n 1. Adding the second and second to last values gives n 1 2 n 1. If you keep pairing up the values working from the outside in, all of the pairs add to n 1. Since there are n numbers, there must be n 2 pairs. That means the sum of all n n 1 2 the pairs is . You can see that the final formula contains an n 2 term. That means that the number of steps in the algorithm is proportional to the square of the size of the list. If the size of the list doubles, the number of steps quadruples. If the size triples, it will take nine times as long to finish. Computer scientists call this a quadratic or n-squared algorithm.13.4. HARD PROBLEMS 235 Let's see how that compares to the merge sort algorithm. In the case of merge sort, we divided a list into two pieces and sorted the individual pieces before merging them together. The real work is done during the merge process when the values in the sublists are copied back into the original list. Figure 13.2 depicts the merging process to sort the list [3, 1, 4, 1, 5, 9, 2, 6]. The dashed lines show how the original list is continually halved until each item is its own list with the values shown at the bottom. The single-item lists are then merged back up into the two item lists to produce the values shown in the second level. The merging process continues up the diagram to produce the final sorted version of the list shown at the top. 1 1 1 1 2 1 3 4 3 3 1 1 3 4 4 4 5 1 5 5 6 9 2 5 6 9 9 2 9 2 6 6 Figure 13.2: Merges required to sort [3, 1, 4, 1, 5, 9, 2, 6]. The diagram makes analysis of the merge sort trivial. Starting at the bottom level, we have to copy the n values into the second level. From the second to third level, the n values need to be copied again. Each level of merging involves copying n values. The only question left to answer is how many levels are there? This boils down to how many times a list of size n can be split in half. You already know from the analysis of binary search that this is just log 2 n. Therefore, the total work required to sort n items is n log 2 n. Computer scientists call this an n log n algorithm. So which is going to be better, the n-squared selection sort or the n-log-n merge sort? If the input size is small, the selection sort might be a little faster, because the code is simpler and there is less overhead. What happens, though as n gets larger? We saw in the analysis of binary search that the log function grows very slowly (log 2 16 000 000 24) so n log 2 n will grow much slower than n n . Empirical testing of these two algorithms confirms this analysis. On my computer, selection sort beats merge sort on lists up to size about 50, which takes around 0.008 seconds. On larger lists, the merge sort dominates. Figure 13.3 shows a comparison of the time required to sort lists up to size 3000. You can see that the curve for selection sort veers rapidly upward (forming half of a parabola), while the merge sort curve looks almost straight (look at the bottom). For 3000 items, selection sort requires over 30 seconds while merge sort completes the task in about 34 of a second. Merge sort can sort a list of 20,000 items in less than six seconds; selection sort takes around 20 minutes. That's quite a difference! 13.4 Hard Problems Using our divide-and-conquer approach we were able to design good algorithms for the searching and sorting problems. Divide and conquer and recursion are very powerful techniques for algorithm design. However, not all problems have efficient solutions.CHAPTER 13. ALGORITHM ANALYSIS AND DESIGN 236 35 $$$selSort#.' %-.mergeSort%.- 30 25 20 15 10 5 0 0 500 1000 1500 List Size 2000 2500 3000 Figure 13.3: Experimental comparison of selection sort and merge sort. 13.4.1 Towers of Hanoi One very elegant application of recursive problem solving is the solution to a mathematical puzzle usually called the Tower of Hanoi or Tower of Brahma. This puzzle is generally attributed to the French mathe- matician Edouard Lucas, who published an article about it in 1883. The legend sorrounding the puzzle goes something like this. Somewhere in a remote region of the world is a monastery of a very devout religious order. The monks have been charged with a sacred task that keeps time for the universe. At the beginning of all things, the monks were given a table that supports three vertical posts. On one of the posts was a stack of 64 concentric golden disks. The disks are of varying radii and stacked in the shape of a beautiful pyramid. The monks were charged with the task of moving the disks from the first post to the third post. When the monks have completed their task, all things will crumble to dust and the universe will end. Of course, if that's all there were to the problem, the universe would have ended long ago. To maintain divine order, the monks must abide by certain rules. 1. Only one disk may be moved at a time. 2. A disk may not be (&$set aside."+$ It may only be stacked on one of the three posts. 3. A larger disk may never be placed on top of a smaller one. Versions of this puzzle were quite popular at one time, and you can still find variations on this theme in toy and puzzle stores. Figure 13.4 depicts a small version containing only eight disks. The task is to move the tower from the first post to the third post using the center post as sort of a temporary resting place during the process. Of course, you have to follow the three sacred rules given above. We want to develop an algorithm for this puzzle. You can think of our algorithm either as a set of steps that the monks need to carry out, or as a program that generates a set of instructions. For example, if we label the three posts A, B and C. The instructions might start out like this: Move disk from A to C.13.4. HARD PROBLEMS 237 Figure 13.4: Tower of Hanoi puzzle with eight disks. Move disk from A to B. Move disk from C to B. ... This is a difficult puzzle for most people to solve. Of course, that is not surprising, since most people are not trained in algorithm design. The solution process is actually quite simple.,/if you know about recursion. Let's start by considering some really easy cases. Suppose we have a version of the puzzle with only one disk. Moving a tower consisting of a single disk is simple)!" we just remove it from A and put it on C. Problem solved. OK, what if there are two disks? I need to get the larger of the two disks over to post C, but the smaller one is sitting on top of it. I need to move the smaller disk out of the way, and I can do this by moving it to post B. Now the large disk on A is clear; I can move it to C and then move the smaller disk from post B onto post C. Now let's think about a tower of size three. In order to move the largest disk to post C, I first have to move the two smaller disks out of the way. The two smaller disks form a tower of size two. Using the process I outlined above, I could move this tower of two onto post B, and that would free up the largest disk so that I can move it to post C. Then I just have to move the tower of two disks from post B onto post C. Solving the three disk case boils down to three steps. 1. Move a tower of two from A to B. 2. Move one disk from A to C. 3. Move a tower of two from B to C. The first and third steps involve moving a tower of size two. Fortunately, we have already figured out how to do this. It's just like solving the puzzle with two disks, except that we move the tower from A to B using C as the temporary resting place and then from B to C, using A as the temporary. We have just developed the outline of a simple recursive algorithm for the general process of moving a tower of any size from one post to another. Algorithm: move n-disk tower from source to destination via resting place move n-1 disk tower from source to resting place move 1 disk tower from source to destination move n-1 disk tower from resting place to destinationCHAPTER 13. ALGORITHM ANALYSIS AND DESIGN 238 What is the base case for this recursive process? Notice how a move of n disks results in two recursive moves of n 1 disks. Since we are reducing n by one each time, the size of the tower will eventually be 1. A tower of size 1 can be moved directly by just moving a single disk; we don't need any recursive calls to remove disks above it. Fixing up our general algorithm to include the base case gives us a working moveTower algorithm. Let's code it up in Python. Our moveTower function will need parameters to represent the size of the tower, n; the source post, source; the destintation post, dest; and the temporary resting post, temp. We an use an int for n and the strings +-!A"#,, !''B,!%, and #,*C()/ to represent the posts. Here is the code for moveTower. def moveTower(n, source, dest, temp): if n == 1: print "Move disk from", source, "to", dest+"." else: moveTower(n-1, source, temp, dest) moveTower(1, source, dest, temp) moveTower(n-1, temp, dest, source) See how easy that was? Sometimes using recursion can make otherwise difficult problems almost trivial. To get things started, we just need to supply values for our four parameters. Let's write a little function that prints out instructions for moving a tower of size n from post A to post C. def hanoi(n): moveTower(n, "A", "C", "B") Now we're ready to try it out. Here are solutions to the three- and four-disk puzzles. You might want to trace through these solutions to convince yourself that they work. >>> hanoi(3) Move disk from Move disk from Move disk from Move disk from Move disk from Move disk from Move disk from A A C A B B A to to to to to to to C. B. B. C. A. C. C. >>> hanoi(4) Move disk from Move disk from Move disk from Move disk from Move disk from Move disk from Move disk from Move disk from Move disk from Move disk from Move disk from Move disk from Move disk from Move disk from Move disk from A A B A C C A A B B C B A A B to to to to to to to to to to to to to to to B. C. C. B. A. B. B. C. C. A. A. C. B. C. C. So, our solution to the Tower of Hanoi is a %!%trivial/"! algorithm requiring only nine lines of code. What is this problem doing in a section labeled hard problems? To answer that question, we have to look at the efficiency of our solution. Remember, when I talk about the efficiency of an algorithm, I mean how many13.4. HARD PROBLEMS 239 steps it requires to solve a given size problem. In this case, the difficulty is determined by the number of disks in the tower. The question we want to answer is how many steps does it take to move a tower of size n? Just looking at the structure of our algorithm, you can see that moving a tower of size n requires us to move a tower of size n 1 twice, once to move it off the largest disk, and again to put it back on top. If we add another disk to the tower, we essentially double the number of steps required to solve it. The relationship becomes clear if you simply try out the program on increasing puzzle sizes. Numer of Disks 1 2 3 4 5 Steps in Solution 1 3 7 15 31 In general, solving a puzzle of size n will require 2 n 1 steps. Computer scientists call this an exponential time algorithm, since the measure of the size of the problem, n, appears in the exponent of this formula. Exponential algorithms blow up very quickly and can only be practically solved for relatively small sizes, even on the fastest computers. Just to illustrate the point, if our monks really started with a tower of just 64 disks and moved one disk every second, 24 hours a day, every day, without making a mistake, it would still take them over 580 billion years to complete their task. Considering that the universe is roughly 15 billion years old now, I'm not too worried about turning to dust just yet. Even though the algorithm for Towers of Hanoi is easy to express, it belongs to a class known as in- tractable problems. These are problems that require too much computing power (either time or memory) to be solved in practice, except for the simplest cases. And in this sense, our toy-store puzzle does indeed represent a hard problem. But some problems are even harder than intractable, and we'll meet one of those in the next section. 13.4.2 The Halting Problem Let's just imagine for a moment that this book has inspired you to pursue a career as a computer professional. It's now six years later, and you are a well-established software developer. One day, your boss comes to you with an important new project, and you are supposed to drop everything and get right on it. It seems that your boss has had a sudden inspiration on how your company can double its productivity. You've recently hired a number of rather inexperienced programmers, and debugging their code is taking an inordinate amount of time. Apparently, these wet-behind-the-ears newbies tend to accidently write a lot of programs with inifinite loops (you've been there, right?). They spend half the day waiting for their computers to reboot so they can track down the bugs. Your boss wants you to design a program that can analyze source code and detect whether it contains an infinite loop before actually running it on test data. This sounds like an interesting problem, so you decide to give it a try. As usual, you start by carefully considering the specifications. Basically, you want a program that can read other programs and determine whether they contain an infinite loop. Of course, the behavior of a program is determined not just by its code, but also by the input it is given when it runs. In order to determine if there is an infinite loop, you will have to know what the input will be. You decide on the following specification: Program: Halting Analyzer Inputs: A Python program file. The input for the program. Outputs: $($OK*!$ if the program will evenutally stop. ++)FAULTY$%+ if the program has an infinite loop. Right away you notice a couple interesting things about this program. One is that it is a program that examines other programs. You have not written many of these before, but you know that it's not a problem in principle. After all, compilers and interpreters are common examples of programs that analyze other programs. You can represent both the program that is being analyzed and the proposed input to the program as Python strings.CHAPTER 13. ALGORITHM ANALYSIS AND DESIGN 240 The second thing you notice is that this description sounds similar to something you've heard about before. Hmmm... a program that determines whether another program will halt or not. Suddenly it dawns on you: this is known as the Halting Problem, and it's unsolvable. There is no possible algorithm that can meet this specification! How do we know that there is no solution to this problem? This is a question that all the design skills in the world will not answer for you. Design can show that problems are solvable, but it can never prove that a problem is not solvable. To do that, we need to use our analytical skills. One way to prove that something is impossible is to first assume that it is possible and show that this leads to a contradiction. Mathematicians call this proof by contradiction. We'll use this technique to show that the halting problem cannot be solved. We begin by assuming that there is some algorithm that can determine if a program terminates when executed on a particular input. If such an algorithm could be written, we could package it up in a function. def terminates(program, inputData): # program and inputData are both strings # RETURNS true if program would halt when run with inputData # as its input. Of course, I can't actually write the function, but let's just assume that this function exists. Using the terminates function, we can write a goofy program. # goofy.py import string def terminates(program, inputData): # program and inputData are both strings # RETURNS true if program would halt when run with inputData # as its input. def main(): # Read a program from standard input lines = [] print "Type in a program (type /,)done./" to quit)." line = raw_input("") while line != "done": lines.append(line) line = raw_input("") testProg = string.join(lines, "\n") # If program halts on itself as input, go into an infinite loop if terminates(testProg, testProg): while 1: pass main() The first thing goofy.py does is read in a program typed by the user. This is accomplished with a sentinel loop that accumulates lines in a list one at a time. The string.join function then concatenates the lines together using a newline character (" n") between them. This effectively creates a multi-line string representing the program that was typed. Goofy.py then calls the terminates function and sends the input program as both the program to test and the input data for the program. Essentially, this is a test to see if the program read from the input terminates when given itself as input. The pass statement actually does nothing; if the terminates function returns true, goofy.py will go into an infinite loop. OK, this seems like a silly program, but there is nothing in principle that keeps us from writing it, provided that the terminates function exists. Goofy.py is constructed in this peculiar way simply to illustrate a point. Here's the million dollar question: What happens if we run goofy.py and, when prompted to type13.4. HARD PROBLEMS 241 in a program, type in the contents of goofy.py? Put more specifically, does goofy.py halt when given itself as its input? Let's think it through. We are running goofy.py and providing goofy.py as its input. In the call to terminates, both the program and the data will be a copy of goofy.py, so if goofy.py halts when given itself as input, terminates will return true. But if terminates returns true, goofy.py then goes into an infinite loop, so it doesn't halt! That's a contradiction; goofy.py can't both halt and not halt. It's got to be one or the other. Let's try it the other way around. Suppose that terminates returns a false value. That means that goofy.py, when given itself as input goes into an infinite loop. But as soon as terminates returns false, goofy.py quits, so it does halt! It's still a contradiction. If you've gotten your head around the previous two paragraphs, you should be convinced that goofy.py represents an impossible program. The existence of a function meeting the specification for terminates leads to a logical impossibility. Therefore, we can safely conclude that no such function exists. That means that there cannot be an algorithm for solving the halting problem! There you have it. Your boss has assigned you an impossible task. Fortunately, your knowledge of computer science is sufficient to recognize this. You can explain to your boss why the problem can't be solved and then move on to more productive pursuits. 13.4.3 Conclusion I hope this chapter has given you a taste of what computer science is all about. As the examples in this chapter have shown, computer science is much more than #)-just$!& programming. The most important computer for any computing professional is still the one between the ears. Hopefully this book has helped you along the road to becoming a computer programmer. Along the way, I have tried to it has pique your curiousity about the science of computing. If you have mastered the concepts in this text, you can already write interesting and useful programs. You should also have a firm foundation of the fundamental ideas of computer science and software engineering. Should you be interested in studying these fields in more depth, I can only say $-.go for it.(,/ Perhaps one day you will also consider yourself a computer scientist; I would be delighted if my book played even a very small part in that process.Index doc , 171 init , 168 name , 106 and, 132 operational definition, 138 Ants Go Marching, The, 100 append, 186 archery, 85, 121 argument, 93 array, 186 associative, 199 arrow (on Lines), 82 ASCII, 46 assignment statement, 10, 17/''20 sematics, 17 simultaneous, 19 syntax, 17 associative array, 199 attributes, 161 private, 178 average n numbers algorithm empty string sentinel, 128 problem description, 123 program counted loop, 123 empty string sentinel, 128 end-of-file loop, 130 from file with readlines, 129 interactive loop, 126 negative sentinel, 127 average two numbers, 20 average1.py, 123 average2.py, 126 average3.py, 127 average4.py, 128 average5.py, 129 average6.py, 130 avg2.py, 20 abstraction, 148 accessor, 68 accumulator, 31 acronym, 61 algorithm analysis, 2, 233 definition of, 2 design strategy, 118 divide and conquer, 235 exponential time, 245 intractable, 246 linear time, 234 log time, 234 quadratic (n-squared) time, 241 algorithms average n numbers counted loop, 123 empty string sentinel, 128 interactive loop, 126 binary search, 233 cannonball simulation, 162 future value, 23 future value graph, 71, 73 input validation, 135 linear search, 232 max-of-three comparing each to all, 115 decision tree, 116 sequential, 117 median, 189 merge sort, 240 message decoding, 48 message encoding, 47 quadratic equation three-way decision, 110 racquetball simulation simOneGame, 150 selection sort, 238 simNGames, 149 temperature conversion, 14 alias, 69 analysis of algorithms, 2, 233 babysitting, 120 batch processing, 58 example program, 58 binary, 4 binary search, 232 bit, 33 black box, 207 Blackjack, 159 242INDEX BMI (Body Mass Index), 120 Boolean algebra (logic), 134 expression, 106, 131 operator, 132 values, 106 break statement, 136 implementing post-test loop, 136 style considerations, 137 Brooks, Fred, 208 bug, 13 butterfly effect, 11 Button class definition, 175 description, 173 methods, 174 button.py, 175 byte code, 8 Caesar cipher, 61 calculator problem description, 194 program, 197 cannonball algorithm, 162 graphical display, 180 problem description, 162 program, 164, 169, 172 Projectile class, 169 card, playing, 181 cball1.py, 164 cball3.py, 169 cball4.py, 172 Celsius, 13 change counter program, 27, 54 change.py, 27 change2.py, 54 chaos discussion, 10$)/11 program, 7 chaos.py, 7 chr, 46 Christmas, 85 cipher, 50 ciphertext, 50 Circle constructor, 82 methods, 82 circle area formula, 38 intersection with line, 85 class, 66, 161 class standing, 120 243 class statement, 167 classes Button, 175 Calculator, 197 Dice, 217 DieView, 176, 193 GraphicsInterface, 225 MSDie, 166 Player, 213 PokerApp, 219 Projectile, 169 Projectile as module file, 171 RBallGame, 211 SimStats, 210 TextInterface, 221 client, 207 clone, 70, 82 close GraphWin, 81 cmp, 203 code duplication in future value graph, 91 maintenance issues, 88 reducing with functions, 88 coffee, 39 Collatz sequence, 140 color changing graphics object, 75 changing GraphWin, 75 fill, 75 outline, 75 specifying, 84 color rgb, 84 comments, 9 compareItems, 203 compiler, 4 diagram, 5 vs. interpreter, 5 compound condition, 115 computer definition of, 1 functional view, 3 program, 1 computer science definition of, 2 methods of investigation, 2 concatenation list, 185 string, 43 condition, 105 compound, 115 design issues, 115 for termination, 134 syntax, 105244 conditional loop, 124 constructor, 67, 161 init , 168 parameters in, 67 control codes, 46 control structure, 103 decision, 103 definition of, 22 loop, 22 nested loops, 130 nesting, 110 control structures Boolean operators, 138 for statement, 22 if, 105 if-elif-else, 111 if-else, 108 while, 124 convert.py, 14, 103 convert2.py, 104 convert gui.pyw, 79 coordinates as instance variables, 67 changing with setCoords, 75 in a GraphWin, 65 of a Point, 65 setCoords example, 76 transforming, 75 counted loop definition of, 21 in Python, 22 CPU (Central Processing Unit), 3 craps, 159 createLabeledWindow, 98 cryptography, 50 cylinder, 180 data, 27, 161 data type automatic conversion, 36 definition of, 28 explicit conversion, 36 in format specifiers, 53 mixed-type expressions, 36 string conversion, 52 string conversions, 49 data types file, 55 float, 28 int, 28 long int, 35 string, 41 date, 120 day number, 120 INDEX debugging, 13 decision, 103 implementation via Boolean operator, 138 multi-way, 110 nested, 110 simple (one-way), 105 two-way, 108 decision tree, 115 decoding, 48 algorithm, 48 program, 49 definite loop, 124 definition of, 20 use as counted loop, 22 degree-days, 140 delete, 187 DeMorgan's laws, 134 design, 13, 207 object oriented, see object oriented design top-down, 146 steps in, 154 design pattern importance of, 124 design patterns counted loop, 21, 123 end-of-file loop, 129 interactive loop, 126 IPO, 14 loop accumulator, 31, 123 model-view, 217 nested loops, 130, 131 sentinel loop, 127 loop and a half, 136 design techniques divide and conquer, 235 spiral development, 156 when to use, 158 dice, 159 dice poker classes Dice, 217 GraphicsInterface, 225 PokerApp, 219 TextInterface, 221 problem description, 216 dice roller problem description, 173 program, 178 dictionary, 199 creation, 200 empty, 200 methods, 200 DieView, 191 class definition, 176, 193INDEX description, 176 Dijkstra, Edsgar, 2 disk, 3 distance function, 96 division, 29 docstring, 171 dot notation, 8, 59, 67 draw, 82 drawBar, 91 duplication, see code duplication Easter, 120 elif, 111 empty list, 186 empty string, 128 encapsulation, 170, 228 encoding, 46 algorithm, 47 program, 47 encryption, 50 Entry, 79, 83 environment, programming, 7 epact, 39 equality, 105 Eratosthenes, 206 error checking, 112 errors KeyError, 201 math range, 30 name, 16, 42 overflow, 33 Euclid's algorithm, 140 eval, 49 event, 77 event loop, 179 event-driven, 77 exam grader, 61, 120 exception handling, 112 exponential notation, 35 expression as input, 18 Boolean, 106, 131 definition of, 15 spaces in, 16 face, 85, 180 fact.py, 236 factorial definition of, 31 program, 32, 35 recursive definition, 235 factorial.py, 32 factorial2, 35 Fahrenheit, 13 245 fetch execute cycle, 3 Fibonacci numbers, 39, 140 file, 55 closing, 56 opening, 56 processing, 56 program to print, 57 read operations, 56 representation, 55 write operations, 57 float, 28 literal, 28 representation, 35 floppy, 3 flowchart, 22 flowcharts for loop, 22 if semantics, 105 loop and a half sentinel, 137 max-of-three decision tree, 116 max-of-three sequential solution, 117 nested decisions, 111 post-test loop, 135 temperature conversion with warnings, 104 two-way decision, 109 while loop, 125 flush, 81 for statement (for loop), 21, 123 as counted loop, 22 flowchart, 22 semantics, 21 syntax, 21 using simultaneous assignment, 196 formal parameter, 93 format specifier, 53 from..import, 64 function, 6 actual parameters, 93 arguments, 93 as parameter, 203 as black box, 207 as subprogram, 88 call, 6, 93 createLabeledWindow, 98 defining, 6, 93 for modularity, 97 invoking, see function, call missing return, 97 multiple parameters, 94 None as default return, 97 parameters, 6 recursive, 236 return value, 95 returning multiple values, 96INDEX 246 signature (interface), 148 to reduce duplication, 88 function definition, 88 functions built-in chr, 46 cmp, 203 eval, 49 float, 37 int, 37 len, 43 long, 37 max, 118 open, 56 ord, 46 range, 32 raw input, 42 read, 56 readline, 56 readlines, 56 round, 37 str, 52 type, 28 write, 57 compareItems, 203 distance, 96 drawBar, 91 gameOver, 152 getInputs, 148 getNumbers, 187 happy, 89 main, 7 why use, 9 math library, see math library, functions mean, 188 median, 189 merge, 239 mergeSort, 240 moveTower, 244 random library, see random library, functions recursive binary search, 237 recursive factorial, 236 selsort, 238 simNGames, 150 simOneGame, 152 singFred, 89 singLucy, 89 square, 95 stdDev, 188 string library, see string library future value algorithm, 23 problem description, 23 program, 24, 99 program specification, 23 future value graph final algorithm, 73 problem, 70 program, 74, 76, 87, 91 rough algorithm, 71 futval.py, 24 futval graph.py, 74 futval graph2.py, 76, 87 futval graph3.py, 91 futval graph4.py, 99 gameOver, 152 GCD (Greatest Common Divisor), 140 getAnchor, 83 getCenter, 82, 83 getInputs, 148 getMouse, 78, 81 example use, 78 getNumbers, 187 getP1, 82, 83 getP2, 82, 83 getPoints, 83 getRadius, 82 getText, 83 getX, 82 getY, 82 goofy.py, 247 gozinta, 29 graphics library, 64, 81'$"84 drawing example, 66 generic methods summary, 82 graphical objects, 82''-83 methods for Text, 83 clone, 70 for Circle, 82 for Entry, 83 for Image, 83 for Line, 82 for Oval, 83 for Point, 82 for Polygon, 83 for Rectangle, 82 getMouse, 78 move, 68 setCoords, 75 objects Circle, 82 Entry, 79, 83 GraphWin, 64, 81 Image, 83 Line, 82 Oval, 83INDEX Point, 65, 82 Polygon, 79, 83 Rectangle, 82 Text, 83 GraphWin, 64, 81 methods summary, 81 Gregorian epact, 39 GUI, 64 hailstone function, 140 halting problem, 246 happy, 89 happy birthday lyrics, 88 problem description, 88 program, 90 happy.py, 90 hard drive, 3 hardware, 2 hash array, 199 hierarchy chart, 148, see structure chart house, 86 house (of representatives), 120 identifier definition of, 15 rules for forming, 15 Idle, 7 if statement flowchart, 105 semantics, 105 syntax, 105 if-elif-else statement semantics, 111 syntax, 111 if-else statement decision tree, 116 nested, 110, 116 semantics, 109 syntax, 108 Image, 83 implementation, 13 import statement, 30, 106 with #.*from#$), 64 indefinite loop, 124 indexing dictionary, 200 from the right, 52 list, 185, 187 negative indexes, 52 string, 42 infinite loop, 125, 136 inheritance, 229 input, 9 247 validation, 135 input statement, 18 multiple values, 20 semantics, 18 syntax, 18 Input/Output Devices, 3 instance, 66, 161 instance variable, 67, 161 accessing, 168 and object state, 168 int, 28 automatic conversion to float, 36 conversion to float, 37 literal, 28 range of, 34 representaiton, 34 integer division, 29 interface, 148 interpreter, 4 diagram, 5 Python, 5 vs. compiler, 5 intractable problems, 2, 246 investment doubling, 140 IPO (Input, Process, Output), 14 iteration, 20 key cipher, 51 private, 51 public, 51 with dictionary, 199 key-value pair, 199 KeyError, 201 label, 72 ladder, 39 leap year, 120 left-justification, 54 len with list, 185, 188 with string, 43 lexicographic ordering, 105 library definition of, 29 graphics, see graphics library math, see math library random, see random library string, see string library lightning, 39 Line, 82 line continuation using backslash ( ), 55 using brackets, 191INDEX 248 linear time, 234 list, 184 as sequence, 185 creation, 186 empty, 186 indexing, 185 merging, 239 methods, 187, 203 operators, 185 removing items, 187 slice, 187 vs. string, 185 literal, 16 float, 28 int, 28 string, 41, 172 log time, 234 long int, 35 when to use, 36 loop, 9 accumulator variable, 31 as control structure, 22 counted, 21, 22 definite, 20, 124 end-of-file, 129 event loop, 179 for statement, 21 indefinite (conditional), 124 index variable, 21 infinite, 125, 136 interactive, 126 loop and a half, 136 nested, 130 over a sequence, 21 post-test, 135 using break, 136 using while, 135 pre-test, 124 vs. recursive function, 237 while statement, 124 loop and a half, 136 lower, 59 Lucas, Edouard, 243 machine code, 4 maintenance, 13 mapping, 199 math library, 29 functions, 30, 31 using, 30 math range error, 30 max, 118 max-of-n program, 117 max-of-three, 114$u maxn.py, 117 mean, 188 median, 184, 189 memory, 3 main, 3 secondary, 3 merge, 239 merge sort, 239, see sorting, merge sort mergeSort, 240 analysis, 241 message decoding algorithm, 48 problem description, 48 program, 49 message encoding algorithm, 47 problem description, 46 program, 47 meta-language, 17 method, 67, 161 parameterless, 68 accessor, 68 call (invoke), 67, 167 mutator, 68 normal parameter, 167 object parameters, 68 self parameter, 167 methods activate, 174 clicked, 174 deactivate, 174 dictionary, 200 list, 186, 187 model-view, 217 module file, 7 module hierarchy chart, see structure chart molecular weight, 39 Monte Carlo, 144, 159 month abbreviation problem description, 44 program, 45 month.py, 45 move, 68, 82 moveTower, 244 MPG, 140 MSDie, 166 mutable, 185, 200 mutator, 68 name error, 16, 42 names, 15 nesting, 110 newline character ( n), 55, 108 with readline, 130INDEX 249 Newton's method, 40 None, 97 numbers2text.py, 49 numerology, 61 object, 161 aliasing, 69 application as, 194 as black box, 207 as parameter, 68 attributes, 161 definition of, 63 state, 68 object oriented design (OOD), 207 object-oriented, 63 objects built-in file, 59 None, 97 string, 59 graphics, see graphics library, objects other, see classes objrball.py, 213 Old MacDonald, 100 one-way decision, 105 open, 56 operator Boolean, 132 as control structure, 138 definition of, 16 precedence, 16, 132 relational, 105 short-circuit, 138 operators Boolean, 132 del, 187 list, 185 mathematical, 16 Python numeric operators, 28 relational, 105 string formatting, 53 or, 132 operational definition, 138 ord, 46 output labeling, 17 output statements, 16 Oval, 83 overflow error, 33 override, 230 overtime, 120 parameter, 6 actual, 93 as function input, 95 formal, 93 functions as parameters, 203 matching by order, 95 multiple, 94 objects as, 68 removing code duplication, 90 scope issues, 92, 93 self, 167 pi math library, 31 Monte Carlo approximation, 159 series approximation, 39 pixel, 65 pizza, 38 plaintext, 50 Player, 213 playing card, 181 plot, 81 plotPixel, 81 Point, 65, 82 poker, see dice poker Polygon, 79, 83 polymorphism, 228 portability, 5 post-test loop, 135 precision, 53 prime number, 140, 206 priming read, 127 print statement, 6, 17 semantics, 17 syntax, 17 printfile.py, 57 prism, 180 private attributes, 178 private key encryption, 51 program, 1 programming definition of, 2 environment, 7 event-driven, 77 why learn, 2 programming language, 4)**5 and portability, 5 vs. natural language, 4 examples, 4 high-level, 4 syntax, 17 translation, 4 programs, 35 average n numbers, 123, 126&%.130 average two numbers, 20 calculator, 197 cannonball simulation, 164, 169, 172 change counter, 27, 54INDEX 250 chaos, 7 dice roller, 178 factorial, 32 future value, 24 future value graph, 74, 76, 87, 91, 99 goofy: an impossible program, 247 happy birthday, 90 max-of-n, 117 message decoding, 49 message encoding, 47 month abbreviation, 45 print file, 57 quadratic equation, 29, 107$,#109, 111$,!113 racquetball simulation, 153 racquetball simulation (object version, 213 simple statistics, 189 temperature conversion, 14, 79, 103, 104 triangle, 78, 96 username generation, 44, 58 word frequency, 204 prompt Python, 6 using Text object, 79 prototype, 156 pseudocode, 14 pseudorandom numbers, 144 public key encryption, 51 pyc file, 8 Python Boolean operators, 132 mathematical operators, 16 numeric operators, 28 programming environment, 7 relational operators, 105 reserved words, 15 running programs, 7 pyw, 78 quadratic equation, 29 algorithm with three-way decision, 110 decision flowchart, 109 program, 29, 107 program (bullet-proof), 113 program (simple if), 108 program (two-way decision), 109 program (using exception), 112 program (using if-elif-else), 111 quadratic time, 241 quadratic.py, 29, 107 quadratic2.py, 108 quadratic3.py, 109 quadratic4.py, 111 quadratic5.py, 112 quadratic6.py, 113 quiz grader, 60, 120 racquetball, 133, 143 racquetball simulation algorithms simNGames, 149 simOneGmae, 150 classes Player, 213 RBallGame, 211 SimStats, 210 discussion, 156 problem description, 144 program, 153 program (object version), 213 specification, 144 structure charts level 2, 150 level 3, 151 top-level, 148 RAM (random access memory), 3 random, 145 random library, 145 functions random, 145 randrange, 145 random numbers, 144 random walk, 159 randrange, 145 range, 21 general form, 32 range error, 30 raw input, 42 RBallGame, 211 read, 56 readline, 56 readlines, 56 recBinSearch, 237 Rectangle, 82 recursion, 235 regression line, 141, 181 relational operator, 105 repetition list, 185 string, 43 requirements, 13 reserved words definition of, 15 in Python, 15 resolution, 72 return statement, 95 multiple values, 96 roller.py, 178 root beer, 30INDEX round, 37 scientific notation, 35 scope, 92 screen resolution, 72 script, 7 search, 231 searching binary search, 232 linear search, 232 problem description, 231 recursive formulation, 237 seed, 145 selection sort, see sorting, selection sort self, 167 selSort, 238 semantics, 4 senate, 120 sentinel, 127 sentinel loop, 127 sequence operators, 185 setArrow, 82 setBackground, 81 setCoords, 75, 81 example, 76 setFace, 83 setFill, 82 setOutline, 82 sets, 206 setSize, 83 setStyle, 83 setText, 83 setWidth, 82 shell game, 179 shuffle, 206 Sieve of Eratosthenes, 206 signature, 148 simNGames, 150 simOneGame, 152 simple decision, 105 simple statistics, 205 problem, 184 program, 189 SimStats, 210 simulation, 143 simultaneous assignment, 19 in for loop, 196 with multiple return values, 97 singFred, 89 singLucy, 89 slicing list, 187 string, 43 slope of line, 39 251 snowman, 85 software, 2 software development, 13 phases design, 13 implementation, 13 maintenance, 13 requirements, 13 specifications, 13 testing/debugging, 13 sort, 203 sorting, 234 merge sort algorithm, 240 analysis, 241 implementation, 240 selection sort algorithm, 238 analysis, 241 implementation, 238 space between program lines, 24 blank line in output, 94, 108 in expressions, 16 in prompts, 18 specifications, 13 speeding fine, 120 sphere, 38, 180 surface area formula, 38 volume formula, 38 split, 48 sqrt, 30 square function, 95 square root, 40 standard deviation, 184 statement, 6 statements assignment, 10, 17"')20 break, 136 class, 167 comment, 9 def (function definition), 6, 88 for, 21, 123 from..import, 64 if, 105 if-elif-else, 111 if-else, 108 import, 30 input, 9, 18 multiple input, 20 print, 6, 16(-%17 return, 95 simultaneous assignment, 19 try-except, 113INDEX 252 while, 124 stats.py, 189 stdDev, 188 step-wise refinement, 154 str, 52 string, 17, 41 as input, 41 as lookup table, 44 ASCII encoding, 46 concatenation, 43 converting to, 52 converting to other types, 49 formatting, see string formatting formatting operator (%), 53 indexing, 42 from back, 52 length, 43 library, see string library literal, 41, 172 multi-line, 172 operators, 44 repetition, 43 representation, 46 slicing, 43 substring, 43 UniCode encoding, 46 vs. list, 185 string formatting, 52 examples, 53 format specifier, 53 leading zeroes, 55 left-justification, 54 using a tuple, 204 string library, 48 function summary, 51 lower, 59 split, 48 structure chart, 148 structure charts racquetball simulation level 2, 150 racquetball simulation level 3, 151 racquetball simulation top level, 148 subprogram, 88 substitution cipher, 50 substring, 43 swap, 19 using simultaneous assignment, 20 syntax, 4, 17 Syracuse numbers, 140 problem description, 13 program, 14, 103 program with GUI, 79 temperature conversion with warnings design, 103 flowchart, 104 problem description, 103 program, 104 tennis, 158 testing, 13 unit, 155 Text, 83 as prompt, 79 methods, 83 text file, 55 text2numbers.py, 47 textpoker.py, 221 Tkinter, 64 top-down design, 146 steps in process, 154 Towers of Hanoi (Brahma), 243 recursive solution, 244 Tracker, 180 triangle area formula, 39 program, 78, 96 triangle.pyw, 78 triangle2.py, 96 truth table, 132 truth tables definition of and, 132 definition of not, 132 definition of or, 132 try-except statement semantics, 113 syntax, 113 tuple, 195 as string formtting argument, 204 unpacking, 196 type conversion to float, 37 automatic, 36 explicit, 36 from string, 49 summary of functions, 52 to int, 37 to long int, 37 to string, 52 type function, 28 table tennis, 158 table-driven, 192 temperature conversion algorithm, 14 undraw, 82 UniCode, 46 unit testing, 155 unpacking, 196INDEX userfile.py, 58 username generation program, 44, 58 username.py, 44 validation of inputs, 135 value returning function, 95 variable changing value, 18 definition of, 9 instance, 67, 161 local, 92 scope, 92 VGA, 72 volleyball, 133, 158 wc, 62 while statement as post-test loop, 135 flow chart, 125 semantics, 124 syntax, 124 whitespace, see space widget, 77, 172 width, 53 windchill, 140 winter, 85 word count, 62 word frequency problem description, 201 program, 204 wordfreq.py, 204 write, 57 C++: A second edition is a second chance to acknowledge and to thank those folks without whose support and help this book literally would have been impossible. First among them are Stacey, Robin, and Rachel Liberty. I must also thank everyone associated with my books, both at Sams and at Wrox press, for being professionals of the highest quality. The editors at Sams did a fantastic job, and I must especially acknowledge and thank Fran Hatton, Mary Ann Abramson, Greg Guntle, and Chris Denny. I have taught an online course based on this book for a couple years, and many folks there contributed to finding and eradicating bugs and errors. A very large debt is owed to these folks, and I must especially thank Greg Newman, Corrinne Thompson, and also Katherine Prouty and Jennifer Goldman. I would also like to acknowledge the folks who taught me how to program: Skip Gilbrech and David McCune, and those who taught me C++, including Steve Rogers and Stephen Zagieboylo. I want particularly to thank Mike Kraley, Ed Belove, Patrick Johnson, Mike Rothman, and Sangam Pant, all of whom taught me how to manage a project and ship a product. Others who contributed directly or indirectly to this book include: Scott Boag, David Bogartz, Gene Broadway, Drew and Al Carlson, Frank Childs, Jim Culbert, Thomas Dobbing, James Efstratiou,David Heath, Eric Helliwell, Gisele and Ed Herlihy, Mushtaq Khalique, Matt Kingman, Steve Leland, Michael Smith, Frank Tino, Donovan White, Mark Woodbury, Wayne Wylupski, and Alan Zeitchek. Programming is as much a business and creative experience as it is a technical one, and I must therefore acknowledge Tom Hottenstein, Jay Leve, David Rollert, David Shnaider, and Robert Spielvogel. Finally, I'd like to thank Mrs. Kalish, who taught my sixth-grade class how to do binary arithmetic in 1965, when neither she nor we knew why. Tell Us What You Think! As a reader, you are the most important critic and commentator of our books. We value your opinion and want to know what we're doing right, what we could do better, what areas you'd like to see us publish in, and any other words of wisdom you're willing to pass our way. You can help us make strong books that meet your needs and give you the computer guidance you require. Do you have access to CompuServe or the World Wide Web? Then check out our CompuServe forum by typing GO SAMS at any prompt. If you prefer the World Wide Web, check out our site at http://www.mcp.com NOTE: If you have a technical question about this book, call the technical support line at 317-581-3833. As the publishing manager of the group that created this book, I welcome your comments. You can fax, e-mail, or write me directly to let me know what you did or didn't like about this book--as well as what we can do to make our books stronger. Here's the information: Fax: 317-581-4669 E-mail: programming_mgr@sams.mcp.com Mail: Greg Wiegand Sams Publishing 201 W. 103rd Street Indianapolis, IN 46290 Introduction This book is designed to help you teach yourself how to program with C++. In just 21 days, you'll learn about such fundamentals as managing I/O, loops and arrays, object-oriented programming, templates, and creating C++ applications--all in well-structured and easy-to-follow lessons. Lessons provide sample listings--complete with sample output and an analysis of the code--to illustrate the topics of the day. Syntax examples are clearly marked for handy reference.To help you become more proficient, each lesson ends with a set of common questions and answers, exercises, and a quiz. You can check your progress by examining the quiz and exercise answers provided in the book's appendix. Who Should Read This Book You don't need any previous experience in programming to learn C++ with this book. This book starts you from the beginning and teaches you both the language and the concepts involved with programming C++. You'll find the numerous examples of syntax and detailed analysis of code an excellent guide as you begin your journey into this rewarding environment. Whether you are just beginning or already have some experience programming, you will find that this book's clear organization makes learning C++ fast and easy. Conventions NOTE: These boxes highlight information that can make your C++ programming more efficient and effective. WARNING: These focus your attention on problems or side effects that can occur in specific situations. These boxes provide clear definitions of essential terms. DO use the "Do/Don't" boxes to find a quick summary of a fundamental principle in a lesson. DON'T overlook the useful information offered in these boxes. This book uses various typefaces to help you distinguish C++ code from regular English. Actual C++ code is typeset in a special monospace font. Placeholders--words or characters temporarily used to represent the real words or characters you would type in code--are typeset in italic monospace. New or important terms are typeset in italic. In the listings in this book, each real code line is numbered. If you see an unnumbered line in a listing, you'll know that the unnumbered line is really a continuation of the preceding numbered code line (some code lines are too long for the width of the book). In this case, you should type the two lines as one; do not divide them. Day 1 Getting Started Introduction A Brief History of C++ Programs Solving Problems Procedural, Structured, and Object-Oriented Programming C++ and Object-Oriented Programming How C++ Evolved The ANSI Standard Should I Learn C First? Preparing to Program Your Development Environment Compiling the Source Code Creating an Executable File with the Linker The Development Cycle Figure 1.1. HELLO.CPPYour First C++ Program Listing 1.1. HELLO.CPP, the Hello World program. Compile Errors Listing 1.2. Demonstration of compiler error. Summary Q&A Workshop Quiz Exercises Day 1 Getting Started IntroductionWelcome to Teach Yourself C++ in 21 Days! Today you will get started on your way to becoming a proficient C++ programmer. You'll learn Why C++ is the emerging standard in software development. The steps to develop a C++ program. How to enter, compile, and link your first working C++ program. A Brief History of C++ Computer languages have undergone dramatic evolution since the first electronic computers were built to assist in telemetry calculations during World War II. Early on, programmers worked with the most primitive computer instructions: machine language. These instructions were represented by long strings of ones and zeroes. Soon, assemblers were invented to map machine instructions to human- readable and -manageable mnemonics, such as ADD and MOV. In time, higher-level languages evolved, such as BASIC and COBOL. These languages let people work with something approximating words and sentences, such as Let I = 100. These instructions were translated back into machine language by interpreters and compilers. An interpreter translates a program as it reads it, turning the program instructions, or code, directly into actions. A compiler translates the code into an intermediary form. This step is called compiling, and produces an object file. The compiler then invokes a linker, which turns the object file into an executable program. Because interpreters read the code as it is written and execute the code on the spot, interpreters are easy for the programmer to work with. Compilers, however, introduce the extra steps of compiling and linking the code, which is inconvenient. Compilers produce a program that is very fast each time it is run. However, the time-consuming task of translating the source code into machine language has already been accomplished. Another advantage of many compiled languages like C++ is that you can distribute the executable program to people who don't have the compiler. With an interpretive language, you must have the language to run the program. For many years, the principle goal of computer programmers was to write short pieces of code that would execute quickly. The program needed to be small, because memory was expensive, and it needed to be fast, because processing power was also expensive. As computers have become smaller, cheaper, and faster, and as the cost of memory has fallen, these priorities have changed. Today the cost of a programmer's time far outweighs the cost of most of the computers in use by businesses. Well-written, easy-to-maintain code is at a premium. Easy- to-maintain means that as business requirements change, the program can be extended and enhanced without great expense. ProgramsThe word program is used in two ways: to describe individual instructions, or source code, created by the programmer, and to describe an entire piece of executable software. This distinction can cause enormous confusion, so we will try to distinguish between the source code on one hand, and the executable on the other. New Term: A program can be defined as either a set of written instructions created by a programmer or an executable piece of software. Source code can be turned into an executable program in two ways: Interpreters translate the source code into computer instructions, and the computer acts on those instructions immediately. Alternatively, compilers translate source code into a program, which you can run at a later time. While interpreters are easier to work with, most serious programming is done with compilers because compiled code runs much faster. C++ is a compiled language. Solving Problems The problems programmers are asked to solve have been changing. Twenty years ago, programs were created to manage large amounts of raw data. The people writing the code and the people using the program were all computer professionals. Today, computers are in use by far more people, and most know very little about how computers and programs work. Computers are tools used by people who are more interested in solving their business problems than struggling with the computer. Ironically, in order to become easier to use for this new audience, programs have become far more sophisticated. Gone are the days when users typed in cryptic commands at esoteric prompts, only to see a stream of raw data. Today's programs use sophisticated "user-friendly interfaces," involving multiple windows, menus, dialog boxes, and the myriad of metaphors with which we've all become familiar. The programs written to support this new approach are far more complex than those written just ten years ago. As programming requirements have changed, both languages and the techniques used for writing programs have evolved. While the complete history is fascinating, this book will focus on the transformation from procedural programming to object-oriented programming. Procedural, Structured, and Object-Oriented Programming Until recently, programs were thought of as a series of procedures that acted upon data. A procedure, or function, is a set of specific instructions executed one after the other. The data was quite separate from the procedures, and the trick in programming was to keep track of which functions called which other functions, and what data was changed. To make sense of this potentially confusing situation, structured programming was created. The principle idea behind structured programming is as simple as the idea of divide and conquer. A computer program can be thought of as consisting of a set of tasks. Any task that is too complex to bedescribed simply would be broken down into a set of smaller component tasks, until the tasks were sufficiently small and self-contained enough that they were easily understood. As an example, computing the average salary of every employee of a company is a rather complex task. You can, however, break it down into these subtasks: 1. Find out what each person earns. 2. Count how many people you have. 3. Total all the salaries. 4. Divide the total by the number of people you have. Totaling the salaries can be broken down into 1. Get each employee's record. 2. Access the salary. 3. Add the salary to the running total. 4. Get the next employee's record. In turn, obtaining each employee's record can be broken down into 1. Open the file of employees. 2. Go to the correct record. 3. Read the data from disk. Structured programming remains an enormously successful approach for dealing with complex problems. By the late 1980s, however, some of the deficiencies of structured programing had became all too clear. First, it is natural to think of your data (employee records, for example) and what you can do with your data (sort, edit, and so on) as related ideas. Second, programmers found themselves constantly reinventing new solutions to old problems. This is often called "reinventing the wheel," and is the opposite of reusability. The idea behind reusability is to build components that have known properties, and then to be able to plug them into your program as you need them. This is modeled after the hardware world--when an engineer needs a new transistor, she doesn't usually invent one, she goes to the big bin of transistors and finds one that works the way she needs it to, or perhaps modifies it. There was no similar option for a software engineer.New Term: The way we are now using computers--with menus and buttons and windows-- fosters a more interactive, event-driven approach to computer programming. Event-driven means that an event happens--the user presses a button or chooses from a menu--and the program must respond. Programs are becoming increasingly interactive, and it has became important to design for that kind of functionality. Old-fashioned programs forced the user to proceed step-by-step through a series of screens. Modern event-driven programs present all the choices at once and respond to the user's actions. Object-oriented programming attempts to respond to these needs, providing techniques for managing enormous complexity, achieving reuse of software components, and coupling data with the tasks that manipulate that data. The essence of object-oriented programming is to treat data and the procedures that act upon the data as a single "object"--a self-contained entity with an identity and certain characteristics of its own. C++ and Object-Oriented Programming C++ fully supports object-oriented programming, including the four pillars of object-oriented development: encapsulation, data hiding, inheritance, and polymorphism. Encapsulation and Data Hiding When an engineer needs to add a resistor to the device she is creating, she doesn't typically build a new one from scratch. She walks over to a bin of resistors, examines the colored bands that indicate the properties, and picks the one she needs. The resistor is a "black box" as far as the engineer is concerned--she doesn't much care how it does its work as long as it conforms to her specifications; she doesn't need to look inside the box to use it in her design. The property of being a self-contained unit is called encapsulation. With encapsulation, we can accomplish data hiding. Data hiding is the highly valued characteristic that an object can be used without the user knowing or caring how it works internally. Just as you can use a refrigerator without knowing how the compressor works, you can use a well-designed object without knowing about its internal data members. Similarly, when the engineer uses the resistor, she need not know anything about the internal state of the resistor. All the properties of the resistor are encapsulated in the resistor object; they are not spread out through the circuitry. It is not necessary to understand how the resistor works in order to use it effectively. Its data is hidden inside the resistor's casing. C++ supports the properties of encapsulation and data hiding through the creation of user-defined types, called classes. You'll see how to create classes on Day 6, "Basic Classes." Once created, a well- defined class acts as a fully encapsulated entity--it is used as a whole unit. The actual inner workings of the class should be hidden. Users of a well-defined class do not need to know how the class works; they just need to know how to use it. Inheritance and Reuse When the engineers at Acme Motors want to build a new car, they have two choices: They can start from scratch, or they can modify an existing model. Perhaps their Star model is nearly perfect, but they'd like to add a turbocharger and a six-speedtransmission. The chief engineer would prefer not to start from the ground up, but rather to say, "Let's build another Star, but let's add these additional capabilities. We'll call the new model a Quasar." A Quasar is a kind of Star, but one with new features. C++ supports the idea of reuse through inheritance. A new type, which is an extension of an existing type, can be declared. This new subclass is said to derive from the existing type and is sometimes called a derived type. The Quasar is derived from the Star and thus inherits all its qualities, but can add to them as needed. Inheritance and its application in C++ are discussed on Day 12, "Inheritance," and Day 15, "Advanced Inheritance." Polymorphism The new Quasar might respond differently than a Star does when you press down on the accelerator. The Quasar might engage fuel injection and a turbocharger, while the Star would simply let gasoline into its carburetor. A user, however, does not have to know about these differences. He can just "floor it," and the right thing will happen, depending on which car he's driving. C++ supports the idea that different objects do "the right thing" through what is called function polymorphism and class polymorphism. Poly means many, and morph means form. Polymorphism refers to the same name taking many forms, and is discussed on Day 10, "Advanced Functions," and Day 13, "Polymorphism." How C++ Evolved As object-oriented analysis, design, and programming began to catch on, Bjarne Stroustrup took the most popular language for commercial software development, C, and extended it to provide the features needed to facilitate object-oriented programming. He created C++, and in less than a decade it has gone from being used by only a handful of developers at AT&T to being the programming language of choice for an estimated one million developers worldwide. It is expected that by the end of the decade, C++ will be the predominant language for commercial software development. While it is true that C++ is a superset of C, and that virtually any legal C program is a legal C++ program, the leap from C to C++ is very significant. C++ benefited from its relationship to C for many years, as C programmers could ease into their use of C++. To really get the full benefit of C++, however, many programmers found they had to unlearn much of what they knew and learn a whole new way of conceptualizing and solving programming problems. The ANSI Standard The Accredited Standards Committee, operating under the procedures of the American National Standards Institute (ANSI), is working to create an international standard for C++. The draft of this standard has been published, and a link is available at www.libertyassociates.com. The ANSI standard is an attempt to ensure that C++ is portable--that code you write for Microsoft's compiler will compile without errors, using a compiler from any other vendor. Further, because thecode in this book is ANSI compliant, it should compile without errors on a Mac, a Windows box, or an Alpha. For most students of C++, the ANSI standard will be invisible. The standard has been stable for a while, and all the major manufacturers support the ANSI standard. We have endeavored to ensure that all the code in this edition of this book is ANSI compliant. Should I Learn C First? The question inevitably arises: "Since C++ is a superset of C, should I learn C first?" Stroustrup and most other C++ programmers agree. Not only is it unnecessary to learn C first, it may be advantageous not to do so. This book attempts to meet the needs of people like you, who come to C++ without prior experience of C. In fact, this book assumes no programming experience of any kind. Preparing to Program C++, perhaps more than other languages, demands that the programmer design the program before writing it. Trivial problems, such as the ones discussed in the first few chapters of this book, don't require much design. Complex problems, however, such as the ones professional programmers are challenged with every day, do require design, and the more thorough the design, the more likely it is that the program will solve the problems it is designed to solve, on time and on budget. A good design also makes for a program that is relatively bug-free and easy to maintain. It has been estimated that fully 90 percent of the cost of software is the combined cost of debugging and maintenance. To the extent that good design can reduce those costs, it can have a significant impact on the bottom-line cost of the project. The first question you need to ask when preparing to design any program is, "What is the problem I'm trying to solve?" Every program should have a clear, well-articulated goal, and you'll find that even the simplest programs in this book do so. The second question every good programmer asks is, "Can this be accomplished without resorting to writing custom software?" Reusing an old program, using pen and paper, or buying software off the shelf is often a better solution to a problem than writing something new. The programmer who can offer these alternatives will never suffer from lack of work; finding less-expensive solutions to today's problems will always generate new opportunities later. Assuming you understand the problem, and it requires writing a new program, you are ready to begin your design. Your Development Environment This book makes the assumption that your computer has a mode in which you can write directly to the screen, without worrying about a graphical environment, such as the ones in Windows or on the Macintosh.Your compiler may have its own built-in text editor, or you may be using a commercial text editor or word processor that can produce text files. The important thing is that whatever you write your program in, it must save simple, plain-text files, with no word processing commands embedded in the text. Examples of safe editors include Windows Notepad, the DOS Edit command, Brief, Epsilon, EMACS, and vi. Many commercial word processors, such as WordPerfect, Word, and dozens of others, also offer a method for saving simple text files. The files you create with your editor are called source files, and for C++ they typically are named with the extension .CPP, .CP, or .C. In this book, we'll name all the source code files with the .CPP extension, but check your compiler for what it needs. NOTE: Most C++ compilers don't care what extension you give your source code, but if you don't specify otherwise, many will use .CPP by default. DO use a simple text editor to create your source code, or use the built-in editor that comes with your compiler. DON'T use a word processor that saves special formatting characters. If you do use a word processor, save the file as ASCII text. DO save your files with the .C, .CP, or .CPP extension. DO check your documentation for specifics about your compiler and linker to ensure that you know how to compile and link your programs. Compiling the Source Code Although the source code in your file is somewhat cryptic, and anyone who doesn't know C++ will struggle to understand what it is for, it is still in what we call human-readable form. Your source code file is not a program, and it can't be executed, or run, as a program can. To turn your source code into a program, you use a compiler. How you invoke your compiler, and how you tell it where to find your source code, will vary from compiler to compiler; check your documentation. In Borland's Turbo C++ you pick the RUN menu command or type tc from the command line, where is the name of your source code file (for example, test.cpp). Other compilers may do things slightly differently. NOTE: If you compile the source code from the operating system's command line, you should type the following: For the Borland C++ compiler: bcc For the Borland C++ for Windows compiler: bcc For the Borland Turbo C++ compiler: tc For the Microsoft compilers: cl After your source code is compiled, an object file is produced. This file is often named with the extension .OBJ. This is still not an executable program, however. To turn this into an executable program, you must run your linker. Creating an Executable File with the Linker C++ programs are typically created by linking together one or more OBJ files with one or more libraries. A library is a collection of linkable files that were supplied with your compiler, that you purchased separately, or that you created and compiled. All C++ compilers come with a library of useful functions (or procedures) and classes that you can include in your program. A function is a block of code that performs a service, such as adding two numbers or printing to the screen. A class is a collection of data and related functions; we'll be talking about classes a lot, starting on Day 5, "Functions." The steps to create an executable file are 1. Create a source code file, with a .CPP extension. 2. Compile the source code into a file with the .OBJ extension. 3. Link your OBJ file with any needed libraries to produce an executable program. The Development Cycle If every program worked the first time you tried it, that would be the complete development cycle: Write the program, compile the source code, link the program, and run it. Unfortunately, almost every program, no matter how trivial, can and will have errors, or bugs, in the program. Some bugs will cause the compile to fail, some will cause the link to fail, and some will only show up when you run the program. Whatever type of bug you find, you must fix it, and that involves editing your source code, recompiling and relinking, and then rerunning the program. This cycle is represented in Figure 1.1, which diagrams the steps in the development cycle. Figure 1.1. The steps in the development of a C++ program.HELLO.CPPYour First C++ Program Traditional programming books begin by writing the words Hello World to the screen, or a variation on that statement. This time-honored tradition is carried on here. Type the first program directly into your editor, exactly as shown. Once you are certain it is correct, save the file, compile it, link it, and run it. It will print the words Hello World to your screen. Don't worry too much about how it works, this is really just to get you comfortable with the development cycle. Every aspect of this program will be covered over the next couple of days. WARNING: The following listing contains line numbers on the left. These numbers are for reference within the book. They should not be typed in to your editor. For example, in line 1 of Listing 1.1, you should enter: #include Listing 1.1. HELLO.CPP, the Hello World program. 1: 2: 3: 4: 5: 6: 7: #include int main() { cout << "Hello World!\n"; return 0; } Make certain you enter this exactly as shown. Pay careful attention to the punctuation. The << in line 5 is the redirection symbol, produced on most keyboards by holding the Shift key and pressing the comma key twice. Line 5 ends with a semicolon; don't leave this off! Also check to make sure you are following your compiler directions properly. Most compilers will link automatically, but check your documentation. If you get errors, look over your code carefully and determine how it is different from the above. If you see an error on line 1, such as cannot find file iostream.h, check your compiler documentation for directions on setting up your include path or environment variables. If you receive an error that there is no prototype for main, add the line int main(); just before line 3. You will need to add this line before the beginning of the main function in every program in this book. Most compilers don't require this, but a few do. Your finished program will look like this: 1: #include 2: 3: 4: 5: 6: 7: 8: int main(); { cout <<"Hello World!\n"; return 0; } Try running HELLO.EXE; it should write Hello World! directly to your screen. If so, congratulations! You've just entered, compiled, and run your first C++ program. It may not look like much, but almost every professional C++ programmer started out with this exact program. Compile Errors Compile-time errors can occur for any number of reasons. Usually they are a result of a typo or other inadvertent minor error. Good compilers will not only tell you what you did wrong, they'll point you to the exact place in your code where you made the mistake. The great ones will even suggest a remedy! You can see this by intentionally putting an error into your program. If HELLO.CPP ran smoothly, edit it now and remove the closing brace on line 6. Your program will now look like Listing 1.2. Listing 1.2. Demonstration of compiler error. 1: 2: 3: 4: 5: 6: #include int main() { cout << "Hello World!\n"; return 0; Recompile your program and you should see an error that looks similar to the following: Hello.cpp, line 5: Compound statement missing terminating } in function main(). This error tells you the file and line number of the problem, and what the problem is (although I admit it is somewhat cryptic). Note that the error message points you to line 5. The compiler wasn't sure if you intended to put the closing brace before or after the cout statement on line 5. Sometimes theerrors just get you to the general vicinity of the problem. If a compiler could perfectly identify every problem, it would fix the code itself. Summary After reading this chapter, you should have a good understanding of how C++ evolved and what problems it was designed to solve. You should feel confident that learning C++ is the right choice for anyone interested in programming in the next decade. C++ provides the tools of object-oriented programming and the performance of a systems-level language, which makes C++ the development language of choice. Today you learned how to enter, compile, link, and run your first C++ program, and what the normal development cycle is. You also learned a little of what object-oriented programming is all about. You will return to these topics during the next three weeks. Q&A Q. What is the difference between a text editor and a word processor? A. A text editor produces files with plain text in them. There are no formatting commands or other special symbols required by a particular word processor. Text files do not have automatic word wrap, bold print, italics, and so forth. Q. If my compiler has a built-in editor, must I use it? A. Almost all compilers will compile code produced by any text editor. The advantages of using the built-in text editor, however, might include the ability to quickly move back and forth between the edit and compile steps of the development cycle. Sophisticated compilers include a fully integrated development environment, allowing the programmer to access help files, edit, and compile the code in place, and to resolve compile and link errors without ever leaving the environment. Q. Can I ignore warning messages from my compiler? A. Many books hedge on this one, but I'll stake myself to this position: No! Get into the habit, from day one, of treating warning messages as errors. C++ uses the compiler to warn you when you are doing something you may not intend. Heed those warnings, and do what is required to make them go away. Q. What is compile time? A. Compile time is the time when you run your compiler, as opposed to link time (when you run the linker) or run-time (when running the program). This is just programmer shorthand to identify the three times when errors usually surface. WorkshopThe Workshop provides quiz questions to help you solidify your understanding of the material covered and exercises to provide you with experience in using what you've learned. Try to answer the quiz and exercise questions before checking the answers in Appendix D, and make sure you understand the answers before continuing to the next chapter. Quiz 1. What is the difference between an interpreter and a compiler? 2. How do you compile the source code with your compiler? 3. What does the linker do? 4. What are the steps in the normal development cycle? Exercises 1. Look at the following program and try to guess what it does without running it. 1: #include 2: int main() 3: { 4: int x = 5; 5: int y = 7; 6: cout "\n"; 7: cout << x + y << " " << x * y; 8: cout "\n"; 9:return 0; 10: } 2. Type in the program from Exercise 1, and then compile and link it. What does it do? Does it do what you guessed? 3. Type in the following program and compile it. What error do you receive? 1: 2: 3: 4: 5: 6: include int main() { cout << "Hello World\n"; return 0; } 4. Fix the error in the program in Exercise 3, and recompile, link, and run it. What does it do?",# Day 2 The Parts of a C++ Program A Simple Program Listing 2.1. HELLO.CPP demonstrates the parts of a C++ program. A Brief Look at cout Listing 2.2. Using cout. Comments Types of Comments Using Comments Listing 2.3. HELP.CPP demonstrates comments. Comments at the Top of Each File A Final Word of Caution About Comments Functions Listing 2.4. Demonstrating a call to a function. Using Functions Listing 2.5. FUNC.CPP demonstrates a simple function. Summary Q&A Workshop Quiz Exercises Day 2 The Parts of a C++ Program C++ programs consist of objects, functions, variables, and other component parts. Most of this book is devoted to explaining these parts in depth, but to get a sense of how a program fits together you must see a complete working program. Today you learn The parts of a C++ program. How the parts work together. What a function is and what it does. A Simple Program Even the simple program HELLO.CPP from Day 1, "Getting Started," had many interesting parts. This section will review this program in more detail. Listing 2.1 reproduces the original version of HELLO.CPP for your convenience. Listing 2.1. HELLO.CPP demonstrates the parts of a C++ program. 1: #include 2: 3: int main() 4: { 5: cout << "Hello World!\n"; 6: return 0; 7: } Hello World! On line 1, the file iostream.h is included in the file. The first character is the # symbol, which is a signal to the preprocessor. Each time you start your compiler, the preprocessor is run. The preprocessor reads through your source code, looking for lines that begin with the pound symbol (#), and acts on those lines before the compiler runs. include is a preprocessor instruction that says, "What follows is a filename. Find that file and read it in right here." The angle brackets around the filename tell the preprocessor to look in all the usual places for this file. If your compiler is set up correctly, the angle brackets will cause the preprocessor to look for the file iostream.h in the directory that holds all the H files for your compiler. The file iostream.h (Input-Output-Stream) is used by cout, which assists with writing to the screen. The effect of line 1 is to include the file iostream.h into this program as if you had typed it in yourself. New Term: The preprocessor runs before your compiler each time the compiler is invoked. The preprocessor translates any line that begins with a pound symbol (#) into a special command, getting your code file ready for the compiler. Line 3 begins the actual program with a function named main(). Every C++ program has a main() function. In general, a function is a block of code that performs one or more actions. Usually functions are invoked or called by other functions, but main() is special. When your program starts, main() is called automatically. main(), like all functions, must state what kind of value it will return. The return value type for main() in HELLO.CPP is void, which means that this function will not return any value at all. Returning values from functions is discussed in detail on Day 4, "Expressions and Statements."All functions begin with an opening brace ({) and end with a closing brace (}). The braces for the main() function are on lines 4 and 7. Everything between the opening and closing braces is considered a part of the function. The meat and potatoes of this program is on line 5. The object cout is used to print a message to the screen. We'll cover objects in general on Day 6, "Basic Classes," and cout and its related object cin in detail on Day 17, "The Preprocessor." These two objects, cout and cin, are used in C++ to print strings and values to the screen. A string is just a set of characters. Here's how cout is used: type the word cout, followed by the output redirection operator (<<). Whatever follows the output redirection operator is written to the screen. If you want a string of characters written, be sure to enclose them in double quotes ("), as shown on line 5. New Term: A text string is a series of printable characters. The final two characters, \n, tell cout to put a new line after the words Hello World! This special code is explained in detail when cout is discussed on Day 17. All ANSI-compliant programs declare main() to return an int. This value is "returned" to the operating system when your program completes. Some programmers signal an error by returning the value 1. In this book, main() will always return 0. The main() function ends on line 7 with the closing brace. A Brief Look at cout On Day 16, "Streams," you will see how to use cout to print data to the screen. For now, you can use cout without fully understanding how it works. To print a value to the screen, write the word cout, followed by the insertion operator (<<), which you create by typing the less-than character (<) twice. Even though this is two characters, C++ treats it as one. Follow the insertion character with your data. Listing 2.2 illustrates how this is used. Type in the example exactly as written, except substitute your own name where you see Jesse Liberty (unless your name is Jesse Liberty, in which case leave it just the way it is; it's perfect-- but I'm still not splitting royalties!). Listing 2.2.Using cout. 1: 2: 3: 4: 5: 6: // Listing 2.2 using cout #include int main() { cout << "Hello there.\n";7: cout << "Here is 5: " << 5 << "\n"; 8: cout << "The manipulator endl writes a new line to the screen." << .&endl; 9: cout << "Here is a very big number:\t" << 70000 << endl; 10: cout << "Here is the sum of 8 and 5:\t" << 8+5 << endl; 11: cout << "Here's a fraction:\t\t" << (float) 5/8 << endl; 12: cout << "And a very very big number:\t" << (double) 7000 * 7000 << ((endl; 13: cout << "Don't forget to replace Jesse Liberty with your name...\n"; 14: cout << "Jesse Liberty is a C++ programmer!\n"; 15: return 0; 16: } Hello there. Here is 5: 5 The manipulator endl writes a new line to the screen. Here is a very big number: 70000 Here is the sum of 8 and 5: 13 Here's a fraction: 0.625 And a very very big number: 4.9e+07 Don't forget to replace Jesse Liberty with your name... Jesse Liberty is a C++ programmer! On line 3, the statement #include causes the iostream.h file to be added to your source code. This is required if you use cout and its related functions. On line 6 is the simplest use of cout, printing a string or series of characters. The symbol \n is a special formatting character. It tells cout to print a newline character to the screen. Three values are passed to cout on line 7, and each value is separated by the insertion operator. The first value is the string "Here is 5: ". Note the space after the colon. The space is part of the string. Next, the value 5 is passed to the insertion operator and the newline character (always in double quotes or single quotes). This causes the line Here is 5: 5 to be printed to the screen. Because there is no newline character after the first string, the next value is printed immediately afterwards. This is called concatenating the two values. On line 8, an informative message is printed, and then the manipulator endl is used. The purpose of endl is to write a new line to the screen. (Other uses for endl are discussed on Day 16.)On line 9, a new formatting character, \t, is introduced. This inserts a tab character and is used on lines 8-12 to line up the output. Line 9 shows that not only integers, but long integers as well can be printed. Line 10 demonstrates that cout will do simple addition. The value of 8+5 is passed to cout, but 13 is printed. On line 11, the value 5/8 is inserted into cout. The term (float) tells cout that you want this value evaluated as a decimal equivalent, and so a fraction is printed. On line 12 the value 7000 * 7000 is given to cout, and the term (double) is used to tell cout that you want this to be printed using scientific notation. All of this will be explained on Day 3, "Variables and Constants," when data types are discussed. On line 14, you substituted your name, and the output confirmed that you are indeed a C++ programmer. It must be true, because the computer said so! Comments When you are writing a program, it is always clear and self-evident what you are trying to do. Funny thing, though--a month later, when you return to the program, it can be quite confusing and unclear. I'm not sure how that confusion creeps into your program, but it always does. To fight the onset of confusion, and to help others understand your code, you'll want to use comments. Comments are simply text that is ignored by the compiler, but that may inform the reader of what you are doing at any particular point in your program. Types of Comments C++ comments come in two flavors: the double-slash (//) comment, and the slash-star (/*) comment. The double-slash comment, which will be referred to as a C++-style comment, tells the compiler to ignore everything that follows this comment, until the end of the line. The slash-star comment mark tells the compiler to ignore everything that follows until it finds a star- slash (*/) comment mark. These marks will be referred to as C-style comments. Every /* must be matched with a closing */. As you might guess, C-style comments are used in the C language as well, but C++-style comments are not part of the official definition of C. Many C++ programmers use the C++-style comment most of the time, and reserve C-style comments for blocking out large blocks of a program. You can include C++-style comments within a block "commented out" by C-style comments; everything, including the C++-style comments, is ignored between the C-style comment marks. Using CommentsAs a general rule, the overall program should have comments at the beginning, telling you what the program does. Each function should also have comments explaining what the function does and what values it returns. Finally, any statement in your program that is obscure or less than obvious should be commented as well. Listing 2.3 demonstrates the use of comments, showing that they do not affect the processing of the program or its output. Listing 2.3. HELP.CPP demonstrates comments . 1: #include 2: 3: int main() 4: { 5: /* this is a comment 6: and it extends until the closing 7: star-slash comment mark */ 8: cout << "Hello World!\n"; 9: // this comment ends at the end of the line 10: cout << "That comment ended!\n"; 11: 12: // double slash comments can be alone on a line 13: /* as can slash-star comments */ 14: return 0; 15: } Hello World! That comment ended! The comments on lines 5 through 7 are completely ignored by the compiler, as are the comments on lines 9, 12, and 13. The comment on line 9 ended with the end of the line, however, while the comments on lines 5 and 13 required a closing comment mark. Comments at the Top of Each File It is a good idea to put a comment block at the top of every file you write. The exact style of this block of comments is a matter of individual taste, but every such header should include at least the following information: The name of the function or program. The name of the file. What the function or program does. A description of how the program works. The author's name. A revision history (notes on each change made). What compilers, linkers, and other tools were used to make the program. Additional notes as needed. For example, the following block of comments might appear at the top of the Hello World program. /************************************************************ Program: Hello World File: Hello.cpp Function: Main (complete program listing in this file) Description: Prints the words "Hello world" to the screen Author: Jesse Liberty (jl) Environment: Turbo C++ version 4, 486/66 32mb RAM, Windows 3.1 DOS 6.0. EasyWin module. Notes: This is an introductory, sample program. Revisions: 1.00 1.01 10/1/94 (jl) First release 10/2/94 (jl) Capitalized "World" ************************************************************/ It is very important that you keep the notes and descriptions up-to-date. A common problem with headers like this is that they are neglected after their initial creation, and over time they become increasingly misleading. When properly maintained, however, they can be an invaluable guide to the overall program. The listings in the rest of this book will leave off the headings in an attempt to save room. That does not diminish their importance, however, so they will appear in the programs provided at the end of each week. A Final Word of Caution About Comments Comments that state the obvious are less than useful. In fact, they can be counterproductive, because the code may change and the programmer may neglect to update the comment. What is obvious to oneperson may be obscure to another, however, so judgment is required. The bottom line is that comments should not say what is happening, they should say why it is happening. DO add comments to your code. DO keep comments up-to-date. DO use comments to tell what a section of code does. DON'T use comments for self-explanatory code. Functions While main() is a function, it is an unusual one. Typical functions are called, or invoked, during the course of your program. A program is executed line by line in the order it appears in your source code, until a function is reached. Then the program branches off to execute the function. When the function finishes, it returns control to the line of code immediately following the call to the function. A good analogy for this is sharpening your pencil. If you are drawing a picture, and your pencil breaks, you might stop drawing, go sharpen the pencil, and then return to what you were doing. When a program needs a service performed, it can call a function to perform the service and then pick up where it left off when the function is finished running. Listing 2.4 demonstrates this idea. Listing 2.4. Demonstrating a call to a function. 1: #include 2: 3: // function Demonstration Function 4: // prints out a useful message 5: void DemonstrationFunction() 6: { 7: cout << "In Demonstration Function\n"; 8: } 9: 10: // function main - prints out a message, then 11: // calls DemonstrationFunction, then prints out 12: // a second message. 13: int main() 14: { 15: cout << "In main\n" ; 16: DemonstrationFunction(); 17: cout << "Back in main\n"; 18: return 0; 19: } In main In Demonstration FunctionBack in main The function DemonstrationFunction() is defined on lines 5-7. When it is called, it prints a message to the screen and then returns. Line 13 is the beginning of the actual program. On line 15, main() prints out a message saying it is in main(). After printing the message, line 16 calls DemonstrationFunction(). This call causes the commands in DemonstrationFunction() to execute. In this case, the entire function consists of the code on line 7, which prints another message. When DemonstrationFunction() completes (line 8), it returns back to where it was called from. In this case the program returns to line 17, where main() prints its final line. Using Functions Functions either return a value or they return void, meaning they return nothing. A function that adds two integers might return the sum, and thus would be defined to return an integer value. A function that just prints a message has nothing to return and would be declared to return void. Functions consist of a header and a body. The header consists, in turn, of the return type, the function name, and the parameters to that function. The parameters to a function allow values to be passed into the function. Thus, if the function were to add two numbers, the numbers would be the parameters to the function. Here's a typical function header: int Sum(int a, int b) A parameter is a declaration of what type of value will be passed in; the actual value passed in by the calling function is called the argument. Many programmers use these two terms, parameters and arguments, as synonyms. Others are careful about the technical distinction. This book will use the terms interchangeably. The body of a function consists of an opening brace, zero or more statements, and a closing brace. The statements constitute the work of the function. A function may return a value, using a return statement. This statement will also cause the function to exit. If you don't put a return statement into your function, it will automatically return void at the end of the function. The value returned must be of the type declared in the function header. NOTE: Functions are covered in more detail on Day 5, "Functions." The types that can be returned from a function are covered in more det+[radical][Delta][infinity]on Day 3. The information provided today is to present you with an overview, because functions will be used in almost all of your C++ programs. Listing 2.5 demonstrates a function that takes two integer parameters and returns an integer value. Don't worry about the syntax or the specifics of how to work with integer values (for example, int x)for now; that is covered in detail on Day 3. Listing 2.5. FUNC.CPP demonstrates a simple function. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: } #include int Add (int x, int y) { cout << "In Add(), received " << x << " and " << y << "\n"; return (x+y); } int main() { cout << "I'm in main()!\n"; int a, b, c; cout << "Enter two numbers: "; cin >> a; cin >> b; cout << "\nCalling Add()\n"; c=Add(a,b); cout << "\nBack in main().\n"; cout << "c was set to " << c; cout << "\nExiting...\n\n"; return 0; I'm in main()! Enter two numbers: 3 5 Calling Add() In Add(), received 3 and 5 Back in main(). c was set to 8 Exiting... The function Add() is defined on line 2. It takes two integer parameters and returns an integer value. The program itself begins on line 9 and on line 11, where it prints a message. The program prompts the user for two numbers (lines 13 to 15). The user types each number, separated by a space, and then presses the Enter key. main() passes the two numbers typed in by the user as arguments to the Add() function on line 17. Processing branches to the Add() function, which starts on line 2. The parameters a and b are printed and then added together. The result is returned on line 6, and the function returns.In lines 14 and 15, the cin object is used to obtain a number for the variables a and b, and cout is used to write the values to the screen. Variables and other aspects of this program are explored in depth in the next few days. Summary The difficulty in learning a complex subject, such as programming, is that so much of what you learn depends on everything else there is to learn. This chapter introduced the basic parts of a simple C++ program. It also introduced the development cycle and a number of important new terms. Q&A Q. What does #include do? A. This is a directive to the preprocessor, which runs when you call your compiler. This specific directive causes the file named after the word include to be read in, as if it were typed in at that location in your source code. Q. What is the difference between // comments and /* style comments? A. The double-slash comments (//) "expire" at the end of the line. Slash-star (/*) comments are in effect until a closing comment (*/). Remember, not even the end of the function terminates a slash-star comment; you must put in the closing comment mark, or you will get a compile-time error. Q. What differentiates a good comment from a bad comment? A. A good comment tells the reader why this particular code is doing whatever it is doing or explains what a section of code is about to do. A bad comment restates what a particular line of code is doing. Lines of code should be written so that they speak for themselves. Reading the line of code should tell you what it is doing without needing a comment. Workshop The Workshop provides quiz questions to help you solidify your understanding of the material covered and exercises to provide you with experience in using what you've learned. Try to answer the quiz and exercise questions before checking the answers in Appendix D, and make sure you understand the answers before continuing to the next chapter. Quiz 1. What is the difference between the compiler and the preprocessor?2. Why is the function main() special? 3. What are the two types of comments, and how do they differ? 4. Can comments be nested? 5. Can comments be longer than one line? Exercises 1. Write a program that writes I love C++ to the screen. 2. Write the smallest program that can be compiled, linked, and run. 3. BUG BUSTERS: Enter this program and compile it. Why does it fail? How can you fix it? 1: 2: 3: 4: 5: #include void main() { cout << Is there a bug here?"; } 4. Fix the bug in Exercise 3 and recompile, link, and run it.(+) Day 3 Variables and Constants What Is a Variable? Figure 3.1. Setting Aside Memory Size of Integers Listing 3.1. Determining the size of variable types on your computer. signed and unsigned Fundamental Variable Types Defining a Variable Case Sensitivity Keywords Creating More Than One Variable at a Time Assigning Values to Your Variables Listing 3.2. A demonstration of the use of variables. typedef Listing 3.3. A demonstration of typedef. When to Use short and When to Use long Wrapping Around an unsigned Integer Listing 3.4. A demonstration of putting too large a value in an unsigned integer. Wrapping Around a signed Integer Listing 3.5. A demonstration of adding too large a number to a signed integer. Characters Characters and Numbers Listing 3.6. Printing characters based on numbers. Special Printing Characters Constants Literal Constants Symbolic Constants Enumerated Constants Listing 3.7. A demonstration of enumerated constants Summary Q&A Workshop Quiz Exercises Day 3 Variables and Constants Programs need a way to store the data they use. Variables and constants offer various ways to represent and manipulate that data. Today you will learn How to declare and define variables and constants. How to assign values to variables and manipulate those values. How to write the value of a variable to the screen. What Is a Variable? In C++ a variable is a place to store information. A variable is a location in your computer's memory in which you can store a value and from which you can later retrieve that value. Your computer's memory can be viewed as a series of cubbyholes. Each cubbyhole is one of many, many such holes all lined up. Each cubbyhole--or memory location--is numbered sequentially. These numbers are known as memory addresses. A variable reserves one or more cubbyholes in which you may store a value. Your variable's name (for example, myVariable) is a label on one of these cubbyholes, so that you can find it easily without knowing its actual memory address. Figure 3.1 is a schematic representation of this idea. As you can see from the figure, myVariable starts at memory address 103. Depending on the size of myVariable, it can take up one or more memory addresses. Figure 3.1. A schematic representation of memory. NOTE: RAM is random access memory. When you run your program, it is loaded into RAM from the disk file. All variables are also created in RAM. When programmers talk of memory, it is usually RAM to which they are referring.Setting Aside Memory When you define a variable in C++, you must tell the compiler what kind of variable it is: an integer, a character, and so forth. This information tells the compiler how much room to set aside and what kind of value you want to store in your variable. Each cubbyhole is one byte large. If the type of variable you create is two bytes in size, it needs two bytes of memory, or two cubbyholes. The type of the variable (for example, integer) tells the compiler how much memory (how many cubbyholes) to set aside for the variable. Because computers use bits and bytes to represent values, and because memory is measured in bytes, it is important that you understand and are comfortable with these concepts. For a full review of this topic, please read Appendix B, "C++ Keywords." Size of Integers On any one computer, each variable type takes up a single, unchanging amount of room. That is, an integer might be two bytes on one machine, and four on another, but on either computer it is always the same, day in and day out. A char variable (used to hold characters) is most often one byte long. A short integer is two bytes on most computers, a long integer is usually four bytes, and an integer (without the keyword short or long) can be two or four bytes. Listing 3.1 should help you determine the exact size of these types on your computer. New Term: A character is a single letter, number, or symbol that takes up one byte of memory. Listing 3.1. Determining the size of variable types on your computer. 1: #include 2: 3: int main() 4: { 5: cout << "The size of " bytes.\n"; 6: cout << "The size of " bytes.\n"; 7: cout << "The size of " bytes.\n"; 8: cout << "The size of " bytes.\n"; 9: cout << "The size of an int is:\t\t" << sizeof(int) << a short int is:\t" << sizeof(short) << a long int is:\t" << sizeof(long) << a char is:\t\t" << sizeof(char) << a float is:\t\t" << sizeof(float) <<" bytes.\n"; 10: cout << "The size of a double is:\t" << sizeof(double) << " bytes.\n"; 11: 12: return 0; 13: } Output: The size of an int is: 2 bytes. The size of a short int is: 2 bytes. The size of a long int is: 4 bytes. The size of a char is: 1 bytes. The size of a float is: 4 bytes. The size of a double is: 8 bytes. NOTE: On your computer, the number of bytes presented might be different. Analysis: Most of Listing 3.1 should be pretty familiar. The one new feature is the use of the sizeof() function in lines 5 through 10. sizeof() is provided by your compiler, and it tells you the size of the object you pass in as a parameter. For example, on line 5 the keyword int is passed into sizeof(). Using sizeof(), I was able to determine that on my computer an int is equal to a short int, which is 2 bytes. signed and unsigned In addition, all integer types come in two varieties: signed and unsigned. The idea here is that sometimes you need negative numbers, and sometimes you don't. Integers (short and long) without the word "unsigned" are assumed to be signed. Signed integers are either negative or positive. Unsigned integers are always positive. Because you have the same number of bytes for both signed and unsigned integers, the largest number you can store in an unsigned integer is twice as big as the largest positive number you can store in a signed integer. An unsigned short integer can handle numbers from 0 to 65,535. Half the numbers represented by a signed short are negative, thus a signed short can only represent numbers from -32,768 to 32,767. If this is confusing, be sure to read Appendix A, "Operator Precedence." Fundamental Variable Types Several other variable types are built into C++. They can be conveniently divided into integer variables (the type discussed so far), floating-point variables, and character variables. Floating-point variables have values that can be expressed as fractions--that is, they are real numbers.Character variables hold a single byte and are used for holding the 256 characters and symbols of the ASCII and extended ASCII character sets. New Term: The ASCII character set is the set of characters standardized for use on computers. ASCII is an acronym for American Standard Code for Information Interchange. Nearly every computer operating system supports ASCII, though many support other international character sets as well. The types of variables used in C++ programs are described in Table 3.1. This table shows the variable type, how much room this book assumes it takes in memory, and what kinds of values can be stored in these variables. The values that can be stored are determined by the size of the variable types, so check your output from Listing 3.1. Table 3.1. Variable Types. Type unsigned short int short int unsigned long int long int int (16 bit) int (32 bit) unsigned int (16 bit) unsigned int (32 bit) char float double Size Values 2 bytes 0 to 65,535 2 bytes -32,768 to 32,767 4 bytes 0 to 4,294,967,295 4 bytes 2 bytes 4 bytes -2,147,483,648 to 2,147,483,647 -32,768 to 32,767 -2,147,483,648 to 2,147,483,647 2 bytes 0 to 65,535 2 bytes 0 to 4,294,967,295 1 byte 4 bytes 8 bytes 256 character values 1.2e-38 to 3.4e38 2.2e-308 to 1.8e308 NOTE: The sizes of variables might be different from those shown in Table 3.1, depending on the compiler and the computer you are using. If your computer had the same output as was presented in Listing 3.1, Table 3.1 should apply to your compiler. If your output from Listing 3.1 was different, you should consult your compiler's manual for the values that your variable types can hold.Defining a Variable You create or define a variable by stating its type, followed by one or more spaces, followed by the variable name and a semicolon. The variable name can be virtually any combination of letters, but cannot contain spaces. Legal variable names include x, J23qrsnf, and myAge. Good variable names tell you what the variables are for; using good names makes it easier to understand the flow of your program. The following statement defines an integer variable called myAge: int myAge; As a general programming practice, avoid such horrific names as J23qrsnf, and restrict single- letter variable names (such as x or i) to variables that are used only very briefly. Try to use expressive names such as myAge or howMany. Such names are easier to understand three weeks later when you are scratching your head trying to figure out what you meant when you wrote that line of code. Try this experiment: Guess what these pieces of programs do, based on the first few lines of code: Example 1 main() { unsigned short x; unsigned short y; ULONG z; z = x * y; } Example 2 main () { unsigned short unsigned short unsigned short Area = Width * } Width; Length; Area; Length; Clearly, the second program is easier to understand, and the inconvenience of having to type the longer variable names is more than made up for by how much easier it is to maintain the second program. Case SensitivityC++ is case-sensitive. In other words, uppercase and lowercase letters are considered to be different. A variable named age is different from Age, which is different from AGE. NOTE: Some compilers allow you to turn case sensitivity off. Don't be tempted to do this; your programs won't work with other compilers, and other C++ programmers will be very confused by your code. There are various conventions for how to name variables, and although it doesn't much matter which method you adopt, it is important to be consistent throughout your program. Many programmers prefer to use all lowercase letters for their variable names. If the name requires two words (for example, my car), there are two popular conventions: my_car or myCar. The latter form is called camel-notation, because the capitalization looks something like a camel's hump. Some people find the underscore character (my_car) to be easier to read, while others prefer to avoid the underscore, because it is more difficult to type. This book uses camel-notation, in which the second and all subsequent words are capitalized: myCar, theQuickBrownFox, and so forth. NOTE: Many advanced programmers employ a notation style that is often referred to as Hungarian notation. The idea behind Hungarian notation is to prefix every variable with a set of characters that describes its type. Integer variables might begin with a lowercase letter i, longs might begin with a lowercase l. Other notations indicate constants, globals, pointers, and so forth. Most of this is much more important in C programming, because C++ supports the creation of user-defined types (see Day 6, "Basic Classes") and because C++ is strongly typed. Keywords Some words are reserved by C++, and you may not use them as variable names. These are keywords used by the compiler to control your program. Keywords include if, while, for, and main. Your compiler manual should provide a complete list, but generally, any reasonable name for a variable is almost certainly not a keyword. DO define a variable by writing the type, then the variable name. DO use meaningful variable names. DO remember that C++ is case sensitive. DON'T use C++ keywords as variable names. DO understand the number of bytes each variable type consumes in memory, and what values can be stored in variables of that type. DON'T use unsigned variables for negative numbers.Creating More Than One Variable at a Time You can create more than one variable of the same type in one statement by writing the type and then the variable names, separated by commas. For example: unsigned int myAge, myWeight; long area, width, length; // two unsigned int variables // three longs As you can see, myAge and myWeight are each declared as unsigned integer variables. The second line declares three individual long variables named area, width, and length. The type (long) is assigned to all the variables, so you cannot mix types in one definition statement. Assigning Values to Your Variables You assign a value to a variable by using the assignment operator (=). Thus, you would assign 5 to Width by writing unsigned short Width; Width = 5; You can combine these steps and initialize Width when you define it by writing unsigned short Width = 5; Initialization looks very much like assignment, and with integer variables, the difference is minor. Later, when constants are covered, you will see that some values must be initialized because they cannot be assigned to. The essential difference is that initialization takes place at the moment you create the variable. Just as you can define more than one variable at a time, you can initialize more than one variable at creation. For example: // create two long variables and initialize them $)long width = 5, length = 7; This example initializes the long integer variable width to the value 5 and the long integer variable length to the value 7. You can even mix definitions and initializations: int myAge = 39, yourAge, hisAge = 40; This example creates three type int variables, and it initializes the first and third. Listing 3.2 shows a complete program, ready to compile, that computes the area of a rectangle and writes the answer to the screen.Listing 3.2. A demonstration of the use of variables. 1: // Demonstration of variables 2: #include 3: 4: int main() 5: { 6: unsigned short int Width = 5, Length; 7: Length = 10; 8: 9: // create an unsigned short and initialize with result 10: // of multiplying Width by Length 11: unsigned short int Area = Width * Length; 12: 13: cout << "Width:" << Width << "\n"; 14: cout << "Length: " << Length << endl; 15: cout << "Area: " << Area << endl; 16: return 0; 17: } Output: Width:5 Length: 10 Area: 50 Analysis: Line 2 includes the required include statement for the iostream's library so that cout will work. Line 4 begins the program. On line 6, Width is defined as an unsigned short integer, and its value is initialized to 5. Another unsigned short integer, Length, is also defined, but it is not initialized. On line 7, the value 10 is assigned to Length. On line 11, an unsigned short integer, Area, is defined, and it is initialized with the value obtained by multiplying Width times Length. On lines 13-15, the values of the variables are printed to the screen. Note that the special word endl creates a new line. typedef It can become tedious, repetitious, and, most important, error-prone to keep writing unsigned short int. C++ enables you to create an alias for this phrase by using the keyword typedef, which stands for type definition. In effect, you are creating a synonym, and it is important to distinguish this from creating a new type (which you will do on Day 6). typedef is used by writing the keyword typedef, followed by the existing type and then the new name. For exampletypedef unsigned short int USHORT creates the new name USHORT that you can use anywhere you might have written unsigned short int. Listing 3.3 is a replay of Listing 3.2, using the type definition USHORT rather than unsigned short int. Listing 3.3. A demonstration of typedef . 1: // ***************** 2: // Demonstrates typedef keyword 3: #include 4: 5: typedef unsigned short int USHORT; //typedef defined 6: 7: void main() 8: { 9: USHORT Width = 5; 10: USHORT Length; 11: Length = 10; 12: USHORT Area = Width * Length; 13: cout << "Width:" << Width << "\n"; 14: cout << "Length: " << Length << endl; 15: cout << "Area: " << Area < 2: int main() 3: { 4: unsigned short int smallNumber; 5: smallNumber = 65535; 6: cout << "small number:" << smallNumber << endl; 7: smallNumber++; 8: cout << "small number:" << smallNumber << endl; 9: smallNumber++; 10: cout << "small number:" << smallNumber << endl; 11: return 0; 12: } Output: small number:65535 small number:0 small number:1 Analysis: On line 4, smallNumber is declared to be an unsigned short int, which on my computer is a two-byte variable, able to hold a value between 0 and 65,535. On line 5, the maximum value is assigned to smallNumber, and it is printed on line 6. On line 7, smallNumber is incremented; that is, 1 is added to it. The symbol for incrementing is ++ (as in the name C++--an incremental increase from C). Thus, the value in smallNumber would be 65,536. However, unsigned short integers can't hold a number larger than 65,535, so the value is wrapped around to 0, which is printed on line 8. On line 9 smallNumber is incremented again, and then its new value, 1, is printed. Wrapping Around a signed Integer A signed integer is different from an unsigned integer, in that half of the values you canrepresent are negative. Instead of picturing a traditional car odometer, you might picture one that rotates up for positive numbers and down for negative numbers. One mile from 0 is either 1 or -1. When you run out of positive numbers, you run right into the largest negative numbers and then count back down to 0. Listing 3.5 shows what happens when you add 1 to the maximum positive number in an unsigned short integer. Listing 3.5. A demonstration of adding too large a number to a signed integer. 1: #include 2: int main() 3: { 4: short int smallNumber; 5: smallNumber = 32767; 6: cout << "small number:" << smallNumber << endl; 7: smallNumber++; 8: cout << "small number:" << smallNumber << endl; 9: smallNumber++; 10: cout << "small number:" << smallNumber << endl; 11: return 0; 12: } Output: small number:32767 small number:-32768 small number:-32767 Analysis: On line 4, smallNumber is declared this time to be a signed short integer (if you don't explicitly say that it is unsigned, it is assumed to be signed). The program proceeds much as the preceding one, but the output is quite different. To fully understand this output, you must be comfortable with how signed numbers are represented as bits in a two-byte integer. For details, check Appendix C, "Binary and Hexadecimal." The bottom line, however, is that just like an unsigned integer, the signed integer wraps around from its highest positive value to its highest negative value. Characters Character variables (type char) are typically 1 byte, enough to hold 256 values (see Appendix C). A char can be interpreted as a small number (0-255) or as a member of the ASCII set. ASCII stands for the American Standard Code for Information Interchange. The ASCII character set and its ISO (International Standards Organization) equivalent are a way to encode all the letters, numerals, and punctuation marks. Computers do not know about letters, punctuation, or sentences. All they understand are numbers. In fact, all they really know about is whether or not a sufficient amount ofelectricity is at a particular junction of wires. If so, it is represented internally as a 1; if not, it is represented as a 0. By grouping ones and zeros, the computer is able to generate patterns that can be interpreted as numbers, and these in turn can be assigned to letters and punctuation. In the ASCII code, the lowercase letter "a" is assigned the value 97. All the lower- and uppercase letters, all the numerals, and all the punctuation marks are assigned values between 1 and 128. Another 128 marks and symbols are reserved for use by the computer maker, although the IBM extended character set has become something of a standard. Characters and Numbers When you put a character, for example, `a', into a char variable, what is really there is just a number between 0 and 255. The compiler knows, however, how to translate back and forth between characters (represented by a single quotation mark and then a letter, numeral, or punctuation mark, followed by a closing single quotation mark) and one of the ASCII values. The value/letter relationship is arbitrary; there is no particular reason that the lowercase "a" is assigned the value 97. As long as everyone (your keyboard, compiler, and screen) agrees, there is no problem. It is important to realize, however, that there is a big difference between the value 5 and the character `5'. The latter is actually valued at 53, much as the letter `a' is valued at 97. Listing 3.6. Printing characters based on numbers 1: #include 2: int main() 3: { 4: for (int i = 32; i<128; i++) 5: cout << (char) i; 6: return 0; 7: } Output: !"#$%G'()*+,./0123456789:;<>?@ABCDEFGHIJKLMNOP _QRSTUVWXYZ[\]^'abcdefghijklmnopqrstuvwxyz<|>~s This simple program prints the character values for the integers 32 through 127. Special Printing Characters The C++ compiler recognizes some special characters for formatting. Table 3.2 shows the most common ones. You put these into your code by typing the backslash (called the escape character), followed by the character. Thus, to put a tab character into your code, you would enter a single quotation mark, the slash, the letter t, and then a closing single quotation mark: char tabCharacter = `\t';This example declares a char variable (tabCharacter) and initializes it with the character value \t, which is recognized as a tab. The special printing characters are used when printing either to the screen or to a file or other output device. New Term: An escape character changes the meaning of the character that follows it. For example, normally the character n means the letter n, but when it is preceded by the escape character (\) it means new line. Table 3.2. The Escape Characters . Character What it means new line \n tab \t backspace \b double quote \" single quote \' question mark \? backslash \\ Constants Like variables, constants are data storage locations. Unlike variables, and as the name implies, constants don't change. You must initialize a constant when you create it, and you cannot assign a new value later. Literal Constants C++ has two types of constants: literal and symbolic. A literal constant is a value typed directly into your program wherever it is needed. For example int myAge = 39; myAge is a variable of type int; 39 is a literal constant. You can't assign a value to 39, and its value can't be changed. Symbolic Constants A symbolic constant is a constant that is represented by a name, just as a variable is represented.Unlike a variable, however, after a constant is initialized, its value can't be changed. If your program has one integer variable named students and another named classes, you could compute how many students you have, given a known number of classes, if you knew there were 15 students per class: students = classes * 15; NOTE: * indicates multiplication. In this example, 15 is a literal constant. Your code would be easier to read, and easier to maintain, if you substituted a symbolic constant for this value: students = classes * studentsPerClass If you later decided to change the number of students in each class, you could do so where you define the constant studentsPerClass without having to make a change every place you used that value. There are two ways to declare a symbolic constant in C++. The old, traditional, and now obsolete way is with a preprocessor directive, #define. Defining Constants with #define To define a constant the traditional way, you would enter this: #define studentsPerClass 15 Note that studentsPerClass is of no particular type (int, char, and so on). #define does a simple text substitution. Every time the preprocessor sees the word studentsPerClass, it puts in the text 15. Because the preprocessor runs before the compiler, your compiler never sees your constant; it sees the number 15. Defining Constants with const Although #define works, there is a new, much better way to define constants in C++: const unsigned short int studentsPerClass = 15; This example also declares a symbolic constant named studentsPerClass, but this time studentsPerClass is typed as an unsigned short int. This method has several advantages in making your code easier to maintain and in preventing bugs. The biggest difference is that this constant has a type, and the compiler can enforce that it is used according to its type. NOTE: Constants cannot be changed while the program is running. If you need to change studentsPerClass, for example, you need to change the code andrecompile. DON'T use the term int. Use short and long to make it clear which size number you intended. DO watch for numbers overrunning the size of the integer and wrapping around incorrect values. DO give your variables meaningful names that reflect their use. DON'T use keywords as variable names. Enumerated Constants Enumerated constants enable you to create new types and then to define variables of those types whose values are restricted to a set of possible values. For example, you can declare COLOR to be an enumeration, and you can define that there are five values for COLOR: RED, BLUE, GREEN, WHITE, and BLACK. The syntax for enumerated constants is to write the keyword enum, followed by the type name, an open brace, each of the legal values separated by a comma, and finally a closing brace and a semicolon. Here's an example: enum COLOR { RED, BLUE, GREEN, WHITE, BLACK }; This statement performs two tasks: 1. It makes COLOR the name of an enumeration, that is, a new type. 2. It makes RED a symbolic constant with the value 0, BLUE a symbolic constant with the value 1, GREEN a symbolic constant with the value 2, and so forth. Every enumerated constant has an integer value. If you don't specify otherwise, the first constant will have the value 0, and the rest will count up from there. Any one of the constants can be initialized with a particular value, however, and those that are not initialized will count upward from the ones before them. Thus, if you write enum Color { RED=100, BLUE, GREEN=500, WHITE, BLACK=700 }; then RED will have the value 100; BLUE, the value 101; GREEN, the value 500; WHITE, the value 501; and BLACK, the value 700. You can define variables of type COLOR, but they can be assigned only one of the enumerated values (in this case, RED, BLUE, GREEN, WHITE, or BLACK, or else 100, 101, 500, 501, or 700). You can assign any color value to your COLOR variable. In fact, you can assign any integer value, even if it is not a legal color, although a good compiler will issue a warning if you do. It is important to realize that enumerator variables actually are of type unsigned int, and that the enumerated constantsequate to integer variables. It is, however, very convenient to be able to name these values when working with colors, days of the week, or similar sets of values. Listing 3.7 presents a program that uses an enumerated type. Listing 3.7. A demonstration of enumerated constants . 1: #include 2: int main() 3: { 4: enum Days { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, #,_Saturday }; 5: 6: Days DayOff; 7: int x; 8: 9: cout << "What day would you like off (0-6)? "; 10: cin >> x; 11: DayOff = Days(x); 12: 13: if (DayOff == Sunday || DayOff == Saturday) 14: cout << "\nYou're already off on weekends!\n"; 15: else 16: cout << "\nOkay, I'll put in the vacation day.\n"; 17: return 0; 18: } Output: What day would you like off (0-6)? 1 Okay, I'll put in the vacation day. What day would you like off (0-6)? 0 You're already off on weekends! Analysis: On line 4, the enumerated constant DAYS is defined, with seven values counting upward from 0. The user is prompted for a day on line 9. The chosen value, a number between 0 and 6, is compared on line 13 to the enumerated values for Sunday and Saturday, and action is taken accordingly. The if statement will be covered in more detail on Day 4, "Expressions and Statements." You cannot type the word "Sunday" when prompted for a day; the program does not know how to translate the characters in Sunday into one of the enumerated values. NOTE: For this and all the small programs in this book, I've left out all the code you would normally write to deal with what happens when the user types inappropriate data.For example, this program doesn't check, as it would in a real program, to make sure that the user types a number between 0 and 6. This detail has been left out to keep these programs small and simple, and to focus on the issue at hand. Summary This chapter has discussed numeric and character variables and constants, which are used by C++ to store data during the execution of your program. Numeric variables are either integral (char, short, and long int) or they are floating point (float and double). Numeric variables can also be signed or unsigned. Although all the types can be of various sizes among different computers, the type specifies an exact size on any given computer. You must declare a variable before it can be used, and then you must store the type of data that you've declared as correct for that variable. If you put too large a number into an integral variable, it wraps around and produces an incorrect result. This chapter also reviewed literal and symbolic constants, as well as enumerated constants, and showed two ways to declare a symbolic constant: using #define and using the keyword const. Q&A Q. If a short int can run out of room and wrap around, why not always use long integers? A .Both short integers and long integers will run out of room and wrap around, but a long integer will do so with a much larger number. For example, an unsigned short int will wrap around after 65,535, whereas an unsigned long int will not wrap around until 4,294,967,295. However, on most machines, a long integer takes up twice as much memory every time you declare one (4 bytes versus 2 bytes), and a program with 100 such variables will consume an extra 200 bytes of RAM. Frankly, this is less of a problem than it used to be, because most personal computers now come with many thousands (if not millions) of bytes of memory. Q. What happens if I assign a number with a decimal point to an integer rather than to a float? Consider the following line of code: int aNumber = 5.4; A. A good compiler will issue a warning, but the assignment is completely legal. The number you've assigned will be truncated into an integer. Thus, if you assign 5.4 to an integer variable, that variable will have the value 5. Information will be lost, however, and if you then try to assign the value in that integer variable to a float variable, the float variable will have only 5. Q. Why not use literal constants; why go to the bother of using symbolic constants?A. If you use the value in many places throughout your program, a symbolic constant allows all the values to change just by changing the one definition of the constant. Symbolic constants also speak for themselves. It might be hard to understand why a number is being multiplied by 360, but it's much easier to understand what's going on if the number is being multiplied by degreesInACircle. Q. What happens if I assign a negative number to an unsigned variable? Consider the following line of code: unsigned int aPositiveNumber = -1; A. A good compiler will warn, but the assignment is legal. The negative number will be assessed as a bit pattern and assigned to the variable. The value of that variable will then be interpreted as an unsigned number. Thus, -1, whose bit pattern is 11111111 11111111 (0xFF in hex), will be assessed as the unsigned value 65,535. If this information confuses you, refer to Appendix C. Q. Can I work with C++ without understanding bit patterns, binary arithmetic, and hexadecimal? A. Yes, but not as effectively as if you do understand these topics. C++ does not do as good a job as some languages at "protecting" you from what the computer is really doing. This is actually a benefit, because it provides you with tremendous power that other languages don't. As with any power tool, however, to get the most out of C++ you must understand how it works. Programmers who try to program in C++ without understanding the fundamentals of the binary system often are confused by their results. Workshop The Workshop provides quiz questions to help you solidify your understanding of the material covered, and exercises to provide you with experience in using what you've learned. Try to answer the quiz and exercise questions before checking the answers in Appendix D, and make sure that you understand the answers before continuing to the next chapter. Quiz 1. What is the difference between an integral variable and a floating-point variable? 2. What are the differences between an unsigned short int and a long int? 3. What are the advantages of using a symbolic constant rather than a literal constant? 4. What are the advantages of using the const keyword rather than #define? 5. What makes for a good or bad variable name?6. Given this enum, what is the value of BLUE? enum COLOR { WHITE, BLACK = 100, RED, BLUE, GREEN = 300 }; 7. Which of the following variable names are good, which are bad, and which are invalid? a. Age b. !ex c. R79J d. TotalIncome e. __Invalid Exercises 1. What would be the correct variable type in which to store the following information? a. Your age. b. The area of your backyard. c. The number of stars in the galaxy. d. The average rainfall for the month of January. 2. Create good variable names for this information. 3. Declare a constant for pi as 3.14159. 4. Declare a float variable and initialize it using your pi constant. Although this last variation is perfectly legal, it is also perfectly foolish. Whitespace can be used to make your programs more readable and easier to maintain, or it can be used to create horrific and indecipherable code. In this, as in all things, C++ provides the power; you supply the judgment. New Term: Whitespace characters (spaces, tabs, and newlines) cannot be seen. If these characters are printed, you see only the white of the paper. Blocks and Compound Statements Any place you can put a single statement, you can put a compound statement, also called a block. A block begins with an opening brace ({) and ends with a closing brace (}). Although every statement in the block must end with a semicolon, the block itself does not end with a semicolon. For example { temp = a; a = b; b = temp; } This block of code acts as one statement and swaps the values in the variables a and b. DO use a closing brace any time you have an opening brace. DO end your statements with a semicolon. DO use whitespace judiciously to make your code clearer. ExpressionsAnything that evaluates to a value is an expression in C++. An expression is said to return a value. Thus, 3+2; returns the value 5 and so is an expression. All expressions are statements. The myriad pieces of code that qualify as expressions might surprise you. Here are three examples: 3.2 PI 3.14 SecondsPerMinute // returns the value 3.2 // float const that returns the value // int const that returns 60 Assuming that PI is a constant equal to 3.14 and SecondsPerMinute is a constant equal to 60, all three of these statements are expressions. The complicated expression x = a + b; not only adds a and b and assigns the result to x, but returns the value of that assignment (the value of x) as well. Thus, this statement is also an expression. Because it is an expression, it can be on the right side of an assignment operator: y = x = a + b; This line is evaluated in the following order: Add a to b. Assign the result of the expression a + b to x. Assign the result of the assignment expression x = a + b to y. If a, b, x, and y are all integers, and if a has the value 2 and b has the value 5, both x and y will be assigned the value 7. Listing 4.1. Evaluating complex expressions. 1: 2: 3: 4: 5: 6: 7: 8: 9: #include int main() { int a=0, b=0, x=0, y=35; cout << "a: " << a << " b: " << b; cout << " x: " << x << " y: " << y << endl; a = 9; b = 7; y = x = a+b;10: cout << "a: " << a << " b: " << b; 11: cout << " x: " << x << " y: " << y << endl; 12: return 0; 13: } Output: a: 0 b: 0 x: 0 y: 35 a: 9 b: 7 x: 16 y: 16 Analysis: On line 4, the four variables are declared and initialized. Their values are printed on lines 5 and 6. On line 7, a is assigned the value 9. One line 8, b is assigned the value 7. On line 9, the values of a and b are summed and the result is assigned to x. This expression (x = a+b) evaluates to a value (the sum of a + b), and that value is in turn assigned to y. Operators An operator is a symbol that causes the compiler to take an action. Operators act on operands, and in C++ all operands are expressions. In C++ there are several different categories of operators. Two of these categories are Assignment operators. Mathematical operators. Assignment Operator The assignment operator (=) causes the operand on the left side of the assignment operator to have its value changed to the value on the right side of the assignment operator. The expression x = a + b; assigns the value that is the result of adding a and b to the operand x. An operand that legally can be on the left side of an assignment operator is called an lvalue. That which can be on the right side is called (you guessed it) an rvalue. Constants are r-values. They cannot be l-values. Thus, you can write x = 35; // ok but you can't legally write 35 = x; // error, not an lvalue! New Term: An lvalue is an operand that can be on the left side of an expression. An rvalue isan operand that can be on the right side of an expression. Note that all l-values are r-values, but not all r-values are l-values. An example of an rvalue that is not an lvalue is a literal. Thus, you can write x = 5;, but you cannot write 5 = x;. Mathematical Operators There are five mathematical operators: addition (+), subtraction (-), multiplication (*), division (/), and modulus (%). Addition and subtraction work as you would expect, although subtraction with unsigned integers can lead to surprising results, if the result is a negative number. You saw something much like this yesterday, when variable overflow was described. Listing 4.2 shows what happens when you subtract a large unsigned number from a small unsigned number. Listing 4.2. A demonstration of subtraction and integer overflow . 1: // Listing 4.2 - demonstrates subtraction and 2: // integer overflow 3: #include 4: 5: int main() 6: { 7: unsigned int difference; 8: unsigned int bigNumber = 100; 9: unsigned int smallNumber = 50; 10: difference = bigNumber - smallNumber; 11: cout << "Difference is: " << difference; 12: difference = smallNumber - bigNumber; 13: cout << "\nNow difference is: " << difference < int main() { int myAge = 39; // initialize two integers int yourAge = 39; cout << "I am: " << myAge << " years old.\n"; cout << "You are: " << yourAge << " years old\n"; myAge++; // postfix increment ++yourAge; // prefix increment cout << "One year passes...\n"; cout << "I am: " << myAge << " years old.\n"; cout << "You are: " << yourAge << " years old\n"; cout << "Another year passes\n"; cout << "I am: " << myAge++ << " years old.\n"; cout << "You are: " << ++yourAge << " years old\n"; cout << "Let's print it again.\n"; cout << "I am: " << myAge << " years old.\n"; cout << "You are: " << yourAge << " years old\n"; return 0; }Output: I am 39 years old You are 39 years old One year passes I am 40 years old You are 40 years old Another year passes I am 40 years old You are 41 years old Let's print it again I am 41 years old You are 41 years old Analysis: On lines 7 and 8, two integer variables are declared, and each is initialized with the value 39. Their values are printed on lines 9 and 10. On line 11, myAge is incremented using the postfix increment operator, and on line 12, yourAge is incremented using the prefix increment operator. The results are printed on lines 14 and 15, and they are identical (both 40). On line 17, myAge is incremented as part of the printing statement, using the postfix increment operator. Because it is postfix, the increment happens after the print, and so the value 40 is printed again. In contrast, on line 18, yourAge is incremented using the prefix increment operator. Thus, it is incremented before being printed, and the value displays as 41. Finally, on lines 20 and 21, the values are printed again. Because the increment statement has completed, the value in myAge is now 41, as is the value in yourAge. Precedence In the complex statement x = 5 + 3 * 8; which is performed first, the addition or the multiplication? If the addition is performed first, the answer is 8 * 8, or 64. If the multiplication is performed first, the answer is 5 + 24, or 29. Every operator has a precedence value, and the complete list is shown in Appendix A, "Operator Precedence." Multiplication has higher precedence than addition, and thus the value of the expression is 29. When two mathematical operators have the same precedence, they are performed in left-to-right order. Thus x = 5 + 3 + 8 * 9 + 6 * 4; is evaluated multiplication first, left to right. Thus, 8*9 = 72, and 6*4 = 24. Now the expression isessentially x = 5 + 3 + 72 + 24; Now the addition, left to right, is 5 + 3 = 8; 8 + 72 = 80; 80 + 24 = 104. Be careful with this. Some operators, such as assignment, are evaluated in right-to-left order! In any case, what if the precedence order doesn't meet your needs? Consider the expression TotalSeconds = NumMinutesToThink + NumMinutesToType * 60 In this expression, you do not want to multiply the NumMinutesToType variable by 60 and then add it to NumMinutesToThink. You want to add the two variables to get the total number of minutes, and then you want to multiply that number by 60 to get the total seconds. In this case, you use parentheses to change the precedence order. Items in parentheses are evaluated at a higher precedence than any of the mathematical operators. Thus TotalSeconds = (NumMinutesToThink + NumMinutesToType) * 60 will accomplish what you want. Nesting Parentheses For complex expressions, you might need to nest parentheses one within another. For example, you might need to compute the total seconds and then compute the total number of people who are involved before multiplying seconds times people: TotalPersonSeconds = ( ( (NumMinutesToThink + NumMinutesToType) * 60) * #,(PeopleInTheOffice + PeopleOnVacation) ) This complicated expression is read from the inside out. First, NumMinutesToThink is added to NumMinutesToType, because these are in the innermost parentheses. Then this sum is multiplied by 60. Next, PeopleInTheOffice is added to PeopleOnVacation. Finally, the total number of people found is multiplied by the total number of seconds. This example raises an important related issue. This expression is easy for a computer to understand, but very difficult for a human to read, understand, or modify. Here is the same expression rewritten, using some temporary integer variables: TotalMinutes = NumMinutesToThink + NumMinutesToType; TotalSeconds = TotalMinutes * 60; TotalPeople = PeopleInTheOffice + PeopleOnVacation; TotalPersonSeconds = TotalPeople * TotalSeconds;This example takes longer to write and uses more temporary variables than the preceding example, but it is far easier to understand. Add a comment at the top to explain what this code does, and change the 60 to a symbolic constant. You then will have code that is easy to understand and maintain. DO remember that expressions have a value. DO use the prefix operator (++variable) to increment or decrement the variable before it is used in the expression. DO use the postfix operator (variable++) to increment or decrement the variable after it is used. DO use parentheses to change the order of precedence. DON'T nest too deeply, because the expression becomes hard to understand and maintain. The Nature of Truth In C++, zero is considered false, and all other values are considered true, although true is usually represented by 1. Thus, if an expression is false, it is equal to zero, and if an expression is equal to zero, it is false. If a statement is true, all you know is that it is nonzero, and any nonzero statement is true. Relational Operators The relational operators are used to determine whether two numbers are equal, or if one is greater or less than the other. Every relational statement evaluates to either 1 (TRUE) or 0 (FALSE). The relational operators are presented later, in Table 4.1. If the integer variable myAge has the value 39, and the integer variable yourAge has the value 40, you can determine whether they are equal by using the relational "equals" operator: myAge == yourAge; // is the value in myAge the same as in yourAge? This expression evaluates to 0, or false, because the variables are not equal. The expression myAge > yourAge; // is myAge greater than yourAge? evaluates to 0 or false. WARNING: Many novice C++ programmers confuse the assignment operator (=) with the equals operator (==). This can create a nasty bug in your program. There are six relational operators: equals (==), less than (<), greater than (>), less than or equal to (<=), greater than or equal to (>=), and not equals (!=). Table 4.1 shows each relational operator, its use, and a sample code use.Table 4.1. The Relational Operators. Name Equals Operator Sample Evaluates == 100 == 50; false 50 == 50; true Not Equals != 100 != 50; true 50 != 50; false Greater Than > 100 > 50; true 50 > 50; false Greater Than >= 100 >= 50; true or Equals 50 >= 50; true Less Than < 100 < 50; false 50 < 50; false Less Than <= 100 <= 50; false or Equals 50 <= 50; true DO remember that relational operators return the value 1 (true) or 0 (false). DON'T confuse the assignment operator (=) with the equals relational operator (==). This is one of the most common C++ programming mistakes--be on guard for it. The if Statement Normally, your program flows along line by line in the order in which it appears in your source code. The if statement enables you to test for a condition (such as whether two variables are equal) and branch to different parts of your code, depending on the result. The simplest form of an if statement is this: if (expression) statement; The expression in the parentheses can be any expression at all, but it usually contains one of the relational expressions. If the expression has the value 0, it is considered false, and the statement is skipped. If it has any nonzero value, it is considered true, and the statement is executed. Consider the following example: if (bigNumber > smallNumber) bigNumber = smallNumber;This code compares bigNumber and smallNumber. If bigNumber is larger, the second line sets its value to the value of smallNumber. Because a block of statements surrounded by braces is exactly equivalent to a single statement, the following type of branch can be quite large and powerful: if (expression) { statement1; statement2; statement3; } Here's a simple example of this usage: if (bigNumber > smallNumber) { bigNumber = smallNumber; cout << "bigNumber: " << bigNumber << "\n"; cout << "smallNumber: " << smallNumber << "\n"; } This time, if bigNumber is larger than smallNumber, not only is it set to the value of smallNumber, but an informational message is printed. Listing 4.4 shows a more detailed example of branching based on relational operators. // Listing 4.4 - demonstrates if statement // used with relational operators #include int main() { int RedSoxScore, YankeesScore; cout << "Enter the score for the Red Sox: "; cin >> RedSoxScore; cout << "\nEnter the score for the Yankees: "; cin >> YankeesScore; cout << "\n"; if (RedSoxScore > YankeesScore) cout << "Go Sox!\n";17: 18: if (RedSoxScore < YankeesScore) 19: { 20: cout << "Go Yankees!\n"; 21: cout << "Happy days in New York!\n"; 22: } 23: 24: if (RedSoxScore == YankeesScore) 25: { 26: cout << "A tie? Naah, can't be.\n"; 27: cout << "Give me the real score for the Yanks: "; 28: cin >> YankeesScore; 29: 30: if (RedSoxScore > YankeesScore) 31: cout << "Knew it! Go Sox!"; 32: 33: if (YankeesScore > RedSoxScore) 34: cout << "Knew it! Go Yanks!"; 35: 36: if (YankeesScore == RedSoxScore) 37: cout << "Wow, it really was a tie!"; 38: } 39: 40: cout << "\nThanks for telling me.\n"; 41: return 0; 42: } Output: Enter the score for the Red Sox: 10 Enter the score for the Yankees: 10 A tie? Naah, can't be Give me the real score for the Yanks: 8 Knew it! Go Sox! Thanks for telling me. Analysis: This program asks for user input of scores for two baseball teams, which are stored in integer variables. The variables are compared in the if statement on lines 15, 18, and 24. If one score is higher than the other, an informational message is printed. If the scores are equal, the block of code that begins on line 24 and ends on line 38 is entered. The second score is requested again, and then the scores are compared again. Note that if the initial Yankees score was higher than the Red Sox score, the if statement on line 15 would evaluate as FALSE, and line 16 would not be invoked. The test on line 18 would evaluate as true, and the statements on lines 20 and 21 would be invoked. Then the if statement on line 24 would be tested, and this would be false (if line 18 was true). Thus, the program would skip the entire block, falling through to line 39.In this example, getting a true result in one if statement does not stop other if statements from being tested. Indentation Styles Listing 4.3 shows one style of indenting if statements. Nothing is more likely to create a religious war, however, than to ask a group of programmers what is the best style for brace alignment. Although there are dozens of variations, these appear to be the favorite three: Putting the initial brace after the condition and aligning the closing brace under the if to close the statement block. if (expression){ statements } Aligning the braces under the if and indenting the statements. if (expression) { statements } Indenting the braces and statements. if (expression) { statements } This book uses the middle alternative, because I find it easier to understand where blocks of statements begin and end if the braces line up with each other and with the condition being tested. Again, it doesn't matter much which style you choose, as long as you are consistent with it. else Often your program will want to take one branch if your condition is true, another if it is false. In Listing 4.3, you wanted to print one message (Go Sox!) if the first test (RedSoxScore > Yankees) evaluated TRUE, and another message (Go Yanks!) if it evaluated FALSE. The method shown so far, testing first one condition and then the other, works fine but is a bit cumbersome. The keyword else can make for far more readable code: if (expression)statement; else statement; Listing 4.5 demonstrates the use of the keyword else. Listing 4.5. Demonstrating the else keyword. 1: // Listing 4.5 - demonstrates if statement 2: // with else clause 3: #include 4: int main() 5: { 6: int firstNumber, secondNumber; 7: cout << "Please enter a big number: "; 8: cin >> firstNumber; 9: cout << "\nPlease enter a smaller number: "; 10: cin >> secondNumber; 11: if (firstNumber > secondNumber) 12: cout << "\nThanks!\n"; 13: else 14: cout << "\nOops. The second is bigger!"; 15: 16: return 0; 17: } Output: Please enter a big number: 10 Please enter a smaller number: 12 Oops. The second is bigger! Analysis: The if statement on line 11 is evaluated. If the condition is true, the statement on line 12 is run; if it is false, the statement on line 14 is run. If the else clause on line 13 were removed, the statement on line 14 would run whether or not the if statement was true. Remember, the if statement ends after line 12. If the else was not there, line 14 would just be the next line in the program. Remember that either or both of these statements could be replaced with a block of code in braces. The if Statement The syntax for the if statement is as follows: Form 1 if (expression) statement;next statement; If the expression is evaluated as TRUE, the statement is executed and the program continues with the next statement. If the expression is not true, the statement is ignored and the program jumps to the next statement. Remember that the statement can be a single statement ending with a semicolon or a block enclosed in braces. Form 2 if (expression) statement1; else statement2; next statement; If the expression evaluates TRUE, statement1 is executed; otherwise, statement2 is executed. Afterwards, the program continues with the next statement. Example 1 Example if (SomeValue < 10) cout << "SomeValue is less than 10"); else cout << "SomeValue is not less than 10!"); cout << "Done." << endl; Advanced if Statements It is worth noting that any statement can be used in an if or else clause, even another if or else statement. Thus, you might see complex if statements in the following form: if (expression1) { if (expression2) statement1; else { if (expression3) statement2; else statement3; } } else statement4; This cumbersome if statement says, "If expression1 is true and expression2 is true, execute statement1. If expression1 is true but expression2 is not true, then if expression3 is true executestatement2. If expression1 is true but expression2 and expression3 are false, execute statement3. Finally, if expression1 is not true, execute statement4." As you can see, complex if statements can be confusing! Listing 4.6 gives an example of such a complex if statement. Listing 4.6. A complex, nested if statement. 1: // Listing 4.5 - a complex nested 2: // if statement 3: #include 4: int main() 5: { 6: // Ask for two numbers 7: // Assign the numbers to bigNumber and littleNumber 8: // If bigNumber is bigger than littleNumber, 9: // see if they are evenly divisible 10: // If they are, see if they are the same number 11: 12: int firstNumber, secondNumber; 13: cout << "Enter two numbers.\nFirst: "; 14: cin >> firstNumber; 15: cout << "\nSecond: "; 16: cin >> secondNumber; 17: cout << "\n\n"; 18: 19: if (firstNumber >= secondNumber) 20: { 21: if ( (firstNumber % secondNumber) == 0) // evenly divisible? 22: { 23: if (firstNumber == secondNumber) 24: cout << "They are the same!\n"; 25: else 26: cout << "They are evenly divisible!\n"; 27: } 28: else 29: cout << "They are not evenly divisible!\n"; 30: } 31: else 32: cout << "Hey! The second one is larger!\n"; 33: return 0; 34: } Output: Enter two numbers. First: 10Second: 2 They are evenly divisible! Analysis: Two numbers are prompted for one at a time, and then compared. The first if statement, on line 19, checks to ensure that the first number is greater than or equal to the second. If not, the else clause on line 31 is executed. If the first if is true, the block of code beginning on line 20 is executed, and the second if statement is tested, on line 21. This checks to see whether the first number modulo the second number yields no remainder. If so, the numbers are either evenly divisible or equal. The if statement on line 23 checks for equality and displays the appropriate message either way. If the if statement on line 21 fails, the else statement on line 28 is executed. Using Braces in Nested if Statements Although it is legal to leave out the braces on if statements that are only a single statement, and it is legal to nest if statements, such as if (x > y) if (x < z) x = y; // if x is bigger than y // and if x is smaller than z // then set x to the value in z when writing large nested statements, this can cause enormous confusion. Remember, whitespace and indentation are a convenience for the programmer; they make no difference to the compiler. It is easy to confuse the logic and inadvertently assign an else statement to the wrong if statement. Listing 4.7 illustrates this problem. Listing 4.7. A demonstration of why braces help clarify which else statement goes with which if statement. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: // Listing 4.7 - demonstrates why braces // are important in nested if statements #include int main() { int x; cout << "Enter a number less than 10 or greater than 100: "; cin >> x; cout << "\n"; if (x > 10) if (x > 100)13: 14: 15: 16: 17: 18: } cout << "More than 100, Thanks!\n"; else // not the else intended! cout << "Less than 10, Thanks!\n"; return 0; Output: Enter a number less than 10 or greater than 100: 20 Less than 10, Thanks! Analysis: The programmer intended to ask for a number between 10 and 100, check for the correct value, and then print a thank-you note. If the if statement on line 11 evaluates TRUE, the following statement (line 12) is executed. In this case, line 12 executes when the number entered is greater than 10. Line 12 contains an if statement also. This if statement evaluates TRUE if the number entered is greater than 100. If the number is not greater than 100, the statement on line 13 is executed. If the number entered is less than or equal to 10, the if statement on line 10 evaluates to FALSE. Program control goes to the next line following the if statement, in this case line 16. If you enter a number less than 10, the output is as follows: Enter a number less than 10 or greater than 100: 9 The else clause on line 14 was clearly intended to be attached to the if statement on line 11, and thus is indented accordingly. Unfortunately, the else statement is really attached to the if statement on line 12, and thus this program has a subtle bug. It is a subtle bug because the compiler will not complain. This is a legal C++ program, but it just doesn't do what was intended. Further, most of the times the programmer tests this program, it will appear to work. As long as a number that is greater than 100 is entered, the program will seem to work just fine. Listing 4.8 fixes the problem by putting in the necessary braces. Listing 4.8. A demonstration of the proper use of braces with an if statement 1: 2: 3: 4: 5: 6: 7: // Listing 4.8 - demonstrates proper use of braces // in nested if statements #include int main() { int x; cout << "Enter a number less than 10 or greater than 100:"; 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: } Output: cin >> x; cout << "\n"; if (x > 10) { if (x > 100) cout << "More than 100, Thanks!\n"; } else // not the else intended! cout << "Less than 10, Thanks!\n"; return 0; Enter a number less than 10 or greater than 100: 20 Analysis: The braces on lines 12 and 15 make everything between them into one statement, and now the else on line 16 applies to the if on line 11 as intended. The user typed 20, so the if statement on line 11 is true; however, the if statement on line 13 is false, so nothing is printed. It would be better if the programmer put another else clause after line 14 so that errors would be caught and a message printed. NOTE: The programs shown in this book are written to demonstrate the particular issues being discussed. They are kept intentionally simple; there is no attempt to "bulletproof" the code to protect against user error. In professional-quality code, every possible user error is anticipated and handled gracefully. Logical Operators Often you want to ask more than one relational question at a time. "Is it true that x is greater than y, and also true that y is greater than z?" A program might need to determine that both of these conditions are true, or that some other condition is true, in order to take an action. Imagine a sophisticated alarm system that has this logic: "If the door alarm sounds AND it is after six p.m. AND it is NOT a holiday, OR if it is a weekend, then call the police." C++'s three logical operators are used to make this kind of evaluation. These operators are listed in Table 4.2. Table 4.2. The Logical Operators. Operator Symbol Example expression1 && expression2 AND && expression1 || expression2 OR || !expression NOT !Logical AND A logical AND statement evaluates two expressions, and if both expressions are true, the logical AND statement is true as well. If it is true that you are hungry, AND it is true that you have money, THEN it is true that you can buy lunch. Thus, if ( (x == 5) && (y == 5) ) would evaluate TRUE if both x and y are equal to 5, and it would evaluate FALSE if either one is not equal to 5. Note that both sides must be true for the entire expression to be true. Note that the logical AND is two && symbols. A single & symbol is a different operator, discussed on Day 21, "What's Next." Logical OR A logical OR statement evaluates two expressions. If either one is true, the expression is true. If you have money OR you have a credit card, you can pay the bill. You don't need both money and a credit card; you need only one, although having both would be fine as well. Thus, if ( (x == 5) || (y == 5) ) evaluates TRUE if either x or y is equal to 5, or if both are. Note that the logical OR is two || symbols. A single | symbol is a different operator, discussed on Day 21. Logical NOT A logical NOT statement evaluates true if the expression being tested is false. Again, if the expression being tested is false, the value of the test is TRUE! Thus if ( !(x == 5) ) is true only if x is not equal to 5. This is exactly the same as writing if (x != 5) Relational Precedence Relational operators and logical operators, being C++ expressions, each return a value: 1 (TRUE) or 0 (FALSE). Like all expressions, they have a precedence order (see Appendix A) that determines which relations are evaluated first. This fact is important when determining the value of the statementif ( x > 5 && y > 5 || z > 5) It might be that the programmer wanted this expression to evaluate TRUE if both x and y are greater than 5 or if z is greater than 5. On the other hand, the programmer might have wanted this expression to evaluate TRUE only if x is greater than 5 and if it is also true that either y is greater than 5 or z is greater than 5. If x is 3, and y and z are both 10, the first interpretation will be true (z is greater than 5, so ignore x and y), but the second will be false (it isn't true that both x and y are greater than 5 nor is it true that z is greater than 5). Although precedence will determine which relation is evaluated first, parentheses can both change the order and make the statement clearer: if ( (x > 5) && (y > 5 || z > 5) ) Using the values from earlier, this statement is false. Because it is not true that x is greater than 5, the left side of the AND statement fails, and thus the entire statement is false. Remember that an AND statement requires that both sides be true--something isn't both "good tasting" AND "good for you" if it isn't good tasting. NOTE: It is often a good idea to use extra parentheses to clarify what you want to group. Remember, the goal is to write programs that work and that are easy to read and understand. More About Truth and Falsehood In C++, zero is false, and any other value is true. Because an expression always has a value, many C++ programmers take advantage of this feature in their if statements. A statement such as if (x) x = 0; // if x is true (nonzero) can be read as "If x has a nonzero value, set it to 0." This is a bit of a cheat; it would be clearer if written if (x != 0) x = 0; // if x is nonzero Both statements are legal, but the latter is clearer. It is good programming practice to reserve the former method for true tests of logic, rather than for testing for nonzero values.These two statements also are equivalent: if (!x) if (x == 0) // if x is false (zero) // if x is zero The second statement, however, is somewhat easier to understand and is more explicit. DO put parentheses around your logical tests to make them clearer and to make the precedence explicit. DO use braces in nested if statements to make the else statements clearer and to avoid bugs. DON'T use if(x) as a synonym for if(x != 0); the latter is clearer. DON'T use if(!x) as a synonym for if(x == 0); the latter is clearer. NOTE: It is common to define your own enumerated Boolean (logical) type with enum Bool {FALSE, TRUE};. This serves to set FALSE to 0 and TRUE to 1. Conditional (Ternary) Operator The conditional operator (?:) is C++'s only ternary operator; that is, it is the only operator to take three terms. The conditional operator takes three expressions and returns a value: (expression1) ? (expression2) : (expression3) This line is read as "If expression1 is true, return the value of expression2; otherwise, return the value of expression3." Typically, this value would be assigned to a variable. Listing 4.9 shows an if statement rewritten using the conditional operator. Listing 4.9. A demonstration of the conditional operator . 1: 2: 3: 4: 5: 6: 7: 8: // Listing 4.9 - demonstrates the conditional operator // #include int main() { int x, y, z; cout << "Enter two numbers.\n"; cout << "First: ";9: cin >> x; 10: cout << "\nSecond: "; 11: cin >> y; 12: cout << "\n"; 13: 14: if (x > y) 15: z = x; 16: else 17: z = y; 18: 19: cout << "z: " << z; 20: cout << "\n"; 21: 22: z = (x > y) ? x : y; 23: 24: cout << "z: " << z; 25: cout << "\n"; 26: return 0; 27: } Output: Enter two numbers. First: 5 Second: 8 z: 8 z: 8 Analysis: Three integer variables are created: x, y, and z. The first two are given values by the user. The if statement on line 14 tests to see which is larger and assigns the larger value to z. This value is printed on line 19. The conditional operator on line 22 makes the same test and assigns z the larger value. It is read like this: "If x is greater than y, return the value of x; otherwise, return the value of y." The value returned is assigned to z. That value is printed on line 24. As you can see, the conditional statement is a shorter equivalent to the if...else statement. Summary This chapter has covered a lot of material. You have learned what C++ statements and expressions are, what C++ operators do, and how C++ if statements work. You have seen that a block of statements enclosed by a pair of braces can be used anywhere a single statement can be used. You have learned that every expression evaluates to a value, and that value can be tested in an if statement or by using the conditional operator. You've also seen how to evaluate multiple statementsusing the logical operator, how to compare values using the relational operators, and how to assign values using the assignment operator. You have explored operator precedence. And you have seen how parentheses can be used to change the precedence and to make precedence explicit and thus easier to manage. Q&A Q. Why use unnecessary parentheses when precedence will determine which operators are acted on first? A. Although it is true that the compiler will know the precedence and that a programmer can look up the precedence order, code that is easy to understand is easier to maintain. Q. If the relational operators always return 1 or 0, why are other values considered true? A. The relational operators return 1 or 0, but every expression returns a value, and those values can also be evaluated in an if statement. Here's an example: if ( (x = a + b) == 35 ) This is a perfectly legal C++ statement. It evaluates to a value even if the sum of a and b is not equal to 35. Also note that x is assigned the value that is the sum of a and b in any case. Q. What effect do tabs, spaces, and new lines have on the program? A. Tabs, spaces, and new lines (known as whitespace) have no effect on the program, although judicious use of whitespace can make the program easier to read. Q. Are negative numbers true or false? A. All nonzero numbers, positive and negative, are true. Workshop The Workshop provides quiz questions to help you solidify your understanding of the material covered, and exercises to provide you with experience in using what you've learned. Try to answer the quiz and exercise questions before checking the answers in Appendix D, and make sure that you understand the answers before continuing to the next chapter. Quiz 1. What is an expression? 2. Is x = 5 + 7 an expression? What is its value?3. What is the value of 201 / 4? 4. What is the value of 201 % 4? 5. If myAge, a, and b are all int variables, what are their values after: myAge = 39; a = myAge++; b = ++myAge; 6. What is the value of 8+2*3? 7. What is the difference between x = 3 and x == 3? 8. Do the following values evaluate to TRUE or FALSE? a. 0 b. 1 c. -1 d. x = 0 e. x == 0 // assume that x has the value of 0 Exercises 1. Write a single if statement that examines two integer variables and changes the larger to the smaller, using only one else clause. 2. Examine the following program. Imagine entering three numbers, and write what output you expect. 1: #include 2: int main() 3: { 4: int a, b, c; 5: cout << "Please enter three numbers\n"; 6: cout << "a: "; 7: cin >> a; 8: cout << "\nb: "; 9: cin >> b; 10: cout << "\nc: "; 11: cin >> c; 12: 13: if (c = (a-b)) 14: {cout << "a: "; 15: cout << a; 16: cout << "minus b: "; 17: cout << b;18: 19: 20: 21: 22: 23: cout << "equals c: "; cout << c << endl;} else cout << "a-b does not equal c: " << endl; return 0; } 3. Enter the program from Exercise 2; compile, link, and run it. Enter the numbers 20, 10, and 50. Did you get the output you expected? Why not? 4. Examine this program and anticipate the output: #include int main() { int a = 1, b = 1, c; if (c = (a-b)) cout << "The value of c is: " << c; return 0; } 5. Enter, compile, link, and run the program from Exercise 4. What was the output? Why? Day 5 Functions Although object-oriented programming has shifted attention from functions and toward objects, functions nonetheless remain a central component of any program. Today you will learn What a function is and what its parts are. How to declare and define functions. How to pass parameters into functions. How to return a value from a function. What Is a Function? A function is, in effect, a subprogram that can act on data and return a value. Every C++ program has at least one function, main(). When your program starts, main() is called automatically. main() might call other functions, some of which might call still others. Each function has its own name, and when that name is encountered, the execution of the program branches to the body of that function. When the function returns, execution resumes on the next line of the calling function. This flow is illustrated in Figure 5.1. Figure 5.1. Illusrtation of flowWhen a program calls a function, execution switches to the function and then resumes at the line after the function call. Well-designed functions perform a specific and easily understood task. Complicated tasks should be broken down into multiple functions, and then each can be called in turn. Functions come in two varieties: user-defined and built-in. Built-in functions are part of your compiler package--they are supplied by the manufacturer for your use. Declaring and Defining Functions Using functions in your program requires that you first declare the function and that you then define the function. The declaration tells the compiler the name, return type, and parameters of the function. The definition tells the compiler how the function works. No function can be called from any other function that hasn't first been declared. The declaration of a function is called its prototype. Declaring the Function There are three ways to declare a function: Write your prototype into a file, and then use the #include directive to include it in your program. Write the prototype into the file in which your function is used. Define the function before it is called by any other function. When you do this, the definition acts as its own declaration. Although you can define the function before using it, and thus avoid the necessity of creating a function prototype, this is not good programming practice for three reasons. First, it is a bad idea to require that functions appear in a file in a particular order. Doing so makes it hard to maintain the program as requirements change. Second, it is possible that function A() needs to be able to call function B(), but function B() also needs to be able to call function A() under some circumstances. It is not possible to define function A() before you define function B() and also to define function B() before you define function A(), so at least one of them must be declared in any case. Third, function prototypes are a good and powerful debugging technique. If your prototype declares that your function takes a particular set of parameters, or that it returns a particular type of value, and then your function does not match the prototype, the compiler can flag your error instead of waiting for it to show itself when you run the program. Function PrototypesMany of the built-in functions you use will have their function prototypes already written in the files you include in your program by using #include. For functions you write yourself, you must include the prototype. The function prototype is a statement, which means it ends with a semicolon. It consists of the function's return type, name, and parameter list. The parameter list is a list of all the parameters and their types, separated by commas. Figure 5.2 illustrates the parts of the function prototype. Figure 5.2. Parts of a function prototype. The function prototype and the function definition must agree exactly about the return type, the name, and the parameter list. If they do not agree, you will get a compile-time error. Note, however, that the function prototype does not need to contain the names of the parameters, just their types. A prototype that looks like this is perfectly legal: long Area(int, int); This prototype declares a function named Area() that returns a long and that has two parameters, both integers. Although this is legal, it is not a good idea. Adding parameter names makes your prototype clearer. The same function with named parameters might be long Area(int length, int width); It is now obvious what this function does and what the parameters are. Note that all functions have a return type. If none is explicitly stated, the return type defaults to int. Your programs will be easier to understand, however, if you explicitly declare the return type of every function, including main(). Listing 5.1 demonstrates a program that includes a function prototype for the Area() function. Listing 5.1. A function declaration and the definition and use of that function. 1: // Listing 5.1 - demonstrates the use of function prototypes 2: 3: typedef unsigned short USHORT; 4: #include 5: USHORT FindArea(USHORT length, USHORT width); //function prototype 6: 7: int main() 8: { 9: USHORT lengthOfYard;10: USHORT widthOfYard; 11: USHORT areaOfYard; 12: 13: cout << "\nHow wide is your yard? "; 14: cin >> widthOfYard; 15: cout << "\nHow long is your yard? "; 16: cin >> lengthOfYard; 17: 18: areaOfYard= FindArea(lengthOfYard,widthOfYard); 19: 20: cout << "\nYour yard is "; 21: cout << areaOfYard; 22: cout << " square feet\n\n"; 23: return 0; 24: } 25: 26: USHORT FindArea(USHORT l, USHORT w) 27: { 28: return l * w; 29: } Output: How wide is your yard? 100 How long is your yard? 200 Your yard is 20000 square feet Analysis: The prototype for the FindArea() function is on line 5. Compare the prototype with the definition of the function on line 26. Note that the name, the return type, and the parameter types are the same. If they were different, a compiler error would have been generated. In fact, the only required difference is that the function prototype ends with a semicolon and has no body. Also note that the parameter names in the prototype are length and width, but the parameter names in the definition are l and w. As discussed, the names in the prototype are not used; they are there as information to the programmer. When they are included, they should match the implementation when possible. This is a matter of good programming style and reduces confusion, but it is not required, as you see here. The arguments are passed in to the function in the order in which they are declared and defined, but there is no matching of the names. Had you passed in widthOfYard, followed by lengthOfYard, the FindArea() function would have used the value in widthOfYard for length and lengthOfYard for width. The body of the function is always enclosed in braces, even when it consists of only one statement, as in this case. Defining the Function The definition of a function consists of the function header and its body. The header is exactly like the function prototype, except that the parameters must be named, and there is no terminating semicolon.The body of the function is a set of statements enclosed in braces. Figure 5.3 shows the header and body of a function. Figure 5.3. The header and body of a function. Functions Function Prototype Syntax return_type function_name ( [type [parameterName]]...); Function Definition Syntax return_type function_name ( [type parameterName]...) { statements; } A function prototype tells the compiler the return type, name, and parameter list. Func-tions are not required to have parameters, and if they do, the prototype is not required to list their names, only their types. A prototype always ends with a semicolon (;). A function definition must agree in return type and parameter list with its prototype. It must provide names for all the parameters, and the body of the function definition must be surrounded by braces. All statements within the body of the function must be terminated with semicolons, but the function itself is not ended with a semicolon; it ends with a closing brace. If the function returns a value, it should end with a return statement, although return statements can legally appear anywhere in the body of the function. Every function has a return type. If one is not explicitly designated, the return type will be int. Be sure to give every function an explicit return type. If a function does not return a value, its return type will be void. Function Prototype Examples long FindArea(long length, long width); // returns long, has two parameters void PrintMessage(int messageNumber); // returns void, has one parameter int GetChoice(); // returns int, has no parameters BadFunction(); parameters // returns int, has noFunction Definition Examples long Area(long l, long w) { return l * w; } void PrintMessage(int whichMsg) { if (whichMsg == 0) cout << "Hello.\n"; if (whichMsg == 1) cout << "Goodbye.\n"; if (whichMsg > 1) cout << "I'm confused.\n"; } Execution of Functions When you call a function, execution begins with the first statement after the opening brace ({). Branching can be accomplished by using the if statement (and related statements that will be discussed on Day 7, "More Program Flow"). Functions can also call other functions and can even call themselves (see the section "Recursion," later in this chapter). Local Variables Not only can you pass in variables to the function, but you also can declare variables within the body of the function. This is done using local variables, so named because they exist only locally within the function itself. When the function returns, the local variables are no longer available. Local variables are defined like any other variables. The parameters passed in to the function are also considered local variables and can be used exactly as if they had been defined within the body of the function. Listing 5.2 is an example of using parameters and locally defined variables within a function. Listing 5.2. The use of local variables and parameters. 1: 2: 3: #include float Convert(float);4: 22: } int main() { float TempFer; float TempCel; cout << "Please enter the temperature in Fahrenheit: "; cin >> TempFer; TempCel = Convert(TempFer); cout << "\nHere's the temperature in Celsius: "; cout << TempCel << endl; return 0; } float Convert(float TempFer) { float TempCel; TempCel = ((TempFer - 32) * 5) / 9; return TempCel; Output: Please enter the temperature in Fahrenheit: 212 Here's the temperature in Celsius: 100 Please enter the temperature in Fahrenheit: 32 Here's the temperature in Celsius: 0 Please enter the temperature in Fahrenheit: 85 Here's the temperature in Celsius: 29.4444 Analysis: On lines 6 and 7, two float variables are declared, one to hold the temperature in Fahrenheit and one to hold the temperature in degrees Celsius. The user is prompted to enter a Fahrenheit temperature on line 9, and that value is passed to the function Convert(). Execution jumps to the first line of the function Convert() on line 19, where a local variable, also named TempCel, is declared. Note that this local variable is not the same as the variable TempCel on line 7. This variable exists only within the function Convert(). The value passed as a parameter, TempFer, is also just a local copy of the variable passed in by main(). This function could have named the parameter FerTemp and the local variable CelTemp, and the program would work equally well. You can enter these names again and recompile the program to see this work. The local function variable TempCel is assigned the value that results from subtracting 32 from the parameter TempFer, multiplying by 5, and then dividing by 9. This value is then returned as the return value of the function, and on line 11 it is assigned to the variable TempCel in the main()function. The value is printed on line 13. The program is run three times. The first time, the value 212 is passed in to ensure that the boiling point of water in degrees Fahrenheit (212) generates the correct answer in degrees Celsius (100). The second test is the freezing point of water. The third test is a random number chosen to generate a fractional result. As an exercise, try entering the program again with other variable names as illustrated here: #include float Convert(float); int main() { float TempFer; float TempCel; cout << "Please enter the temperature in Fahrenheit: "; cin >> TempFer; TempCel = Convert(TempFer); cout << "\nHere's the temperature in Celsius: "; cout << TempCel << endl; } float Convert(float Fer) { float Cel; Cel = ((Fer - 32) * 5) / 9; return Cel; } You should get the same results. New Term: A variable has scope, which determines how long it is available to your program and where it can be accessed. Variables declared within a block are scoped to that block; they can be accessed only within that block and "go out of existence" when that block ends. Global variables have global scope and are available anywhere within your program. Normally scope is obvious, but there are some tricky exceptions. Currently, variables declared within the header of a for loop (for int i = 0; i 2: void myFunction(); // prototype 3: 4: int x = 5, y = 7; // global variables 5: int main() 6: { 7: 8: cout << "x from main: " << x << "\n"; 9: cout << "y from main: " << y << "\n\n"; 10: myFunction(); 11: cout << "Back from myFunction!\n\n"; 12: cout << "x from main: " << x << "\n"; 13: cout << "y from main: " << y << "\n"; 14: return 0; 15: } 16: 17: void myFunction() 18: { 19: int y = 10; 20: 21: cout << "x from myFunction: " << x << "\n"; 22: cout << "y from myFunction: " << y << "\n\n"; 23: } Output: x from main: 5 y from main: 7 x from myFunction: 5 y from myFunction: 10 Back from myFunction! x from main: 5y from main: 7 Analysis: This simple program illustrates a few key, and potentially confusing, points about local and global variables. On line 1, two global variables, x and y, are declared. The global variable x is initialized with the value 5, and the global variable y is initialized with the value 7. On lines 8 and 9 in the function main(), these values are printed to the screen. Note that the function main() defines neither variable; because they are global, they are already available to main(). When myFunction() is called on line 10, program execution passes to line 18, and a local variable, y, is defined and initialized with the value 10. On line 21, myFunction() prints the value of the variable x, and the global variable x is used, just as it was in main(). On line 22, however, when the variable name y is used, the local variable y is used, hiding the global variable with the same name. The function call ends, and control returns to main(), which again prints the values in the global variables. Note that the global variable y was totally unaffected by the value assigned to myFunction()'s local y variable. Global Variables: A Word of Caution In C++, global variables are legal, but they are almost never used. C++ grew out of C, and in C global variables are a dangerous but necessary tool. They are necessary because there are times when the programmer needs to make data available to many functions and he does not want to pass that data as a parameter from function to function. Globals are dangerous because they are shared data, and one function can change a global variable in a way that is invisible to another function. This can and does create bugs that are very difficult to find. On Day 14, "Special Classes and Functions," you'll see a powerful alternative to global variables that C++ offers, but that is unavailable in C. More on Local Variables Variables declared within the function are said to have "local scope." That means, as discussed, that they are visible and usable only within the function in which they are defined. In fact, in C++ you can define variables anywhere within the function, not just at its top. The scope of the variable is the block in which it is defined. Thus, if you define a variable inside a set of braces within the function, that variable is available only within that block. Listing 5.4 illustrates this idea. Listing 5.4. Variables scoped within a block. 1: 2: 3: 4: // Listing 5.4 - demonstrates variables // scoped within a block #include 5: 34: } void myFunc(); int main() { int x = 5; cout << "\nIn main x is: " << x; myFunc(); cout << "\nBack in main, x is: " << x; return 0; } void myFunc() { int x = 8; cout << "\nIn myFunc, local x: " << x << endl; { cout << "\nIn block in myFunc, x is: " << x; int x = 9; cout << "\nVery local x: " << x; } cout << "\nOut of block, in myFunc, x: " << x << endl; Output: In main x is: 5 In myFunc, local x: 8 In block in myFunc, x is: 8 Very local x: 9 Out of block, in myFunc, x: 8 Back in main, x is: 5 Analysis: This program begins with the initialization of a local variable, x, on line 10, in main(). The printout on line 11 verifies that x was initialized with the value 5. MyFunc() is called, and a local variable, also named x, is initialized with the value 8 on line 22. Its value is printed on line 23. A block is started on line 25, and the variable x from the function is printed again on line 26. A newvariable also named x, but local to the block, is created on line 28 and initialized with the value 9. The value of the newest variable x is printed on line 30. The local block ends on line 31, and the variable created on line 28 goes "out of scope" and is no longer visible. When x is printed on line 33, it is the x that was declared on line 22. This x was unaffected by the x that was defined on line 28; its value is still 8. On line 34, MyFunc() goes out of scope, and its local variable x becomes unavailable. Execution returns to line 15, and the value of the local variable x, which was created on line 10, is printed. It was unaffected by either of the variables defined in MyFunc(). Needless to say, this program would be far less confusing if these three variables were given unique names! Function Statements There is virtually no limit to the number or types of statements that can be in a function body. Although you can't define another function from within a function, you can call a function, and of course main() does just that in nearly every C++ program. Functions can even call themselves, which is discussed soon, in the section on recursion. Although there is no limit to the size of a function in C++, well-designed functions tend to be small. Many programmers advise keeping your functions short enough to fit on a single screen so that you can see the entire function at one time. This is a rule of thumb, often broken by very good programmers, but a smaller function is easier to understand and maintain. Each function should carry out a single, easily understood task. If your functions start getting large, look for places where you can divide them into component tasks. Function Arguments Function arguments do not have to all be of the same type. It is perfectly reasonable to write a function that takes an integer, two longs, and a character as its arguments. Any valid C++ expression can be a function argument, including constants, mathematical and logical expressions, and other functions that return a value. Using Functions as Parameters to Functions Although it is legal for one function to take as a parameter a second function that returns a value, it can make for code that is hard to read and hard to debug. As an example, say you have the functions double(), triple(), square(), and cube(), eachof which returns a value. You could write Answer = (double(triple(square(cube(myValue))))); This statement takes a variable, myValue, and passes it as an argument to the function cube(), whose return value is passed as an argument to the function square(), whose return value is in turn passed to triple(), and that return value is passed to double(). The return value of this doubled, tripled, squared, and cubed number is now passed to Answer. It is difficult to be certain what this code does (was the value tripled before or after it was squared?), and if the answer is wrong it will be hard to figure out which function failed. An alternative is to assign each step to its own intermediate variable: unsigned long myValue = cubed = squared = tripled = Answer = 2; double(tripled); // // // // cubed = 8 squared = 64 tripled = 196 Answer = 392 Now each intermediate result can be examined, and the order of execution is explicit. Parameters Are Local Variables The arguments passed in to the function are local to the function. Changes made to the arguments do not affect the values in the calling function. This is known as passing by value, which means a local copy of each argument is made in the function. These local copies are treated just like any other local variables. Listing 5.5 illustrates this point. Listing 5.5. A demonstration of passing by value. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: "\n"; 12: // Listing 5.5 - demonstrates passing by value #include void swap(int x, int y); int main() { int x = 5, y = 10; cout << "Main. Before swap, x: " << x << " y: " << y << swap(x,y);13: "\n"; "\n"; 28: 29: } cout << "Main. After swap, x: " << x << " y: " << y << return 0; } void swap (int x, int y) { int temp; cout << "Swap. Before swap, x: " << x << " y: " << y << temp = x; x = y; y = temp; cout << "Swap. After swap, x: " << x << " y: " << y << Output: Main. Before swap, x: 5 y: 10 Swap. Before swap, x: 5 y: 10 Swap. After swap, x: 10 y: 5 Main. After swap, x: 5 y: 10 Analysis: This program initializes two variables in main() and then passes them to the swap() function, which appears to swap them. When they are examined again in main(), however, they are unchanged! The variables are initialized on line 9, and their values are displayed on line 11. swap() is called, and the variables are passed in. Execution of the program switches to the swap() function, where on line 21 the values are printed again. They are in the same order as they were in main(), as expected. On lines 23 to 25 the values are swapped, and this action is confirmed by the printout on line 27. Indeed, while in the swap() function, the values are swapped. Execution then returns to line 13, back in main(), where the values are no longer swapped. As you've figured out, the values passed in to the swap() function are passed by value, meaning that copies of the values are made that are local to swap(). These local variables are swapped in lines 23 to 25, but the variables back in main() are unaffected. On Days 8 and 10 you'll see alternatives to passing by value that will allow the values in main() to be changed.Return Values Functions return a value or return void. Void is a signal to the compiler that no value will be returned. To return a value from a function, write the keyword return followed by the value you want to return. The value might itself be an expression that returns a value. For example: return 5; return (x > 5); return (MyFunction()); These are all legal return statements, assuming that the function MyFunction() itself returns a value. The value in the second statement, return (x > 5), will be zero if x is not greater than 5, or it will be 1. What is returned is the value of the expression, 0 (false) or 1 (true), not the value of x. When the return keyword is encountered, the expression following return is returned as the value of the function. Program execution returns immediately to the calling function, and any statements following the return are not executed. It is legal to have more than one return statement in a single function. Listing 5.6 illustrates this idea. Listing 5.6. A demonstration of multiple return statements . 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: "; 15: 16: 17: // Listing 5.6 - demonstrates multiple return // statements #include int Doubler(int AmountToDouble); int main() { int result = 0; int input; cout << "Enter a number between 0 and 10,000 to double: cin >> input; cout << "\nBefore doubler is called... ";18: cout << "\ninput: " << input << " doubled: " << result << "\n"; 19: 20: result = Doubler(input); 21: 22: cout << "\nBack from Doubler...\n"; 23: cout << "\ninput: " << input << " doubled: " << result << "\n"; 24: 25: 26: return 0; 27: } 28: 29: int Doubler(int original) 30: { 31: if (original <= 10000) 32: return original * 2; 33: else 34: return -1; 35: cout << "You can't get here!\n"; 36: } Output: Enter a number between 0 and 10,000 to double: 9000 Before doubler is called... input: 9000 doubled: 0 Back from doubler... input: 9000 doubled: 18000 Enter a number between 0 and 10,000 to double: 11000 Before doubler is called... input: 11000 doubled: 0 Back from doubler... input: 11000 doubled: -1 Analysis: A number is requested on lines 14 and 15, and printed on line 18, along with the local variable result. The function Doubler() is called on line 20, and the input value is passed as a parameter. The result will be assigned to the local variable result, and the values will be reprinted on lines 22 and 23. On line 31, in the function Doubler(), the parameter is tested to see whether it is greater than 10,000. If it is not, the function returns twice the original number. If it is greater than 10,000, the function returns -1 as an error value. The statement on line 35 is never reached, because whether or not the value is greater than 10,000, thefunction returns before it gets to line 35, on either line 32 or line 34. A good compiler will warn that this statement cannot be executed, and a good programmer will take it out! Default Parameters For every parameter you declare in a function prototype and definition, the calling function must pass in a value. The value passed in must be of the declared type. Thus, if you have a function declared as long myFunction(int); the function must in fact take an integer variable. If the function definition differs, or if you fail to pass in an integer, you will get a compiler error. The one exception to this rule is if the function prototype declares a default value for the parameter. A default value is a value to use if none is supplied. The preceding declaration could be rewritten as long myFunction (int x = 50); This prototype says, "myFunction() returns a long and takes an integer parameter. If an argument is not supplied, use the default value of 50." Because parameter names are not required in function prototypes, this declaration could have been written as long myFunction (int = 50); The function definition is not changed by declaring a default parameter. The function definition header for this function would be long myFunction (int x) If the calling function did not include a parameter, the compiler would fill x with the default value of 50. The name of the default parameter in the prototype need not be the same as the name in the function header; the default value is assigned by position, not name. Any or all of the function's parameters can be assigned default values. The one restriction is this: If any of the parameters does not have a default value, no previous parameter may have a default value. If the function prototype looks like long myFunction (int Param1, int Param2, int Param3); you can assign a default value to Param2 only if you have assigned a default value to Param3. You can assign a default value to Param1 only if you've assigned default values to both Param2 and Param3. Listing 5.7 demonstrates the use of default values.Listing 5.7. A demonstration of default parameter values. 1: // Listing 5.7 - demonstrates use 2: // of default parameter values 3: 4: #include 5: 6: int AreaCube(int length, int width = 25, int height = 1); 7: 8: int main() 9: { 10: int length = 100; 11: int width = 50; 12: int height = 2; 13: int area; 14: 15: area = AreaCube(length, width, height); 16: cout << "First area equals: " << area << "\n"; 17: 18: area = AreaCube(length, width); 19: cout << "Second time area equals: " << area << "\n"; 20: 21: area = AreaCube(length); 22: cout << "Third time area equals: " << area << "\n"; 23: return 0; 24: } 25: 26: AreaCube(int length, int width, int height) 27: { 28: 29: return (length * width * height); 30: } Output: First area equals: 10000 Second time area equals: 5000 Third time area equals: 2500 Analysis: On line 6, the AreaCube() prototype specifies that the AreaCube() function takes three integer parameters. The last two have default values. This function computes the area of the cube whose dimensions are passed in. If no width is passed in, a width of 25 is used and a height of 1 is used. If the width but not the height is passed in, a height of 1 is used. It is not possible to pass in the height without passing in a width. On lines 10-12, the dimensions length, height, and width are initialized, and they are passed to the AreaCube() function on line 15. The values are computed, and the result is printed on line 16.Execution returns to line 18, where AreaCube() is called again, but with no value for height. The default value is used, and again the dimensions are computed and printed. Execution returns to line 21, and this time neither the width nor the height is passed in. Execution branches for a third time to line 27. The default values are used. The area is computed and then printed. DO remember that function parameters act as local variables within the function. DON'T try to create a default value for a first parameter if there is no default value for the second. DON'T forget that arguments passed by value can not affect the variables in the calling function. DON'T forget that changes to a global variable in one function change that variable for all functions. Overloading Functions C++ enables you to create more than one function with the same name. This is called function overloading. The functions must differ in their parameter list, with a different type of parameter, a different number of parameters, or both. Here's an example: int myFunction (int, int); int myFunction (long, long); int myFunction (long); myFunction() is overloaded with three different parameter lists. The first and second versions differ in the types of the parameters, and the third differs in the number of parameters. The return types can be the same or different on overloaded functions. You should note that two functions with the same name and parameter list, but different return types, generate a compiler error. New Term: Function overloading i s also called function polymorphism. Poly means many, and morph means form: a polymorphic function is many-formed. Function polymorphism refers to the ability to "overload" a function with more than one meaning. By changing the number or type of the parameters, you can give two or more functions the same function name, and the right one will be called by matching the parameters used. This allows you to create a function that can average integers, doubles, and other values without having to create individual names for each function, such as AverageInts(), AverageDoubles(), and so on. Suppose you write a function that doubles whatever input you give it. You would like to be able to pass in an int, a long, a float, or a double. Without function overloading, you would have tocreate four function names: int DoubleInt(int); long DoubleLong(long); float DoubleFloat(float); double DoubleDouble(double); With function overloading, you make this declaration: int Double(int); long Double(long); float Double(float); double Double(double); This is easier to read and easier to use. You don't have to worry about which one to call; you just pass in a variable, and the right function is called automatically. Listing 5.8 illustrates the use of function overloading. Listing 5.8. A demonstration of function polymorphism . 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: // Listing 5.8 - demonstrates // function polymorphism #include int Double(int); long Double(long); float Double(float); double Double(double); int main() { int long float double int long float double myInt = 6500; myLong = 65000; myFloat = 6.5F; myDouble = 6.5e20; doubledInt; doubledLong; doubledFloat; doubledDouble; cout << "myInt: " << myInt << "\n"; cout << "myLong: " << myLong << "\n"; cout << "myFloat: " << myFloat << "\n";26: cout << "myDouble: " << myDouble << "\n"; 27: 28: doubledInt = Double(myInt); 29: doubledLong = Double(myLong); 30: doubledFloat = Double(myFloat); 31: doubledDouble = Double(myDouble); 32: 33: cout << "doubledInt: " << doubledInt << "\n"; 34: cout << "doubledLong: " << doubledLong << "\n"; 35: cout << "doubledFloat: " << doubledFloat << "\n"; 36: cout << "doubledDouble: " << doubledDouble << "\n"; 37: 38: return 0; 39: } 40: 41: int Double(int original) 42: { 43: cout << "In Double(int)\n"; 44: return 2 * original; 45: } 46: 47: long Double(long original) 48: { 49: cout << "In Double(long)\n"; 50: return 2 * original; 51: } 52: 53: float Double(float original) 54: { 55: cout << "In Double(float)\n"; 56: return 2 * original; 57: } 58: 59: double Double(double original) 60: { 61: cout << "In Double(double)\n"; 62: return 2 * original; 63: } Output: myInt: 6500 myLong: 65000 myFloat: 6.5 myDouble: 6.5e+20 In Double(int) In Double(long) In Double(float)In Double(double) DoubledInt: 13000 DoubledLong: 130000 DoubledFloat: 13 DoubledDouble: 1.3e+21 Analysis: The Double()function is overloaded with int, long, float, and double. The prototypes are on lines 6-9, and the definitions are on lines 41-63. In the body of the main program, eight local variables are declared. On lines 13-16, four of the values are initialized, and on lines 28-31, the other four are assigned the results of passing the first four to the Double() function. Note that when Double() is called, the calling function does not distinguish which one to call; it just passes in an argument, and the correct one is invoked. The compiler examines the arguments and chooses which of the four Double() functions to call. The output reveals that each of the four was called in turn, as you would expect. Special Topics About Functions Because functions are so central to programming, a few special topics arise which might be of interest when you confront unusual problems. Used wisely, inline functions can help you squeak out that last bit of performance. Function recursion is one of those wonderful, esoteric bits of programming which, every once in a while, can cut through a thorny problem otherwise not easily solved. Inline Functions When you define a function, normally the compiler creates just one set of instructions in memory. When you call the function, execution of the program jumps to those instructions, and when the function returns, execution jumps back to the next line in the calling function. If you call the function 10 times, your program jumps to the same set of instructions each time. This means there is only one copy of the function, not 10. There is some performance overhead in jumping in and out of functions. It turns out that some functions are very small, just a line or two of code, and some efficiency can be gained if the program can avoid making these jumps just to execute one or two instructions. When programmers speak of efficiency, they usually mean speed: the program runs faster if the function call can be avoided. If a function is declared with the keyword inline, the compiler does not create a real function: it copies the code from the inline function directly into the calling function. No jump is made; it is just as if you had written the statements of the function right into the calling function. Note that inline functions can bring a heavy cost. If the function is called 10 times, the inline code is copied into the calling functions each of those 10 times. The tiny improvement in speed you might achieve is more than swamped by the increase in size of the executable program. Even the speed increase might be illusory. First, today's optimizing compilers do a terrific job on their own, and there is almost never a big gain from declaring a function inline. More important, the increased sizebrings its own performance cost. What's the rule of thumb? If you have a small function, one or two statements, it is a candidate for inline. When in doubt, though, leave it out. Listing 5.9 demonstrates an inline function. Listing 5.9. Demonstrates an inline function. 1: // Listing 5.9 - demonstrates inline functions 2: 3: #include 4: 5: inline int Double(int); 6: 7: int main() 8: { 9: int target; 10: 11: cout << "Enter a number to work with: "; 12: cin >> target; 13: cout << "\n"; 14: 15: target = Double(target); 16: cout << "Target: " << target << endl; 17: 18: target = Double(target); 19: cout << "Target: " << target << endl; 20: 21: 22: target = Double(target); 23: cout << "Target: " << target << endl; 24: return 0; 25: } 26: 27: int Double(int target) 28: { 29: return 2*target; 30: } Output: Enter a number to work with: 20 Target: 40 Target: 80 Target: 160 Analysis: On line 5, Double() is declared to be an inline function taking an int parameterand returning an int. The declaration is just like any other prototype except that the keyword inline is prepended just before the return value. This compiles into code that is the same as if you had written the following: target = 2 * target; everywhere you entered target = Double(target); By the time your program executes, the instructions are already in place, compiled into the OBJ file. This saves a jump in the execution of the code, at the cost of a larger program. NOTE: Inline is a hint to the compiler that you would like the function to be inlined. The compiler is free to ignore the hint and make a real function call. Recursion A function can call itself. This is called recursion, and recursion can be direct or indirect. It is direct when a function calls itself; it is indirect recursion when a function calls another function that then calls the first function. Some problems are most easily solved by recursion, usually those in which you act on data and then act in the same way on the result. Both types of recursion, direct and indirect, come in two varieties: those that eventually end and produce an answer, and those that never end and produce a runtime failure. Programmers think that the latter is quite funny (when it happens to someone else). It is important to note that when a function calls itself, a new copy of that function is run. The local variables in the second version are independent of the local variables in the first, and they cannot affect one another directly, any more than the local variables in main() can affect the local variables in any function it calls, as was illustrated in Listing 5.4. To illustrate solving a problem using recursion, consider the Fibonacci series: 1,1,2,3,5,8,13,21,34... Each number, after the second, is the sum of the two numbers before it. A Fibonacci problem might be to determine what the 12th number in the series is.One way to solve this problem is to examine the series carefully. The first two numbers are 1. Each subsequent number is the sum of the previous two numbers. Thus, the seventh number is the sum of the sixth and fifth numbers. More generally, the nth number is the sum of n - 2 and n - 1, as long as n > 2. Recursive functions need a stop condition. Something must happen to cause the program to stop recursing, or it will never end. In the Fibonacci series, n < 3 is a stop condition. The algorithm to use is this: 1. Ask the user for a position in the series. 2. Call the fib() function with that position, passing in the value the user entered. 3. The fib() function examines the argument (n). If n < 3 it returns 1; otherwise, fib() calls itself (recursively) passing in n-2, calls itself again passing in n-1, and returns the sum. If you call fib(1), it returns 1. If you call fib(2), it returns 1. If you call fib(3), it returns the sum of calling fib(2) and fib(1). Because fib(2) returns 1 and fib(1) returns 1, fib(3) will return 2. If you call fib(4), it returns the sum of calling fib(3) and fib(2). We've established that fib(3) returns 2 (by calling fib(2) and fib(1)) and that fib(2) returns 1, so fib(4) will sum these numbers and return 3, which is the fourth number in the series. Taking this one more step, if you call fib(5), it will return the sum of fib(4) and fib(3). We've established that fib(4) returns 3 and fib(3) returns 2, so the sum returned will be 5. This method is not the most efficient way to solve this problem (in fib(20) the fib() function is called 13,529 times!), but it does work. Be careful: if you feed in too large a number, you'll run out of memory. Every time fib() is called, memory is set aside. When it returns, memory is freed. With recursion, memory continues to be set aside before it is freed, and this system can eat memory very quickly. Listing 5.10 implements the fib() function. WARNING: When you run Listing 5.10, use a small number (less than 15). Because this uses recursion, it can consume a lot of memory. Listing 5.10. Demonstrates recursion using the Fibonacci series . 1: 2: 3: 4: // // // // Listing 5.10 - demonstrates recursion Fibonacci find. Finds the nth Fibonacci number Uses this algorithm: Fib(n) = fib(n-1) + fib(n-2)5: // Stop conditions: n = 2 || n = 1 6: 7: #include 8: 9: int fib(int n); 10: 11: int main() 12: { 13: 14: int n, answer; 15: cout << "Enter number to find: "; 16: cin >> n; 17: 18: cout << "\n\n"; 19: 20: answer = fib(n); 21: 22: cout << answer << " is the " << n << "th Fibonacci number\n"; 23: return 0; 24: } 25: 26: int fib (int n) 27: { 28: cout << "Processing fib(" << n << ")... "; 29: 30: if (n < 3 ) 31: { 32: cout << "Return 1!\n"; 33: return (1); 34: } 35: else 36: { 37: cout << "Call fib(" << n-2 << ") and fib(" << n-1 << ").\n"; 38: return( fib(n-2) + fib(n-1)); 39: } 40: } Output: Enter number to find: Processing Processing Processing Processing Processing fib(5)... fib(3)... fib(1)... fib(2)... fib(4)... 5 Call fib(3) and fib(4). Call fib(1) and fib(2). Return 1! Return 1! Call fib(2) and fib(3).Processing fib(2)... Return 1! Processing fib(3)... Call fib(1) and fib(2). Processing fib(1)... Return 1! Processing fib(2)... Return 1! 5 is the 5th Fibonacci number Analysis: The program asks for a number to find on line 15 and assigns that number to target. It then calls fib() with the target. Execution branches to the fib() function, where, on line 28, it prints its argument. The argument n is tested to see whether it equals 1 or 2 on line 30; if so, fib() returns. Otherwise, it returns the sums of the values returned by calling fib() on n-2 and n-1. In the example, n is 5 so fib(5) is called from main(). Execution jumps to the fib() function, and n is tested for a value less than 3 on line 30. The test fails, so fib(5) returns the sum of the values returned by fib(3) and fib(4). That is, fib() is called on n-2 (5 - 2 = 3) and n-1 (5 - 1 = 4). fib(4) will return 3 and fib(3) will return 2, so the final answer will be 5. Because fib(4) passes in an argument that is not less than 3, fib() will be called again, this time with 3 and 2. fib(3) will in turn call fib(2) and fib(1). Finally, the calls to fib(2) and fib(1) will both return 1, because these are the stop conditions. The output traces these calls and the return values. Compile, link, and run this program, entering first 1, then 2, then 3, building up to 6, and watch the output carefully. Then, just for fun, try the number 20. If you don't run out of memory, it makes quite a show! Recursion is not used often in C++ programming, but it can be a powerful and elegant tool for certain needs. NOTE: Recursion is a very tricky part of advanced programming. It is presented here because it can be very useful to understand the fundamentals of how it works, but don't worry too much if you don't fully understand all the details. How Functions WorkA Look Under the Hood When you call a function, the code branches to the called function, parameters are passed in, and the body of the function is executed. When the function completes, a value is returned (unless the function returns void), and control returns to the calling function. How is this task accomplished? How does the code know where to branch to? Where are the variables kept when they are passed in? What happens to variables that are declared in the body of the function? How is the return value passed back out? How does the code know where to resume? Most introductory books don't try to answer these questions, but without understanding thisinformation, you'll find that programming remains a fuzzy mystery. The explanation requires a brief tangent into a discussion of computer memory. Levels of Abstraction One of the principal hurdles for new programmers is grappling with the many layers of intellectual abstraction. Computers, of course, are just electronic machines. They don't know about windows and menus, they don't know about programs or instructions, and they don't even know about 1s and 0s. All that is really going on is that voltage is being measured at various places on an integrated circuit. Even this is an abstraction: electricity itself is just an intellectual concept, representing the behavior of subatomic particles. Few programmers bother much with any level of detail below the idea of values in RAM. After all, you don't need to understand particle physics to drive a car, make toast, or hit a baseball, and you don't need to understand the electronics of a computer to program one. You do need to understand how memory is organized, however. Without a reasonably strong mental picture of where your variables are when they are created, and how values are passed among functions, it will all remain an unmanageable mystery. Partitioning RAM When you begin your program, your operating system (such as DOS or Microsoft Windows) sets up various areas of memory based on the requirements of your compiler. As a C++ programmer, you'll often be concerned with the global name space, the free store, the registers, the code space, and the stack. Global variables are in global name space. We'll talk more about global name space and the free store in coming days, but for now we'll focus on the registers, code space, and stack. Registers are a special area of memory built right into the Central Processing Unit (or CPU). They take care of internal housekeeping. A lot of what goes on in the registers is beyond the scope of this book, but what we are concerned about is the set of registers responsible for pointing, at any given moment, to the next line of code. We'll call these registers, together, the instruction pointer. It is the job of the instruction pointer to keep track of which line of code is to be executed next. The code itself is in code space, which is that part of memory set aside to hold the binary form of the instructions you created in your program. Each line of source code is translated into a series of instructions, and each of these instructions is at a particular address in memory. The instruction pointer has the address of the next instruction to execute. Figure 5.4 illustrates this idea. Figure 5.4.The instruction pointer. The stack is a special area of memory allocated for your program to hold the data required by each of the functions in your program. It is called a stack because it is a last-in, first-out queue, much like astack of dishes at a cafeteria, as shown in Figure 5.5. Last-in, first-out means that whatever is added to the stack last will be the first thing taken off. Most queues are like a line at a theater: the first one on line is the first one off. A stack is more like a stack of coins: if you stack 10 pennies on a tabletop and then take some back, the last three you put on will be the first three you take off. When data is "pushed" onto the stack, the stack grows; as data is "popped" off the stack, the stack shrinks. It isn't possible to pop a dish off the stack without first popping off all the dishes placed on after that dish. Figure 5.5. A stack. A stack of dishes is the common analogy. It is fine as far as it goes, but it is wrong in a fundamental way. A more accurate mental picture is of a series of cubbyholes aligned top to bottom. The top of the stack is whatever cubby the stack pointer (which is another register) happens to be pointing to. Each of the cubbies has a sequential address, and one of those addresses is kept in the stack pointer register. Everything below that magic address, known as the top of the stack, is considered to be on the stack. Everything above the top of the stack is considered to be off the stack and invalid. Figure 5.6 illustrates this idea. Figure 5.6.The stack pointer. When data is put on the stack, it is placed into a cubby above the stack pointer, and then the stack pointer is moved to the new data. When data is popped off the stack, all that really happens is that the address of the stack pointer is changed by moving it down the stack. Figure 5.7 makes this rule clear. Figure 5.7. Moving the stack pointer. The Stack and Functions Here's what happens when a program, running on a PC under DOS, branches to a function: 1. The address in the instruction pointer is incremented to the next instruction past the function call. That address is then placed on the stack, and it will be the return address when the function returns. 2. Room is made on the stack for the return type you've declared. On a system with two-byte integers, if the return type is declared to be int, another two bytes are added to the stack, but no value is placed in these bytes. 3. The address of the called function, which is kept in a special area of memory set aside for that purpose, is loaded into the instruction pointer, so the next instruction executed will be in the called function.4. The current top of the stack is now noted and is held in a special pointer called the stack frame. Everything added to the stack from now until the function returns will be considered "local" to the function. 5. All the arguments to the function are placed on the stack. 6. The instruction now in the instruction pointer is executed, thus executing the first instruction in the function. 7. Local variables are pushed onto the stack as they are defined. When the function is ready to return, the return value is placed in the area of the stack reserved at step 2. The stack is then popped all the way up to the stack frame pointer, which effectively throws away all the local variables and the arguments to the function. The return value is popped off the stack and assigned as the value of the function call itself, and the address stashed away in step 1 is retrieved and put into the instruction pointer. The program thus resumes immediately after the function call, with the value of the function retrieved. Some of the details of this process change from compiler to compiler, or between computers, but the essential ideas are consistent across environments. In general, when you call a function, the return address and the parameters are put on the stack. During the life of the function, local variables are added to the stack. When the function returns, these are all removed by popping the stack. In coming days we'll look at other places in memory that are used to hold data that must persist beyond the life of the function. Summary This chapter introduced functions. A function is, in effect, a subprogram into which you can pass parameters and from which you can return a value. Every C++ program starts in the main() function, and main() in turn can call other functions. A function is declared with a function prototype, which describes the return value, the function name, and its parameter types. A function can optionally be declared inline. A function prototype can also declare default variables for one or more of the parameters. The function definition must match the function prototype in return type, name, and parameter list. Function names can be overloaded by changing the number or type of parameters; the compiler finds the right function based on the argument list. Local function variables, and the arguments passed in to the function, are local to the block in which they are declared. Parameters passed by value are copies and cannot affect the value of variables in the calling function.Q&A Q. Why not make all variables global? A. There was a time when this was exactly how programming was done. As programs became more complex, however, it became very difficult to find bugs in programs because data could be corrupted by any of the functions--global data can be changed anywhere in the program. Years of experience have convinced programmers that data should be kept as local as possible, and access to changing that data should be narrowly defined. Q. When should the keyword inline be used in a function prototype? A. If the function is very small, no more than a line or two, and won't be called from many places in your program, it is a candidate for inlining. Q. Why aren't changes to the value of function arguments reflected in the calling function? A. Arguments passed to a function are passed by value. That means that the argument in the function is actually a copy of the original value. This concept is explained in depth in the "Extra Credit" section that follows the Workshop. Q. If arguments are passed by value, what do I do if I need to reflect the changes back in the calling function? A. On Day 8, pointers will be discussed. Use of pointers will solve this problem, as well as provide a way around the limitation of returning only a single value from a function. Q. What happens if I have the following two functions? int Area (int width, int length = 1); int Area (int size); Will these overload? There are a different number of parameters, but the first one has a default value. A. The declarations will compile, but if you invoke Area with one parameter you will receive a compile-time error: ambiguity between Area(int, int) and Area(int). Workshop The Workshop provides quiz questions to help you solidify your understanding of the material covered, and exercises to provide you with experience in using what you've learned. Try to answer the quiz and exercise questions before checking the answers in Appendix D, and make sure that you understand the answers before continuing to the next chapter. Quiz1. What are the differences between the function prototype and the function definition? 2. Do the names of parameters have to agree in the prototype, definition, and call to the function? 3. If a function doesn't return a value, how do you declare the function? 4. If you don't declare a return value, what type of return value is assumed? 5. What is a local variable? 6. What is scope? 7. What is recursion? 8. When should you use global variables? 9. What is function overloading? 10. What is polymorphism? Exercises 1. Write the prototype for a function named Perimeter(), which returns an unsigned long int and that takes two parameters, both unsigned short ints. 2. Write the definition of the function Perimeter() as described in Exercise 1. The two parameters represent the length and width of a rectangle. Have the function return the perimeter (twice the length plus twice the width). 3. BUG BUSTER: What is wrong with the function in the following code? #include void myFunc(unsigned short int x); int main() { unsigned short int x, y; y = myFunc(int); cout << "x: " << x << " y: " << y << "\n"; } void myFunc(unsigned short int x) { return (4*x); } 4. BUG BUSTER: What is wrong with the function in the following code?#include int myFunc(unsigned short int x); int main() { unsigned short int x, y; y = myFunc(x); cout << "x: " << x << " y: " << y << "\n"; } int myFunc(unsigned short int x); { return (4*x); } 5. Write a function that takes two unsigned short integer arguments and returns the result of dividing the first by the second. Do not do the division if the second number is zero, but do return -1. 6. Write a program that asks the user for two numbers and calls the function you wrote in Exercise 5. Print the answer, or print an error message if you get -1. 7. Write a program that asks for a number and a power. Write a recursive function that takes the number to the power. Thus, if the number is 2 and the power is 4, the function will return 16. Day 6 Basic Classes Creating New Types Why Create a New Type? Classes and Members Declaring a Class A Word on Naming Conventions Defining an Object Classes Versus Objects Accessing Class Members Assign to Objects, Not to Classes If You Dont Declare It, Your Class Wont Have It Private Versus Public Listing 6.1. Accessing the public members of a simple class. Make Member Data Private Listing 6.2. A class with accessor methods. Privacy Versus Security The class keyword Implementing Class Methods Listing 6.3. Implementing the methods of a simple class. Constructors and Destructors Default Constructors and Destructors Listing 6.4. Using constructors and destructors. const Member Functions Interface Versus Implementation Listing 6.5. A demonstration of violations of the interface. Why Use the Compiler to Catch Errors? Where to Put Class Declarations and Method Definitions Inline Implementation Listing 6.6. Cat class declaration in CAT.HPP. Listing 6.7. Cat implementation in CAT.CPP. Classes with Other Classes as Member Data Listing 6.8. Declaring a complete class. Listing 6.9. RECT.CPP. Structures Why Two Keywords Do the Same Thing Summary Q&A Workshop Quiz Exercises Day 6 Basic Classes Classes extend the built-in capabilities of C++ to assist you in representing and solving complex, real- world problems. Today you will learn What classes and objects are. How to define a new class and create objects of that class. What member functions and member data are. What constructors are and how to use them. Creating New Types You've already learned about a number of variable types, including unsigned integers and characters. The type of a variable tells you quite a bit about it. For example, if you declare Height and Width to be unsigned integers, you know that each one can hold a number between 0 and 65,535, assuming an integer is two bytes. That is the meaning of saying they are unsigned integers; trying to hold anything else in these variables causes an error. You can't store your name in an unsigned short integer, and you shouldn't try. Just by declaring these variables to be unsigned short integers, you know that it is possible to add Height to Width and to assign that number to another number. The type of these variables tells you: Their size in memory. What information they can hold. W What actions can be performed on them.More generally, a type is a category. Familiar types include car, house, person, fruit, and shape. In C++, the programmer can create any type needed, and each of these new types can have all the functionality and power of the built-in types. Why Create a New Type? Programs are usually written to solve real-world problems, such as keeping track of employee records or simulating the workings of a heating system. Although it is possible to solve complex problems by using programs written with only integers and characters, it is far easier to grapple with large, complex problems if you can create representations of the objects that you are talking about. In other words, simulating the workings of a heating system is easier if you can create variables that represent rooms, heat sensors, thermostats, and boilers. The closer these variables correspond to reality, the easier it is to write the program. Classes and Members You make a new type by declaring a class. A class is just a collection of variables--often of different types--combined with a set of related functions. One way to think about a car is as a collection of wheels, doors, seats, windows, and so forth. Another way is to think about what a car can do: It can move, speed up, slow down, stop, park, and so on. A class enables you to encapsulate, or bundle, these various parts and various functions into one collection, which is called an object. Encapsulating everything you know about a car into one class has a number of advantages for a programmer. Everything is in one place, which makes it easy to refer to, copy, and manipulate the data. Likewise, clients of your class--that is, the parts of the program that use your class--can use your object without worry about what is in it or how it works. A class can consist of any combination of the variable types and also other class types. The variables in the class are referred to as the member variables or data members. A Car class might have member variables representing the seats, radio type, tires, and so forth. New Term: Member variables , also known as data members , are the variables in your class. Member variables are part of your class, just like the wheels and engine are part of your car. The functions in the class typically manipulate the member variables. They are referred to as member functions or methods of the class. Methods of the Car class might include Start() and Brake(). A Cat class might have data members that represent age and weight; its methods might include Sleep(), Meow(), and ChaseMice(). New Term: Member functions , also known as methods , are the functions in your class.Member functions are as much a part of your class as the member variables. They determine what the objects of your class can do. Declaring a Class To declare a class, use the class keyword followed by an opening brace, and then list the data members and methods of that class. End the declaration with a closing brace and a semicolon. Here's the declaration of a class called Cat: class Cat { unsigned int unsigned int Meow(); }; itsAge; itsWeight; Declaring this class doesn't allocate memory for a Cat. It just tells the compiler what a Cat is, what data it contains (itsAge and itsWeight), and what it can do (Meow()). It also tells the compiler how big a Cat is--that is, how much room the compiler must set aside for each Cat that you create. In this example, if an integer is two bytes, a Cat is only four bytes big: itsAge is two bytes, and itsWeight is another two bytes. Meow() takes up no room, because no storage space is set aside for member functions (methods). A Word on Naming Conventions As a programmer, you must name all your member variables, member functions, and classes. As you learned on Day 3, "Variables and Constants," these should be easily understood and meaningful names. Cat, Rectangle, and Employee are good class names. Meow(), ChaseMice(), and StopEngine() are good function names, because they tell you what the functions do. Many programmers name the member variables with the prefix its, as in itsAge, itsWeight, and itsSpeed. This helps to distinguish member variables from nonmember variables. C++ is case-sensitive, and all class names should follow the same pattern. That way you never have to check how to spell your class name; was it Rectangle, rectangle, or RECTANGLE? Some programmers like to prefix every class name with a particular letter--for example, cCat or cPerson-- whereas others put the name in all uppercase or all lowercase. The convention that I use is to name all classes with initial-capitalization, as in Cat and Person. Similarly, many programmers begin all functions with capital letters and all variables with lowercase. Words are usually separated with an underbar--as in Chase_Mice--or by capitalizing each word--for example, ChaseMice or DrawCircle. The important idea is that you should pick one style and stay with it through each program. Over time, your style will evolve to include not only naming conventions, but also indentation, alignment ofbraces, and commenting style. NOTE: It's common for development companies to have house standards for many style issues. This ensures that all developers can easily read one another's code. Defining an Object You define an object of your new type just as you define an integer variable: unsigned int GrossWeight; Cat Frisky; // define an unsigned integer // define a Cat This code defines a variable called Gross Weight whose type is an unsigned integer. It also defines Frisky, which is an object whose class (or type) is Cat. Classes Versus Objects You never pet the definition of a cat; you pet individual cats. You draw a distinction between the idea of a cat, and the particular cat that right now is shedding all over your living room. In the same way, C++ differentiates between the class Cat, which is the idea of a cat, and each individual Cat object. Thus, Frisky is an object of type Cat in the same way in which GrossWeight is a variable of type unsigned int. New Term: An object is an individual instance of a class. Accessing Class Members Once you define an actual Cat object--for example, Frisky--you use the dot operator (.) to access the members of that object. Therefore, to assign 50 to Frisky's Weight member variable, you would write Frisky.Weight = 50; In the same way, to call the Meow() function, you would write Frisky.Meow(); When you use a class method, you call the method. In this example, you are calling Meow() on Frisky.Assign to Objects, Not to Classes In C++ you don't assign values to types; you assign values to variables. For example, you would never write int = 5; // wrong The compiler would flag this as an error, because you can't assign 5 to an integer. Rather, you must define an integer variable and assign 5 to that variable. For example, int x; x = 5; // define x to be an int // set x's value to 5 This is a shorthand way of saying, "Assign 5 to the variable x, which is of type int." In the same way, you wouldn't write Cat.age=5; ??? // wrong The compiler would flag this as an error, because you can't assign 5 to the age part of a Cat. Rather, you must define a Cat object and assign 5 to that object. For example, Cat Frisky; Frisky.age = 5; // just like // just like int x; x = 5; If You Dont Declare It, Your Class Wont Have It Try this experiment: Walk up to a three-year-old and show her a cat. Then say, "This is Frisky. Frisky knows a trick. Frisky, bark." The child will giggle and say, "No, silly, cats can't bark." If you wrote Cat Frisky; Frisky.Bark() // make a Cat named Frisky // tell Frisky to bark the compiler would say, No, silly, Cats can't bark. (Your compiler's wording may vary). The compiler knows that Frisky can't bark because the Cat class doesn't have a Bark() function. The compiler wouldn't even let Frisky meow if you didn't define a Meow() function. DO use the keyword class to declare a class. DON'T confuse a declaration with a definition. A declaration says what a class is. A definition sets aside memory for an object. DON'T confuse a class with an object. DON'T assign values to a class. Assign values to the data members of an object. DO use the dot operator (.) to access classmembers and functions. Private Versus Public Other keywords are used in the declaration of a class. Two of the most important are public and private. All members of a class--data and methods--are private by default. Private members can be accessed only within methods of the class itself. Public members can be accessed through any object of the class. This distinction is both important and confusing. To make it a bit clearer, consider an example from earlier in this chapter: class Cat { unsigned int unsigned int Meow(); }; itsAge; itsWeight; In this declaration, itsAge, itsWeight, and Meow() are all private, because all members of a class are private by default. This means that unless you specify otherwise, they are private. However, if you write Cat Boots; Boots.itsAge=5; // error! can't access private data! the compiler flags this as an error. In effect, you've said to the compiler, "I'll access itsAge, itsWeight, and Meow() only from within member functions of the Cat class." Yet here you've accessed the itsAge member variable of the Boots object from outside a Cat method. Just because Boots is an object of class Cat, that doesn't mean that you can access the parts of Boots that are private. This is a source of endless confusion to new C++ programmers. I can almost hear you yelling, "Hey! I just said Boots is a cat. Why can't Boots access his own age?" The answer is that Boots can, but you can't. Boots, in his own methods, can access all his parts--public and private. Even though you've created a Cat, that doesn't mean that you can see or change the parts of it that are private. The way to use Cat so that you can access the data members is class Cat { public: unsigned int itsAge;unsigned int Meow(); }; itsWeight; Now itsAge, itsWeight, and Meow() are all public. Boots.itsAge=5 compiles without problems. Listing 6.1 shows the declaration of a Cat class with public member variables. Listing 6.1. Accessing the public members of a simple class. 1: // Demonstrates declaration of a class and 2: // definition of an object of the class, 3: 4: #include // for cout 5: 6: class Cat // declare the class object 7: { 8: public: // members which follow are public 9: int itsAge; 10: int itsWeight; 11: }; 12: 13: 14: void main() 15: { 16: Cat Frisky; 17: Frisky.itsAge = 5; // assign to the member variable 18: cout << "Frisky is a cat who is " ; 19: cout << Frisky.itsAge << " years old.\n"; 20: Output: Frisky is a cat who is 5 years old. Analysis: Line 6 contains the keyword class. This tells the compiler that what follows is a declaration. The name of the new class comes after the keyword class. In this case, it is Cat. The body of the declaration begins with the opening brace in line 7 and ends with a closing brace and a semicolon in line 11. Line 8 contains the keyword public, which indicates that everything that follows is public until the keyword private or the end of the class declaration. Lines 9 and 10 contain the declarations of the class members itsAge and itsWeight. Line 14 begins the main function of the program. Frisky is defined in line 16 as an instance of a Cat--that is, as a Cat object. Frisky's age is set in line 17 to 5. In lines 18 and 19, the itsAge member variable is used to print out a message about Frisky.NOTE: Try commenting out line 8 and try to recompile. You will receive an error on line 17 because itsAge will no longer have public access. The default for classes is private access. Make Member Data Private As a general rule of design, you should keep the member data of a class private. Therefore, you must create public functions known as accessor methods to set and get the private member variables. These accessor methods are the member functions that other parts of your program call to get and set your private member variables. New Term: A public accessor method is a class member function used either to read the value of a private class member variable or to set its value. Why bother with this extra level of indirect access? After all, it is simpler and easier to use the data, instead of working through accessor functions. Accessor functions enable you to separate the details of how the data is stored from how it is used. This enables you to change how the data is stored without having to rewrite functions that use the data. If a function that needs to know a Cat's age accesses itsAge directly, that function would need to be rewritten if you, as the author of the Cat class, decided to change how that data is stored. By having the function call GetAge(), your Cat class can easily return the right value no matter how you arrive at the age. The calling function doesn't need to know whether you are storing it as an unsigned integer or a long, or whether you are computing it as needed. This technique makes your program easier to maintain. It gives your code a longer life because design changes don't make your program obsolete. Listing 6.2 shows the Cat class modified to include private member data and public accessor methods. Note that this is not an executable listing. Listing 6.2. A class with accessor methods. 1: // Cat class declaration 2: // Data members are private, public accessor methods 3: // mediate setting and getting the values of the private data 4: 5: class Cat6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: { public: // public accessors unsigned int GetAge(); void SetAge(unsigned int Age); unsigned int GetWeight(); void SetWeight(unsigned int Weight); // public member functions Meow(); // private member data private: unsigned int itsAge; unsigned int itsWeight; }; Analysis: This class has five public methods. Lines 9 and 10 contain the accessor methods for itsAge. Lines 12 and 13 contain the accessor methods for itsWeight. These accessor functions set the member variables and return their values. The public member function Meow() is declared in line 16. Meow() is not an accessor function. It doesn't get or set a member variable; it performs another service for the class, printing the word Meow. The member variables themselves are declared in lines 20 and 21. To set Frisky's age, you would pass the value to the SetAge() method, as in Cat Frisky; Frisky.SetAge(5); // set Frisky's age using the public accessor Privacy Versus Security Declaring methods or data private enables the compiler to find programming mistakes before they become bugs. Any programmer worth his consulting fees can find a way around privacy if he wants to. Stroustrup, the inventor of C++, said, "The C++ access control mechanisms provide protection against accident--not against fraud." (ARM, 1990.) The class keyword Syntax for the class keyword is as follows. class class_name {// access control keywords here // class variables and methods declared here }; You use the class keyword to declare new types. A class is a collection of class member data, which are variables of various types, including other classes. The class also contains class functions--or methods--which are functions used to manipulate the data in the class and to perform other services for the class. You define objects of the new type in much the same way in which you define any variable. State the type (class) and then the variable name (the object). You access the class members and functions by using the dot (.) operator. You use access control keywords to declare sections of the class as public or private. The default for access control is private. Each keyword changes the access control from that point on to the end of the class or until the next access control keyword. Class declarations end with a closing brace and a semicolon. Example 1 class Cat { public: unsigned int Age; unsigned int Weight; void Meow(); }; Cat Frisky; Frisky.Age = 8; Frisky.Weight = 18; Frisky.Meow(); Example class Car { public: // the next five are public void Start(); void Accelerate(); void Brake(); void SetYear(int year); int GetYear(); private: // the rest is private int Year; Char Model [255]; }; // end of class declaration Car OldFaithful; // make an instance of carint bought; OldFaithful.SetYear(84) ; bought = OldFaithful.GetYear(); OldFaithful.Start(); // // // // a local variable of type int assign 84 to the year set bought to 84 call the start method DO declare member variables private. DO use public accessor methods. DON'T try to use private member variables from outside the class. DO access private member variables from within class member functions. Implementing Class Methods As you've seen, an accessor function provides a public interface to the private member data of the class. Each accessor function, along with any other class methods that you declare, must have an implementation. The implementation is called the function definition. A member function definition begins with the name of the class, followed by two colons, the name of the function, and its parameters. Listing 6.3 shows the complete declaration of a simple Cat class and the implementation of its accessor function and one general class member function. Listing 6.3. Implementing the methods of a simple class. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: // Demonstrates declaration of a class and // definition of class methods, #include class Cat { public: int GetAge(); void SetAge (int age); void Meow(); private: int itsAge; }; // for cout // begin declaration of the class // // // // // // begin public section accessor function accessor function general function begin private section member variable // GetAge, Public accessor function // returns value of itsAge member int Cat::GetAge() { return itsAge; }23: // definition of SetAge, public 24: // accessor function 25: // returns sets itsAge member 26: void Cat::SetAge(int age) 27: { 28: // set member variable its age to 29: // value passed in by parameter age 30: itsAge = age; 31: } 32: 33: // definition of Meow method 34: // returns: void 35: // parameters: None 36: // action: Prints "meow" to screen 37: void Cat::Meow() 38: { 39: cout << "Meow.\n"; 40: } 41: 42: // create a cat, set its age, have it 43: // meow, tell us its age, then meow again. 44: int main() 45: { 46: Cat Frisky; 47: Frisky.SetAge(5); 48: Frisky.Meow(); 49: cout << "Frisky is a cat who is " ; 50: cout << Frisky.GetAge() << " years old.\n"; 51: Frisky.Meow(); 52; return 0; 53: } Output: Meow. Frisky is a cat who is 5 years old. Meow. Analysis: Lines 6-14 contain the definition of the Cat class. Line 8 contains the keyword public, which tells the compiler that what follows is a set of public members. Line 9 has the declaration of the public accessor method GetAge(). GetAge() provides access to the private member variable itsAge, which is declared in line 13. Line 10 has the public accessor function SetAge(). SetAge() takes an integer as an argument and sets itsAge to the value of that argument. Line 11 has the declaration of the class method Meow(). Meow() is not an accessor function. Here it is a general method that prints the word Meow to the screen. Line 12 begins the private section, which includes only the declaration in line 13 of the private member variable itsAge. The class declaration ends with a closing brace and semicolon in line 14.Lines 18-21 contain the definition of the member function GetAge(). This method takes no parameters; it returns an integer. Note that class methods include the class name followed by two colons and the function name (Line 18). This syntax tells the compiler that the GetAge() function that you are defining here is the one that you declared in the Cat class. With the exception of this header line, the GetAge() function is created like any other function. The GetAge() function takes only one line; it returns the value in itsAge. Note that the main() function cannot access itsAge because itsAge is private to the Cat class. The main() function has access to the public method GetAge(). Because GetAge() is a member function of the Cat class, it has full access to the itsAge variable. This access enables GetAge() to return the value of itsAge to main(). Line 26 contains the definition of the SetAge() member function. It takes an integer parameter and sets the value of itsAge to the value of that parameter in line 30. Because it is a member of the Cat class, SetAge() has direct access to the member variable itsAge. Line 37 begins the definition, or implementation, of the Meow() method of the Cat class. It is a one- line function that prints the word Meow to the screen, followed by a new line. Remember that the \n character prints a new line to the screen. Line 44 begins the body of the program with the familiar main() function. In this case, it takes no arguments and returns void. In line 46, main() declares a Cat named Frisky. In line 47, the value 5 is assigned to the itsAge member variable by way of the SetAge() accessor method. Note that the method is called by using the class name (Frisky) followed by the member operator (.) and the method name (SetAge()). In this same way, you can call any of the other methods in a class. Line 48 calls the Meow() member function, and line 49 prints a message using the GetAge() accessor. Line 51 calls Meow() again. Constructors and Destructors There are two ways to define an integer variable. You can define the variable and then assign a value to it later in the program. For example, int Weight; ... Weight = 7; // define a variable // other code here // assign it a value Or you can define the integer and immediately initialize it. For example, int Weight = 7; // define and initialize to 7 Initialization combines the definition of the variable with its initial assignment. Nothing stops you from changing that value later. Initialization ensures that your variable is never without a meaningfulvalue. How do you initialize the member data of a class? Classes have a special member function called a constructor. The constructor can take parameters as needed, but it cannot have a return value--not even void. The constructor is a class method with the same name as the class itself. Whenever you declare a constructor, you'll also want to declare a destructor. Just as constructors create and initialize objects of your class, destructors clean up after your object and free any memory you might have allocated. A destructor always has the name of the class, preceded by a tilde (~). Destructors take no arguments and have no return value. Therefore, the Cat declaration includes ~Cat(); Default Constructors and Destructors If you don't declare a constructor or a destructor, the compiler makes one for you. The default constructor and destructor take no arguments and do nothing. What good is a constructor that does nothing? In part, it is a matter of form. All objects must be constructed and destructed, and these do-nothing functions are called at the right time. However, to declare an object without passing in parameters, such as Cat Rags; // Rags gets no parameters you must have a constructor in the form Cat(); When you define an object of a class, the constructor is called. If the Cat constructor took two parameters, you might define a Cat object by writing Cat Frisky (5,7); If the constructor took one parameter, you would write Cat Frisky (3); In the event that the constructor takes no parameters at all, you leave off the parentheses and write Cat Frisky ; This is an exception to the rule that states all functions require parentheses, even if they take no parameters. This is why you are able to writeCat Frisky; which is a call to the default constructor. It provides no parameters, and it leaves off the parentheses. You don't have to use the compiler-provided default constructor. You are always free to write your own constructor with no parameters. Even constructors with no parameters can have a function body in which they initialize their objects or do other work. As a matter of form, if you declare a constructor, be sure to declare a destructor, even if your destructor does nothing. Although it is true that the default destructor would work correctly, it doesn't hurt to declare your own. It makes your code clearer. Listing 6.4 rewrites the Cat class to use a constructor to initialize the Cat object, setting its age to whatever initial age you provide, and it demonstrates where the destructor is called. Listing 6.4. Using constructors and destructors. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: // Demonstrates declaration of a constructors and // destructor for the Cat class #include class Cat { public: Cat(int initialAge); ~Cat(); int GetAge(); void SetAge(int age); void Meow(); private: int itsAge; }; // for cout // begin declaration of the class // // // // // begin public section constructor destructor accessor function accessor function // begin private section // member variable // constructor of Cat, Cat::Cat(int initialAge) { itsAge = initialAge; } Cat::~Cat() { } // destructor, takes no action // GetAge, Public accessor function // returns value of itsAge member30: int Cat::GetAge() 31: { 32: return itsAge; 33: } 34: 35: // Definition of SetAge, public 36: // accessor function 37: 38: void Cat::SetAge(int age) 39: { 40: // set member variable its age to 41: // value passed in by parameter age 42: itsAge = age; 43: } 44: 45: // definition of Meow method 46: // returns: void 47: // parameters: None 48: // action: Prints "meow" to screen 49: void Cat::Meow() 50: { 51: cout << "Meow.\n"; 52: } 53: 54: // create a cat, set its age, have it 55 // meow, tell us its age, then meow again. 56: int main() 57: { 58: Cat Frisky(5); 59: Frisky.Meow(); 60: cout << "Frisky is a cat who is " ; 61: cout << Frisky.GetAge() << " years old.\n"; 62: Frisky.Meow(); 63: Frisky.SetAge(7); 64: cout << "Now Frisky is " ; 65: cout << Frisky.GetAge() << " years old.\n"; 66; return 0; 67: } Output: Meow. Frisky is a cat who is 5 years old. Meow. Now Frisky is 7 years old. Analysis: Listing 6.4 is similar to 6.3, except that line 9 adds a constructor that takes an integer. Line 10 declares the destructor, which takes no parameters. Destructors never take parameters, and neitherconstructors nor destructors return a value--not even void. Lines 19-22 show the implementation of the constructor. It is similar to the implementation of the SetAge() accessor function. There is no return value. Lines 24-26 show the implementation of the destructor ~Cat(). This function does nothing, but you must include the definition of the function if you declare it in the class declaration. Line 58 contains the definition of a Cat object, Frisky. The value 5 is passed in to Frisky's constructor. There is no need to call SetAge(), because Frisky was created with the value 5 in its member variable itsAge, as shown in line 61. In line 63, Frisky's itsAge variable is reassigned to 7. Line 65 prints the new value. DO use constructors to initialize your objects. DON'T give constructors or destructors a return value. DON'T give destructors parameters. const Member Functions If you declare a class method const, you are promising that the method won't change the value of any of the members of the class. To declare a class method constant, put the keyword const after the parentheses but before the semicolon. The declaration of the constant member function SomeFunction() takes no arguments and returns void. It looks like this: void SomeFunction() const; Accessor functions are often declared as constant functions by using the const modifier. The Cat class has two accessor functions: void SetAge(int anAge); int GetAge(); SetAge() cannot be const because it changes the member variable itsAge. GetAge(), on the other hand, can and should be const because it doesn't change the class at all. GetAge() simply returns the current value of the member variable itsAge. Therefore, the declaration of these functions should be written like this: void SetAge(int anAge); int GetAge() const; If you declare a function to be const, and the implementation of that function changes the object by changing the value of any of its members, the compiler flags it as an error. For example, if you wrote GetAge() in such a way that it kept count of the number of times that the Cat was asked its age, it would generate a compiler error. This is because you would be changing the Cat object by calling this method.NOTE: Use const whenever possible. Declare member functions to be const whenever they should not change the object. This lets the compiler help you find errors; it's faster and less expensive than doing it yourself. It is good programming practice to declare as many methods to be const as possible. Each time you do, you enable the compiler to catch your errors, instead of letting your errors become bugs that will show up when your program is running. Interface Versus Implementation As you've learned, clients are the parts of the program that create and use objects of your class. You can think of the interface to your class--the class declaration--as a contract with these clients. The contract tells what data your class has available and how your class will behave. For example, in the Cat class declaration, you create a contract that every Cat will have a member variable itsAge that can be initialized in its constructor, assigned to by its SetAge() accessor function, and read by its GetAge() accessor. You also promise that every Cat will know how to Meow(). If you make GetAge() a const function--as you should--the contract also promises that GetAge() won't change the Cat on which it is called. C++ is strongly typed, which means that the compiler enforces these contracts by giving you a compiler error when you violate them. Listing 6.5 demonstrates a program that doesn't compile because of violations of these contracts. WARNING: Listing 6.5 does not compile! Listing 6.5. A demonstration of violations of the interface . 1: 2: 3: 4: 5: 6: 7: 8: 9: // Demonstrates compiler errors #include class Cat { public: Cat(int initialAge); // for cout10: ~Cat(); 11: int GetAge() const; // const accessor function 12: void SetAge (int age); 13: void Meow(); 14: private: 15: int itsAge; 16: }; 17: 18: // constructor of Cat, 19: Cat::Cat(int initialAge) 20: { 21: itsAge = initialAge; 21: cout << "Cat Constructor\n"; 22: } 23: 24: Cat::~Cat() // destructor, takes no action 25: { 26: cout << "Cat Destructor\n"; 27: } 28: // GetAge, const function 29: // but we violate const! 30: int Cat::GetAge() const 31: { 32: return (itsAge++); // violates const! 33: } 34: 35: // definition of SetAge, public 36: // accessor function 37: 38: void Cat::SetAge(int age) 39: { 40: // set member variable its age to 41: // value passed in by parameter age 42: itsAge = age; 43: } 44: 45: // definition of Meow method 46: // returns: void 47: // parameters: None 48: // action: Prints "meow" to screen 49: void Cat::Meow() 50: { 51: cout << "Meow.\n"; 52: } 53: 54: // demonstrate various violations of the55 // interface, and resulting compiler errors 56: int main() 57: { 58: Cat Frisky; // doesn't match declaration 59: Frisky.Meow(); 60: Frisky.Bark(); // No, silly, cat's can't bark. 61: Frisky.itsAge = 7; // itsAge is private 62: return 0; 63: } Analysis: As it is written, this program doesn't compile. Therefore, there is no output. This program was fun to write because there are so many errors in it. Line 11 declares GetAge() to be a const accessor function--as it should be. In the body of GetAge(), however, in line 32, the member variable itsAge is incremented. Because this method is declared to be const, it must not change the value of itsAge. Therefore, it is flagged as an error when the program is compiled. In line 13, Meow() is not declared const. Although this is not an error, it is bad programming practice. A better design takes into account that this method doesn't change the member variables of Cat. Therefore, Meow() should be const. Line 58 shows the definition of a Cat object, Frisky. Cats now have a constructor, which takes an integer as a parameter. This means that you must pass in a parameter. Because there is no parameter in line 58, it is flagged as an error. Line 60 shows a call to a class method, Bark(). Bark() was never declared. Therefore, it is illegal. Line 61 shows itsAge being assigned the value 7. Because itsAge is a private data member, it is flagged as an error when the program is compiled. Why Use the Compiler to Catch Errors? While it would be wonderful to write 100 percent bug-free code, few programmers have been able to do so. However, many programmers have developed a system to help minimize bugs by catching and fixing them early in the process. Although compiler errors are infuriating and are the bane of a programmer's existence, they are far better than the alternative. A weakly typed language enables you to violate your contracts without a peep from the compiler, but your program will crash at run-time-- when, for example, your boss is watching. Compile-time errors--that is, errors found while you are compiling--are far better than run-time errors--that is, errors found while you are executing the program. This is because compile-time errors can be found much more reliably. It is possible to run a program many times without going down every possible code path. Thus, a run-time error can hide for quite a while. Compile-time errors are found every time you compile. Thus, they are easier to identify and fix. It is the goal of quality programming to ensure that the code has no runtime bugs. One tried- and-true technique to accomplish this is to use the compiler to catch your mistakes early in thedevelopment process. Where to Put Class Declarations and Method Definitions Each function that you declare for your class must have a definition. The definition is also called the function implementation. Like other functions, the definition of a class method has a function header and a function body. The definition must be in a file that the compiler can find. Most C++ compilers want that file to end with .C or .CPP. This book uses .CPP, but check your compiler to see what it prefers. NOTE: Many compilers assume that files ending with .C are C programs, and that C++ program files end with .CPP. You can use any extension, but .CPP will minimize confusion. You are free to put the declaration in this file as well, but that is not good programming practice. The convention that most programmers adopt is to put the declaration into what is called a header file, usually with the same name but ending in .H, .HP, or .HPP. This book names the header files with .HPP, but check your compiler to see what it prefers. For example, you put the declaration of the Cat class into a file named CAT.HPP, and you put the definition of the class methods into a file called CAT.CPP. You then attach the header file to the .CPP file by putting the following code at the top of CAT.CPP: #include Cat.hpp This tells the compiler to read CAT.HPP into the file, just as if you had typed in its contents at this point. Why bother separating them if you're just going to read them back in? Most of the time, clients of your class don't care about the implementation specifics. Reading the header file tells them everything they need to know; they can ignore the implementation files. NOTE: The declaration of a class tells the compiler what the class is, what data it holds, and what functions it has. The declaration of the class is called its interface because it tells the user how to interact with the class. The interface is usually stored in an .HPP file, which is referred to as a header file. The function definition tells the compiler how the function works. The function definition is called the implementation of the class method, and it is kept in a .CPP file. The implementation details of the class are of concern only to the author of the class. Clients of the class--that is, the parts of the program that use the class--don't need to know, and don't care, how the functions are implemented.Inline Implementation Just as you can ask the compiler to make a regular function inline, you can make class methods inline. The keyword inline appears before the return value. The inline implementation of the GetWeight() function, for example, looks like this: inline int Cat::GetWeight() { return itsWeight; // return the Weight data member } You can also put the definition of a function into the declaration of the class, which automatically makes that function inline. For example, class Cat { public: int GetWeight() { return itsWeight; } void SetWeight(int aWeight); }; // inline Note the syntax of the GetWeight() definition. The body of the inline function begins im- mediately after the declaration of the class method; there is no semicolon after the paren-theses. Like any function, the definition begins with an opening brace and ends with a closing brace. As usual, whitespace doesn't matter; you could have written the declaration as class Cat { public: int GetWeight() { return itsWeight; } // inline void SetWeight(int aWeight); }; Listings 6.6 and 6.7 re-create the Cat class, but they put the declaration in CAT.HPP and the implementation of the functions in CAT.CPP. Listing 6.7 also changes the accessor functions and the Meow() function to inline. Listing 6.6. Cat class declaration in CAT.HPP 1: 2: #include class Cat3: { 4: public: 5: Cat (int initialAge); 6: ~Cat(); 7: int GetAge() { return itsAge;} 8: void SetAge (int age) { itsAge = age;} 9: void Meow() { cout << "Meow.\n";} 10: private: 11: int itsAge; 12: }; // inline! // inline! // inline! Listing 6.7. Cat implementation in CAT.CPP . 1: // Demonstrates inline functions 2: // and inclusion of header files 3: 4: #include "cat.hpp" // be sure to include the header files! 5: 6: 7: Cat::Cat(int initialAge) //constructor 8: { 9: itsAge = initialAge; 10: } 11: 12: Cat::~Cat() //destructor, takes no action 13: { 14: } 15: 16: // Create a cat, set its age, have it 17: // meow, tell us its age, then meow again. 18: int main() 19: { 20: Cat Frisky(5); 21: Frisky.Meow(); 22: cout << "Frisky is a cat who is " ; 23: cout << Frisky.GetAge() << " years old.\n"; 24: Frisky.Meow(); 25: Frisky.SetAge(7); 26: cout << "Now Frisky is " ; 27: cout << Frisky.GetAge() << " years old.\n"; 28: return 0; 29: } Output: Meow. Frisky is a cat who is 5 years old. Meow.Now Frisky is 7 years old. Analysis: The code presented in Listing 6.6 and Listing 6.7 is similar to the code in Listing 6.4, except that three of the methods are written inline in the declaration file and the declaration has been separated into CAT.HPP. GetAge() is declared in line 7, and its inline implementation is provided. Lines 8 and 9 provide more inline functions, but the functionality of these functions is unchanged from the previous "outline" implementations. Line 4 of Listing 6.7 shows #include "cat.hpp", which brings in the listings from CAT.HPP. By including cat.hpp, you have told the precompiler to read cat.hpp into the file as if it had been typed there, starting on line 5. This technique allows you to put your declarations into a different file from your implementation, yet have that declaration available when the compiler needs it. This is a very common technique in C++ programming. Typically, class declarations are in an .HPP file that is then #included into the associated CPP file. Lines 18-29 repeat the main function from Listing 6.4. This shows that making these functions inline doesn't change their performance. Classes with Other Classes as Member Data It is not uncommon to build up a complex class by declaring simpler classes and including them in the declaration of the more complicated class. For example, you might declare a wheel class, a motor class, a transmission class, and so forth, and then combine them into a car class. This declares a has-a relationship. A car has a motor, it has wheels, and it has a transmission. Consider a second example. A rectangle is composed of lines. A line is defined by two points. A point is defined by an x-coordinate and a y-coordinate. Listing 6.8 shows a complete declaration of a Rectangle class, as might appear in RECTANGLE.HPP. Because a rectangle is defined as four lines connecting four points and each point refers to a coordinate on a graph, we first declare a Point class, to hold the x,y coordinates of each point. Listing 6.9 shows a complete declaration of both classes. Listing 6.8. Declaring a complete class. 1: 2: 3: 4: 5: 6: 7: // Begin Rect.hpp #include class Point // holds x,y coordinates { // no constructor, use default public: void SetX(int x) { itsX = x; }8: void SetY(int y) { itsY = y; } 9: int GetX()const { return itsX;} 10: int GetY()const { return itsY;} 11: private: 12: int itsX; 13: int itsY; 14: }; // end of Point class declaration 15: 16: 17: class Rectangle 18: { 19: public: 20: Rectangle (int top, int left, int bottom, int right); 21: ~Rectangle () {} 22: 23: int GetTop() const { return itsTop; } 24: int GetLeft() const { return itsLeft; } 25: int GetBottom() const { return itsBottom; } 26: int GetRight() const { return itsRight; } 27: 28: Point GetUpperLeft() const { return itsUpperLeft; } 29: Point GetLowerLeft() const { return itsLowerLeft; } 30: Point GetUpperRight() const { return itsUpperRight; } 31: Point GetLowerRight() const { return itsLowerRight; } 32: 33: void SetUpperLeft(Point Location) {itsUpperLeft = Location;} 34: void SetLowerLeft(Point Location) {itsLowerLeft = Location;} 35: void SetUpperRight(Point Location) {itsUpperRight = Location;} 36: void SetLowerRight(Point Location) {itsLowerRight = Location;} 37: 38: void SetTop(int top) { itsTop = top; } 39: void SetLeft (int left) { itsLeft = left; } 40: void SetBottom (int bottom) { itsBottom = bottom; } 41: void SetRight (int right) { itsRight = right; } 42: 43: int GetArea() const; 44: 45: private: 46: Point itsUpperLeft; 47: Point itsUpperRight; 48: Point itsLowerLeft; 49: Point itsLowerRight;50: 51: 52: 53: 54: }; 55: // end int int int int itsTop; itsLeft; itsBottom; itsRight; Rect.hpp Listing 6.9. RECT.CPP. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: // Begin rect.cpp #include "rect.hpp" Rectangle::Rectangle(int top, int left, int bottom, int right) { itsTop = top; itsLeft = left; itsBottom = bottom; itsRight = right; itsUpperLeft.SetX(left); itsUpperLeft.SetY(top); itsUpperRight.SetX(right); itsUpperRight.SetY(top); itsLowerLeft.SetX(left); itsLowerLeft.SetY(bottom); itsLowerRight.SetX(right); itsLowerRight.SetY(bottom); } // compute area of the rectangle by finding corners, // establish width and height and then multiply int Rectangle::GetArea() const { int Width = itsRight-itsLeft; int Height = itsTop - itsBottom; return (Width * Height); } int main() { //initialize a local Rectangle variable Rectangle MyRectangle (100, 20, 50, 80 );38: int Area = MyRectangle.GetArea(); 39: 40: cout << "Area: " << Area << "\n"; 41: cout << "Upper Left X Coordinate: "; 42: cout << MyRectangle.GetUpperLeft().GetX(); 43: return 0; 44: } Output: Area: 3000 Upper Left X Coordinate: 20 Analysis: Lines 3-14 in Listing 6.8 declare the class Point, which is used to hold a specific x,y coordinate on a graph. As written, this program doesn't use Points much. However, other drawing methods require Points. Within the declaration of the class Point, you declare two member variables (itsX and itsY) on lines 12 and 13. These variables hold the values of the coordinates. As the x-coordinate increases, you move to the right on the graph. As the y-coordinate increases, you move upward on the graph. Other graphs use different systems. Some windowing programs, for example, increase the y-coordinate as you move down in the window. The Point class uses inline accessor functions to get and set the X and Y points declared on lines 7- 10. Points use the default constructor and destructor. Therefore, you must set their coordinates explicitly. Line 17 begins the declaration of a Rectangle class. A Rectangle consists of four points that represent the corners of the Rectangle. The constructor for the Rectangle (line 20) takes four integers, known as top, left, bottom, and right. The four parameters to the constructor are copied into four member variables (Listing 6.9) and then the four Points are established. In addition to the usual accessor functions, Rectangle has a function GetArea() declared in line 43. Instead of storing the area as a variable, the GetArea() function computes the area on lines 28- 29 of Listing 6.9. To do this, it computes the width and the height of the rectangle, and then it multiplies these two values. Getting the x-coordinate of the upper-left corner of the rectangle requires that you access the UpperLeft point, and ask that point for its X value. Because GetUpperLeft()is ()a method of Rectangle, it can directly access the private data of Rectangle, including itsUpperLeft. Because itsUpperLeft is a Point and Point's itsX value is private, GetUpperLeft() cannot directly access this data. Rather, it must use the public accessor function GetX() to obtain that value. Line 33 of Listing 6.9 is the beginning of the body of the actual program. Until line 36, no memory has been allocated, and nothing has really happened. The only thing you've done is tell the compiler how to make a point and how to make a rectangle, in case one is ever needed.In line 36, you define a Rectangle by passing in values for Top, Left, Bottom, and Right. In line 38, you make a local variable, Area, of type int. This variable holds the area of the Rectangle that you've created. You initialize Area with the value returned by Rectangle's GetArea() function. A client of Rectangle could create a Rectangle object and get its area without ever looking at the implementation of GetArea(). RECT.HPP is shown in Listing 6.8. Just by looking at the header file, which contains the declaration of the Rectangle class, the programmer knows that GetArea() returns an int. How GetArea() does its magic is not of concern to the user of class Rectangle. In fact, the author of Rectangle could change GetArea() without affecting the programs that use the Rectangle class. Structures A very close cousin to the class keyword is the keyword struct, which is used to declare a structure. In C++, a structure is exactly like a class, except that its members are public by default. You can declare a structure exactly as you declare a class, and you can give it exactly the same data members and functions. In fact, if you follow the good programming practice of always explicitly declaring the private and public sections of your class, there will be no difference whatsoever. Try re-entering Listing 6.8 with these changes: In line 3, change class Point to struct Point. In line 17, change class Rectangle to struct Rectangle. Now run the program again and compare the output. There should be no change. Why Two Keywords Do the Same Thing You're probably wondering why two keywords do the same thing. This is an accident of history. When C++ was developed, it was built as an extension of the C language. C has structures, although C structures don't have class methods. Bjarne Stroustrup, the creator of C++, built upon structs, but he changed the name to class to represent the new, expanded functionality. DO put your class declaration in an HPP file and your member functions in a CPP file. DO use const whenever you can. DO understand classes before you move on. SummaryToday you learned how to create new data types called classes. You learned how to define variables of these new types, which are called objects. A class has data members, which are variables of various types, including other classes. A class also includes member functions--also known as methods. You use these member functions to manipulate the member data and to perform other services. Class members, both data and functions, can be public or private. Public members are accessible to any part of your program. Private members are accessible only to the member functions of the class. It is good programming practice to isolate the interface, or declaration, of the class in a header file. You usually do this in a file with an .HPP extension. The implementation of the class methods is written in a file with a .CPP extension. Class constructors initialize objects. Class destructors destroy objects and are often used to free memory allocated by methods of the class. Q&A Q. How big is a class object? A. A class object's size in memory is determined by the sum of the sizes of its member variables. Class methods don't take up room as part of the memory set aside for the object. Some compilers align variables in memory in such a way that two-byte variables actually consume somewhat more than two bytes. Check your compiler manual to be sure, but at this point there is no reason to be concerned with these details. Q. If I declare a class Cat with a private member itsAge and then define two Cat objects, Frisky and Boots, can Boots access Frisky's itsAge member variable? A. No. While private data is available to the member functions of a class, different instances of the class cannot access each other's data. In other words, Frisky's member functions can access Frisky's data, but not Boots'. In fact, Frisky is a completely independent cat from Boots, and that is just as it should be. Q. Why shouldn't I make all the member data public? A. Making member data private enables the client of the class to use the data without worrying about how it is stored or computed. For example, if the Cat class has a method GetAge(), clients of the Cat class can ask for the cat's age without knowing or caring if the cat stores its age in a member variable, or computes its age on the fly. Q. If using a const function to change the class causes a compiler error, why shouldn't I just leave out the word const and be sure to avoid errors?A. If your member function logically shouldn't change the class, using the keyword const is a good way to enlist the compiler in helping you find silly mistakes. For example, GetAge() might have no reason to change the Cat class, but your implementation has this line: if (itsAge = 100) cout << "Hey! You're 100 years old\n"; Declaring GetAge() to be const causes this code to be flagged as an error. You meant to check whether itsAge is equal to 100, but instead you inadvertently assigned 100 to itsAge. Because this assignment changes the class--and you said this method would not change the class--the compiler is able to find the error. This kind of mistake can be hard to find just by scanning the code. The eye often sees only what it expects to see. More importantly, the program might appear to run correctly, but itsAge has now been set to a bogus number. This will cause problems sooner or later. Q. Is there ever a reason to use a structure in a C++ program? A. Many C++ programmers reserve the struct keyword for classes that have no functions. This is a throwback to the old C structures, which could not have functions. Frankly, I find it confusing and poor programming practice. Today's methodless structure might need methods tomorrow. Then you'll be forced either to change the type to class or to break your rule and end up with a structure with methods. Workshop The Workshop provides quiz questions to help you solidify your understanding of the material covered and exercises to provide you with experience in using what you've learned. Try to answer the quiz and exercise questions before checking the answers in Appendix D, and make sure you understand the answers before continuing to the next chapter. Quiz 1. What is the dot operator, and what is it used for? 2. Which sets aside memory--declaration or definition? 3. Is the declaration of a class its interface or its implementation? 4. What is the difference between public and private data members? 5. Can member functions be private? 6. Can member data be public? 7. If you declare two Cat objects, can they have different values in their itsAge member data? 8. Do class declarations end with a semicolon? Do class method definitions?9. What would the header for a Cat function, Meow, that takes no parameters and returns void look like? 10. What function is called to initialize a class? Exercises 1. Write the code that declares a class called Employee with these data members: age, yearsOfService, and Salary. 2. Rewrite the Employee class to make the data members private, and provide public accessor methods to get and set each of the data members. 3. Write a program with the Employee class that makes two Employees; sets their age, YearsOfService, and Salary; and prints their values. 4. Continuing from Exercise 3, provide a method of Employee that reports how many thousands of dollars the employee earns, rounded to the nearest 1,000. 5. Change the Employee class so that you can initialize age, YearsOfService, and Salary when you create the employee. 6. BUG BUSTERS: What is wrong with the following declaration? class Square { public: int Side; } 7. BUG BUSTERS: Why isn't the following class declaration very useful? class Cat { int GetAge()const; private: int itsAge; }; 8. BUG BUSTERS: What three bugs in this code will the compiler find? class TV { public: void SetStation(int Station);int GetStation() const; private: int itsStation; }; main() { TV myTV; myTV.itsStation = 9; TV.SetStation(10); TV myOtherTv(2); }a Day 7 ".# More Program Flow Looping The Roots of Looping goto Listing 7.1. Looping with the keyword goto. Why goto Is Shunned The goto Statement ile Loops Listing 7.2. while loops. The while Statement More Complicated while Statements Listing 7.3. Complex while loops. continue and break Listing 7.4. break and continue. The continue Statement The break Statement while (1) Loops Listing 7.5. while (1) loops. do...while Loops Listing 7.6. Skipping the body of the while Loop. do...while Listing 7.7. Demonstrates do...while loop. The do...while Statement for Loops Listing 7.8. While reexamined. Listing 7.9. Demonstrating the for loop. The for Statement Advanced for Loops Listing 7.10. Demonstrating multiple statements in for loops. Listing 7.11. Null statements in for loops. Listing 7.12. Illustrating empty for loop statement. Empty for Loops Listing 7.13. Illustrates the null statement in a for loop. Nested Loops Listing 7.14. Illustrates nested for loops. Scoping in for Loops Summing Up Loops Listing 7.15. Solving the nth Fibonacci number using iteration. switch Statements Listing 7.16. Demonstrating the switch statement. The switch Statement Using a switch Statement with a Menu Listing 7.17. Demonstrating a forever loop. Summary Q&A Workshop Quiz Exercises Day 7 More Program Flow Programs accomplish most of their work by branching and looping. On Day 4, "Expressions and Statements," you learned how to branch your program using the if statement. Today you learn What loops are and how they are used. How to build various loops. An alternative to deeply-nested if/else statements. Looping Many programming problems are solved by repeatedly acting on the same data. There are two ways to do this: recursion (discussed yesterday) and iteration. Iteration means doing the same thing again and again. The principal method of iteration is the loop. The Roots of Looping goto In the primitive days of early computer science, programs were nasty, brutish, and short. Loops consisted of a label, some statements, and a jump. In C++, a label is just a name followed by a colon (:). The label is placed to the left of a legal C++ statement, and a jump is accomplished by writing goto followed by the label name. Listing 7.1 illustrates this.Listing 7.1. Looping with the keyword goto. 1: // Listing 7.1 2: // Looping with goto 3: 4: #include 5: 6: int main() 7: { 8: int counter = 0; // initialize counter 9: loop: counter ++; // top of the loop 10: cout << "counter: " << counter << "\n"; 11: if (counter < 5) // test the value 12: goto loop; // jump to the top 13: 14: cout << "Complete. Counter: " << counter << ".\n"; 15: return 0; 16: } Output: counter: 1 counter: 2 counter: 3 counter: 4 counter: 5 Complete. Counter: 5. Analysis: On line 8, counter is initialized to 0. The label loop is on line 9, marking the top of the loop. Counter is incremented and its new value is printed. The value of counter is tested on line 11. If it is less than 5, the if statement is true and the goto statement is executed. This causes program execution to jump back to line 9. The program continues looping until counter is equal to 5, at which time it "falls through" the loop and the final output is printed. Why goto Is Shunned goto has received some rotten press lately, and it's well deserved. goto statements can cause a jump to any location in your source code, backward or forward. The indiscriminate use of goto statements has caused tangled, miserable, impossible-to-read programs known as "spaghetti code." Because of this, computer science teachers have spent the past 20 years drumming one lesson into the heads of their students: "Never, ever, ever use goto! It is evil!" To avoid the use of goto, more sophisticated, tightly controlled looping commands have been introduced: for, while, and do...while. Using these makes programs that are more easily understood, and goto is generally avoided, but one might argue that the case has been a bit overstated. Like any tool, carefully used and in the right hands, goto can be a useful construct, and the ANSI committee decided to keep it in the language because it has its legitimate uses. But as theysay, kids, don't try this at home. The goto Statement To use the goto statement, you write goto followed by a label name. This causes an unconditioned jump to the label. Example if (value > 10) goto end;if (value < 10) "value is &*10!";end:cout << "done"; goto end;cout << WARNING: Use of goto is almost always a sign of bad design. The best advice is to avoid using it. In 10 years of programming, I've needed it only once. while Loops A while loop causes your program to repeat a sequence of statements as long as the starting condition remains true. In the example of goto, in Listing 7.1, the counter was incremented until it was equal to 5. Listing 7.2 shows the same program rewritten to take advantage of a while loop. Listing 7.2. while loops. 1: // Listing 7.2 2: // Looping with while 3: 4: #include 5: 6: int main() 7: { 8: int counter = 0; // initialize the condition 9: 10: while(counter < 5) // test condition still true 11: { 12: counter++; // body of the loop 13: cout << "counter: " << counter << "\n"; 14: } 15: 16: cout << "Complete. Counter: " << counter << ".\n"; 17: return 0; 18: } Output: counter: 1 counter: 2 counter: 3counter: 4 counter: 5 Complete. Counter: 5. Analysis: This simple program demonstrates the fundamentals of the while loop. A condition is tested, and if it is true, the body of the while loop is executed. In this case, the condition tested on line 10 is whether counter is less than 5. If the condition is true, the body of the loop is executed; on line 12 the counter is incremented, and on line 13 the value is printed. When the conditional statement on line 10 fails (when counter is no longer less than 5), the entire body of the while loop (lines 11-14) is skipped. Program execution falls through to line 15. The while Statement The syntax for the while statement is as follows: while ( condition ) statement; condition is any C++ expression, and statement is any valid C++ statement or block of statements. When condition evaluates to TRUE (1), statement is executed, and then condition is tested again. This continues until condition tests FALSE, at which time the while loop terminates and execution continues on the first line below statement. Example // count to 10 int x = 0; while (x < 10) cout << "X: " << x++; More Complicated while Statements The condition tested by a while loop can be as complex as any legal C++ expression. This can include expressions produced using the logical && (AND), || (OR), and ! (NOT) operators. Listing 7.3 is a somewhat more complicated while statement. Listing 7.3. Complex while loops. 1: 2: 3: 4: 5: 6: // Listing 7.3 // Complex while statements #include int main()7: { 8: unsigned short small; 9: unsigned long large; 10: const unsigned short MAXSMALL=65535; 11: 12: cout << "Enter a small number: "; 13: cin >> small; 14: cout << "Enter a large number: "; 15: cin >> large; 16: 17: cout << "small: " << small << "..."; 18: 19: // for each iteration, test three conditions 20: while (small < large && large > 0 && small < MAXSMALL) 21: 22: { 23: if (small % 5000 == 0) // write a dot every 5k lines 24: cout << "."; 25: 26: small++; 27: 28: large-=2; 29: } 30: 31: cout << "\nSmall: " << small << " Large: " << large << endl; 32: return 0; 33: } Output: Enter a small number: 2 Enter a large number: 100000 small: 2......... Small: 33335 Large: 33334 Analysis: This program is a game. Enter two numbers, one small and one large. The smaller number will count up by ones, and the larger number will count down by twos. The goal of the game is to guess when they'll meet. On lines 12-15, the numbers are entered. Line 20 sets up a while loop, which will continue only as long as three conditions are met: small is not bigger than large. large isn't negative. small doesn't overrun the size of a small integer (MAXSMALL). On line 23, the value in small is calculated modulo 5,000. This does not change the value in small; however, it only returns the value 0 when small is an exact multiple of 5,000. Each time itis, a dot (.) is printed to the screen to show progress. On line 26, small is incremented, and on line 28, large is decremented by 2. When any of the three conditions in the while loop fails, the loop ends and execution of the program continues after the while loop's closing brace on line 29. NOTE: The modulus operator (%) and compound conditions are covered on Day 3, "Variables and Constants." continue and break At times you'll want to return to the top of a while loop before the entire set of statements in the while loop is executed. The continue statement jumps back to the top of the loop. At other times, you may want to exit the loop before the exit conditions are met. The break statement immediately exits the while loop, and program execution resumes after the closing brace. Listing 7.4 demonstrates the use of these statements. This time the game has become more complicated. The user is invited to enter a small number and a large number, a skip number, and a target number. The small number will be incremented by one, and the large number will be decremented by 2. The decrement will be skipped each time the small number is a multiple of the skip. The game ends if small becomes larger than large. If the large number reaches the target exactly, a statement is printed and the game stops. The user's goal is to put in a target number for the large number that will stop the game. Listing 7.4. break and continue. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: // Listing 7.4 // Demonstrates break and continue #include int main() { unsigned short small; unsigned long large; unsigned long skip; unsigned long target; const unsigned short MAXSMALL=65535; cout << "Enter a small number: "; cin >> small; cout << "Enter a large number: ";17: cin >> large; 18: cout << "Enter a skip number: "; 19: cin >> skip; 20: cout << "Enter a target number: "; 21: cin >> target; 22: 23: cout << "\n"; 24: 25: // set up 3 stop conditions for the loop 26: while (small < large && large > 0 && small < 65535) 27: 28: { 29: 30: small++; 31: 32: if (small % skip == 0) // skip the decrement? 33: { 34: cout << "skipping on " << small << endl; 35: continue; 36: } 37: 38: if (large == target) // exact match for the target? 39: { 40: cout << "Target reached!"; 41: break; 42: } 43: 44: large-=2; 45: } // end of while loop 46: 47: cout << "\nSmall: " << small << " Large: " << large << endl; 48: return 0; 49: } Output: Enter a small number: 2 Enter a large number: 20 Enter a skip number: 4 Enter a target number: 6 skipping on 4 skipping on 8 Small: 10 Large: 8 Analysis: In this play, the user lost; small became larger than large before the target number of 6 was reached. On line 26, the while conditions are tested. If small continues to be smaller than large, largeis larger than 0, and small hasn't overrun the maximum value for a small int, the body of the while loop is entered. On line 32, the small value is taken modulo the skip value. If small is a multiple of skip, the continue statement is reached and program execution jumps to the top of the loop at line 26. This effectively skips over the test for the target and the decrement of large. On line 38, target is tested against the value for large. If they are the same, the user has won. A message is printed and the break statement is reached. This causes an immediate break out of the while loop, and program execution resumes on line 46. NOTE: Both continue and break should be used with caution. They are the next most dangerous commands after goto, for much the same reason. Programs that suddenly change direction are harder to understand, and liberal use of continue and break can render even a small while loop unreadable. The continue Statement continue; causes a while or for loop to begin again at the top of the loop. Example if (value > 10) goto end; if (value < 10) goto end; cout << "value is 10!"; end: cout << "done"; The break Statement break; causes the immediate end of a while or for loop. Execution jumps to the closing brace. Example while (condition) { if (condition2) break; // statements; }while (1) Loops The condition tested in a while loop can be any valid C++ expression. As long as that condition remains true, the while loop will continue. You can create a loop that will never end by using the number 1 for the condition to be tested. Since 1 is always true, the loop will never end, unless a break statement is reached. Listing 7.5 demonstrates counting to 10 using this construct. Listing 7.5. while (1) loops. 1: // Listing 7.5 2: // Demonstrates a while true loop 3: 4: #include 5: 6: int main() 7: { 8: int counter = 0; 9: 10: while (1) 11: { 12: counter ++; 13: if (counter > 10) 14: break; 15: } 16: cout << "Counter: " << counter << "\n"; 17: return 0; 18: Output: Counter: 11 Analysis: On line 10, a while loop is set up with a condition that can never be false. The loop increments the counter variable on line 12 and then on line 13 tests to see whether counter has gone past 10. If it hasn't, the while loop iterates. If counter is greater than 10, the break on line 14 ends the while loop, and program execution falls through to line 16, where the results are printed. This program works, but it isn't pretty. This is a good example of using the wrong tool for the job. The same thing can be accomplished by putting the test of counter's value where it belongs--in the while condition. WARNING: Eternal loops such as while (1) can cause your computer to hang if the exit condition is never reached. Use these with caution and test them thoroughly. C++ gives you many different ways to accomplish the same task. The real trick is picking the right tool for the particular job.DON'T use the goto statement. DO use while loops to iterate while a condition is true. DO exercise caution when using continue and break statements. DO make sure your loop will eventually end. do...while Loops It is possible that the body of a while loop will never execute. The while statement checks its condition before executing any of its statements, and if the condition evaluates false, the entire body of the while loop is skipped. Listing 7.6 illustrates this. Listing 7.6. Skipping the body of the while Loop . 1: // Listing 7.6 2: // Demonstrates skipping the body of 3: // the while loop when the condition is false. 4: 5: #include 6: 7: int main() 8: { 9: int counter; 10: cout << "How many hellos?: "; 11: cin >> counter; 12: while (counter > 0) 13: { 14: cout << "Hello!\n"; 15: counter--; 16: } 17: cout << "Counter is OutPut: " << counter; 18: return 0; 19: } Output: How many hellos?: 2 Hello! Hello! Counter is OutPut: 0 How many hellos?: 0 Counter is OutPut: 0 Analysis: The user is prompted for a starting value on line 10. This starting value is stored in the integer variable counter. The value of counter is tested on line 12, and decremented in the body of the while loop. The first time through counter was set to 2, and so the body of the while loop ran twice. The second time through, however, the user typed in 0. The value of counter was testedon line 12 and the condition was false; counter was not greater than 0. The entire body of the while loop was skipped, and Hello was never printed. What if you want to ensure that Hello is always printed at least once? The while loop can't accomplish this, because the if condition is tested before any printing is done. You can force the issue with an if statement just before entering the while: if (counter < 1) counter = 1; // force a minimum value but that is what programmers call a "kludge," an ugly and inelegant solution. do...while The do...while loop executes the body of the loop before its condition is tested and ensures that the body always executes at least one time. Listing 7.7 rewrites Listing 7.6, this time using a do...while loop. Listing 7.7. Demonstrates do...while loop. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: } // Listing 7.7 // Demonstrates do while #include int main() { int counter; cout << "How many hellos? "; cin >> counter; do { cout << "Hello\n"; counter--; } while (counter >0 ); cout << "Counter is: " << counter << endl; return 0; Output: How many hellos? 2 Hello Hello Counter is: 0 Analysis: The user is prompted for a starting value on line 9, which is stored in the integer variable counter. In the do...while loop, the body of the loop is entered before the condition is tested,and therefore the body of the loop is guaranteed to run at least once. On line 13 the message is printed, on line 14 the counter is decremented, and on line 15 the condition is tested. If the condition evaluates TRUE, execution jumps to the top of the loop on line 13; otherwise, it falls through to line 16. The continue and break statements work in the do...while loop exactly as they do in the while loop. The only difference between a while loop and a do...while loop is when the condition is tested. The do...while Statement The syntax for the do...while statement is as follows: do statement while (condition); statement is executed, and then condition is evaluated. If condition is TRUE, the loop is repeated; otherwise, the loop ends. The statements and conditions are otherwise identical to the while loop. Example 1 // count to 10 int x = 0; do cout << "X: " << x++; while (x < 10) Example 2 // print lowercase alphabet. char ch = `a'; do { cout << ch << ` `; ch++; } while ( ch <= `z' ); DO use do...while when you want to ensure the loop is executed at least once. DO use while loops when you want to skip the loop if the condition is false. DO test all loops to make sure they do what you expect. for Loops When programming while loops, you'll often find yourself setting up a starting condition, testing tosee if the condition is true, and incrementing or otherwise changing a variable each time through the loop. Listing 7.8 demonstrates this. Listing 7.8. While reexamined . 1: // Listing 7.8 2: // Looping with while 3: 4: #include 5: 6: int main() 7: { 8: int counter = 0; 9: 10: while(counter < 5) 11: { 12: counter++; 13: cout << "Looping! "; 14: } 15: 16: cout << "\nCounter: " << counter << ".\n"; 17: return 0; 18: } Output: Looping! Looping! Looping! Looping! Looping! Counter: 5. Analysis: The condition is set on line 8: counter is initialized to 0. On line 10, counter is tested to see whether it is less than 5. counter is incremented on line 12. On line 16, a simple message is printed, but you can imagine that more important work could be done for each increment of the counter. A for loop combines three steps into one statement. The three steps are initialization, test, and increment. A for statement consists of the keyword for followed by a pair of parentheses. Within the parentheses are three statements separated by semicolons. The first statement is the initialization. Any legal C++ statement can be put here, but typically this is used to create and initialize a counting variable. Statement 2 is the test, and any legal C++ expression can be used here. This serves the same role as the condition in the while loop. Statement 3 is the action. Typically a value is incremented or decremented, though any legal C++ statement can be put here. Note that statements 1 and 3 can be any legal C++ statement, but statement 2 must be an expression--a C++ statement that returns a value. Listing 7.9 demonstrates a for loop. Listing 7.9. Demonstrating the for loop. 1: 2: // Listing 7.9 // Looping with for3: 4: #include 5: 6: int main() 7: { 8: int counter; 9: for (counter = 0; counter < 5; counter++) 10: cout << "Looping! "; 11: 12: cout << "\nCounter: " << counter << ".\n"; 13: return 0; 14: } Output: Looping! Looping! Looping! Looping! Looping! Counter: 5. Analysis: The for statement on line 8 combines the initialization of counter, the test that counter is less than 5, and the increment of counter all into one line. The body of the for statement is on line 9. Of course, a block could be used here as well. The for Statement The syntax for the for statement is as follows: for (initialization; test; action ) statement; The initialization statement is used to initialize the state of a counter, or to otherwise prepare for the loop. test is any C++ expression and is evaluated each time through the loop. If test is TRUE, the action in the header is executed (typically the counter is incremented) and then the body of the for loop is executed. Example 1 // print Hello ten times for (int i = 0; i<10; i++) cout << "Hello! "; Example 2 for (int i = 0; i < 10; i++) { cout << "Hello!" << endl; cout << "the value of i is: " << i << endl; } Advanced for Loopsfor statements are powerful and flexible. The three independent statements (initialization, test, and action) lend themselves to a number of variations. A for loop works in the following sequence: 1. Performs the operations in the initialization. 2. Evaluates the condition. 3. If the condition is TRUE, executes the action statement and the loop. After each time through, the loop repeats steps 2 and 3. Multiple Initialization and Increments It is not uncommon to initialize more than one variable, to test a compound logical expression, and to execute more than one statement. The initialization and the action may be replaced by multiple C++ statements, each separated by a comma. Listing 7.10 demonstrates the initialization and increment of two variables. Listing 7.10. Demonstrating multiple statements in for loops. 1: //listing 7.10 2: // demonstrates multiple statements in 3: // for loops 4: 5: #include 6: 7: int main() 8: { 9: for (int i=0, j=0; i<3; i++, j++) 10: cout << "i: " << i << " j: " << j << endl; 11: return 0; 12: } Output: i: 0 i: 1 j: 1 i: 2 j: 2 j: 0 Analysis: On line 9, two variables, i and j, are each initialized with the value 0. The test (i<3) is evaluated, and because it is true, the body of the for statement is executed, and the values are printed. Finally, the third clause in the for statement is executed, and i and j are incremented. Once line 10 completes, the condition is evaluated again, and if it remains true the actions are repeated (i and j are again incremented), and the body of loop is executed again. This continues until the test fails, in which case the action statement is not executed, and control falls out of the loop. Null Statements in for Loops Any or all of the statements in a for loop can be null. To accomplish this, use the semicolon to mark where the statement would have been. To create a for loop that acts exactly like a while loop, leave out the first and third statements. Listing 7.11 illustrates this idea.Listing 7.11. Null statements in for loops . 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: } // Listing 7.11 // For loops with null statements #include int main() { int counter = 0; for( ; counter < 5; ) { counter++; cout << "Looping! "; } cout << "\nCounter: " << counter << ".\n"; return 0; output: Looping! Counter: 5. Looping! Looping! Looping! Looping! Analysis: You may recognize this as exactly like the while loop illustrated in Listing 7.8! On line 8, the counter variable is initialized. The for statement on line 10 does not initialize any values, but it does include a test for counter < 5. There is no increment statement, so this loop behaves exactly as if it had been written: while (counter < 5) Once again, C++ gives you a number of ways to accomplish the same thing. No experienced C++ programmer would use a for loop in this way, but it does illustrate the flexibility of the for statement. In fact, it is possible, using break and continue, to create a for loop with none of the three statements. Listing 7.12 illustrates how. Listing 7.12. Illustrating empty for loop statement. 1: 2: 3: 4: 5: 6: //Listing 7.12 illustrating //empty for loop statement #include int main()7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: } { int counter=0; // initialization int max; cout << "How many hellos?"; cin >> max; for (;;) // a for loop that doesn't end { if (counter < max) // test { cout << "Hello!\n"; counter++; // increment } else break; } return 0; Output: How many hellos?3 Hello! Hello! Hello! Analysis: The for loop has now been pushed to its absolute limit. Initialization, test, and action have all been taken out of the for statement. The initialization is done on line 8, before the for loop begins. The test is done in a separate if statement on line 14, and if the test succeeds, the action, an increment to counter, is performed on line 17. If the test fails, breaking out of the loop occurs on line 20. While this particular program is somewhat absurd, there are times when a for(;;) loop or a while (1) loop is just what you'll want. You'll see an example of a more reasonable use of such loops when switch statements are discussed later today. Empty for Loops So much can be done in the header of a for statement, there are times you won't need the body to do anything at all. In that case, be sure to put a null statement (;) as the body of the loop. The semicolon can be on the same line as the header, but this is easy to overlook. Listing 7.13 illustrates how to use a null body in a for loop. Listing 7.13. Illustrates the null statement in a for loop. 1: 2: 3: 4: //Listing 7.13 //Demonstrates null statement // as body of for loop5: 6: 7: 8: 9: 10: 11: } #include int main() { for (int i = 0; i<5; cout << "i: " << i++ << endl) ; return 0; Output: i: 0 i: 1 i: 2 i: 3 i: 4 Analysis: The for loop on line 8 includes three statements: the initialization statement establishes the counter i and initializes it to 0. The condition statement tests for i<5, and the action statement prints the value in i and increments it. There is nothing left to do in the body of the for loop, so the null statement (;) is used. Note that this is not a well-designed for loop: the action statement is doing far too much. This would be better rewritten as 8: 9: for (int i = 0; i<5; i++) cout << "i: " << i << endl; While both do exactly the same thing, this example is easier to understand. Nested Loops Loops may be nested, with one loop sitting in the body of another. The inner loop will be executed in full for every execution of the outer loop. Listing 7.14 illustrates writing marks into a matrix using nested for loops. Listing 7.14. Illustrates nested for loops. 1: 2: 3: 4: 5: 6: 7: 8: 9: //Listing 7.14 //Illustrates nested for loops #include int main() { int rows, columns; char theChar;10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: } cout << "How many rows? "; cin >> rows; cout << "How many columns? "; cin >> columns; cout << "What character? "; cin >> theChar; for (int i = 0; i typedef unsigned long int ULONG; ULONG fib(ULONG position); int main() { ULONG answer, position; cout << "Which position? "; cin >> position; cout << "\n"; answer = fib(position);18: cout << answer << " is the "; 19: cout << position << "th Fibonacci number.\n"; 20: return 0; 21: } 22: 23: ULONG fib(ULONG n) 24: { 25: ULONG minusTwo=1, minusOne=1, answer=2; 26: 27: if (n < 3) 28: return 1; 29: 30: for (n -= 3; n; n--) 31: { 32: minusTwo = minusOne; 33: minusOne = answer; 34: answer = minusOne + minusTwo; 35: } 36: 37: return answer; 38: } Output: Which position? 4 3 is the 4th Fibonacci number. Which position? 5 5 is the 5th Fibonacci number. Which position? 20 6765 is the 20th Fibonacci number. Which position? 100 3314859971 is the 100th Fibonacci number. Analysis: Listing 7.15 solves the Fibonacci series using iteration rather than recursion. This approach is faster and uses less memory than the recursive solution. On line 13, the user is asked for the position to check. The function fib() is called, which evaluates the position. If the position is less than 3, the function returns the value 1. Starting with position 3, the function iterates using the following algorithm: 1. Establish the starting position: Fill variable answer with 2, minusTwo with 0 (answer-2), and minusOne with 1 (answer-1). Decrement the position by 3, because the first two numbers are handled by the starting position.2. For every number, count up the Fibonacci series. This is done by a. Putting the value currently in minusOne into minusTwo. b. Putting the value currently in answer into minusOne. c. Adding minusOne and minusTwo and putting the sum in answer. d. Decrementing n. 3. When n reaches 0, return the answer. This is exactly how you would solve this problem with pencil and paper. If you were asked for the fifth Fibonacci number, you would write: 1, 1, 2, and think, "two more to do." You would then add 2+1 and write 3, and think, "one more to find." Finally you would write 3+2 and the answer would be 5. In effect, you are shifting your attention right one number each time through, and decrementing the number remaining to be found. Note the condition tested on line 30 (n). This is a C++ idiom, and is exactly equivalent to n != 0. This for loop relies on the fact that when n reaches 0 it will evaluate false, because 0 is false in C++. The for loop header could have been written: for (n-=3; n>0; n++) which might have been clearer. However, this idiom is so common in C++ that there is little sense in fighting it. Compile, link, and run this program, along with the recursive solution offered on Day 5. Try finding position 25 and compare the time it takes each program. Recursion is elegant, but because the function call brings a performance overhead, and because it is called so many times, its performance is noticeably slower than iteration. Microcomputers tend to be optimized for the arithmetic operations, so the iterative solution should be blazingly fast. Be careful how large a number you enter. fib grows quickly, and long integers will overflow after a while. switch Statements On Day 4, you saw how to write if and if/else statements. These can become quite confusing when nested too deeply, and C++ offers an alternative. Unlike if, which evaluates one value, switch statements allow you to branch on any of a number of different values. The general form of the switch statement is:switch (expression) { case valueOne: statement; break; case valueTwo: statement; break; .... case valueN: statement; break; default: statement; } expression is any legal C++ expression, and the statements are any legal C++ statements or block of statements. switch evaluates expression and compares the result to each of the case values. Note, however, that the evaluation is only for equality; relational operators may not be used here, nor can Boolean operations. If one of the case values matches the expression, execution jumps to those statements and continues to the end of the switch block, unless a break statement is encountered. If nothing matches, execution branches to the optional default statement. If there is no default and there is no matching value, execution falls through the switch statement and the statement ends. NOTE: It is almost always a good idea to have a default case in switch statements. If you have no other need for the default, use it to test for the supposedly impossible case, and print out an error message; this can be a tremendous aid in debugging. It is important to note that if there is no break statement at the end of a case statement, execution will fall through to the next case statement. This is sometimes necessary, but usually is an error. If you decide to let execution fall through, be sure to put a comment, indicating that you didn't just forget the break. Listing 7.16 illustrates use of the switch statement. Listing 7.16. Demonstrating the switch statement . 1: 2: 3: 4: 5: 6: 7: //Listing 7.16 // Demonstrates switch statement #include int main() {8: unsigned short int number; 9: cout << "Enter a number between 1 and 5: "; 10: cin >> number; 11: switch (number) 12: { 13: case 0: cout << "Too small, sorry!"; 14: break; 15: case 5: cout << "Good job!\n"; // fall 16: case 4: cout << "Nice Pick!\n"; // fall 17: case 3: cout << "Excellent!\n"; // fall 18: case 2: cout << "Masterful!\n"; // fall 19: case 1: cout << "Incredible!\n"; 20: break; 21: default: cout << "Too large!\n"; 22: break; 23: } 24: cout << "\n\n"; 25: return 0; 26: } through through through through Output: Enter a number between 1 and 5: 3 Excellent! Masterful! Incredible! Enter a number between 1 and 5: 8 Too large! Analysis: The user is prompted for a number. That number is given to the switch statement. If the number is 0, the case statement on line 13 matches, the message Too small, sorry! is printed, and the break statement ends the switch. If the value is 5, execution switches to line 15 where a message is printed, and then falls through to line 16, another message is printed, and so forth until hitting the break on line 20. The net effect of these statements is that for a number between 1 and 5, that many messages are printed. If the value of number is not 0-5, it is assumed to be too large, and the default statement is invoked on line 21. The switch Statement The syntax for the switch statement is as follows: switch (expression) { case valueOne: statement; case valueTwo: statement; ....case valueN: statement default: statement; } The switch statement allows for branching on multiple values of expression. The expression is evaluated, and if it matches any of the case values, execution jumps to that line. Execution continues until either the end of the switch statement or a break statement is encountered. If expression does not match any of the case statements, and if there is a default statement, execution switches to the default statement, otherwise the switch statement ends. Example 1 switch (choice) { case 0: cout << "Zero!" << endl; break case 1: cout << "One!" << endl; break; case 2: cout << "Two!" << endl; default: cout << "Default!" << endl; } Example 2 switch { choice choice choice (choice) 0: 1: 2: cout << "Less than 3!"; break; choice 3: cout << "Equals 3!"; break; default: cout << "greater than 3!"; } Using a switch Statement with a Menu Listing 7.17 returns to the for(;;) loop discussed earlier. These loops are also called forever loops, as they will loop forever if a break is not encountered. The forever loop is used to put up a menu, solicit a choice from the user, act on the choice, and then return to the menu. This will continue untilthe user chooses to exit. NOTE: Some programmers like to write #define EVER ;; for (EVER) { // statements... } Using #define is covered on Day 17, "The Preprocessor." New Term: A forever loop is a loop that does not have an exit condition. In order to exit the loop, a break statement must be used. Forever loops are also known as eternal loops. Listing 7.17. Demonstrating a forever loop. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: //Listing 7.17 //Using a forever loop to manage //user interaction #include // types & defines enum BOOL { FALSE, TRUE }; typedef unsigned short int USHORT; // prototypes USHORT menu(); void DoTaskOne(); void DoTaskMany(USHORT); int main() { BOOL exit = FALSE; for (;;) { USHORT choice = menu(); switch(choice) { case (1):25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: } } DoTaskOne(); break; case (2): DoTaskMany(2); break; case (3): DoTaskMany(3); break; case (4): continue; // redundant! break; case (5): exit=TRUE; break; default: cout << "Please select again!\n"; break; // end switch if (exit) break; } // end forever return 0; // end main() USHORT menu() { USHORT choice; cout << " **** Menu ****\n\n"; cout << "(1) Choice one.\n"; cout << "(2) Choice two.\n"; cout << "(3) Choice three.\n"; cout << "(4) Redisplay menu.\n"; cout << "(5) Quit.\n\n"; cout << ": "; cin >> choice; return choice; } void DoTaskOne() { cout << "Task One!\n"; } void DoTaskMany(USHORT which)71: 72: 73: 74: 75: 76: } { if (which == 2) cout << "Task Two!\n"; else cout << "Task Three!\n"; Output: **** Menu **** (1) (2) (3) (4) (5) Choice one. Choice two. Choice three. Redisplay menu. Quit. : 1 Task One! **** Menu **** (1) Choice one. (2) Choice two. (3) Choice three. (4) Redisplay menu. (5) Quit. : 3 Task Three! **** Menu **** (1) Choice one. (2) Choice two. (3) Choice three. (4) Redisplay menu. (5) Quit. : 5 Analysis: This program brings together a number of concepts from today and previous days. It also shows a common use of the switch statement. On line 7, an enumeration, BOOL, is created, with two possible values: FALSE, which equals 0, as it should, and TRUE, which equals 1. On line 8, typedef is used to create an alias, USHORT, for unsigned short int. The forever loop begins on 19. The menu() function is called, which prints the menu to the screen and returns the user's selection. The switch statement, which begins on line 22 and ends on line 42, switches on the user's choice. If the user enters 1, execution jumps to the case 1: statement on line 24. Line 25 switches execution to the DoTaskOne() function, which prints a message and returns. On its return, execution resumes on line 26, where the break ends the switch statement, and execution falls through to line 43. On line 44, the variable exit is evaluated. If it evaluates true, the break on line45 will be executed and the for(;;) loop will end, but if it evaluates false, execution resumes at the top of the loop on line 19. Note that the continue statement on line 34 is redundant. If it were left out and the break statement were encountered, the switch would end, exit would evaluate FALSE, the loop would reiterate, and the menu would be reprinted. The continue does, however, bypass the test of exit. DO use switch statements to avoid deeply nested if statements. DON'T forget break at the end of each case unless you wish to fall through. DO carefully document all intentional fall-through cases. DO put a default case in switch statements, if only to detect seemingly impossible situations. Summary There are different ways to cause a C++ program to loop. While loops check a condition, and if it is true, execute the statements in the body of the loop. do...while loops execute the body of the loop and then test the condition. for loops initialize a value, then test an expression. If an expression is true, the final statement in the for header is executed, as is the body of the loop. Each subsequent time through the loop the expression is tested again. The goto statement is generally avoided, as it causes an unconditional jump to a seemingly arbitrary location in the code, and thus makes source code difficult to understand and maintain. continue causes while, do...while, and for loops to start over, and break causes while, do...while, for, and switch statements to end. Q&A Q. How do you choose between if/else and switch? A. If there are more than just one or two else clauses, and all are testing the same value, consider using a switch statement. Q. How do you choose between while and do...while? A. If the body of the loop should always execute at least once, consider a do...while loop; otherwise, try to use the while loop. Q. How do you choose between while and for? A If you are initializing a counting variable, testing that variable, and incrementing it each time through the loop, consider the for loop. If your variable is already initialized and is not incremented on each loop, a while loop may be the better choice. Q. How do you choose between recursion and iteration?A. Some problems cry out for recursion, but most problems will yield to iteration as well. Put recursion in your back pocket; it may come in handy someday. Q. Is it better to use while (1) or for (;;)? A. There is no significant difference. Workshop The Workshop provides quiz questions to help you solidify your understanding of the material covered, as well as exercises to provide you with experience in using what you've learned. Try to answer the quiz and exercise questions before checking the answers in Appendix D, and make sure you understand the answers before continuing to the next chapter. Quiz 1. How do I initialize more than one variable in a for loop? 2. Why is goto avoided? 3. Is it possible to write a for loop with a body that is never executed? 4. Is it possible to nest while loops within for loops? 5. Is it possible to create a loop that never ends? Give an example. 6. What happens if you create a loop that never ends? Exercises 1. What is the value of x when the for loop completes? for (int x = 0; x < 100; x++) 2. Write a nested for loop that prints a 10x10 pattern of 0s. 3. Write a for statement to count from 100 to 200 by 2s. 4. Write a while loop to count from 100 to 200 by 2s. 5. Write a do...while loop to count from 100 to 200 by 2s. 6. BUG BUSTERS: What is wrong with this code? int counter = 0 while (counter < 10){ cout << "counter: " << counter; } 7. BUG BUSTERS: What is wrong with this code? for (int counter = 0; counter < 10; counter++); cout << counter << " "; 8. BUG BUSTERS: What is wrong with this code? int counter = 100; while (counter < 10) { cout << "counter now: " << counter; counter--; } 9. BUG BUSTERS: What is wrong with this code? cout << "Enter a number cin >> theNumber; switch (theNumber) { case 0: doZero(); case 1: case 2: case 3: case 4: case 5: doOneToFive(); break; default: doDefault(); break; } between 0 and 5: "; // // // // fall fall fall fall through through through throughIn Review Listing R1.1. Week 1 in Review listing. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: } 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: #include typedef unsigned short int USHORT; typedef unsigned long int ULONG; enum BOOL { FALSE, TRUE}; enum CHOICE { DrawRect = 1, GetArea, GetPerim, ChangeDimensions, Quit}; // Rectangle class declaration class Rectangle { public: // constructors Rectangle(USHORT width, USHORT height); ~Rectangle(); // accessors USHORT GetHeight() const { return itsHeight; } USHORT GetWidth() const { return itsWidth; } ULONG GetArea() const { return itsHeight * itsWidth; } ULONG GetPerim() const { return 2*itsHeight + 2*itsWidth; void SetSize(USHORT newWidth, USHORT newHeight); // Misc. methods void DrawShape() const; private: USHORT itsWidth; USHORT itsHeight; }; // Class method implementations void Rectangle::SetSize(USHORT newWidth, USHORT newHeight) { itsWidth = newWidth; itsHeight = newHeight; }37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: Rectangle::Rectangle(USHORT width, USHORT height) { itsWidth = width; itsHeight = height; } Rectangle::~Rectangle() {} USHORT DoMenu(); void DoDrawRect(Rectangle); void DoGetArea(Rectangle); void DoGetPerim(Rectangle); void main () { // initialize a rectangle to 10,20 Rectangle theRect(30,5); USHORT choice = DrawRect; USHORT fQuit = FALSE; while (!fQuit) { choice = DoMenu(); if (choice < DrawRect || choice > Quit) { cout << "\nInvalid Choice, please try again.\n\n"; continue; } switch (choice) { case DrawRect: DoDrawRect(theRect); break; case GetArea: DoGetArea(theRect); break; case GetPerim: DoGetPerim(theRect); break; case ChangeDimensions: USHORT newLength, newWidth; cout << "\nNew width: "; cin >> newWidth;83: cout << "New height: "; 84: cin >> newLength; 85: theRect.SetSize(newWidth, newLength); 86: DoDrawRect(theRect); 87: break; 88: case Quit: 89: fQuit = TRUE; 90: cout << "\nExiting...\n\n"; 91: break; 92: default: 93: cout << "Error in choice!\n"; 94: fQuit = TRUE; 95: break; 96: } // end switch 97: } // end while 98: } // end main 99: 100: 101: USHORT DoMenu() 102: { 103: USHORT choice; 104: cout << "\n\n *** Menu *** \n"; 105: cout << "(1) Draw Rectangle\n"; 106: cout << "(2) Area\n"; 107: cout << "(3) Perimeter\n"; 108: cout << "(4) Resize\n"; 109: cout << "(5) Quit\n"; 110: 111: cin >> choice; 112: return choice; 113: } 114: 115: void DoDrawRect(Rectangle theRect) 116: { 117: USHORT height = theRect.GetHeight(); 118: USHORT width = theRect.GetWidth(); 119: 120: for (USHORT i = 0; i int main() { unsigned short shortVar=5;9: unsigned long longVar=65535; 10: long sVar = -65535; 11: 12: cout << "shortVar:\t" << shortVar; 13: cout << " Address of shortVar:\t"; 14: cout << &shortVar _<< "\n"; 15: 16: cout << "longVar:\t" << longVar; 17: cout << " Address of longVar:\t" ; 18: cout << &longVar _<< "\n"; 19: 20: cout << "sVar:\t" << sVar; 21: cout << " Address of sVar:\t" ; 22: cout << &sVar _<< "\n"; 23: 24: return 0; 25: } Output: shortVar: 5 Address of shortVar: 0x8fc9:fff4 longVar: 65535 Address of longVar: 0x8fc9:fff2 sVar: -65535 Address of sVar: 0x8fc9:ffee (Your printout may look different.) Analysis: Three variables are declared and initialized: a short in line 8, an unsigned long in line 9, and a long in line 10. Their values and addresses are printed in lines 12-16, by using the address of operator (&). The value of shortVar is 5, as expected, and its address is 0x8fc9:fff4 when run on my 80386-based computer. This complicated address is computer-specific and may change slightly each time the program is run. Your results will be different. What doesn't change, however, is that the difference in the first two addresses is two bytes if your computer uses two-byte short integers. The difference between the second and third is four bytes if your computer uses four-byte long integers. Figure 8.2 illustrates how the variables in this program would be stored in memory. Figure 8.2. Illustration of variable storage. There is no reason why you need to know the actual numeric value of the address of each variable. What you care about is that each one has an address and that the right amount of memory is set aside. You tell the compiler how much memory to allow for your variables by declaring the variable type; the compiler automatically assigns an address for it. For example, a long integer is typically four bytes, meaning that the variable has an address to four bytes of memory. Storing the Address in a Pointer Every variable has an address. Even without knowing the specific address of a given variable, you can store that address in a pointer. For example, suppose that howOld is an integer. To declare a pointer called pAge to hold its address, you would writeint *pAge = 0; This declares pAge to be a pointer to int. That is, pAge is declared to hold the address of an int. Note that pAge is a variable like any of the variables. When you declare an integer variable (type int), it is set up to hold an integer. When you declare a pointer variable like pAge, it is set up to hold an address. pAge is just a different type of variable. In this example, pAge is initialized to zero. A pointer whose value is zero is called a null pointer. All pointers, when they are created, should be initialized to something. If you don't know what you want to assign to the pointer, assign 0. A pointer that is not initialized is called a wild pointer. Wild pointers are very dangerous. NOTE: Practice safe computing: Initialize your pointers! If you do initialize the pointer to 0, you must specifically assign the address of howOld to pAge. Here's an example that shows how to do that: unsigned short int howOld = 50; unsigned short int * pAge = 0; pAge = &howOld; // make a variable // make a pointer // put howOld's address in pAge The first line creates a variable--howOld, whose type is unsigned short int--and initializes it with the value 50. The second line declares pAge to be a pointer to type unsigned short int and initializes it to zero. You know that pAge is a pointer because of the asterisk (*) after the variable type and before the variable name. The third and final line assigns the address of howOld to the pointer pAge. You can tell that the address of howOld is being assigned because of the address of operator (&). If the address of operator had not been used, the value of howOld would have been assigned. That might, or might not, have been a valid address. At this point, pAge has as its value the address of howOld. howOld, in turn, has the value 50. You could have accomplished this with one less step, as in unsigned short int howOld = 50; unsigned short int * pAge = &howOld; // make a variable // make pointer to howOld pAge is a pointer that now contains the address of the howOld variable. Using pAge, you can actually determine the value of howOld, which in this case is 50. Accessing howOld by using the pointer pAge is called indirection because you are indirectly accessing howOld by means of pAge. Later today you will see how to use indirection to access a variable's value. New Term: Indirection means accessing the value at the address held by a pointer. The pointer provides an indirect way to get the value held at that address.Pointer Names Pointers can have any name that is legal for other variables. This book follows the convention of naming all pointers with an initial p, as in pAge or pNumber. The Indirection Operator The indirection operator (*) is also called the dereference operator. When a pointer is dereferenced, the value at the address stored by the pointer is retrieved. Normal variables provide direct access to their own values. If you create a new variable of type unsigned short int called yourAge, and you want to assign the value in howOld to that new variable, you would write unsigned short int yourAge; yourAge = howOld; A pointer provides indirect access to the value of the variable whose address it stores. To assign the value in howOld to the new variable yourAge by way of the pointer pAge, you would write unsigned short int yourAge; yourAge = *pAge; The indirection operator (*) in front of the variable pAge means "the value stored at." This assignment says, "Take the value stored at the address in pAge and assign it to yourAge." NOTE: The indirection operator (*) is used in two distinct ways with pointers: declaration and dereference. When a pointer is declared, the star indicates that it is a pointer, not a normal variable. For example, unsigned short * pAge = 0; // make a pointer to an unsigned short When the pointer is dereferenced, the indirection operator indicates that the value at the memory location stored in the pointer is to be accessed, rather than the address itself. *pAge = 5; // assign 5 to the value at pAge Also note that this same character (*) is used as the multiplication operator. The compiler knows which operator to call, based on context. Pointers, Addresses, and Variables It is important to distinguish between a pointer, the address that the pointer holds, and the value at the address held by the pointer. This is the source of much of the confusion about pointers.Consider the following code fragment: int theVariable = 5; int * pPointer = &theVariable ; theVariable is declared to be an integer variable initialized with the value 5. pPointer is declared to be a pointer to an integer; it is initialized with the address of theVariable. pPointer is the pointer. The address that pPointer holds is the address of theVariable. The value at the address that pPointer holds is 5. Figure 8.3 shows a schematic representation of theVariable and pPointer. Figure 8.3. A schematic representation of memory. Manipulating Data by Using Pointers Once a pointer is assigned the address of a variable, you can use that pointer to access the data in that variable. Listing 8.2 demonstrates how the address of a local variable is assigned to a pointer and how the pointer manipulates the values in that variable. Listing 8.2. Manipulating data by using pointers. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: // Listing 8.2 Using pointers #include typedef unsigned short int USHORT; int main() { USHORT myAge; // a variable USHORT * pAge = 0; // a pointer myAge = 5; cout << "myAge: " << myAge << "\n"; pAge = &myAge; // assign address of myAge to pAge cout << "*pAge: " << *pAge << "\n\n"; cout << "*pAge = 7\n"; *pAge = 7; // sets myAge to 7 cout << "*pAge: " << *pAge << "\n"; cout << "myAge: " << myAge << "\n\n"; cout << "myAge = 9\n"; myAge = 9; cout << "myAge: " << myAge << "\n";30: 31: 32: 33: } cout << "*pAge: " << *pAge << "\n"; return 0; Output: myAge: 5 *pAge: 5 *pAge = 7 *pAge: 7 myAge: 7 myAge = 9 myAge: 9 *pAge: 9 Analysis: This program declares two variables: an unsigned short, myAge, and a pointer to an unsigned short, pAge. myAge is assigned the value 5 on line 10; this is verified by the printout in line 11. On line 13, pAge is assigned the address of myAge. On line 15, pAge is dereferenced and printed, showing that the value at the address that pAge stores is the 5 stored in myAge. In line 17, the value 7 is assigned to the variable at the address stored in pAge. This sets myAge to 7, and the printouts in lines 21-22 confirm this. In line 27, the value 9 is assigned to the variable myAge. This value is obtained directly in line 29 and indirectly (by dereferencing pAge) in line 30. Examining the Address Pointers enable you to manipulate addresses without ever knowing their real value. After today, you'll take it on faith that when you assign the address of a variable to a pointer, it really has the address of that variable as its value. But just this once, why not check to make sure? Listing 8.3 illustrates this idea. Listing 8.3. Finding out what is stored in pointers. 1: // Listing 8.3 What is stored in a pointer. 2: 3: #include 4: 5: typedef unsigned short int USHORT; 6: int main() 7: { 8: unsigned short int myAge = 5, yourAge = 10; 9: unsigned short int * pAge = &myAge; // a pointer 10: 11: cout << "myAge:\t" << myAge << "\tyourAge:\t" << yourAge << "\n"; 12: cout << "&myAge:\t" << &myAge << "\t&yourAge:\t" << &yourAge <<"\n"; 13: 14: cout << "pAge:\t" << pAge << "\n";15: cout << "*pAge:\t" << *pAge << "\n"; 16: 17: pAge = &yourAge; // reassign the pointer 18: 19: cout << "myAge:\t" << myAge << "\tyourAge:\t" << yourAge << "\n"; 20: cout << "&myAge:\t" << &myAge << "\t&yourAge:\t" << &yourAge <<"\n"; 21: 22: cout << "pAge:\t" << pAge << "\n"; 23: cout << "*pAge:\t" << *pAge << "\n"; 24: 25: cout << "&pAge:\t" << &pAge << "\n"; 26: return 0; 27: } Output: myAge: 5 yourAge: 10 &myAge: 0x355C &yourAge: 0x355E pAge: 0x355C *pAge: 5 myAge: 5 yourAge: 10 &myAge: 0x355C &yourAge: 0x355E pAge: 0x355E *pAge: 10 &pAge: 0x355A (Your output may look different.) Analysis: In line 8, myAge and yourAge are declared to be variables of type unsigned short integer. In line 9, pAge is declared to be a pointer to an unsigned short integer, and it is initialized with the address of the variable myAge. Lines 11 and 12 print the values and the addresses of myAge and yourAge. Line 14 prints the contents of pAge, which is the address of myAge. Line 15 prints the result of dereferencing pAge, which prints the value at pAge--the value in myAge, or 5. This is the essence of pointers. Line 14 shows that pAge stores the address of myAge, and line 15 shows how to get the value stored in myAge by dereferencing the pointer pAge. Make sure that you understand this fully before you go on. Study the code and look at the output. In line 17, pAge is reassigned to point to the address of yourAge. The values and addresses are printed again. The output shows that pAge now has the address of the variable yourAge and that dereferencing obtains the value in yourAge. Line 25 prints the address of pAge itself. Like any variable, it has an address, and that address can be stored in a pointer. (Assigning the address of a pointer to another pointer will be discussed shortly.) DO use the indirection operator (*) to access the data stored at the address in a pointer. DO initialize all pointers either to a valid address or to null (0). DO remember the difference between the address in a pointer and the value at that address.Pointers To declare a pointer, write the type of the variable or object whose address will be stored in the pointer, followed by the pointer operator (*) and the name of the pointer. For example, unsigned short int * pPointer = 0; To assign or initialize a pointer, prepend the name of the variable whose address is being assigned with the address of operator (&). For example, unsigned short int theVariable = 5; unsigned short int * pPointer = & theVariable; To dereference a pointer, prepend the pointer name with the dereference operator (*). For example, unsigned short int theValue = *pPointer Why Would You Use Pointers? So far you've seen step-by-step details of assigning a variable's address to a pointer. In practice, though, you would never do this. After all, why bother with a pointer when you already have a variable with access to that value? The only reason for this kind of pointer manipulation of an automatic variable is to demonstrate how pointers work. Now that you are comfortable with the syntax of pointers, you can put them to good use. Pointers are used, most often, for three tasks: This rest of this chapter focuses on managing data on the free store and accessing class member data and functions. Tomorrow you will learn about passing variables by reference. The Stack and the Free Store In the "Extra Credit" section following the discussion of functions in Day 5, five areas of memory are mentioned: Local variables are on the stack, along with function parameters. Code is in code space, of course, and global variables are in global name space. The registers are used for internal housekeeping functions, such as keeping track of the top of the stack and the instruction pointer. Just about all remaining memory is given over to the free store, which is sometimes referred to as the heap. The problem with local variables is that they don't persist: When the function returns, the local variables are thrown away. Global variables solve that problem at the cost of unrestricted access throughout the program, which leads to the creation of code that is difficult to understand and maintain. Putting data in the free store solves both of these problems. You can think of the free store as a massive section of memory in which thousands of sequentially numbered cubbyholes lie waiting for your data. You can't label these cubbyholes, though, as you can with the stack. You must ask for the address of the cubbyhole that you reserve and then stash that address away in a pointer. One way to think about this is with an analogy: A friend gives you the 800 number for Acme Mail Order. You go home and program your telephone with that number, and then you throw away the piece of paper with the number on it. If you push the button, a telephone rings somewhere, and Acme Mail Order answers. You don't remember the number, and you don't know where the other telephone is located, but the button gives you access to Acme Mail Order. Acme Mail Order is your data on the free store. You don't know where it is, but you know how to get to it. You access it by using its address--in this case, the telephone number. You don't have to know that number; you just have to put it into a pointer (the button). The pointer gives you access to your data without bothering you with the details. The stack is cleaned automatically when a function returns. All the local variables go out of scope, and they are removed from the stack. The free store is not cleaned until your program ends, and it is your responsibility to free any memory that you've reserved when you are done with it. The advantage to the free store is that the memory you reserve remains available until you explicitly free it. If you reserve memory on the free store while in a function, the memory is still available when the function returns. The advantage of accessing memory in this way, rather than using global variables, is that only functions with access to the pointer have access to the data. This provides a tightly controlled interface to that data, and it eliminates the problem of one function changing that data in unexpected and unanticipated ways. For this to work, you must be able to create a pointer to an area on the free store and to pass that pointer among functions. The following sections describe how to do this. new You allocate memory on the free store in C++ by using the new keyword. new is followed by the type of the object that you want to allocate so that the compiler knows how much memory is required. Therefore, new unsigned short int allocates two bytes in the free store, and new long allocates four. The return value from new is a memory address. It must be assigned to a pointer. To create an unsignedshort on the free store, you might write unsigned short int * pPointer; pPointer = new unsigned short int; You can, of course, initialize the pointer at its creation with unsigned short int * pPointer = new unsigned short int; In either case, pPointer now points to an unsigned short int on the free store. You can use this like any other pointer to a variable and assign a value into that area of memory by writing *pPointer = 72; This means, "Put 72 at the value in pPointer," or "Assign the value 72 to the area on the free store to which pPointer points." If new cannot create memory on the free store (memory is, after all, a limited resource) it returns the null pointer. You must check your pointer for null each time you request new memory. WARNING: Each time you allocate memory using the new keyword, you must check to make sure the pointer is not null. delete When you are finished with your area of memory, you must call delete on the pointer. delete returns the memory to the free store. Remember that the pointer itself--as opposed to the memory to which it points--is a local variable. When the function in which it is declared returns, that pointer goes out of scope and is lost. The memory allocated with new is not freed automatically, however. That memory becomes unavailable--a situation called a memory leak. It's called a memory leak because that memory can't be recovered until the program ends. It is as though the memory has leaked out of your computer. To restore the memory to the free store, you use the keyword delete. For example, delete pPointer; When you delete the pointer, what you are really doing is freeing up the memory whose address is stored in the pointer. You are saying, "Return to the free store the memory that this pointer points to." The pointer is still a pointer, and it can be reassigned. Listing 8.4 demonstrates allocating a variable on the heap, using that variable, and deleting it. WARNING: When you call delete on a pointer, the memory it points to is freed. Calling delete on that pointer again will crash your program! When you delete a pointer, set it to zero (null). Calling delete on a null pointer is guaranteed to be safe. For example: Animal *pDog = new Animal; delete pDog; //frees the memorypDog = 0; //sets pointer to null //... delete pDog; //harmless Listing 8.4. Allocating, using, and deleting pointers. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: } // Listing 8.4 // Allocating and deleting a pointer #include int main() { int localVariable = 5; int * pLocal= &localVariable; int * pHeap = new int; if (pHeap == NULL) { cout << "Error! No memory for pHeap!!"; return 0; } *pHeap = 7; cout << "localVariable: " << localVariable << "\n"; cout << "*pLocal: " << *pLocal << "\n"; cout << "*pHeap: " << *pHeap << "\n"; delete pHeap; pHeap = new int; if (pHeap == NULL) { cout << "Error! No memory for pHeap!!"; return 0; } *pHeap = 9; cout << "*pHeap: " << *pHeap << "\n"; delete pHeap; return 0; Output: localVariable: 5 *pLocal: 5 *pHeap: 7 *pHeap: 9 Analysis: Line 7 declares and initializes a local variable. Line 8 declares and initializes a pointer with the address of the local variable. Line 9 declares another pointer but initializes it with the result obtained from calling new int. This allocates space on the free store for an int. Line 10 verifies that memory was allocated and the pointer is valid (not null). If no memory can be allocated, the pointer is null and an error message is printed. To keep things simple, this error checking often won't be reproduced in future programs, but you must include some sort of error checking in your own programs.Line 15 assigns the value 7 to the newly allocated memory. Line 16 prints the value of the local variable, and line 17 prints the value pointed to by pLocal. As expected, these are the same. Line 19 prints the value pointed to by pHeap. It shows that the value assigned in line 15 is, in fact, accessible. In line 19, the memory allocated in line 9 is returned to the free store by a call to delete. This frees the memory and disassociates the pointer from that memory. pHeap is now free to point to other memory. It is reassigned in lines 20 and 26, and line 27 prints the result. Line 28 restores that memory to the free store. Although line 28 is redundant (the end of the program would have returned that memory) it is a good idea to free this memory explicitly. If the program changes or is extended, it will be beneficial that this step was already taken care of. Memory Leaks Another way you might inadvertently create a memory leak is by reassigning your pointer before deleting the memory to which it points. Consider this code fragment: 1: 2: 3: 4: unsigned short int * pPointer = new unsigned short int; *pPointer = 72; pPointer = new unsigned short int; *pPointer = 84; Line 1 creates pPointer and assigns it the address of an area on the free store. Line 2 stores the value 72 in that area of memory. Line 3 reassigns pPointer to another area of memory. Line 4 places the value 84 in that area. The original area--in which the value 72 is now held--is unavailable because the pointer to that area of memory has been reassigned. There is no way to access that original area of memory, nor is there any way to free it before the program ends. The code should have been written like this: 1: 2: 3: 4: 5: unsigned short int * pPointer = new unsigned short int; *pPointer = 72; delete pPointer; pPointer = new unsigned short int; *pPointer = 84; Now the memory originally pointed to by pPointer is deleted, and thus freed, in line 3. NOTE: For every time in your program that you call new, there should be a call to delete. It is important to keep track of which pointer owns an area of memory and to ensure that the memory is returned to the free store when you are done with it. Creating Objects on the Free Store Just as you can create a pointer to an integer, you can create a pointer to any object. If you have declared anobject of type Cat, you can declare a pointer to that class and instantiate a Cat object on the free store, just as you can make one on the stack. The syntax is the same as for integers: Cat *pCat = new Cat; This calls the default constructor--the constructor that takes no parameters. The constructor is called whenever an object is created (on the stack or on the free store). Deleting Objects When you call delete on a pointer to an object on the free store, that object's destructor is called before the memory is released. This gives your class a chance to clean up, just as it does for objects destroyed on the stack. Listing 8.5 illustrates creating and deleting objects on the free store. Listing 8.5. Creating and deleting objects on the free store. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 // Listing 8.5 // Creating objects on the free store #include class SimpleCat { public: SimpleCat(); ~SimpleCat(); private: int itsAge; }; SimpleCat::SimpleCat() { cout << "Constructor called.\n"; itsAge = 1; } SimpleCat::~SimpleCat() { cout << "Destructor called.\n"; } int main() { cout << "SimpleCat Frisky...\n"; SimpleCat Frisky; cout << "SimpleCat *pRags = new SimpleCat...\n"; SimpleCat * pRags = new SimpleCat; cout << "delete pRags...\n"; delete pRags;34 35 36 } cout << "Exiting, watch Frisky go...\n"; return 0; Output: SimpleCat Frisky... Constructor called. SimpleCat *pRags = new SimpleCat.. Constructor called. delete pRags... Destructor called. Exiting, watch Frisky go... Destructor called. Analysis: Lines 6-13 declare the stripped-down class SimpleCat. Line 9 declares SimpleCat's constructor, and lines 15-19 contain its definition. Line 10 declares SimpleCat's destructor, and lines 21-24 contain its definition. In line 29, Frisky is created on the stack, which causes the constructor to be called. In line 31, the SimpleCat pointed to by pRags is created on the heap; the constructor is called again. In line 33, delete is called on pRags, and the destructor is called. When the function ends, Frisky goes out of scope, and the destructor is called. Accessing Data Members You accessed data members and functions by using the dot (.) operator for Cat objects created locally. To access the Cat object on the free store, you must dereference the pointer and call the dot operator on the object pointed to by the pointer. Therefore, to access the GetAge member function, you would write (*pRags).GetAge(); Parentheses are used to assure that pRags is dereferenced before GetAge() is accessed. Because this is cumbersome, C++ provides a shorthand operator for indirect access: the points-to operator (->), which is created by typing the dash (-) immediately followed by the greater-than symbol (>). C++ treats this as a single symbol. Listing 8.6 demonstrates accessing member variables and functions of objects created on the free store. Listing 8.6. Accessing member data of objects on the free store. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: // Listing 8.6 // Accessing data members of objects on the heap #include class SimpleCat { public: SimpleCat() {itsAge = 2; } ~SimpleCat() {} int GetAge() const { return itsAge; }12: void SetAge(int age) 13: private: 14: int itsAge; 15: }; 16: 17: int main() 18: { 19: SimpleCat * Frisky 20: cout << "Frisky is years old\n"; 21: Frisky->SetAge(5); 22: cout << "Frisky is years old\n"; 23: delete Frisky; 24: return 0; 25: } { itsAge = age; } = new SimpleCat; " << Frisky->GetAge() << " " << Frisky->GetAge() << " Output: Frisky is 2 years old Frisky is 5 years old Analysis: In line 19, a SimpleCat object is instantiated on the free store. The default constructor sets its age to 2, and the GetAge() method is called in line 20. Because this is a pointer, the indirection operator (->) is used to access the member data and functions. In line 21, the SetAge() method is called, and GetAge() is accessed again in line 22. Member Data on the Free Store One or more of the data members of a class can be a pointer to an object on the free store. The memory can be allocated in the class constructor or in one of its methods, and it can be deleted in its destructor, as Listing 8.7 illustrates. Listing 8.7. Pointers as member data. 1: // Listing 8.7 2: // Pointers as data members 3: 4: #include 5: 6: class SimpleCat 7: { 8: public: 9: SimpleCat(); 10: ~SimpleCat(); 11: int GetAge() const { return *itsAge; } 12: void SetAge(int age) { *itsAge = age; } 13: 14: int GetWeight() const { return *itsWeight; } 15: void setWeight (int weight) { *itsWeight = weight; } 16:17: private: 18: int * itsAge; 19: int * itsWeight; 20: }; 21: 22: SimpleCat::SimpleCat() 23: { 24: itsAge = new int(2); 25: itsWeight = new int(5); 26: } 27: 28: SimpleCat::~SimpleCat() 29: { 30: delete itsAge; 31: delete itsWeight; 32: } 33: 34: int main() 35: { 36: SimpleCat *Frisky = new SimpleCat; 37: cout << "Frisky is " << Frisky->GetAge() << " years old\n"; 38: Frisky->SetAge(5); 39: cout << "Frisky is " << Frisky->GetAge() << " years old\n"; 40: delete Frisky; 41: return 0; 42: } Output: Frisky is 2 years old Frisky is 5 years old Analysis: The class SimpleCat is declared to have two member variables--both of which are pointers to integers--on lines 14 and 15. The constructor (lines 22-26) initializes the pointers to memory on the free store and to the default values. The destructor (lines 28-32) cleans up the allocated memory. Because this is the destructor, there is no point in assigning these pointers to null, as they will no longer be accessible. This is one of the safe places to break the rule that deleted pointers should be assigned to null, although following the rule doesn't hurt. The calling function (in this case, main()) is unaware that itsAge and itsWeight are point-ers to memory on the free store. main() continues to call GetAge() and SetAge(), and the details of the memory management are hidden in the implementation of the class--as they should be. When Frisky is deleted in line 40, its destructor is called. The destructor deletes each of its member pointers. If these, in turn, point to objects of other user-defined classes, their destructors are called as well. The this Pointer Every class member function has a hidden parameter: the this pointer. this points to the individual object. Therefore, in each call to GetAge() or SetAge(), the this pointer for the object is included as a hiddenparameter. It is possible to use the this pointer explicitly, as Listing 8.8 illustrates. Listing 8.8. Using the this pointer. 1: // Listing 8.8 2: // Using the this pointer 3: 4: #include 5: 6: class Rectangle 7: { 8: public: 9: Rectangle(); 10: ~Rectangle(); 11: void SetLength(int length) { this->itsLength = length; } 12: int GetLength() const { return this->itsLength; } 13: 14: void SetWidth(int width) { itsWidth = width; } 15: int GetWidth() const { return itsWidth; } 16: 17: private: 18: int itsLength; 19: int itsWidth; 20: }; 21: 22: Rectangle::Rectangle() 23: { 24: itsWidth = 5; 25: itsLength = 10; 26: } 27: Rectangle::~Rectangle() 28: {} 29: 30: int main() 31: { 32: Rectangle theRect; 33: cout << "theRect is " << theRect.GetLength() << " feet long.\n"; 34: cout << "theRect is " << theRect.GetWidth() << " feet wide.\n"; 35: theRect.SetLength(20); 36: theRect.SetWidth(10); 37: cout << "theRect is " << theRect.GetLength()<< " feet long.\n"; 38: cout << "theRect is " << theRect.GetWidth()<< " feet wide.\n"; 39: return 0;40: } Output: theRect theRect theRect theRect is 10 feet long. is 5 feet long. is 20 feet long. is 10 feet long. Analysis: The SetLength() and GetLength() accessor functions explicitly use the this pointer to access the member variables of the Rectangle object. The SetWidth and GetWidth accessors do not. There is no difference in their behavior, although the syntax is easier to understand. If that were all there was to the this pointer, there would be little point in bothering you with it. The this pointer, however, is a pointer; it stores the memory address of an object. As such, it can be a powerful tool. You'll see a practical use for the this pointer on Day 10, "Advanced Functions," when operator overloading is discussed. For now, your goal is to know about the this pointer and to understand what it is: a pointer to the object itself. You don't have to worry about creating or deleting the this pointer. The compiler takes care of that. Stray or Dangling Pointers One source of bugs that are nasty and difficult to find is stray pointers. A stray pointer is created when you call delete on a pointer--thereby freeing the memory that it points to--and later try to use that pointer again without reassigning it. It is as though the Acme Mail Order company moved away, and you still pressed the programmed button on your phone. It is possible that nothing terrible happens--a telephone rings in a deserted warehouse. Perhaps the telephone number has been reassigned to a munitions factory, and your call detonates an explosive and blows up your whole city! In short, be careful not to use a pointer after you have called delete on it. The pointer still points to the old area of memory, but the compiler is free to put other data there; using the pointer can cause your program to crash. Worse, your program might proceed merrily on its way and crash several minutes later. This is called a time bomb, and it is no fun. To be safe, after you delete a pointer, set it to null (0). This disarms the pointer. NOTE: Stray pointers are often called wild pointers or dangling pointers. Listing 8.9 illustrates creating a stray pointer. WARNING: This program intentionally creates a stray pointer. Do NOT run this program--it will crash, if you are lucky. Listing 8.9. Creating a stray pointer. 1: // Listing 8.92: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: } // Demonstrates a stray pointer typedef unsigned short int USHORT; #include int main() { USHORT * pInt = new USHORT; *pInt = 10; cout << "*pInt: " << *pInt << endl; delete pInt; pInt = 0; long * pLong = new long; *pLong = 90000; cout << "*pLong: " << *pLong << endl; *pInt = 20; // uh oh, this was deleted! cout << "*pInt: " << *pInt << endl; cout << "*pLong: " << *pLong << endl; delete pLong; return 0; Output: *pInt: 10 *pLong: 90000 *pInt: 20 *pLong: 65556 Null pointer assignment (Your output may look different.) Analysis: Line 8 declares pInt to be a pointer to USHORT, and pInt is pointed to newly allocated memory. Line 9 puts the value 10 in that memory, and line 10 prints its value. After the value is printed, delete is called on the pointer. pInt is now a stray, or dangling, pointer. Line 13 declares a new pointer, pLong, which is pointed at the memory allocated by new. Line 14 assigns the value 90000 to pLong, and line 15 prints its value. Line 17 assigns the value 20 to the memory that pInt points to, but pInt no longer points anywhere that is valid. The memory that pInt points to was freed by the call to delete, so assigning a value to that memory is certain disaster. Line 19 prints the value at pInt. Sure enough, it is 20. Line 20 prints 20, the value at pLong; it has suddenly been changed to 65556. Two questions arise: 1. How could pLong's value change, given that pLong wasn't touched? 2. Where did the 20 go when pInt was used in line 17? As you might guess, these are related questions. When a value was placed at pInt in line 17, the compiler happily placed the value 20 at the memory location that pInt previously pointed to. However, because thatmemory was freed in line 11, the compiler was free to reassign it. When pLong was created in line 13, it was given pInt's old memory location. (On some computers this may not happen, depending on where in memory these values are stored.) When the value 20 was assigned to the location that pInt previously pointed to, it wrote over the value pointed to by pLong. This is called "stomping on a pointer." It is often the unfortunate outcome of using a stray pointer. This is a particularly nasty bug, because the value that changed wasn't associated with the stray pointer. The change to the value at pLong was a side effect of the misuse of pInt. In a large program, this would be very difficult to track down. Just for fun, here are the details of how 65,556 got into that memory address: 1. pInt was pointed at a particular memory location, and the value 10 was assigned. 2. delete was called on pInt, which told the compiler that it could put something else at that location. Then pLong was assigned the same memory location. 3. The value 90000 was assigned to *pLong. The particular computer used in this example stored the four-byte value of 90,000 (00 01 5F 90) in byte-swapped order. Therefore, it was stored as 5F 90 00 01. 4. pInt was assigned the value 20--or 00 14 in hexadecimal notation. Because pInt still pointed to the same address, the first two bytes of pLong were overwritten, leaving 00 14 00 01. 5. The value at pLong was printed, reversing the bytes back to their correct order of 00 01 00 14, which was translated into the DOS value of 65556. DO use new to create objects on the free store. DO use delete to destroy objects on the free store and to return their memory. DON'T forget to balance all new statements with a delete statement. DON'T forget to assign null (0) to all pointers that you call delete on. DO check the value returned by new. const Pointers You can use the keyword const for pointers before the type, after the type, or in both places. For example, all of the following are legal declarations: const int * pOne; int * const pTwo; const int * const pThree; pOne is a pointer to a constant integer. The value that is pointed to can't be changed. pTwo is a constant pointer to an integer. The integer can be changed, but pTwo can't point to anything else. pThree is a constant pointer to a constant integer. The value that is pointed to can't be changed, and pThree can't be changed to point to anything else. The trick to keeping this straight is to look to the right of the keyword const to find out what is being declared constant. If the type is to the right of the keyword, it is the value that is constant. If the variable is tothe right of the keyword const, it is the pointer variable itself that is constant. const int * p1; int * const p2; // the int pointed to is constant // p2 is constant, it can't point to anything else const Pointers and const Member Functions On Day 6, "Basic Classes," you learned that you can apply the keyword const to a member function. When a function is declared const, the compiler flags as an error any attempt to change data in the object from within that function. If you declare a pointer to a const object, the only methods that you can call with that pointer are const methods. Listing 8.10 illustrates this. Listing 8.10. Using pointers to const objects. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: // Listing 8.10 // Using pointers with const methods #include class Rectangle { public: Rectangle(); ~Rectangle(); void SetLength(int length) { itsLength = length; } int GetLength() const { return itsLength; } void SetWidth(int width) { itsWidth = width; } int GetWidth() const { return itsWidth; } private: int itsLength; int itsWidth; }; Rectangle::Rectangle(): itsWidth(5), itsLength(10) {} Rectangle::~Rectangle() {} int main() { Rectangle* pRect = new Rectangle; const Rectangle * pConstRect = new Rectangle;34: Rectangle * const pConstPtr = new Rectangle; 35: 36: cout << "pRect width: " << pRect->GetWidth() << " feet\n"; 37: cout << "pConstRect width: " << pConstRect- >GetWidth() << " feet\n"; 38: cout << "pConstPtr width: " << pConstPtr->GetWidth() << " feet\n"; 39: 40: pRect->SetWidth(10); 41: // pConstRect->SetWidth(10); 42: pConstPtr->SetWidth(10); 43: 44: cout << "pRect width: " << pRect->GetWidth() << " feet\n"; 45: cout << "pConstRect width: " << pConstRect- >GetWidth() << " feet\n"; 46: cout << "pConstPtr width: " << pConstPtr->GetWidth() << " feet\n"; 47: return 0; 48: } Output: pRect width: 5 feet pConstRect width: 5 feet pConstPtr width: 5 feet pRect width: 10 feet pConstRect width: 5 feet pConstPtr width: 10 feet Analysis: Lines 6-20 declare Rectangle. Line 15 declares the GetWidth() member method const. Line 32 declares a pointer to Rectangle. Line 33 declares pConstRect, which is a pointer to a constant Rectangle. Line 34 declares pConstPtr, which is a constant pointer to Rectangle. Lines 36-38 print their values. In line 40, pRect is used to set the width of the rectangle to 10. In line 41, pConstRect would be used, but it was declared to point to a constant Rectangle. Therefore, it cannot legally call a non-const member function; it is commented out. In line 38, pConstPtr calls SetAge(). pConstPtr is declared to be a constant pointer to a rectangle. In other words, the pointer is constant and cannot point to anything else, but the rectangle is not constant. const this Pointers When you declare an object to be const, you are in effect declaring that the this pointer is a pointer to a const object. A const this pointer can be used only with const mem- ber functions. Constant objects and constant pointers will be discussed again tomorrow, when references to constant objects are discussed. DO protect objects passed by reference with const if they should not be changed. DO pass byreference when the object can be changed. DO pass by value when small objects should not be changed. Summary Pointers provide a powerful way to access data by indirection. Every variable has an address, which can be obtained using the address of operator (&). The address can be stored in a pointer. Pointers are declared by writing the type of object that they point to, followed by the indirection operator (*) and the name of the pointer. Pointers should be initialized to point to an object or to null (0). You access the value at the address stored in a pointer by using the indirection operator (*). You can declare const pointers, which can't be reassigned to point to other objects, and pointers to const objects, which can't be used to change the objects to which they point. To create new objects on the free store, you use the new keyword and assign the address that is returned to a pointer. You free that memory by calling the delete keyword on the pointer. delete frees the memory, but it doesn't destroy the pointer. Therefore, you must reassign the pointer after its memory has been freed. Q&A Q. Why are pointers so important? A. Today you saw how pointers are used to hold the address of objects on the free store and how they are used to pass arguments by reference. In addition, on Day 13, "Polymorphism," you'll see how pointers are used in class polymorphism. Q. Why should I bother to declare anything on the free store? A. Objects on the free store persist after the return of a function. Additionally, the ability to store objects on the free store enables you to decide at runtime how many objects you need, instead of having to declare this in advance. This is explored in greater depth tomorrow. Q. Why should I declare an object const if it limits what I can do with it? A. As a programmer, you want to enlist the compiler in helping you find bugs. One serious bug that is difficult to find is a function that changes an object in ways that aren't obvious to the calling function. Declaring an object const prevents such changes. Workshop The Workshop provides quiz questions to help you solidify your understanding of the material covered and exercises to provide you with experience in using what you've learned. Try to answer the quiz and exercise questions before checking the answers in Appendix D, and make sure you understand the answers before continuing to the next chapter. Quiz1. What operator is used to determine the address of a variable? 2. What operator is used to find the value stored at an address held in a pointer? 3. What is a pointer? 4. What is the difference between the address stored in a pointer and the value at that address? 5. What is the difference between the indirection operator and the address of operator? 6. What is the difference between const int * ptrOne and int * const ptrTwo? Exercises 1. What do these declarations do? a. int * pOne; b. int vTwo; c. int * pThree = &vTwo; 2. If you have an unsigned short variable named yourAge, how would you declare a pointer to manipulate yourAge? 3. Assign the value 50 to the variable yourAge by using the pointer that you declared in Exercise 2. 4. Write a small program that declares an integer and a pointer to integer. Assign the address of the integer to the pointer. Use the pointer to set a value in the integer variable. 5. BUG BUSTERS: What is wrong with this code? #include int main() { int *pInt; *pInt = 9; cout << "The value at pInt: " << *pInt; return 0; } 6. BUG BUSTERS: What is wrong with this code? int main() { int SomeVariable = 5; cout << "SomeVariable: " << SomeVariable << "\n"; int *pVar = & SomeVariable; pVar = 9; cout << "SomeVariable: " << *pVar << "\n"; return 0; } What Is a Reference? A reference is an alias; when you create a reference, you initialize it with the name of another object, the target. From that moment on, the reference acts as an alternative name for the target, and anything you do to the reference is really done to the target. You create a reference by writing the type of the target object, followed by the reference operator (&), followed by the name of the reference. References can use any legal variable name, but for this book we'll prefix all reference names with "r." Thus, if you have an integer variable named someInt, you can make a reference to that variable by writing the following: int &rSomeRef = someInt; This is read as "rSomeRef is a reference to an integer that is initialized to refer to someInt." Listing 9.1 shows how references are created and used.NOTE: Note that the reference operator (&) is the same symbol as the one used for the address of the operator. These are not the same operators, however, though clearly they are related. Listing 9.1. Creating and using references. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: } //Listing 9.1 // Demonstrating the use of References #include int main() { int intOne; int &rSomeRef = intOne; intOne = 5; cout << "intOne: " << intOne << endl; cout << "rSomeRef: " << rSomeRef << endl; rSomeRef = 7; cout << "intOne: " << intOne << endl; cout << "rSomeRef: " << rSomeRef << endl; return 0; Output: intOne: 5 rSomeRef: 5 intOne: 7 rSomeRef: 7 Anaylsis: On line 8, a local int variable, intOne, is declared. On line 9, a reference to an int, rSomeRef, is declared and initialized to refer to intOne. If you declare a reference, but don't initialize it, you will get a compile-time error. References must be initialized. On line 11, intOne is assigned the value 5. On lines 12 and 13, the values in intOne and rSomeRef are printed, and are, of course, the same. On line 15, 7 is assigned to rSomeRef. Since this is a reference, it is an alias for intOne, and thus the 7 is really assigned to intOne, as is shown by the printouts on lines 16 and 17. Using the Address of Operator & on References If you ask a reference for its address, it returns the address of its target. That is the nature of references. They are aliases for the target. Listing 9.2 demonstrates this.Listing 9.2. Taking the address of a reference . 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: } //Listing 9.2 // Demonstrating the use of References #include int main() { int intOne; int &rSomeRef = intOne; intOne = 5; cout << "intOne: " << intOne << endl; cout << "rSomeRef: " << rSomeRef << endl; cout << "&intOne: " << &intOne << endl; cout << "&rSomeRef: " << &rSomeRef << endl; return 0; Output: intOne: 5 rSomeRef: 5 &intOne: 0x3500 &rSomeRef: 0x3500 NOTE: Your output may differ on the last two lines. Anaylsis: Once again rSomeRef is initialized as a reference to intOne. This time the addresses of the two variables are printed, and they are identical. C++ gives you no way to access the address of the reference itself because it is not meaningful, as it would be if you were using a pointer or other variable. References are initialized when created, and always act as a synonym for their target, even when the address of operator is applied. For example, if you have a class called President, you might declare an instance of that class as follows: President William_Jefferson_Clinton; You might then declare a reference to President and initialize it with this object:President &Bill_Clinton = William_Jefferson_Clinton; There is only one President; both identifiers refer to the same object of the same class. Any action you take on Bill_Clinton will be taken on William_Jefferson_Clinton as well. Be careful to distinguish between the & symbol on line 9 of Listing 9.2, which declares a reference to int named rSomeRef, and the & symbols on lines 15 and 16, which return the addresses of the integer variable intOne and the reference rSomeRef. Normally, when you use a reference, you do not use the address of operator. You simply use the reference as you would use the target variable. This is shown on line 13. Even experienced C++ programmers, who know the rule that references cannot be reassigned and are always aliases for their target, can be confused by what happens when you try to reassign a reference. What appears to be a reassignment turns out to be the assignment of a new value to the target. Listing 9.3 illustrates this fact. Listing 9.3. Assigning to a reference. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: } //Listing 9.3 //Reassigning a reference #include int main() { int intOne; int &rSomeRef = intOne; intOne = 5; cout << "intOne:\t" << intOne << endl; cout << "rSomeRef:\t" << rSomeRef << endl; cout << "&intOne:\t" << &intOne << endl; cout << "&rSomeRef:\t" << &rSomeRef << endl; int intTwo = 8; rSomeRef = intTwo; // not what you think! cout << "\nintOne:\t" << intOne << endl; cout << "intTwo:\t" << intTwo << endl; cout << "rSomeRef:\t" << rSomeRef << endl; cout << "&intOne:\t" << &intOne << endl; cout << "&intTwo:\t" << &intTwo << endl; cout << "&rSomeRef:\t" << &rSomeRef << endl; return 0;Output: intOne: rSomeRef: 5 &intOne: 0x213e &rSomeRef: 0x213e intOne: intTwo: rSomeRef: &intOne: &intTwo: &rSomeRef: 5 8 8 8 0x213e 0x2130 0x213e Anaylsis: Once again, an integer variable and a reference to an integer are declared, on lines 8 and 9. The integer is assigned the value 5 on line 11, and the values and their addresses are printed on lines 12-15. On line 17, a new variable, intTwo, is created and initialized with the value 8. On line 18, the programmer tries to reassign rSomeRef to now be an alias to the variable intTwo, but that is not what happens. What actually happens is that rSomeRef continues to act as an alias for intOne, so this assignment is exactly equivalent to the following: intOne = intTwo; Sure enough, when the values of intOne and rSomeRef are printed (lines 19-21) they are the same as intTwo. In fact, when the addresses are printed on lines 22-24, you see that rSomeRef continues to refer to intOne and not intTwo. DO use references to create an alias to an object. DO initialize all references. DON'T try to reassign a reference. DON'T confuse the address of operator with the reference operator. What Can Be Referenced? Any object can be referenced, including user-defined objects. Note that you create a reference to an object, but not to a class. You do not write this: int & rIntRef = int; // wrong You must initialize rIntRef to a particular integer, such as this: int howBig = 200; int & rIntRef = howBig;In the same way, you don't initialize a reference to a CAT: CAT & rCatRef = CAT; // wrong You must initialize rCatRef to a particular CAT object: CAT frisky; CAT & rCatRef = frisky; References to objects are used just like the object itself. Member data and methods are accessed using the normal class member access operator (.), and just as with the built-in types, the reference acts as an alias to the object. Listing 9.4 illustrates this. Listing 9.4. References to objects. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: // Listing 9.4 // References to class objects #include class SimpleCat { public: SimpleCat (int age, int weight); ~SimpleCat() {} int GetAge() { return itsAge; } int GetWeight() { return itsWeight; } private: int itsAge; int itsWeight; }; SimpleCat::SimpleCat(int age, int weight) { itsAge = age; itsWeight = weight; } int main() { SimpleCat Frisky(5,8); SimpleCat & rCat = Frisky; cout << "Frisky is: "; cout << Frisky.GetAge() << " years old. \n";31: cout << "And Frisky weighs: "; 32: cout << rCat.GetWeight() << " pounds. \n"; 33: return 0; 34: } Output: Frisky is: 5 years old. And Frisky weighs 8 pounds. Anaylsis: On line 26, Frisky is declared to be a SimpleCat object. On line 27, a SimpleCat reference, rCat, is declared and initialized to refer to Frisky. On lines 30 and 32, the SimpleCat accessor methods are accessed by using first the SimpleCat object and then the SimpleCat reference. Note that the access is identical. Again, the reference is an alias for the actual object. References Declare a reference by writing the type, followed by the reference operator (&), followed by the reference name. References must be initialized at the time of creation. Example 1 int hisAge; int &rAge = hisAge; Example 2 CAT boots; CAT &rCatRef = boots; Null Pointers and Null References When pointers are not initialized, or when they are deleted, they ought to be assigned to null (0). This is not true for references. In fact, a reference cannot be null, and a program with a reference to a null object is considered an invalid program. When a program is invalid, just about anything can happen. It can appear to work, or it can erase all the files on your disk. Both are possible outcomes of an invalid program. Most compilers will support a null object without much complaint, crashing only if you try to use the object in some way. Taking advantage of this, however, is still not a good idea. When you move your program to another machine or compiler, mysterious bugs may develop if you have null objects. Passing Function Arguments by Reference On Day 5, "Functions," you learned that functions have two limitations: Arguments are passed by value, and the return statement can return only one value.Passing values to a function by reference can overcome both of these limitations. In C++, passing by reference is accomplished in two ways: using pointers and using references. The syntax is different, but the net effect is the same. Rather than a copy being created within the scope of the function, the actual original object is passed into the function. NOTE: If you read the extra credit section after Day 5, you learned that functions are passed their parameters on the stack. When a function is passed a value by reference (either using pointers or references), the address of the object is put on the stack, not the entire object. In fact, on some computers the address is actually held in a register and nothing is put on the stack. In either case, the compiler now knows how to get to the original object, and changes are made there and not in a copy. Passing an object by reference allows the function to change the object being referred to. Recall that Listing 5.5 in Day 5 demonstrated that a call to the swap() function did not affect the values in the calling function. Listing 5.5 is reproduced here as Listing 9.5, for your convenience. Listing 9.5. Demonstrating passing by value. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: "\n"; 12: 13: "\n"; 14: 15: 16: 17: 18: 19: 20: 21: "\n"; //Listing 9.5 Demonstrates passing by value #include void swap(int x, int y); int main() { int x = 5, y = 10; cout << "Main. Before swap, x: " << x << " y: " << y << swap(x,y); cout << "Main. After swap, x: " << x << " y: " << y << return 0; } void swap (int x, int y) { int temp; cout << "Swap. Before swap, x: " << x << " y: " << y <<22: 23: 24: 25: 26: 27: "\n"; 28: 29: } temp = x; x = y; y = temp; cout << "Swap. After swap, x: " << x << " y: " << y << Output: Main. Before swap, x: 5 y: 10 Swap. Before swap, x: 5 y: 10 Swap. After swap, x: 10 y: 5 Main. After swap, x: 5 y: 10 Anaylsis: This program initializes two variables in main() and then passes them to the swap() function, which appears to swap them. When they are examined again in main(), they are unchanged! The problem here is that x and y are being passed to swap() by value. That is, local copies were made in the function. What you want is to pass x and y by reference. There are two ways to solve this problem in C++: You can make the parameters of swap() pointers to the original values, or you can pass in references to the original values. Making swap() Work with Pointers When you pass in a pointer, you pass in the address of the object, and thus the function can manipulate the value at that address. To make swap() change the actual values using pointers, the function, swap(), should be declared to accept two int pointers. Then, by dereferencing the pointers, the values of x and y will, in fact, be swapped. Listing 9.6 demonstrates this idea. Listing 9.6. Passing by reference using pointers. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: "\n"; //Listing 9.6 Demonstrates passing by reference #include void swap(int *x, int *y); int main() { int x = 5, y = 10; cout << "Main. Before swap, x: " << x << " y: " << y <<12: swap(&x,&y); 13: cout << "Main. After swap, x: " << x << " y: " << y << "\n"; 14: return 0; 15: } 16 17: void swap (int *px, int *py) 18: { 19: int temp; 20: 21: cout << "Swap. Before swap, *px: " << *px << " *py: " << *py << "\n"; 22: 23: temp = *px; 24: *px = *py; 25: *py = temp; 26: 27: cout << "Swap. After swap, *px: " << *px << " *py: " << *py << "\n"; 28: 29: } Output: Main. Before swap, x: 5 y: 10 Swap. Before swap, *px: 5 *py: 10 Swap. After swap, *px: 10 *py: 5 Main. After swap, x: 10 y: 5 Anaylsis: Success! On line 5, the prototype of swap() is changed to indicate that its two parameters will be pointers to int rather than int variables. When swap() is called on line 12, the addresses of x and y are passed as the arguments. On line 19, a local variable, temp, is declared in the swap() function. Temp need not be a pointer; it will just hold the value of *px (that is, the value of x in the calling function) for the life of the function. After the function returns, temp will no longer be needed. On line 23, temp is assigned the value at px. On line 24, the value at px is assigned to the value at py. On line 25, the value stashed in temp (that is, the original value at px) is put into py. The net effect of this is that the values in the calling function, whose address was passed to swap(), are, in fact, swapped. Implementing swap() with References The preceding program works, but the syntax of the swap() function is cumbersome in two ways. First, the repeated need to dereference the pointers within the swap() function makes it error-prone and hard to read. Second, the need to pass the address of the variables in the calling function makes the inner workings of swap() overly apparent to its users.It is a goal of C++ to prevent the user of a function from worrying about how it works. Passing by pointers takes the burden off of the called function, and puts it where it belongs--on the calling function. Listing 9.7 rewrites the swap() function, using references. Listing 9.7. swap() rewritten with references. 1: //Listing 9.7 Demonstrates passing by reference 2: // using references! 3: 4: #include 5: 6: void swap(int &x, int &y); 7: 8: int main() 9: { 10: int x = 5, y = 10; 11: 12: cout << "Main. Before swap, x: " << x << " y: " << y << "\n"; 13: swap(x,y); 14: cout << "Main. After swap, x: " << x << " y: " << y << "\n"; 15: return 0; 16: } 17: 18: void swap (int &rx, int &ry) 19: { 20: int temp; 21: 22: cout << "Swap. Before swap, rx: " << rx << " ry: " << ry << "\n"; 23: 24: temp = rx; 25: rx = ry; 26: ry = temp; 27: 28: cout << "Swap. After swap, rx: " << rx << " ry: " << ry << "\n"; 29: 30: } Output: Main. Before swap, x:5 y: 10 Swap. Before swap, rx:5 ry:10 Swap. After swap, rx:10 ry:5 Main. After swap, x:10, y:5Anaylsis:Just as in the example with pointers, two variables are declared on line 10 and their values are printed on line 12. On line 13, the function swap() is called, but note that x and y, not their addresses, are passed. The calling function simply passes the variables. When swap() is called, program execution jumps to line 18, where the variables are identified as references. Their values are printed on line 22, but note that no special operators are required. These are aliases for the original values, and can be used as such. On lines 24-26, the values are swapped, and then they're printed on line 28. Program execution jumps back to the calling function, and on line 14, the values are printed in main(). Because the parameters to swap() are declared to be references, the values from main() are passed by reference, and thus are changed in main() as well. References provide the convenience and ease of use of normal variables, with the power and pass-by- reference capability of pointers! Understanding Function Headers and Prototypes Listing 9.6 shows swap() using pointers, and Listing 9.7 shows it using references. Using the function that takes references is easier, and the code is easier to read, but how does the calling function know if the values are passed by reference or by value? As a client (or user) of swap(), the programmer must ensure that swap() will, in fact, change the parameters. This is another use for the function prototype. By examining the parameters declared in the prototype, which is typically in a header file along with all the other prototypes, the programmer knows that the values passed into swap() are passed by reference, and thus will be swapped properly. If swap() had been a member function of a class, the class declaration, also available in a header file, would have supplied this information. In C++, clients of classes and functions rely on the header file to tell all that is needed; it acts as the interface to the class or function. The actual implementation is hidden from the client. This allows the programmer to focus on the problem at hand and to use the class or function without concern for how it works. When Colonel John Roebling designed the Brooklyn Bridge, he worried in detail about how the concrete was poured and how the wire for the bridge was manufactured. He was intimately involved in the mechanical and chemical processes required to create his materials. Today, however, engineers make more efficient use of their time by using well-understood building materials, without regard to how their manufacturer produced them. It is the goal of C++ to allow programmers to rely on well-understood classes and functions without regard to their internal workings. These "component parts" can be assembled to produce a program, much the same way wires, pipes, clamps, and other parts are assembled to produce buildings and bridges.In much the same way that an engineer examines the spec sheet for a pipe to determine its load- bearing capacity, volume, fitting size, and so forth, a C++ programmer reads the interface of a function or class to determine what services it provides, what parameters it takes, and what values it returns. Returning Multiple Values As discussed, functions can only return one value. What if you need to get two values back from a function? One way to solve this problem is to pass two objects into the function, by reference. The function can then fill the objects with the correct values. Since passing by reference allows a function to change the original objects, this effectively lets the function return two pieces of information. This approach bypasses the return value of the function, which can then be reserved for reporting errors. Once again, this can be done with references or pointers. Listing 9.8 demonstrates a function that returns three values: two as pointer parameters and one as the return value of the function. Listing 9.8. Returning values with pointers. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: //Listing 9.8 // Returning multiple values from a function #include typedef unsigned short USHORT; short Factor(USHORT, USHORT*, USHORT*); int main() { USHORT number, squared, cubed; short error; cout << "Enter a number (0 - 20): "; cin >> number; error = Factor(number, &squared, &cubed); if (!error) { cout << "number: " << number << "\n"; cout << "square: " << squared << "\n"; cout << "cubed: " << cubed << "\n"; } else27: cout << "Error encountered!!\n"; 28: return 0; 29: } 30: 31: short Factor(USHORT n, USHORT *pSquared, USHORT *pCubed) 32: { 33: short Value = 0; 34: if (n > 20) 35: Value = 1; 36: else 37: { 38: *pSquared = n*n; 39: *pCubed = n*n*n; 40: Value = 0; 41: } 42: return Value; 43: } Output: Enter a number (0-20): 3 number: 3 square: 9 cubed: 27 Anaylsis: On line 12, number, squared, and cubed are defined as USHORTs. number is assigned a value based on user input. This number and the addresses of squared and cubed are passed to the function Factor(). Factor()examines the first parameter, which is passed by value. If it is greater than 20 (the maximum value this function can handle), it sets return Value to a simple error value. Note that the return value from Function() is reserved for either this error value or the value 0, indicating all went well, and note that the function returns this value on line 42. The actual values needed, the square and cube of number, are returned not by using the return mechanism, but rather by changing the pointers that were passed into the function. On lines 38 and 39, the pointers are assigned their return values. On line 40, return Value is assigned a success value. On line 41, return Value is returned. One improvement to this program might be to declare the following: enum ERROR_VALUE { SUCCESS, FAILURE}; Then, rather than returning 0 or 1, the program could return SUCCESS or FAILURE. Returning Values by Reference Although Listing 9.8 works, it can be made easier to read and maintain by using references rather thanpointers. Listing 9.9 shows the same program rewritten to use references and to incorporate the ERROR enumeration. Listing 9.9.Listing 9.8 rewritten using references. 1: //Listing 9.9 2: // Returning multiple values from a function 3: // using references 4: 5: #include 6: 7: typedef unsigned short USHORT; 8: enum ERR_CODE { SUCCESS, ERROR }; 9: 10: ERR_CODE Factor(USHORT, USHORT&, USHORT&); 11: 12: int main() 13: { 14: USHORT number, squared, cubed; 15: ERR_CODE result; 16: 17: cout << "Enter a number (0 - 20): "; 18: cin >> number; 19: 20: result = Factor(number, squared, cubed); 21: 22: if (result == SUCCESS) 23: { 24: cout << "number: " << number << "\n"; 25: cout << "square: " << squared << "\n"; 26: cout << "cubed: " << cubed << "\n"; 27: } 28: else 29: cout << "Error encountered!!\n"; 30: return 0; 31: } 32: 33: ERR_CODE Factor(USHORT n, USHORT &rSquared, USHORT &rCubed) 34: { 35: if (n > 20) 36: return ERROR; // simple error code 37: else 38: { 39: rSquared = n*n; 40: rCubed = n*n*n;41: 42: 43: } return SUCCESS; } Output: Enter a number (0 - 20): 3 number: 3 square: 9 cubed: 27 Anaylsis: Listing 9.9 is identical to 9.8, with two exceptions. The ERR_CODE enumeration makes the error reporting a bit more explicit on lines 36 and 41, as well as the error handling on line 22. The larger change, however, is that Factor() is now declared to take references to squared and cubed rather than to pointers. This makes the manipulation of these parameters far simpler and easier to understand. Passing by Reference for Efficiency Each time you pass an object into a function by value, a copy of the object is made. Each time you return an object from a function by value, another copy is made. In the "Extra Credit" section at the end of Day 5, you learned that these objects are copied onto the stack. Doing so takes time and memory. For small objects, such as the built-in integer values, this is a trivial cost. However, with larger, user-created objects, the cost is greater. The size of a user-created object on the stack is the sum of each of its member variables. These, in turn, can each be user-created objects, and passing such a massive structure by copying it onto the stack can be very expensive in performance and memory consumption. There is another cost as well. With the classes you create, each of these temporary copies is created when the compiler calls a special constructor: the copy constructor. Tomorrow you will learn how copy constructors work and how you can make your own, but for now it is enough to know that the copy constructor is called each time a temporary copy of the object is put on the stack. When the temporary object is destroyed, which happens when the function returns, the object's destructor is called. If an object is returned by the function by value, a copy of that object must be made and destroyed as well. With large objects, these constructor and destructor calls can be expensive in speed and use of memory. To illustrate this idea, Listing 9.9 creates a stripped-down user-created object: SimpleCat. A real object would be larger and more expensive, but this is sufficient to show how often the copy constructor and destructor are called.Listing 9.10 creates the SimpleCat object and then calls two functions. The first function receives the Cat by value and then returns it by value. The second one receives a pointer to the object, rather than the object itself, and returns a pointer to the object. Listing 9.10. Passing objects by reference. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: //Listing 9.10 // Passing pointers to objects #include class SimpleCat { public: SimpleCat (); SimpleCat(SimpleCat&); ~SimpleCat(); }; // constructor // copy constructor // destructor SimpleCat::SimpleCat() { cout << "Simple Cat Constructor...\n"; } SimpleCat::SimpleCat(SimpleCat&) { cout << "Simple Cat Copy Constructor...\n"; } SimpleCat::~SimpleCat() { cout << "Simple Cat Destructor...\n"; } SimpleCat FunctionOne (SimpleCat theCat); SimpleCat* FunctionTwo (SimpleCat *theCat); int main() { cout << "Making a cat...\n"; SimpleCat Frisky; cout << "Calling FunctionOne...\n"; FunctionOne(Frisky); cout << "Calling FunctionTwo...\n"; FunctionTwo(&Frisky); return 0;41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: } } // FunctionOne, passes by value SimpleCat FunctionOne(SimpleCat theCat) { cout << "Function One. Returning...\n"; return theCat; } // functionTwo, passes by reference SimpleCat* FunctionTwo (SimpleCat *theCat) { cout << "Function Two. Returning...\n"; return theCat; Output: 1: Making a cat... 2: Simple Cat Constructor... 3: Calling FunctionOne... 4: Simple Cat Copy Constructor... 5: Function One. Returning... 6: Simple Cat Copy Constructor... 7: Simple Cat Destructor... 8: Simple Cat Destructor... 9: Calling FunctionTwo... 10: Function Two. Returning... 11: Simple Cat Destructor... NOTE: Line numbers will not print. They were added to aid in the analysis. Anaylsis: A very simplified SimpleCat class is declared on lines 6-12. The constructor, copy constructor, and destructor all print an informative message so that you can tell when they've been called. On line 34, main() prints out a message, and that is seen on output line 1. On line 35, a SimpleCat object is instantiated. This causes the constructor to be called, and the output from the constructor is seen on output line 2. On line 36, main() reports that it is calling FunctionOne, which creates output line 3. Because FunctionOne() is called passing the SimpleCat object by value, a copy of the SimpleCat object is made on the stack as an object local to the called function. This causes the copy constructor to be called, which creates output line 4. Program execution jumps to line 46 in the called function, which prints an informative message, output line 5. The function then returns, and returns the SimpleCat object by value. This creates yetanother copy of the object, calling the copy constructor and producing line 6. The return value from FunctionOne() is not assigned to any object, and so the temporary created for the return is thrown away, calling the destructor, which produces output line 7. Since FunctionOne() has ended, its local copy goes out of scope and is destroyed, calling the destructor and producing line 8. Program execution returns to main(), and FunctionTwo() is called, but the parameter is passed by reference. No copy is produced, so there's no output. FunctionTwo() prints the message that appears as output line 10 and then returns the SimpleCat object, again by reference, and so again produces no calls to the constructor or destructor. Finally, the program ends and Frisky goes out of scope, causing one final call to the destructor and printing output line 11. The net effect of this is that the call to FunctionOne(), because it passed the cat by value, produced two calls to the copy constructor and two to the destructor, while the call to FunctionTwo() produced none. Passing a const Pointer Although passing a pointer to FunctionTwo() is more efficient, it is dangerous. FunctionTwo() is not allowed to change the SimpleCat object it is passed, yet it is given the address of the SimpleCat. This seriously exposes the object to change and defeats the protection offered in passing by value. Passing by value is like giving a museum a photograph of your masterpiece instead of the real thing. If vandals mark it up, there is no harm done to the original. Passing by reference is like sending your home address to the museum and inviting guests to come over and look at the real thing. The solution is to pass a const pointer to SimpleCat. Doing so prevents calling any non-const method on SimpleCat, and thus protects the object from change. Listing 9.11 demonstrates this idea. Listing 9.11. Passing const pointers. 1: 2: 3: 4: 5: 6: 7: 8: //Listing 9.11 // Passing pointers to objects #include class SimpleCat { public:9: SimpleCat(); 10: SimpleCat(SimpleCat&); 11: ~SimpleCat(); 12: 13: int GetAge() const { return itsAge; } 14: void SetAge(int age) { itsAge = age; } 15: 16: private: 17: int itsAge; 18: }; 19: 20: SimpleCat::SimpleCat() 21: { 22: cout << "Simple Cat Constructor...\n"; 23: itsAge = 1; 24: } 25: 26: SimpleCat::SimpleCat(SimpleCat&) 27: { 28: cout << "Simple Cat Copy Constructor...\n"; 29: } 30: 31: SimpleCat::~SimpleCat() 32: { 33: cout << "Simple Cat Destructor...\n"; 34: } 35: 36:const SimpleCat * const FunctionTwo (const SimpleCat * const theCat); 37: 38: int main() 39: { 40: cout << "Making a cat...\n"; 41: SimpleCat Frisky; 42: cout << "Frisky is " ; 43 cout << Frisky.GetAge(); 44: cout << " years _old\n"; 45: int age = 5; 46: Frisky.SetAge(age); 47: cout << "Frisky is " ; 48 cout << Frisky.GetAge(); 49: cout << " years _old\n"; 50: cout << "Calling FunctionTwo...\n"; 51: FunctionTwo(&Frisky); 52: cout << "Frisky is " ; 53 cout << Frisky.GetAge();54: cout << " years _old\n"; 55: return 0; 56: } 57: 58: // functionTwo, passes a const pointer 59: const SimpleCat * const FunctionTwo (const SimpleCat * const theCat) 60: { 61: cout << "Function Two. Returning...\n"; 62: cout << "Frisky is now " << theCat->GetAge(); 63: cout << " years old \n"; 64: // theCat->SetAge(8); const! 65: return theCat; 66: } Output: Making a cat... Simple Cat constructor... Frisky is 1 years old Frisky is 5 years old Calling FunctionTwo... FunctionTwo. Returning... Frisky is now 5 years old Frisky is 5 years old Simple Cat Destructor... Anaylsis: SimpleCat has added two accessor functions, GetAge() on line 13, which is a const function, and SetAge() on line 14, which is not a const function. It has also added the member variable itsAge on line 17. The constructor, copy constructor, and destructor are still defined to print their messages. The copy constructor is never called, however, because the object is passed by reference and so no copies are made. On line 41, an object is created, and its default age is printed, starting on line 42. On line 46, itsAge is set using the accessor SetAge, and the result is printed on line 47. FunctionOne is not used in this program, but FunctionTwo() is called. FunctionTwo() has changed slightly; the parameter and return value are now declared, on line 36, to take a constant pointer to a constant object and to return a constant pointer to a constant object. Because the parameter and return value are still passed by reference, no copies are made and the copy constructor is not called. The pointer in FunctionTwo(), however, is now constant, and thus cannot call the non-const method, SetAge(). If the call to SetAge() on line 64 was not commented out, the program would not compile. Note that the object created in main() is not constant, and Frisky can call SetAge(). The address of this non-constant object is passed to FunctionTwo(), but because FunctionTwo()'s declaration declares the pointer to be a constant pointer, the object is treated as if it were constant!References as an Alternative Listing 9.11 solves the problem of making extra copies, and thus saves the calls to the copy constructor and destructor. It uses constant pointers to constant objects, and thereby solves the problem of the function changing the object. It is still somewhat cumbersome, however, because the objects passed to the function are pointers. Since you know the object will never be null, it would be easier to work with in the function if a reference were passed in, rather than a pointer. Listing 9.12 illustrates this. Listing 9.12. Passing references to objects. 1: //Listing 9.12 2: // Passing references to objects 3: 4: #include 5: 6: class SimpleCat 7: { 8: public: 9: SimpleCat(); 10: SimpleCat(SimpleCat&); 11: ~SimpleCat(); 12: 13: int GetAge() const { return itsAge; } 14: void SetAge(int age) { itsAge = age; } 15: 16: private: 17: int itsAge; 18: }; 19: 20: SimpleCat::SimpleCat() 21: { 22: cout << "Simple Cat Constructor...\n"; 23: itsAge = 1; 24: } 25: 26: SimpleCat::SimpleCat(SimpleCat&) 27: { 28: cout << "Simple Cat Copy Constructor...\n"; 29: } 30: 31: SimpleCat::~SimpleCat() 32: { 33: cout << "Simple Cat Destructor...\n";34: } 35: 36: const SimpleCat & FunctionTwo (const SimpleCat & theCat); 37: 38: int main() 39: { 40: cout << "Making a cat...\n"; 41: SimpleCat Frisky; 42: cout << "Frisky is " << Frisky.GetAge() << " years old\n"; 43: int age = 5; 44: Frisky.SetAge(age); 45: cout << "Frisky is " << Frisky.GetAge() << " years old\n"; 46: cout << "Calling FunctionTwo...\n"; 47: FunctionTwo(Frisky); 48: cout << "Frisky is " << Frisky.GetAge() << " years old\n"; 49: return 0; 50: } 51: 52: // functionTwo, passes a ref to a const object 53: const SimpleCat & FunctionTwo (const SimpleCat & theCat) 54: { 55: cout << "Function Two. Returning...\n"; 56: cout << "Frisky is now " << theCat.GetAge(); 57: cout << " years old \n"; 58: // theCat.SetAge(8); const! 59: return theCat; 60: } Output: Making a cat... Simple Cat constructor... Frisky is 1 years old Frisky is 5 years old Calling FunctionTwo... FunctionTwo. Returning... Frisky is now 5 years old Frisky is 5 years old Simple Cat Destructor... Analysis: The output is identical to that produced by Listing 9.11. The only significant change is that FunctionTwo() now takes and returns a reference to a constant object. Once again, working with references is somewhat simpler than working with pointers, and the same savings and efficiencyare achieved, as well as the safety provided by using const. const References C++ programmers do not usually differentiate between "constant reference to a SimpleCat object" and "reference to a constant SimpleCat object." References themselves can never be reassigned to refer to another object, and so are always constant. If the keyword const is applied to a reference, it is to make the object referred to constant. When to Use References and When to Use Pointers C++ programmers strongly prefer references to pointers. References are cleaner and easier to use, and they do a better job of hiding information, as we saw in the previous example. References cannot be reassigned, however. If you need to point first to one object and then another, you must use a pointer. References cannot be null, so if there is any chance that the object in question may be null, you must not use a reference. You must use a pointer. An example of the latter concern is the operator new. If new cannot allocate memory on the free store, it returns a null pointer. Since a reference can't be null, you must not initialize a reference to this memory until you've checked that it is not null. The following example shows how to handle this: int *pInt = new int; if (pInt != NULL) int &rInt = *pInt; In this example a pointer to int, pInt, is declared and initialized with the memory returned by the operator new. The address in pInt is tested, and if it is not null, pInt is dereferenced. The result of dereferencing an int variable is an int object, and rInt is initialized to refer to that object. Thus, rInt becomes an alias to the int returned by the operator new. DO pass parameters by reference whenever possible. DO return by reference whenever possible. DON'T use pointers if references will work. DO use const to protect references and pointers whenever possible. DON'T return a reference to a local object. Mixing References and Pointers It is perfectly legal to declare both pointers and references in the same function parameter list, along with objects passed by value. Here's an example: CAT * SomeFunction (Person &theOwner, House *theHouse, int age);This declaration says that SomeFunction takes three parameters. The first is a reference to a Person object, the second is a pointer to a house object, and the third is an integer. It returns a pointer to a CAT object. NOTE: The question of where to put the reference (&) or indirection (*) operator when declaring these variables is a great controversy. You may legally write any of the following: 1: 2: 3: CAT& rFrisky; CAT & rFrisky; CAT &rFrisky; White space is completely ignored, so anywhere you see a space here you may put as many spaces, tabs, and new lines as you like. Setting aside freedom of expression issues, which is best? Here are the arguments for all three: The argument for case 1 is that rFrisky is a variable whose name is rFrisky and whose type can be thought of as "reference to CAT object." Thus, this argument goes, the & should be with the type. The counterargument is that the type is CAT. The & is part of the "declarator," which includes the variable name and the ampersand. More important, having the & near the CAT can lead to the following bug: CAT& rFrisky, rBoots; Casual examination of this line would lead you to think that both rFrisky and rBoots are references to CAT objects, but you'd be wrong. This really says that rFrisky is a reference to a CAT, and rBoots (despite its name) is not a reference but a plain old CAT variable. This should be rewritten as follows: CAT &rFrisky, rBoots; The answer to this objection is that declarations of references and variables should never be combined like this. Here's the right answer: CAT& rFrisky; CAT boots; Finally, many programmers opt out of the argument and go with the middle position, that of putting the & in the middle of the two, as illustrated in case 2. Of course, everything said so far about the reference operator (&) applies equally well to the indirection operator (*). The important thing is to recognize that reasonable people differ in their perceptions of the one true way. Choose a style that works for you, and be consistent within any one program; clarity is, and remains, the goal. This book will adopt two conventions when declaring references and pointers: 1. Put the ampersand and asterisk in the middle, with a space on either side.2. Never declare references, pointers, and variables all on the same line. Dont Return a Reference to an Object that Isnt in Scope! Once C++ programmers learn to pass by reference, they have a tendency to go hog-wild. It is possible, however, to overdo it. Remember that a reference is always an alias to some other object. If you pass a reference into or out of a function, be sure to ask yourself, "What is the object I'm aliasing, and will it still exist every time it's used?" Listing 9.13 illustrates the danger of returning a reference to an object that no longer exists. Listing 9.13. Returning a reference to a non-existent object. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: // Listing 9.13 // Returning a reference to an object // which no longer exists #include class SimpleCat { public: SimpleCat (int age, int weight); ~SimpleCat() {} int GetAge() { return itsAge; } int GetWeight() { return itsWeight; } private: int itsAge; int itsWeight; }; SimpleCat::SimpleCat(int age, int weight): itsAge(age), itsWeight(weight) {} SimpleCat &TheFunction(); int main() { SimpleCat &rCat = TheFunction(); int age = rCat.GetAge(); cout << "rCat is " << age << " years old!\n"; return 0; }32: SimpleCat &TheFunction() 33: { 34: SimpleCat Frisky(5,9); 35: return Frisky; 36: } Output: Compile error: Attempting to return a reference to a local object! WARNING: This program won't compile on the Borland compiler. It will compile on Microsoft compilers; however, it should be noted that it is a bad coding practice. Anaylsis: On lines 7-17, SimpleCat is declared. On line 26, a reference to a SimpleCat is initialized with the results of calling TheFunction(), which is declared on line 22 to return a reference to a SimpleCat. The body of TheFunction() declares a local object of type SimpleCat and initializes its age and weight. It then returns that local object by reference. Some compilers are smart enough to catch this error and won't let you run the program. Others will let you run the program, with unpredictable results. When TheFunction() returns, the local object, Frisky, will be destroyed (painlessly, I assure you). The reference returned by this function will be an alias to a non-existent object, and this is a bad thing. Returning a Reference to an Object on the Heap You might be tempted to solve the problem in Listing 9.13 by having TheFunction() create Frisky on the heap. That way, when you return from TheFunction(), Frisky will still exist. The problem with this approach is: What do you do with the memory allocated for Frisky when you are done with it? Listing 9.14 illustrates this problem. Listing 9.14. Memory leaks. 1: 2: 3: 4: 5: 6: 7: 8: 9: // Listing 9.14 // Resolving memory leaks #include class SimpleCat { public: SimpleCat (int age, int weight); ~SimpleCat() {}10: 11: 12: 13 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: } int GetAge() { return itsAge; } int GetWeight() { return itsWeight; } private: int itsAge; int itsWeight; }; SimpleCat::SimpleCat(int age, int weight): itsAge(age), itsWeight(weight) {} SimpleCat & TheFunction(); int main() { SimpleCat & rCat = TheFunction(); int age = rCat.GetAge(); cout << "rCat is " << age << " years old!\n"; cout << "&rCat: " << &rCat << endl; // How do you get rid of that memory? SimpleCat * pCat = &rCat; delete pCat; // Uh oh, rCat now refers to ?? return 0; } SimpleCat &TheFunction() { SimpleCat * pFrisky = new SimpleCat(5,9); cout << "pFrisky: " << pFrisky << endl; return *pFrisky; Output: pFrisky: 0x2bf4 rCat is 5 years old! &rCat: 0x2bf4 WARNING: This compiles, links, and appears to work. But it is a time bomb waiting to go off. Anaylss: TheFunction() has been changed so that it no longer returns a reference to a local variable. Memory is allocated on the free store and assigned to a pointer on line 38. The address that pointer holds is printed, and then the pointer is dereferenced and the SimpleCat object is returned by reference.On line 25, the return of TheFunction() is assigned to a reference to SimpleCat, and that object is used to obtain the cat's age, which is printed on line 27. To prove that the reference declared in main() is referring to the object put on the free store in TheFunction(), the address of operator is applied to rCat. Sure enough, it displays the address of the object it refers to and this matches the address of the object on the free store. So far, so good. But how will that memory be freed? You can't call delete on the reference. One clever solution is to create another pointer and initialize it with the address obtained from rCat. This does delete the memory, and plugs the memory leak. One small problem, though: What is rCat referring to after line 31? As stated earlier, a reference must always alias an actual object; if it references a null object (as this does now), the program is invalid. NOTE: It cannot be overemphasized that a program with a reference to a null object may compile, but it is invalid and its performance is unpredictable. There are actually three solutions to this problem. The first is to declare a SimpleCat object on line 25, and to return that cat from TheFunction by value. The second is to go ahead and declare the SimpleCat on the free store in TheFunction(), but have TheFunction() return a pointer to that memory. Then the calling function can delete the pointer when it is done. The third workable solution, and the right one, is to declare the object in the calling function and then to pass it to TheFunction() by reference. Pointer, Pointer, Who Has the Pointer? When your program allocates memory on the free store, a pointer is returned. It is imperative that you keep a pointer to that memory, because once the pointer is lost, the memory cannot be deleted and becomes a memory leak. As you pass this block of memory between functions, someone will "own" the pointer. Typically the value in the block will be passed using references, and the function that created the memory is the one that deletes it. But this is a general rule, not an ironclad one. It is dangerous for one function to create memory and another to free it, however. Ambiguity about who owns the pointer can lead to one of two problems: forgetting to delete a pointer or deleting it twice. Either one can cause serious problems in your program. It is safer to build your functions so that they delete the memory they create. If you are writing a function that needs to create memory and then pass it back to the calling function, consider changing your interface. Have the calling function allocate the memory and then pass it into your function by reference. This moves all memory management out of your program and back to the function that is prepared to delete it.DO pass parameters by value when you must. DO return by value when you must. DON'T pass by reference if the item referred to may go out of scope. DON'T use references to null objects. Summary Today you learned what references are and how they compare to pointers. You saw that references must be initialized to refer to an existing object, and cannot be reassigned to refer to anything else. Any action taken on a reference is in fact taken on the reference's target object. Proof of this is that taking the address of a reference returns the address of the target. You saw that passing objects by reference can be more efficient than passing by value. Passing by reference also allows the called function to change the value in the arguments back in the calling function. You saw that arguments to functions and values returned from functions can be passed by reference, and that this can be implemented with pointers or with references. You saw how to use const pointers and const references to safely pass values between functions while achieving the efficiency of passing by reference. Q&A Q. Why have references if pointers can do everything references can? A. References are easier to use and understand. The indirection is hidden, and there is no need to repeatedly dereference the variable. Q. Why have pointers if references are easier? A. References cannot be null, and they cannot be reassigned. Pointers offer greater flexibility, but are slightly more difficult to use. Q. Why would you ever return by value from a function? A. If the object being returned is local, you must return by value or you will be returning a reference to a non-existent object. Q. Given the danger in returning by reference, why not always return by value? A. There is far greater efficiency in returning by reference. Memory is saved and the program runs faster.Workshop The Workshop contains quiz questions to help solidify your understanding of the material covered and exercises to provide you with experience in using what you've learned. Try to answer the quiz and exercise questions before checking the answers in Appendix D, and make sure you understand the answers before going to the next chapter. Quiz 1. What is the difference between a reference and a pointer? 2. When must you use a pointer rather than a reference? 3. What does new return if there is insufficient memory to make your new object? 4. What is a constant reference? 5. What is the difference between passing by reference and passing a reference? Exercises 1. Write a program that declares an int, a reference to an int, and a pointer to an int. Use the pointer and the reference to manipulate the value in the int. 2. Write a program that declares a constant pointer to a constant integer. Initialize the pointer to an integer variable, varOne. Assign 6 to varOne. Use the pointer to assign 7 to varOne. Create a second integer variable, varTwo. Reassign the pointer to varTwo. 3. Compile the program in Exercise 2. What produces errors? What produces warnings? 4. Write a program that produces a stray pointer. 5. Fix the program from Exercise 4. 6. Write a program that produces a memory leak. 7. Fix the program from Exercise 6. 8. BUG BUSTERS: What is wrong with this program? 1: 2: 3: 4: 5: 6: 7: #include class CAT { public: CAT(int age) { itsAge = age; } ~CAT(){}8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: int GetAge() const { return itsAge;} private: int itsAge; }; CAT & MakeCat(int age); int main() { int age = 7; CAT Boots = MakeCat(age); cout << "Boots is " << Boots.GetAge() << " years old\n"; } CAT & MakeCat(int age) { CAT * pCat = new CAT(age); return *pCat; } Overloaded Member Functions On Day 5, you learned how to implement function polymorphism, or function overloading, by writing two or more functions with the same name but with different parameters. Class member functions can be overloaded as well, in much the same way. The Rectangle class, demonstrated in Listing 10.1, has two DrawShape() functions. One, which takes no parameters, draws the Rectangle based on the class's current values. The other takes two values, width and length, and draws the rectangle based on those values, ignoring the current class values.Listing 10.1. Overloading member functions. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: //Listing 10.1 Overloading class member functions #include typedef unsigned short int USHORT; enum BOOL { FALSE, TRUE}; // Rectangle class declaration class Rectangle { public: // constructors Rectangle(USHORT width, USHORT height); ~Rectangle(){} // overloaded class function DrawShape void DrawShape() const; void DrawShape(USHORT aWidth, USHORT aHeight) const; private: USHORT itsWidth; USHORT itsHeight; }; //Constructor implementation Rectangle::Rectangle(USHORT width, USHORT height) { itsWidth = width; itsHeight = height; } // Overloaded DrawShape - takes no values // Draws based on current class member values void Rectangle::DrawShape() const { DrawShape( itsWidth, itsHeight); } // overloaded DrawShape - takes two values // draws shape based on the parameters void Rectangle::DrawShape(USHORT width, USHORT height) const { for (USHORT i = 0; i 3: 4: typedef unsigned short int USHORT; 5: enum BOOL { FALSE, TRUE}; 6: 7: // Rectangle class declaration 8: class Rectangle 9: { 10: public: 11: // constructors 12: Rectangle(USHORT width, USHORT height); 13: ~Rectangle(){} 14: void DrawShape(USHORT aWidth, USHORT aHeight, BOOL UseCurrentVals = %! FALSE) const; 15: 16: private: 17: USHORT itsWidth; 18: USHORT itsHeight; 19: }; 20: 21: //Constructor implementation 22: Rectangle::Rectangle(USHORT width, USHORT height): 23: itsWidth(width), // initializations 24: itsHeight(height) 25: {} // empty body 26: 27: 28: // default values used for third parameter29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: values 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: } void Rectangle::DrawShape( USHORT width, USHORT height, BOOL UseCurrentValue ) const { int printWidth; int printHeight; if (UseCurrentValue == TRUE) { printWidth = itsWidth; // use current class printHeight = itsHeight; } else { printWidth = width; printHeight = height; } // use parameter values for (int i = 0; i class Rectangle { public: Rectangle(); Rectangle(int width, int length); ~Rectangle() {} int GetWidth() const { return itsWidth; } int GetLength() const { return itsLength; }14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: endl; 36: 37: 38: 39: 40: 41: 42: 43: 44: endl; 45: endl; 46: 47: } private: int itsWidth; int itsLength; }; Rectangle::Rectangle() { itsWidth = 5; itsLength = 10; } Rectangle::Rectangle (int width, int length) { itsWidth = width; itsLength = length; } int main() { Rectangle Rect1; cout << "Rect1 width: " << Rect1.GetWidth() << endl; cout << "Rect1 length: " << Rect1.GetLength() << int aWidth, aLength; cout << "Enter a width: "; cin >> aWidth; cout << "\nEnter a length: "; cin >> aLength; Rectangle Rect2(aWidth, aLength); cout << "\nRect2 width: " << Rect2.GetWidth() << cout << "Rect2 length: " << Rect2.GetLength() << return 0; Output: Rect1 width: 5 Rect1 length: 10 Enter a width: 20 Enter a length: 50 Rect2 width: 20 Rect2 length: 50Analysis: The Rectangle class is declared on lines 6-17. Two constructors are declared: the "default constructor" on line 9 and a constructor taking two integer variables. On line 33, a rectangle is created using the default constructor, and its values are printed on lines 34- 35. On lines 37-41, the user is prompted for a width and length, and the constructor taking two parameters is called on line 43. Finally, the width and height for this rectangle are printed on lines 44- 45. Just as it does any overloaded function, the compiler chooses the right constructor, based on the number and type of the parameters. Initializing Objects Up to now, you've been setting the member variables of objects in the body of the constructor. Constructors, however, are invoked in two stages: the initialization stage and the body. Most variables can be set in either stage, either by initializing in the initialization stage or by assigning in the body of the constructor. It is cleaner, and often more efficient, to initialize member variables at the initialization stage. The following example shows how to initialize member variables: CAT(): itsAge(5), itsWeight(8) { } // constructor name and parameters // initialization list // body of constructor After the closing parentheses on the constructor's parameter list, write a colon. Then write the name of the member variable and a pair of parentheses. Inside the parentheses, write the expression to be used to initialize that member variable. If there is more than one initialization, separate each one with a comma. Listing 10.4 shows the definition of the constructors from Listing 10.3, with initialization of the member variables rather than assignment. Listing 10.4. A code snippet showing initialization of member variables. 1: Rectangle::Rectangle(): 2: itsWidth(5), 3: itsLength(10) 4: { 5: }; 6: 7: Rectangle::Rectangle (int width, int length) 8: itsWidth(width), 9: itsLength(length) 10: 11: };Output: No output. There are some variables that must be initialized and cannot be assigned to: references and constants. It is common to have other assignments or action statements in the body of the constructor; however, it is best to use initialization as much as possible. The Copy Constructor In addition to providing a default constructor and destructor, the compiler provides a default copy constructor. The copy constructor is called every time a copy of an object is made. When you pass an object by value, either into a function or as a function's return value, a temporary copy of that object is made. If the object is a user-defined object, the class's copy constructor is called, as you saw yesterday in Listing 9.6. All copy constructors take one parameter, a reference to an object of the same class. It is a good idea to make it a constant reference, because the constructor will not have to alter the object passed in. For example: CAT(const CAT & theCat); Here the CAT constructor takes a constant reference to an existing CAT object. The goal of the copy constructor is to make a copy of theCAT. The default copy constructor simply copies each member variable from the object passed as a parameter to the member variables of the new object. This is called a member-wise (or shallow) copy, and although this is fine for most member variables, it breaks pretty quickly for member variables that are pointers to objects on the free store. New Term: A shallow or member-wise copy copies the exact values of one object's member variables into another object. Pointers in both objects end up pointing to the same memory. A deep copy copies the values allocated on the heap to newly allocated memory. If the CAT class includes a member variable, itsAge, that points to an integer on the free store, the default copy constructor will copy the passed-in CAT's itsAge member variable to the new CAT's itsAge member variable. The two objects will now point to the same memory, as illustrated in Figure 10.1. Figure 10.1.Using the default copy constructor. This will lead to a disaster when either CAT goes out of scope. As mentioned on Day 8, "Pointers," thejob of the destructor is to clean up this memory. If the original CAT's destructor frees this memory and the new CAT is still pointing to the memory, a stray pointer has been created, and the program is in mortal danger. Figure 10.2 illustrates this problem. Figure 10.2. Creating a stray pointer. The solution to this is to create your own copy constructor and to allocate the memory as required. Once the memory is allocated, the old values can be copied into the new memory. This is called a deep copy. Listing 10.5 illustrates how to do this. Listing 10.5. Copy constructors. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: // Listing 10.5 // Copy constructors #include class CAT { public: CAT(); // default constructor CAT (const CAT &); // copy constructor ~CAT(); // destructor int GetAge() const { return *itsAge; } int GetWeight() const { return *itsWeight; } void SetAge(int age) { *itsAge = age; } private: int *itsAge; int *itsWeight; }; CAT::CAT() { itsAge = new int; itsWeight = new int; *itsAge = 5; *itsWeight = 9; } CAT::CAT(const CAT & rhs) { itsAge = new int; itsWeight = new int; *itsAge = rhs.GetAge(); *itsWeight = rhs.GetWeight();35: } 36: 37: CAT::~CAT() 38: { 39: delete itsAge; 40: itsAge = 0; 41: delete itsWeight; 42: itsWeight = 0; 43: } 44: 45: int main() 46: { 47: CAT frisky; 48: cout << "frisky's age: " << frisky.GetAge() << endl; 49: cout << "Setting frisky to 6...\n"; 50: frisky.SetAge(6); 51: cout << "Creating boots from frisky\n"; 52: CAT boots(frisky); 53: cout << "frisky's age: " << frisky.GetAge() << endl; 54: cout << "boots' age: " << boots.GetAge() << endl; 55: cout << "setting frisky to 7...\n"; 56: frisky.SetAge(7); 57: cout << "frisky's age: " << frisky.GetAge() << endl; 58: cout << "boot's age: " << boots.GetAge() << endl; 59: return 0; 60: } Output: frisky's age: 5 Setting frisky to 6... Creating boots from frisky frisky's age: 6 boots' age: 6 setting frisky to 7... frisky's age: 7 boots' age: 6 Analysis: On lines 6-19, the CAT class is declared. Note that on line 9 a default constructor is declared, and on line 10 a copy constructor is declared. On lines 17 and 18, two member variables are declared, each as a pointer to an integer. Typically there'd be little reason for a class to store int member variables as pointers, but this was done to illustrate how to manage member variables on the free store. The default constructor, on lines 21-27, allocates room on the free store for two int variables and then assigns values to them. The copy constructor begins on line 29. Note that the parameter is rhs. It is common to refer to theparameter to a copy constructor as rhs, which stands for right-hand side. When you look at the assignments in lines 33 and 34, you'll see that the object passed in as a parameter is on the right-hand side of the equals sign. Here's how it works. On lines 31 and 32, memory is allocated on the free store. Then, on lines 33 and 34, the value at the new memory location is assigned the values from the existing CAT. The parameter rhs is a CAT that is passed into the copy constructor as a constant reference. The member function rhs.GetAge() returns the value stored in the memory pointed to by rhs's member variable itsAge. As a CAT object, rhs has all the member variables of any other CAT. When the copy constructor is called to create a new CAT, an existing CAT is passed in as a parameter. The new CAT can refer to its own member variables directly; however, it must access rhs's member variables using the public accessor methods. Figure 10.3 diagrams what is happening here. The values pointed to by the existing CAT are copied to the memory allocated for the new CAT Figure 10.3. Deep copy illustrated. On line 47, a CAT called frisky is created. frisky's age is printed, and then his age is set to 6 on line 50. On line 52, a new CAT boots is created, using the copy constructor and passing in frisky. Had frisky been passed as a parameter to a function, this same call to the copy constructor would have been made by the compiler. On lines 53 and 54, the ages of both CATs are printed. Sure enough, boots has frisky's age, 6, not the default age of 5. On line 56, frisky's age is set to 7, and then the ages are printed again. This time frisky's age is 7, but boots' age is still 6, demonstrating that they are stored in separate areas of memory. When the CATs fall out of scope, their destructors are automatically invoked. The implementation of the CAT destructor is shown on lines 37-43. delete is called on both pointers, itsAge and itsWeight, returning the allocated memory to the free store. Also, for safety, the pointers are reassigned to NULL. Operator Overloading C++ has a number of built-in types, including int, real, char, and so forth. Each of these has a number of built-in operators, such as addition (+) and multiplication (*). C++ enables you to add these operators to your own classes as well. In order to explore operator overloading fully, Listing 10.6 creates a new class, Counter. A Counter object will be used in counting (surprise!) in loops and other applications where a number must be incremented, decremented, or otherwise tracked.Listing 10.6. The Counter class. 1: // Listing 10.6 2: // The Counter class 3: 4: typedef unsigned short USHORT; 5: #include 6: 7: class Counter 8: { 9: public: 10: Counter(); 11: ~Counter(){} 12: USHORT GetItsVal()const { return itsVal; } 13: void SetItsVal(USHORT x) {itsVal = x; } 14: 15: private: 16: USHORT itsVal; 17: 18: }; 19: 20: Counter::Counter(): 21: itsVal(0) 22: {}; 23: 24: int main() 25: { 26: Counter i; 27: cout << "The value of i is " << i.GetItsVal() << endl; 28: return 0; 29: } Output: The value of i is 0 Analysis: As it stands, this is a pretty useless class. It is defined on lines 7-18. Its only member variable is a USHORT. The default constructor, which is declared on line 10 and whose implementation is on line 20, initializes the one member variable, itsVal, to zero. Unlike an honest, red-blooded USHORT, the Counter object cannot be incremented, decremented, added, assigned, or otherwise manipulated. In exchange for this, it makes printing its value far more difficult! Writing an Increment Function Operator overloading restores much of the functionality that has been stripped out of this class. For example, there are two ways to add the ability to increment a Counter object. The first is to write anincrement method, as shown in Listing 10.7. Listing 10.7. Adding an increment operator. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: } // Listing 10.7 // The Counter class typedef unsigned short #include USHORT; class Counter { public: Counter(); ~Counter(){} USHORT GetItsVal()const { return itsVal; } void SetItsVal(USHORT x) {itsVal = x; } void Increment() { ++itsVal; } private: USHORT itsVal; }; Counter::Counter(): itsVal(0) {}; int main() { Counter i; cout << "The value of i is " << i.GetItsVal() << endl; i.Increment(); cout << "The value of i is " << i.GetItsVal() << endl; return 0; Output: The value of i is 0 The value of i is 1 Analysis: Listing 10.7 adds an Increment function, defined on line 14. Although this works, it is cumbersome to use. The program cries out for the ability to add a ++ operator, and of course this can be done. Overloading the Prefix OperatorPrefix operators can be overloaded by declaring functions with the form: returnType Operator op (parameters) Here, op is the operator to overload. Thus, the ++ operator can be overloaded with the following syntax: void operator++ () Listing 10.8 demonstrates this alternative. Listing 10.8. Overloading operator++. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: // Listing 10.8 // The Counter class typedef unsigned short #include USHORT; class Counter { public: Counter(); ~Counter(){} USHORT GetItsVal()const { return itsVal; } void SetItsVal(USHORT x) {itsVal = x; } void Increment() { ++itsVal; } void operator++ () { ++itsVal; } private: USHORT itsVal; }; Counter::Counter(): itsVal(0) {}; int main() { Counter i; cout << "The value of i is " << i.GetItsVal() << endl; i.Increment(); cout << "The value of i is " << i.GetItsVal() << endl;32: ++i; 33: cout << 34: return 0; 35: } Output: The value The value of i is The value of i is "The value of i is " << i.GetItsVal() << endl; of i is 0 1 2 Analysis: On line 15, operator++ is overloaded, and it's used on line 32. This is far closer to the syntax one would expect with the Counter object. At this point, you might consider putting in the extra abilities for which Counter was created in the first place, such as detecting when the Counter overruns its maximum size. There is a significant defect in the way the increment operator was written, however. If you want to put the Counter on the right side of an assignment, it will fail. For example: Counter a = ++i; This code intends to create a new Counter, a, and then assign to it the value in i after i is incremented. The built-in copy constructor will handle the assignment, but the current increment operator does not return a Counter object. It returns void. You can't assign a void object to a Counter object. (You can't make something from nothing!) Returning Types in Overloaded Operator Functions Clearly, what you want is to return a Counter object so that it can be assigned to another Counter object. Which object should be returned? One approach would be to create a temporary object and return that. Listing 10.9 illustrates this approach. Listing 10.9. Returning a temporary object. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: // Listing 10.9 // operator++ returns a temporary object typedef unsigned short #include USHORT; class Counter { public: Counter(); ~Counter(){} USHORT GetItsVal()const { return itsVal; } void SetItsVal(USHORT x) {itsVal = x; } void Increment() { ++itsVal; } Counter operator++ ();16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: } private: USHORT itsVal; }; Counter::Counter(): itsVal(0) {}; Counter Counter::operator++() { ++itsVal; Counter temp; temp.SetItsVal(itsVal); return temp; } int main() { Counter i; cout << "The value i.Increment(); cout << "The value ++i; cout << "The value Counter a = ++i; cout << "The value cout << " and i: " return 0; Output: The value The value of i is The value of i is The value of a: 3 of i is " << i.GetItsVal() << endl; of i is " << i.GetItsVal() << endl; of i is " << i.GetItsVal() << endl; of a: " << a.GetItsVal(); << i.GetItsVal() << endl; of i is 0 1 2 and i: 3 Analysis: In this version, operator++ has been declared on line 15 to return a Counter object. On line 29, a temporary variable, temp, is created and its value is set to match that in the current object. That temporary variable is returned and immediately assigned to a on line 42. Returning Nameless Temporaries There is really no need to name the temporary object created on line 29. If Counter had a constructor that took a value, you could simply return the result of that constructor as the return value of the increment operator. Listing 10.10 illustrates this idea.Listing 10.10. Returning a nameless temporary object. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: // Listing 10.10 // operator++ returns a nameless temporary object typedef unsigned short #include USHORT; class Counter { public: Counter(); Counter(USHORT val); ~Counter(){} USHORT GetItsVal()const { return itsVal; } void SetItsVal(USHORT x) {itsVal = x; } void Increment() { ++itsVal; } Counter operator++ (); private: USHORT itsVal; }; Counter::Counter(): itsVal(0) {} Counter::Counter(USHORT val): itsVal(val) {} Counter Counter::operator++() { ++itsVal; return Counter (itsVal); } int main() { Counter i; cout << "The value of i is " << i.GetItsVal() << endl; i.Increment(); cout << "The value of i is " << i.GetItsVal() << endl; ++i;44: 45: 46: 47: 48: 49: } cout << Counter cout << cout << return 0; Output: The value The value of i is The value of i is The value of a: 3 "The value of i is " << i.GetItsVal() << endl; a = ++i; "The value of a: " << a.GetItsVal(); " and i: " << i.GetItsVal() << endl; of i is 0 1 2 and i: 3 Analysis: On line 11, a new constructor is declared that takes a USHORT. The implementation is on lines 27-29. It initializes itsVal with the passed-in value. The implementation of operator++ is now simplified. On line 33, itsVal is incremented. Then on line 34, a temporary Counter object is created, initialized to the value in itsVal, and then returned as the result of the operator++. This is more elegant, but begs the question, "Why create a temporary object at all?" Remember that each temporary object must be constructed and later destroyed--each one potentially an expensive operation. Also, the object i already exists and already has the right value, so why not return it? We'll solve this problem by using the this pointer. Using the this Pointer The this pointer, as discussed yesterday, was passed to the operator++ member function as to all member functions. The this pointer points to i, and if it's dereferenced it will return the object i, which already has the right value in its member variable itsVal. Listing 10.11 illustrates returning the dereferenced this pointer and avoiding the creation of an unneeded temporary object. Listing 10.11. Returning the this pointer. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: // Listing 10.11 // Returning the dereferenced this pointer typedef unsigned short #include USHORT; class Counter { public: Counter(); ~Counter(){} USHORT GetItsVal()const { return itsVal; } void SetItsVal(USHORT x) {itsVal = x; }14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 48: 49: } void Increment() { ++itsVal; } const Counter& operator++ (); private: USHORT itsVal; }; Counter::Counter(): itsVal(0) {}; const Counter& Counter::operator++() { ++itsVal; return *this; } int main() { Counter i; cout << "The value i.Increment(); cout << "The value ++i; cout << "The value Counter a = ++i; cout << "The value cout << " and i: " return 0; Output: The value The value of i is The value of i is The value of a: 3 of i is " << i.GetItsVal() << endl; of i is " << i.GetItsVal() << endl; of i is " << i.GetItsVal() << endl; of a: " << a.GetItsVal(); << i.GetItsVal() << endl; of i is 0 1 2 and i: 3 Analysis: The implementation of operator++, on lines 26-30, has been changed to dereference the this pointer and to return the current object. This provides a Counter object to be assigned to a. As discussed above, if the Counter object allocated memory, it would be important to override the copy constructor. In this case, the default copy constructor works fine. Note that the value returned is a Counter reference, thereby avoiding the creation of an extra temporary object. It is a const reference because the value should not be changed by the function using this Counter.Overloading the Postfix Operator So far you've overloaded the prefix operator. What if you want to overload the postfix increment operator? Here the compiler has a problem: How is it to differentiate between prefix and postfix? By convention, an integer variable is supplied as a parameter to the operator declaration. The parameter's value is ignored; it is just a signal that this is the postfix operator. Difference Between Prefix and Postfix Before we can write the postfix operator, we must understand how it is different from the prefix operator. We reviewed this in detail on Day 4, "Expressions and Statements" (see Listing 4.3). To review, prefix says "increment, and then fetch," while postfix says "fetch, and then increment." Thus, while the prefix operator can simply increment the value and then return the object itself, the postfix must return the value that existed before it was incremented. To do this, we must create a temporary object that will hold the original value, then increment the value of the original object, and then return the temporary. Let's go over that again. Consider the following line of code: a = x++; If x was 5, after this statement a is 5, but x is 6. Thus, we returned the value in x and assigned it to a, and then we increased the value of x. If x is an object, its postfix increment operator must stash away the original value (5) in a temporary object, increment x's value to 6, and then return that temporary to assign its value to a. Note that since we are returning the temporary, we must return it by value and not by reference, as the temporary will go out of scope as soon as the function returns. Listing 10.12 demonstrates the use of both the prefix and the postfix operators. Listing 10.12. Prefix and postfix operators. 1: 2: 3: 4: 5: 6: 7: 8: 9: // Listing 10.12 // Returning the dereferenced this pointer typedef unsigned short #include class Counter { public: USHORT;10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: } Counter(); ~Counter(){} USHORT GetItsVal()const { return itsVal; } void SetItsVal(USHORT x) {itsVal = x; } const Counter& operator++ (); // prefix const Counter operator++ (int); // postfix private: USHORT itsVal; }; Counter::Counter(): itsVal(0) {} const Counter& Counter::operator++() { ++itsVal; return *this; } const Counter Counter::operator++(int) { Counter temp(*this); ++itsVal; return temp; } int main() { Counter i; cout << "The value i++; cout << "The value ++i; cout << "The value Counter a = ++i; cout << "The value cout << " and i: " a = i++; cout << "The value cout << " and i: " return 0; Output: The value of i is 0 of i is " << i.GetItsVal() << endl; of i is " << i.GetItsVal() << endl; of i is " << i.GetItsVal() << endl; of a: " << a.GetItsVal(); << i.GetItsVal() << endl; of a: " << a.GetItsVal(); << i.GetItsVal() << endl;The The The The value value value value of of of of i is i is a: 3 a: 3 1 2 and i: 3 and i: 4 Analysis: The postfix operator is declared on line 15 and implemented on lines 31-36. Note that the call to the prefix operator on line 14 does not include the flag integer (x), but is used with its normal syntax. The postfix operator uses a flag value (x) to signal that it is the postfix and not the prefix. The flag value (x) is never used, however. Operator Overloading Unary Operators Declare an overloaded operator as you would a function. Use the keyword operator, followed by the operator to overload. Unary operator functions do not take parameters, with the exception of the postfix increment and decrement, which take an integer as a flag. Example 1 const Counter& Counter::operator++ (); Example 2 Counter Counter::operator-(int); DO use a parameter to operator++ if you want the postfix operator. DO return a const reference to the object from operator++. DON'T create temporary objects as return values from operator++. The Addition Operator The increment operator is a unary operator. It operates on only one object. The addition operator (+) is a binary operator, where two objects are involved. How do you implement overloading the + operator for Count? The goal is to be able to declare two Counter variables and then add them, as in this example: Counter varOne, varTwo, varThree; VarThree = VarOne + VarTwo; Once again, you could start by writing a function, Add(), which would take a Counter as its argument, add the values, and then return a Counter with the result. Listing 10.13 illustrates this approach. Listing 10.13. The Add() function.1: // Listing 10.13 2: // Add function 3: 4: typedef unsigned short USHORT; 5: #include 6: 7: class Counter 8: { 9: public: 10: Counter(); 11: Counter(USHORT initialValue); 12: ~Counter(){} 13: USHORT GetItsVal()const { return itsVal; } 14: void SetItsVal(USHORT x) {itsVal = x; } 15: Counter Add(const Counter &); 16: 17: private: 18: USHORT itsVal; 19: 20: }; 21: 22: Counter::Counter(USHORT initialValue): 23: itsVal(initialValue) 24: {} 25: 26: Counter::Counter(): 27: itsVal(0) 28: {} 29: 30: Counter Counter::Add(const Counter & rhs) 31: { 32: return Counter(itsVal+ rhs.GetItsVal()); 33: } 34: 35: int main() 36: { 37: Counter varOne(2), varTwo(4), varThree; 38: varThree = varOne.Add(varTwo); 39: cout << "varOne: " << varOne.GetItsVal()<< endl; 40: cout << "varTwo: " << varTwo.GetItsVal() << endl; 41: cout << "varThree: " << varThree.GetItsVal() << endl; 42: 43: return 0; 44: } Output: varOne: 2varTwo: 4 varThree: 6 Analysis: The Add()function is declared on line 15. It takes a constant Counter reference, which is the number to add to the current object. It returns a Counter object, which is the result to be assigned to the left side of the assignment statement, as shown on line 38. That is, VarOne is the object, varTwo is the parameter to the Add() function, and the result is assigned to VarThree. In order to create varThree without having to initialize a value for it, a default constructor is required. The default constructor initializes itsVal to 0, as shown on lines 26-28. Since varOne and varTwo need to be initialized to a non-zero value, another constructor was created, as shown on lines 22-24. Another solution to this problem is to provide the default value 0 to the constructor declared on line 11. Overloading operator+ The Add() function itself is shown on lines 30-33. It works, but its use is unnatural. Overloading the + operator would make for a more natural use of the Counter class. Listing 10.14 illustrates this. Listing 10.14. operator+. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: // Listing 10.14 //Overload operator plus (+) typedef unsigned short #include USHORT; class Counter { public: Counter(); Counter(USHORT initialValue); ~Counter(){} USHORT GetItsVal()const { return itsVal; } void SetItsVal(USHORT x) {itsVal = x; } Counter operator+ (const Counter &); private: USHORT itsVal; }; Counter::Counter(USHORT initialValue): itsVal(initialValue) {}24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: } Counter::Counter(): itsVal(0) {} Counter Counter::operator+ (const Counter & rhs) { return Counter(itsVal + rhs.GetItsVal()); } int main() { Counter varOne(2), varTwo(4), varThree; varThree = varOne + varTwo; cout << "varOne: " << varOne.GetItsVal()<< endl; cout << "varTwo: " << varTwo.GetItsVal() << endl; cout << "varThree: " << varThree.GetItsVal() << endl; return 0; Output: varOne: 2 varTwo: 4 varThree: 6 Analysis: operator+ is declared on line 15 and defined on lines 28-31. Compare these with the declaration and definition of the Add() function in the previous listing; they are nearly identical. The syntax of their use, however, is quite different. It is more natural to say this: varThree = varOne + varTwo; than to say: varThree = varOne.Add(varTwo); Not a big change, but enough to make the program easier to use and understand. NOTE: The techniques used for overloading operator++ can be applied to the other unary operators, such as operator-. Operator Overloading: Binary Operators Binary operators are created like unary operators, except that they do take a parameter. The parameter is a constant reference to an object of the same type. Example 1Counter Counter::operator+ (const Counter & rhs); Example 2 Counter Counter::operator-(const Counter & rhs); Issues in Operator Overloading Overloaded operators can be member functions, as described in this chapter, or non-member functions. The latter will be described on Day 14, "Special Classes and Functions," when we discuss friend functions. The only operators that must be class members are the assignment (=), subscript([]), function call (()), and indirection (->) operators. operator[] will be discussed tomorrow, when arrays are covered. Overloading operator-> will be discussed on Day 14, when smart pointers are discussed. Limitations on Operator Overloading Operators on built-in types (such as int) cannot be overloaded. The precedence order cannot be changed, and the arity of the operator, that is, whether it is unary or binary, cannot be changed. You cannot make up new operators, so you cannot declare ** to be the "power of" operator. New Term: Arity refers to how many terms are used in the operator. Some C++ operators are unary and use only one term (myValue++). Some operators are binary and use two terms (a+b). Only one operator is ternary and uses three terms. The ? operator is often called the ternary operator, as it is the only ternary operator in C++ (a > b ? x : y). What to Overload Operator overloading is one of the aspects of C++ most overused and abused by new programmers. It is tempting to create new and interesting uses for some of the more obscure operators, but these invariably lead to code that is confusing and difficult to read. Of course, making the + operator subtract and the * operator add can be fun, but no professional programmer would do that. The greater danger lies in the well-intentioned but idiosyncratic use of an operator--using + to mean concatenate a series of letters, or / to mean split a string. There is good reason to consider these uses, but there is even better reason to proceed with caution. Remember, the goal of overloading operators is to increase usability and understanding.DO use operator overloading when it will clarify the program. DON'T create counter- intuitive operators. DO return an object of the class from overloaded operators. The Assignment Operator The fourth and final function that is supplied by the compiler, if you don't specify one, is the assignment operator (operator=()). This operator is called whenever you assign to an object. For example: CAT catOne(5,7); CAT catTwo(3,4); // ... other code here catTwo = catOne; Here, catOne is created and initialized with itsAge equal to 5 and itsWeight equal to 7. catTwo is then created and assigned the values 3 and 4. After a while, catTwo is assigned the values in catOne. Two issues are raised here: What happens if itsAge is a pointer, and what happens to the original values in catTwo? Handling member variables that store their values on the free store was discussed earlier during the examination of the copy constructor. The same issues arise here, as you saw illustrated in Figures 10.1 and 10.2. C++ programmers differentiate between a shallow or member-wise copy on the one hand, and a deep copy on the other. A shallow copy just copies the members, and both objects end up pointing to the same area on the free store. A deep copy allocates the necessary memory. This is illustrated in Figure 10.3. There is an added wrinkle with the assignment operator, however. The object catTwo already exists and has memory already allocated. That memory must be deleted if there is to be no memory leak. But what happens if you assign catTwo to itself? catTwo = catTwo; No one is likely to do this on purpose, but the program must be able to handle it. More important, it is possible for this to happen by accident when references and dereferenced pointers hide the fact that the assignment is to itself. If you did not handle this problem carefully, catTwo would delete its memory allocation. Then, when it was ready to copy in the memory from the right-hand side of the assignment, it would have a very big problem: The memory would be gone. To protect against this, your assignment operator must check to see if the right-hand side of theassignment operator is the object itself. It does this by examining the this pointer. Listing 10.15 shows a class with an assignment operator. Listing 10.15. An assignment operator. 1: // Listing 10.15 2: // Copy constructors 3: 4: #include 5: 6: class CAT 7: { 8: public: 9: CAT(); // default constructor 10: // copy constructor and destructor elided! 11: int GetAge() const { return *itsAge; } 12: int GetWeight() const { return *itsWeight; } 13: void SetAge(int age) { *itsAge = age; } 14: CAT operator=(const CAT &); 15: 16: private: 17: int *itsAge; 18: int *itsWeight; 19: }; 20: 21: CAT::CAT() 22: { 23: itsAge = new int; 24: itsWeight = new int; 25: *itsAge = 5; 26: *itsWeight = 9; 27: } 28: 29: 30: CAT CAT::operator=(const CAT & rhs) 31: { 32: if (this == &rhs) 33: return *this; 34: delete itsAge; 35: delete itsWeight; 36: itsAge = new int; 37: itsWeight = new int; 38: *itsAge = rhs.GetAge(); 39: *itsWeight = rhs.GetWeight();40: 41: } 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: endl; 52: 53: 54: endl; 55: 56: } return *this; int main() { CAT frisky; cout << "frisky's age: " << frisky.GetAge() << endl; cout << "Setting frisky to 6...\n"; frisky.SetAge(6); CAT whiskers; cout << "whiskers' age: " << whiskers.GetAge() << cout << "copying frisky to whiskers...\n"; whiskers = frisky; cout << "whiskers' age: " << whiskers.GetAge() << return 0; frisky's age: 5 Setting frisky to 6... whiskers' age: 5 copying frisky to whiskers... whiskers' age: 6 Output: Listing 10.15 brings back the CAT class, and leaves out the copy constructor and destructor to save room. On line 14, the assignment operator is declared, and on lines 30-41 it is defined. Analysis: On line 32, the current object (the CAT being assigned to) is tested to see whether it is the same as the CAT being assigned. This is done by checking whether or not the address of rhs is the same as the address stored in the this pointer. This works fine for single inheritance, but if you are using multiple inheritance, as discussed on Day 13, "Polymorphism," this test will fail. An alternative test is to dereference the this pointer and see if the two objects are the same: if (*this == rhs) Of course, the equality operator (==) can be overloaded as well, allowing you to determine for yourself what it means for your objects to be equal. Conversion Operators What happens when you try to assign a variable of a built-in type, such as int or unsignedshort, to an object of a user-defined class? Listing 10.16 brings back the Counter class, and attempts to assign a variable of type USHORT to a Counter object. WARNING: Listing 10.16 will not compile! Listing 10.16. Attempting to assign a Counter to a USHORT 1: // Listing 10.16 2: // This code won't compile! 3: 4: typedef unsigned short USHORT; 5: #include 6: 7: class Counter 8: { 9: public: 10: Counter(); 11: ~Counter(){} 12: USHORT GetItsVal()const { return itsVal; } 13: void SetItsVal(USHORT x) {itsVal = x; } 14: private: 15: USHORT itsVal; 16: 17: }; 18: 19: Counter::Counter(): 20: itsVal(0) 21: {} 22: 23: int main() 24: { 25: USHORT theShort = 5; 26: Counter theCtr = theShort; 27: cout << "theCtr: " << theCtr.GetItsVal() << endl; 28: return ;0 29: } Output: Compiler error! Unable to convert USHORT to Counter Analysis: The Counter class declared on lines 7-17 has only a default constructor. It declares no particular method for turning a USHORT into a Counter object, and so line 26 causes a compile error. The compiler cannot figure out, unless you tell it, that, given a USHORT, it should assign that value to the member variable itsVal. Listing 10.17 corrects this by creating a conversion operator: a constructor that takes a USHORT andproduces a Counter object. Listing 10.17. Converting USHORT to Counter. 1: // Listing 10.17 2: // Constructor as conversion operator 3: 4: typedef unsigned short USHORT; 5: #include 6: 7: class Counter 8: { 9: public: 10: Counter(); 11: Counter(USHORT val); 12: ~Counter(){} 13: USHORT GetItsVal()const { return itsVal; } 14: void SetItsVal(USHORT x) {itsVal = x; } 15: private: 16: USHORT itsVal; 17: 18: }; 19: 20: Counter::Counter(): 21: itsVal(0) 22: {} 23: 24: Counter::Counter(USHORT val): 25: itsVal(val) 26: {} 27: 28: 29: int main() 30: { 31: USHORT theShort = 5; 32: Counter theCtr = theShort; 33: cout << "theCtr: " << theCtr.GetItsVal() << endl; 34: return 0; 35: Output: theCtr: 5 Analysis: The important change is on line 11, where the constructor is overloaded to take a USHORT, and on lines 24-26, where the constructor is implemented. The effect of this constructor is to create a Counter out of a USHORT. Given this, the compiler is able to call the constructor that takes a USHORT as its argument. Whathappens, however, if you try to reverse the assignment with the following? 1: 2: 3: Counter theCtr(5); USHORT theShort = theCtr; cout << "theShort : " << theShort << endl; Once again, this will generate a compile error. Although the compiler now knows how to create a Counter out of a USHORT, it does not know how to reverse the process. Conversion Operators To solve this and similar problems, C++ provides conversion operators that can be added to your class. This allows your class to specify how to do implicit conversions to built-in types. Listing 10.18 illustrates this. One note, however: Conversion operators do not specify a return value, even though they do, in effect, return a converted value. Listing 10.18. Converting from Counter to unsigned short(). 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: // Listing 10.18 // conversion operator typedef unsigned short #include USHORT; class Counter { public: Counter(); Counter(USHORT val); ~Counter(){} USHORT GetItsVal()const { return itsVal; } void SetItsVal(USHORT x) {itsVal = x; } operator unsigned short(); private: USHORT itsVal; }; Counter::Counter(): itsVal(0) {} Counter::Counter(USHORT val): itsVal(val) {}28: 29: Counter::operator unsigned short () 30: { 31: return ( USHORT (itsVal) ); 32: } 33: 34: int main() 35: { 36: Counter ctr(5); 37: USHORT theShort = ctr; 38: cout << "theShort: " << theShort << endl; 39: return 0; 40: Output: theShort: 5 Analysis: On line 15, the conversion operator is declared. Note that it has no return value. The implementation of this function is on lines 29-32. Line 31 returns the value of itsVal, converted to a USHORT. Now the compiler knows how to turn USHORTs into Counter objects and vice versa, and they can be assigned to one another freely. Summary Today you learned how to overload member functions of your classes. You also learned how to supply default values to functions, and how to decide when to use default values and when to overload. Overloading class constructors allows you to create flexible classes that can be created from other objects. Initialization of objects happens at the initialization stage of construction, and is more efficient than assigning values in the body of the constructor. The copy constructor and the assignment operator are supplied by the compiler if you don't create your own, but they do a member-wise copy of the class. In classes in which member data includes pointers to the free store, these methods must be overridden so that you allocate memory for the target object. Almost all C++ operators can be overloaded, though you want to be cautious not to create operators whose use is counter-intuitive. You cannot change the arity of operators, nor can you invent new operators. The this pointer refers to the current object and is an invisible parameter to all member functions. The dereferenced this pointer is often returned by overloaded operators. Conversion operators allow you to create classes that can be used in expressions that expect adifferent type of object. They are exceptions to the rule that all functions return an explicit value; like constructors and destructors, they have no return type. Q&A Q. Why would you ever use default values when you can overload a function? A. It is easier to maintain one function than two, and often easier to understand a function with default parameters than to study the bodies of two functions. Furthermore, updating one of the functions and neglecting to update the second is a common source of bugs. Q. Given the problems with overloaded functions, why not always use default values instead? A. Overloaded functions supply capabilities not available with default variables, such as varying the list of parameters by type rather than just by number. Q. When writing a class constructor, how do you decide what to put in the initialization and what to put in the body of the constructor? A. A simple rule of thumb is to do as much as possible in the initialization phase--that is, initialize all member variables there. Some things, like computations and print statements, must be in the body of the constructor. Q. Can an overloaded function have a default parameter? A. Yes. There is no reason not to combine these powerful features. One or more of the overloaded functions can have their own default values, following the normal rules for default variables in any function. Q. Why are some member functions defined within the class declaration and others are not? A. Defining the implementation of a member function within the declaration makes it inline. Generally, this is done only if the function is extremely simple. Note that you can also make a member function inline by using the keyword inline, even if the function is declared outside the class declaration. Workshop The Workshop provides quiz questions to help solidify your understanding of the material covered and exercises to provide you with experience in using what you've learned. Try to answer the quiz and exercise questions before checking the answers in Appendix D, and make sure you understand the answers before going to the next chapter. Quiz1. When you overload member functions, in what ways must they differ? 2. What is the difference between a declaration and a definition? 3. When is the copy constructor called? 4. When is the destructor called? 5. How does the copy constructor differ from the assignment operator (=)? 6. What is the this pointer? 7. How do you differentiate between overloading the prefix and postfix increment operators? 8. Can you overload the operator+ for short integers? 9. Is it legal in C++ to overload the operator++ so that it decrements a value in your class? 10. What return value must conversion operators have in their declarations? Exercises 1. Write a SimpleCircle class declaration (only) with one member variable: itsRadius. Include a default constructor, a destructor, and accessor methods for radius. 2. Using the class you created in Exercise 1, write the implementation of the default constructor, initializing itsRadius with the value 5. 3. Using the same class, add a second constructor that takes a value as its parameter and assigns that value to itsRadius. 4. Create a prefix and postfix increment operator for your SimpleCircle class that increments itsRadius. 5. Change SimpleCircle to store itsRadius on the free store, and fix the existing methods. 6. Provide a copy constructor for SimpleCircle. 7. Provide an assignment operator for SimpleCircle. 8. Write a program that creates two SimpleCircle objects. Use the default constructor on one and instantiate the other with the value 9. Call the increment operator on each and then print their values. Finally, assign the second to the first and print its values. 9. BUG BUSTERS: What is wrong with this implementation of the assignment operator? SQUARE SQUARE ::operator=(const SQUARE & rhs){ itsSide = new int; *itsSide = rhs.GetSide(); return *this; } 10. BUG BUSTERS: What is wrong with this implementation of the addition operator? VeryShort VeryShort::operator+ (const VeryShort& rhs) { itsVal += rhs.GetItsVal(); return *this; } What Is an Array? An array is a collection of data storage locations, each of which holds the same type of data. Each storage location is called an element of the array. You declare an array by writing the type, followed by the array name and the subscript. The subscript is the number of elements in the array, surrounded by square brackets. For example, long LongArray[25]; declares an array of 25 long integers, named LongArray. When the compiler sees this declaration, it sets aside enough memory to hold all 25 elements. Because each long integer requires 4 bytes, this declaration sets aside 100 contiguous bytes of memory, as illustrated in Figure 11.1. Figure 11.1. Declaring an array.Array Elements You access each of the array elements by referring to an offset from the array name. Array elements are counted from zero. Therefore, the first array element is arrayName[0]. In the LongArray example, LongArray[0] is the first array element, LongArray[1] the second, and so forth. This can be somewhat confusing. The array SomeArray[3] has three elements. They are SomeArray[0], SomeArray[1], and SomeArray[2]. More generally, SomeArray[n] has n elements that are numbered SomeArray[0] through SomeArray[n-1]. Therefore, LongArray[25] is numbered from LongArray[0] through LongArray[24]. Listing 11.1 shows how to declare an array of five integers and fill each with a value. Listing 11.1. Using an integer array. 1: //Listing 11.1 - Arrays 2: #include 3: 4: int main() 5: { 6: int myArray[5]; 7: int i; 8: for ( i=0; i<5; i++) // 0-4 9: { 10: cout << "Value for myArray[" << i << "]: "; 11: cin >> myArray[i]; 12: } 13: for (i = 0; i<5; i++) 14: cout << i << ": " << myArray[i] << "\n"; 15: return 0; 16: } Output: Value for myArray[0]: 3 Value for myArray[1]: 6 Value for myArray[2]: 9 Value for myArray[3]: 12 Value for myArray[4]: 15 0: 3 1: 6 2: 9 3: 12 4: 15 Analysis: Line 6 declares an array called myArray, which holds five integer variables. Line 8 establishes a loop that counts from 0 through 4, which is the proper set of offsets for a five-element array. The user is prompted for a value, and that value is saved at the correct offset into the array.The first value is saved at myArray[0], the second at myArray[1], and so forth. The second for loop prints each value to the screen. NOTE: Arrays count from 0, not from 1. This is the cause of many bugs in programs written by C++ novices. Whenever you use an array, remember that an array with 10 elements counts from ArrayName[0] to ArrayName[9]. There is no ArrayName[10]. Writing Past the End of an Array When you write a value to an element in an array, the compiler computes where to store the value based on the size of each element and the subscript. Suppose that you ask to write over the value at LongArray[5], which is the sixth element. The compiler multiplies the offset (5) by the size of each element--in this case, 4. It then moves that many bytes (20) from the beginning of the array and writes the new value at that location. If you ask to write at LongArray[50], the compiler ignores the fact that there is no such element. It computes how far past the first element it should look (200 bytes) and then writes over whatever is at that location. This can be virtually any data, and writing your new value there might have unpredictable results. If you're lucky, your program will crash immediately. If you're unlucky, you'll get strange results much later in your program, and you'll have a difficult time figuring out what went wrong. The compiler is like a blind man pacing off the distance from a house. He starts out at the first house, MainStreet[0]. When you ask him to go to the sixth house on Main Street, he says to himself, "I must go five more houses. Each house is four big paces. I must go an additional 20 steps." If you ask him to go to MainStreet[100], and Main Street is only 25 houses long, he will pace off 400 steps. Long before he gets there, he will, no doubt, step in front of a moving bus. So be careful where you send him. Listing 11.2 shows what happens when you write past the end of an array. WARNING: Do not run this program; it may crash your system! Listing 11.2. Writing past the end of an array. 1: 2: 3: 4: //Listing 11.2 // Demonstrates what happens when you write past the end // of an array5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 0) 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: } #include int main() { // sentinels long sentinelOne[3]; long TargetArray[25]; // array to fill long sentinelTwo[3]; int i; for (i=0; i<3; i++) sentinelOne[i] = sentinelTwo[i] = 0; for (i=0; i<25; i++) TargetArray[i] = 0; cout << "Test 1: \n"; // test current values (should be cout << "TargetArray[0]: " << TargetArray[0] << "\n"; cout << "TargetArray[24]: " << TargetArray[24] << "\n\n"; for (i = 0; i<3; i++) { cout << "sentinelOne[" << cout << sentinelOne[i] << cout << "sentinelTwo[" << cout << sentinelTwo[i]<< } i << "]: "; "\n"; i << "]: "; "\n"; cout << "\nAssigning..."; for (i = 0; i<=25; i++) TargetArray[i] = 20; cout << "\nTest 2: \n"; cout << "TargetArray[0]: " << TargetArray[0] << "\n"; cout << "TargetArray[24]: " << TargetArray[24] << "\n"; cout << "TargetArray[25]: " << TargetArray[25] << "\n\n"; for (i = 0; i<3; i++) { cout << "sentinelOne[" << i << "]: "; cout << sentinelOne[i]<< "\n"; cout << "sentinelTwo[" << i << "]: "; cout << sentinelTwo[i]<< "\n"; } return 0;Output: Test 1: TargetArray[0]: 0 TargetArray[24]: 0 SentinelOne[0]: SentinelTwo[0]: SentinelOne[1]: SentinelTwo[1]: SentinelOne[2]: SentinelTwo[2]: 0 0 0 0 0 0 Assigning... Test 2: TargetArray[0]: 20 TargetArray[24]: 20 TargetArray[25]: 20 SentinelOne[0]: SentinelTwo[0]: SentinelOne[1]: SentinelTwo[1]: SentinelOne[2]: SentinelTwo[2]: 20 0 0 0 0 0 Analysis: Lines 9 and 10 declare two arrays of three integers that act as sentinels around TargetArray. These sentinel arrays are initialized with the value 0. If memory is written to beyond the end of TargetArray, the sentinels are likely to be changed. Some compilers count down in memory; others count up. For this reason, the sentinels are placed on both sides of TargetArray. Lines 19-29 confirm the sentinel values in Test 1. In line 33 TargetArray's members are all initialized to the value 20, but the counter counts to TargetArray offset 25, which doesn't exist in TargetArray. Lines 36-38 print TargetArray's values in Test 2. Note that TargetArray[25] is perfectly happy to print the value 20. However, when SentinelOne and SentinelTwo are printed, SentinelTwo[0] reveals that its value has changed. This is because the memory that is 25 elements after TargetArray[0] is the same memory that is at SentinelTwo[0]. When the nonexistent TargetArray[0] was accessed, what was actually accessed was SentinelTwo[0]. This nasty bug can be very hard to find, because SentinelTwo[0]'s value was changed in a part of the code that was not writing to SentinelTwo at all. This code uses "magic numbers" such as 3 for the size of the sentinel arrays and 25 for the size of TargetArray. It is safer to use constants, so that you can change all these values in one place. Fence Post ErrorsIt is so common to write to one past the end of an array that this bug has its own name. It is called a fence post error. This refers to the problem in counting how many fence posts you need for a 10-foot fence if you need one post for every foot. Most people answer 10, but of course you need 11. Figure 11.2 makes this clear. Figure 11.2. Fence post errors. This sort of "off by one" counting can be the bane of any programmer's life. Over time, however, you'll get used to the idea that a 25-element array counts only to element 24, and that everything counts from 0. (Programmers are often confused why office buildings don't have a floor zero. Indeed, some have been known to push the 4 elevator button when they want to get to the fifth floor.) NOTE: Some programmers refer to ArrayName[0] as the zeroth element. Getting into this habit is a big mistake. If ArrayName[0] is the zeroth element, what is ArrayName[1]? The oneth? If so, when you see ArrayName[24], will you realize that it is not the 24th element, but rather the 25th? It is far better to say that ArrayName[0] is at offset zero and is the first element. Initializing Arrays You can initialize a simple array of built-in types, such as integers and characters, when you first declare the array. After the array name, you put an equal sign (=) and a list of comma-separated values enclosed in braces. For example, int IntegerArray[5] = { 10, 20, 30, 40, 50 }; declares IntegerArray to be an array of five integers. It assigns IntegerArray[0] the value 10, IntegerArray[1] the value 20, and so forth. If you omit the size of the array, an array just big enough to hold the initialization is created. Therefore, if you write int IntegerArray[] = { 10, 20, 30, 40, 50 }; you will create exactly the same array as you did in the previous example. If you need to know the size of the array, you can ask the compiler to compute it for you. For example, const USHORT IntegerArrayLength; IntegerArrayLength = sizeof(IntegerArray)/sizeof(IntegerArray[0]); sets the constant USHORT variable IntegerArrayLength to the result obtained from dividing thesize of the entire array by the size of each individual entry in the array. That quotient is the number of members in the array. You cannot initialize more elements than you've declared for the array. Therefore, int IntegerArray[5] = { 10, 20, 30, 40, 50, 60}; generates a compiler error because you've declared a five-member array and initialized six values. It is legal, however, to write int IntegerArray[5] = { 10, 20}; Although uninitialized array members have no guaranteed values, actually, aggregates will be initialized to 0. If you don't initialize an array member, its value will be set to 0. DO let the compiler set the size of initialized arrays. DON'T write past the end of the array. DO give arrays meaningful names, as you would with any variable.DO remember that the first member of the array is at offset 0. Declaring Arrays Arrays can have any legal variable name, but they cannot have the same name as another variable or array within their scope. Therefore, you cannot have an array named myCats[5] and a variable named myCats at the same time. You can dimension the array size with a const or with an enumeration. Listing 11.3 illustrates this. Listing 11.3. Using consts and enums in arrays. 1: 2: 3: 4: 5: 6: 7: 8: 9: }; 10: 11: 12: // Listing 11.3 // Dimensioning arrays with consts and enumerations #include int main() { enum WeekDays { Sun, Mon, Tue, Wed, Thu, Fri, Sat, DaysInWeek }; int ArrayWeek[DaysInWeek] = { 10, 20, 30, 40, 50, 60, 70 cout << "The value at Tuesday is: " << ArrayWeek[Tue]; return 0;13: } Output: The value at Tuesday is: 30 Analysis: Line 7 creates an enumeration called WeekDays. It has eight members. Sunday is equal to 0, and DaysInWeek is equal to 7. Line 11 uses the enumerated constant Tue as an offset into the array. Because Tue evaluates to 2, the third element of the array, DaysInWeek[2], is returned and printed in line 11. Arrays To declare an array, write the type of object stored, followed by the name of the array and a subscript with the number of objects to be held in the array. Example 1 int MyIntegerArray[90]; Example 2 long * ArrayOfPointersToLongs[100]; To access members of the array, use the subscript operator. Example 1 int theNinethInteger = MyIntegerArray[8]; Example 2 long * pLong = ArrayOfPointersToLongs[8] Arrays count from zero. An array of n items is numbered from 0 to n-1. Arrays of Objects Any object, whether built-in or user-defined, can be stored in an array. When you declare the array, you tell the compiler the type of object to store and the number of objects for which to allocate room. The compiler knows how much room is needed for each object based on the class declaration. The class must have a default constructor that takes no arguments so that the objects can be created when the array is defined. Accessing member data in an array of objects is a two-step process. You identify the member of the array by using the index operator ([ ]), and then you add the member operator (.) to access the particular member variable. Listing 11.4 demonstrates how you would create an array of five CATs. Listing 11.4. Creating an array of objects.1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: } // Listing 11.4 - An array of objects #include class CAT { public: CAT() { itsAge = 1; itsWeight=5; } ~CAT() {} int GetAge() const { return itsAge; } int GetWeight() const { return itsWeight; } void SetAge(int age) { itsAge = age; } private: int itsAge; int itsWeight; }; Output: cat #2: cat #3: cat #4: cat #5: int main() { CAT Litter[5]; int i; for (i = 0; i < 5; i++) Litter[i].SetAge(2*i +1); for (i = 0; i < 5; i++) { cout << "Cat #" << i+1<< ": "; cout << Litter[i].GetAge() << endl; } return 0; cat #1: 1 3 5 7 9 Analysis: Lines 5-17 declare the CAT class. The CAT class must have a default constructor so that CAT objects can be created in an array. Remember that if you create any other constructor, the compiler-supplied default constructor is not created; you must create your own. The first for loop (lines 23 and 24) sets the age of each of the five CATs in the array. The second for loop (lines 26 and 27) accesses each member of the array and calls GetAge().Each individual CAT's GetAge() method is called by accessing the member in the array, Litter[i], followed by the dot operator (.), and the member function. Multidimensional Arrays It is possible to have arrays of more than one dimension. Each dimension is represented as a subscript in the array. Therefore, a two-dimensional array has two subscripts; a three-dimensional array has three subscripts; and so on. Arrays can have any number of dimensions, although it is likely that most of the arrays you create will be of one or two dimensions. A good example of a two-dimensional array is a chess board. One dimension represents the eight rows; the other dimension represents the eight columns. Figure 11.3 illustrates this idea. Suppose that you have a class named SQUARE. The declaration of an array named Board that represents it would be SQUARE Board[8][8]; You could also represent the same data with a one-dimensional, 64-square array. For example, SQUARE Board[64] This doesn't correspond as closely to the real-world object as the two-dimension. When the game begins, the king is located in the fourth position in the first row. Counting from zero array, that position corresponds to Board[0][3]; assuming that the first subscript corresponds to row, and the second to column. The layout of positions for the entire board is illustrated in Figure 11.3. Figure 11.3. A chess board and a two-dimensional array. Initializing Multidimensional Arrays You can initialize multidimensional arrays. You assign the list of values to array elements in order, with the last array subscript changing while each of the former holds steady. Therefore, if you have an array int theArray[5][3] the first three elements go into theArray[0]; the next three into theArray[1]; and so forth. You initialize this array by writingint theArray[5][3] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 } For the sake of clarity, you could group the initializations with braces. For example, int theArray[5][3] = { {4,5,6}, {7,8,9}, {10,11,12}, {13,14,15} }; {1,2,3}, The compiler ignores the inner braces, which make it easier to understand how the numbers are distributed. Each value must be separated by a comma, without regard to the braces. The entire initialization set must be within braces, and it must end with a semicolon. Listing 11.5 creates a two-dimensional array. The first dimension is the set of numbers from 0 to 5. The second dimension consists of the double of each value in the first dimension. Listing 11.5. Creating a multidimensional array. 1: #include 2: int main() 3: { 4: int SomeArray[5][2] = { {0,0}, {1,2}, {2,4}, {3,6}, {4,8}}; 5: for (int i = 0; i<5; i++) 6: for (int j=0; j<2; j++) 7: { 8: cout << "SomeArray[" << i << "][" << j << "]: "; 9: cout << SomeArray[i][j]<< endl; 10: } 11: 12: return 0; 13: } Output: SomeArray[0][0]: 0 SomeArray[0][1]: 0 SomeArray[1][0]: 1 SomeArray[1][1]: 2 SomeArray[2][0]: 2 SomeArray[2][1]: 4 SomeArray[3][0]: 3 SomeArray[3][1]: 6SomeArray[4][0]: 4 SomeArray[4][1]: 8 Analysis: Line 4 declares SomeArray to be a two-dimensional array. The first dimension consists of five integers; the second dimension consists of two integers. This creates a 5x2 grid, as Figure 11.4 shows. Figure 11.4. A 5x2 array. The values are initialized in pairs, although they could be computed as well. Lines 5 and 6 create a nested for loop. The outer for loop ticks through each member of the first dimension. For every member in that dimension, the inner for loop ticks through each member of the second dimension. This is consistent with the printout. SomeArray[0][0] is followed by SomeArray[0][1]. The first dimension is incremented only after the second dimension is incremented by 1. Then the second dimension starts over. A Word About Memory When you declare an array, you tell the compiler exactly how many objects you expect to store in it. The compiler sets aside memory for all the objects, even if you never use it. This isn't a problem with arrays for which you have a good idea of how many objects you'll need. For example, a chess board has 64 squares, and cats have between 1 and 10 kittens. When you have no idea of how many objects you'll need, however, you must use more advanced data structures. This book looks at arrays of pointers, arrays built on the free store, and various other collections. Other more advanced data structures that solve large data storage problems are beyond the scope of this book. Two of the great things about programming are that there are always more things to learn and that there are always more books from which to learn. Arrays of Pointers The arrays discussed so far store all their members on the stack. Usually stack memory is severely limited, whereas free store memory is far larger. It is possible to declare each object on the free store and then to store only a pointer to the object in the array. This dramatically reduces the amount of stack memory used. Listing 11.6 rewrites the array from Listing 11.4, but it stores all the objects on the free store. As an indication of the greater memory that this enables, the array is expanded from 5 to 500, and the name is changed from Litter to Family. Listing 11.6. Storing an array on the free store. 1: 2: 3: 4: 5: // Listing 11.6 - An array of pointers to objects #include class CAT6: { 7: public: 8: CAT() { itsAge = 1; itsWeight=5; } 9: ~CAT() {} // destructor 10: int GetAge() const { return itsAge; } 11: int GetWeight() const { return itsWeight; } 12: void SetAge(int age) { itsAge = age; } 13: 14: private: 15: int itsAge; 16: int itsWeight; 17: }; 18: 19: int main() 20: { 21: CAT * Family[500]; 22: int i; 23: CAT * pCat; 24: for (i = 0; i < 500; i++) 25: { 26: pCat = new CAT; 27: pCat->SetAge(2*i +1); 28: Family[i] = pCat; 29: } 30: 31: for (i = 0; i < 500; i++) 32: { 33: cout << "Cat #" << i+1 << ": "; 34: cout << Family[i]->GetAge() << endl; 35: } 36: return 0; 37: } Output: Cat #1: 1 Cat #2: 3 Cat #3: 5 ... Cat #499: 997 Cat #500: 999 Analysis: The CAT object declared in lines 5-17 is identical with the CAT object declared in Listing 11.4. This time, however, the array declared in line 21 is named Family, and it is declared to hold 500 pointers to CAT objects. In the initial loop (lines 24-29), 500 new CAT objects are created on the free store, and each one has itsage set to twice the index plus one. Therefore, the first CAT is set to 1, the second CAT to 3, the third CAT to 5, and so on. Finally, the pointer is added to the array. Because the array has been declared to hold pointers, the pointer--rather than the dereferenced value in the pointer--is added to the array. The second loop (lines 31 and 32) prints each of the values. The pointer is accessed by using the index, Family[i]. That address is then used to access the GetAge() method. In this example, the array Family and all its pointers are stored on the stack, but the 500 CATs that are created are stored on the free store. Declaring Arrays on the Free Store It is possible to put the entire array on the free store, also known as the heap. You do this by calling new and using the subscript operator. The result is a pointer to an area on the free store that holds the array. For example, CAT *Family = new CAT[500]; declares Family to be a pointer to the first in an array of 500 CATs. In other words, Family points to--or has the address of--Family[0]. The advantage of using Family in this way is that you can use pointer arithmetic to access each member of Family. For example, you can write CAT *Family = new CAT[500]; CAT *pCat = Family; pCat->SetAge(10); pCat++; pCat->SetAge(20); //pCat points to Family[0] // set Family[0] to 10 // advance to Family[1] // set Family[1] to 20 This declares a new array of 500 CATs and a pointer to point to the start of the array. Using that pointer, the first CAT's SetAge() function is called with a value of 10. The pointer is then incremented to point to the next CAT, and the second Cat's SetAge() method is then called. A Pointer to an Array Versus an Array of Pointers Examine these three declarations: 1: 2: 3: Cat FamilyOne[500] CAT * FamilyTwo[500]; CAT * FamilyThree = new CAT[500];FamilyOne is an array of 500 CATs. FamilyTwo is an array of 500 pointers to CATs. FamilyThree is a pointer to an array of 500 CATs. The differences among these three code lines dramatically affect how these arrays operate. What is perhaps even more surprising is that FamilyThree is a variant of FamilyOne, but is very different from FamilyTwo. This raises the thorny issue of how pointers relate to arrays. In the third case, FamilyThree is a pointer to an array. That is, the address in FamilyThree is the address of the first item in that array. This is exactly the case for FamilyOne. Pointers and Array Names In C++ an array name is a constant pointer to the first element of the array. Therefore, in the declaration CAT Family[50]; Family is a pointer to &Family[0], which is the address of the first element of the array Family. It is legal to use array names as constant pointers, and vice versa. Therefore, Family + 4 is a legitimate way of accessing the data at Family[4]. The compiler does all the arithmetic when you add to, increment, and decrement pointers. The address accessed when you write Family + 4 isn't 4 bytes past the address of Family--it is four objects. If each object is 4 bytes long, Family + 4 is 16 bytes. If each object is a CAT that has four long member variables of 4 bytes each and two short member variables of 2 bytes each, each CAT is 20 bytes, and Family + 4 is 80 bytes past the start of the array. Listing 11.7 illustrates declaring and using an array on the free store. Listing 11.7. Creating an array by using new. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: // Listing 11.7 - An array on the free store #include class CAT { public: CAT() { itsAge = 1; itsWeight=5; } ~CAT(); int GetAge() const { return itsAge; } int GetWeight() const { return itsWeight; }12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 38: 39: 40: 41: 42: 43: 44: 45: } void SetAge(int age) { itsAge = age; } private: int itsAge; int itsWeight; }; CAT :: ~CAT() { // cout << "Destructor called!\n"; } int main() { CAT * Family = new CAT[500]; int i; CAT * pCat; for (i = 0; i < 500; i++) { pCat = new CAT; pCat->SetAge(2*i +1); Family[i] = *pCat; delete pCat; } for (i = 0; i < 500; i++) { cout << "Cat #" << i+1 << ": "; cout << Family[i].GetAge() << endl; } delete [] Family; return 0; Output: Cat #1: 1 Cat #2: 3 Cat #3: 5 ... Cat #499: 997 Cat #500: 999 Analysis: Line 26 declares the array Family, which holds 500 CAT objects. The entire array is created on the free store with the call to new CAT[500]. Each CAT object added to the array also is created on the free store (line 31). Note, however, that thepointer isn't added to the array this time; the object itself is. This array isn't an array of pointers to CATs. It is an array of CATs. Deleting Arrays on the Free Store Family is a pointer, a pointer to the array on the free store. When, on line 33, the pointer pCat is dereferenced, the CAT object itself is stored in the array (why not? the array is on the free store). But pCat is used again in the next iteration of the loop. Isn't there a danger that there will now be no pointer to that CAT object, and a memory leak has been created? This would be a big problem, except that deleting Family returns all the memory set aside for the array. The compiler is smart enough to destroy each object in the array and to return its memory to the free store. To see this, change the size of the array from 500 to 10 in lines 26, 29, and 37. Then uncomment the cout statement in line 21. When line 40 is reached and the array is destroyed, each CAT object destructor is called. When you create an item on the heap by using new, you always delete that item and free its memory with delete. Similarly, when you create an array by using new [size], you delete that array and free all its memory with delete[]. The brackets signal the compiler that this array is being deleted. If you leave the brackets off, only the first object in the array will be deleted. You can prove this to yourself by removing the bracket on line 40. If you edited line 21 so that the destructor prints, you should now see only one CAT object destroyed. Congratulations! You just created a memory leak. DO remember that an array of n items is numbered from zero through n-1. DON'T write or read past the end of an array. DON'T confuse an array of pointers with a pointer to an array. DO use array indexing with pointers that point to arrays. char Arrays A string is a series of characters. The only strings you've seen until now have been unnamed string constants used in cout statements, such as cout << "hello world.\n"; In C++ a string is an array of chars ending with a null character. You can declare and initialize a string just as you would any other array. For example, char Greeting[] ={ `H', `e', `l', `l', `o', ` `, `W','o','r','l','d', `\0' }; The last character, `\0', is the null character, which many C++ functions recognize as the terminator for a string. Although this character-by-character approach works, it is difficult to type and admits too many opportunities for error. C++ enables you to use a shorthand form of the previous line of code. It is char Greeting[] = "Hello World"; You should note two things about this syntax: -%) $." Instead of single quoted characters separated by commas and surrounded by braces, you have a double-quoted string, no commas, and no braces. You don't need to add the null character because the compiler adds it for you. The string Hello World is 12 bytes. Hello is 5 bytes, the space 1, World 5, and the null character 1. You can also create uninitialized character arrays. As with all arrays, it is important to ensure that you don't put more into the buffer than there is room for. Listing 11.8 demonstrates the use of an uninitialized buffer. Listing 11.8. Filling an array. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: } //Listing 11.8 char array buffers #include int main() { char buffer[80]; cout << "Enter the string: "; cin >> buffer; cout << "Here's the buffer: " << buffer << endl; return 0; Output: Enter the string: Hello World Here's the buffer: Hello Analysis: On line 7, a buffer is declared to hold 80 characters. This is large enough to hold a 79- character string and a terminating null character. On line 8, the user is prompted to enter a string, which is entered into buffer on line 9. It is the syntaxof cin to write a terminating null to buffer after it writes the string. There are two problems with the program in Listing 11.8. First, if the user enters more than 79 characters, cin writes past the end of the buffer. Second, if the user enters a space, cin thinks that it is the end of the string, and it stops writing to the buffer. To solve theseproblems, you must call a special method on cin: get(). cin.get() takes three parameters: The buffer to fill The maximum number of characters to get The delimiter that terminates input The default delimiter is newline. Listing 11.9 illustrates its use. Listing 11.9. Filling an array. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: } //Listing 11.9 using cin.get() #include int main() { char buffer[80]; cout << "Enter the string: "; cin.get(buffer, 79); // get up to 79 or newline cout << "Here's the buffer: " << buffer << endl; return 0; Output: Enter the string: Hello World Here's the buffer: Hello World Analysis: Line 9 calls the method get() of cin. The buffer declared in line 7 is passed in as the first argument. The second argument is the maximum number of characters to get. In this case, it must be 79 to allow for the terminating null. There is no need to provide a terminating character because the default value of newline is sufficient. cin and all its variations are covered on Day 17, "The Preprocessor," when streams are discussed in depth. strcpy() and strncpy()C++ inherits from C a library of functions for dealing with strings. Among the many functions provided are two for copying one string into another: strcpy() and strncpy(). strcpy() copies the entire contents of one string into a designated buffer. Listing 11.10 demonstrates the use of strcpy(). Listing 11.10. Using strcpy(). 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: } #include #include int main() { char String1[] = "No man is an island"; char String2[80]; strcpy(String2,String1); cout << "String1: " << String1 << endl; cout << "String2: " << String2 << endl; return 0; Output: String1: No man is an island String2: No man is an island Analysis: The header file string.h is included in line 2. This file contains the prototype of the strcpy() function. strcpy() takes two character arrays--a destination followed by a source. If the source were larger than the destination, strcpy() would overwrite past the end of the buffer. To protect against this, the standard library also includes strncpy(). This variation takes a maximum number of characters to copy. strncpy() copies up to the first null character or the maximum number of characters specified into the destination buffer. Listing 11.11 illustrates the use of strncpy(). Listing 11.11. Using strncpy(). 1: 2: 3: 4: 5: 6: 7: 8: 9: #include #include int main() { const int MaxLength = 80; char String1[] = "No man is an island"; char String2[MaxLength+1];10: 11: 12: 13: 14: 15: } strncpy(String2,String1,MaxLength); cout << "String1: " << String1 << endl; cout << "String2: " << String2 << endl; return 0; Output: String1: No man is an island String2: No man is an island Analysis: In line 10, the call to strcpy() has been changed to a call to strncpy(), which takes a third parameter: the maximum number of characters to copy. The buffer String2 is declared to take MaxLength+1 characters. The extra character is for the null, which both strcpy() and strncpy() automatically add to the end of the string. String Classes Most C++ compilers come with a class library that includes a large set of classes for data manipulation. A standard component of a class library is a String class. C++ inherited the null-terminated string and the library of functions that includes strcpy() from C, but these functions aren't integrated into an object-oriented framework. A String class provides an encapsulated set of data and functions for manipulating that data, as well as accessor functions so that the data itself is hidden from the clients of the String class. If your compiler doesn't already provide a String class--and perhaps even if it does--you might want to write your own. The remainder of this chapter discusses the design and partial implementation of String classes. At a minimum, a String class should overcome the basic limitations of character arrays. Like all arrays, character arrays are static. You define how large they are. They always take up that much room in memory even if you don't need it all. Writing past the end of the array is disastrous. A good String class allocates only as much memory as it needs, and always enough to hold whatever it is given. If it can't allocate enough memory, it should fail gracefully. Listing 11.12 provides a first approximation of a String class. Listing 11.12. Using a String class. 1: 2: 3: 4: 5: //Listing 11.12 #include #include 6: // Rudimentary string class 7: class String 8: { 9: public: 10: // constructors 11: String(); 12: String(const char *const); 13: String(const String &); 14: ~String(); 15: 16: // overloaded operators 17: char & operator[](unsigned short offset); 18: char operator[](unsigned short offset) const; 19: String operator+(const String&); 20: void operator+=(const String&); 21: String & operator= (const String &); 22: 23: // General accessors 24: unsigned short GetLen()const { return itsLen; } 25: const char * GetString() const { return itsString; } 26: 27: private: 28: String (unsigned short); // private constructor 29: char * itsString; 30: unsigned short itsLen; 31: }; 32: 33: // default constructor creates string of 0 bytes 34: String::String() 35: { 36: itsString = new char[1]; 37: itsString[0] = `\0'; 38: itsLen=0; 39: } 40: 41: // private (helper) constructor, used only by 42: // class methods for creating a new string of 43: // required size. Null filled. 44: String::String(unsigned short len) 45: { 46: itsString = new char[len+1]; 47: for (unsigned short i = 0; i<=len; i++) 48: itsString[i] = `\0'; 49: itsLen=len; 50: }51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: // Converts a character array to a String String::String(const char * const cString) { itsLen = strlen(cString); itsString = new char[itsLen+1]; for (unsigned short i = 0; i itsLen) return itsString[itsLen-1]; else return itsString[offset]; } // constant offset operator for use // on const objects (see copy constructor!) char String::operator[](unsigned short offset) const { if (offset > itsLen) return itsString[itsLen-1]; else return itsString[offset]; } // creates a new string by adding current // string to rhs String String::operator+(const String& rhs) { unsigned short totalLen = itsLen + rhs.GetLen(); String temp(totalLen); for (unsigned short i = 0; i // object to add to list class CAT { public: CAT() { itsAge = 1;} CAT(int age):itsAge(age){} ~CAT(){}; int GetAge() const { return itsAge; } private: int itsAge; }; // manages list, orders by cat's age! class Node { public: Node (CAT*); ~Node(); void SetNext(Node * node) { itsNext = node; } Node * GetNext() const { return itsNext; } CAT * GetCat() const { return itsCat; } void Insert(Node *); void Display(); private: CAT *itsCat; Node * itsNext; }; Node::Node(CAT* pCat): itsCat(pCat), itsNext(0) {} Node::~Node() {42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: ) 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: cout << "Deleting node...\n"; delete itsCat; itsCat = 0; delete itsNext; itsNext = 0; } // ************************************ // Insert // Orders cats based on their ages // Algorithim: If you are last in line, add the cat // Otherwise, if the new cat is older than you // and also younger than next in line, insert it after // this one. Otherwise call insert on the next in line // ************************************ void Node::Insert(Node* newNode) { if (!itsNext) itsNext = newNode; else { int NextCatsAge = itsNext->GetCat()->GetAge(); int NewAge = newNode->GetCat()->GetAge(); int ThisNodeAge = itsCat->GetAge(); if ( NewAge >= ThisNodeAge && NewAge < NextCatsAge { newNode->SetNext(itsNext); itsNext = newNode; } else itsNext->Insert(newNode); } } void Node::Display() { if (itsCat->GetAge() > 0) { cout << "My cat is "; cout << itsCat->GetAge() << " years old\n"; } if (itsNext) itsNext->Display(); }87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: } int main() { Node *pNode = 0; CAT * pCat = new CAT(0); int age; Node *pHead = new Node(pCat); while (1) { cout << "New Cat's age? (0 to quit): "; cin >> age; if (!age) break; pCat = new CAT(age); pNode = new Node(pCat); pHead->Insert(pNode); } pHead->Display(); delete pHead; cout << "Exiting...\n\n"; return 0; Output: New Cat's New Cat's age? (0 New Cat's age? (0 New Cat's age? (0 New Cat's age? (0 New Cat's age? (0 New Cat's age? (0 My cat is 1 years My cat is 2 years My cat is 3 years My cat is 5 years My cat is 7 years My cat is 9 years Deleting node... Deleting node... Deleting node... Deleting node... Deleting node... Deleting node... Deleting node... age? (0 to quit): 1 to quit): 9 to quit): 3 to quit): 7 to quit): 2 to quit): 5 to quit): 0 old old old old old oldExiting... Analysis: Lines 7-16 declare a simplified CAT class. It has two constructors, a default constructor that initializes the member variable itsAge to 1, and a constructor that takes an integer and initializes itsAge to that value. Lines 19-32 declare the class Node. Node is designed specifically to hold a CAT object in a list. Normally, you would hide Node inside a CatList class. It is exposed here to illustrate how linked lists work. It is possible to make a more generic Node that would hold any kind of object in a list. You'll learn about doing that on Day 14, "Special Classes and Functions," when templates are discussed. Node's constructor takes a pointer to a CAT object. The copy constructor and assignment operator have been left out to save space. In a real-world application, they would be included. Three accessor functions are defined. SetNext() sets the member variable itsNext to point to the Node object supplied as its parameter. GetNext() and GetCat() return the appropriate member variables. GetNext() and GetCat() are declared const because they don't change the Node object. Insert() is the most powerful member function in the class. Insert() maintains the linked list and adds Nodes to the list based on the age of the CAT that they point to. The program begins at line 88. The pointer pNode is created and initialized to 0. A dummy CAT object is created, and its age is initialized to 0, to ensure that the pointer to the head of the list (pHead) is always first. Beginning on line 99, the user is prompted for an age. If the user presses 0, this is taken as a signal that no more CAT objects are to be created. For all other values, a CAT object is created on line 103, and the member variable itsAge is set to the supplied value. The CAT objects are created on the free store. For each CAT created, a Node object is created on line 104. After the CAT and Node objects are created, the first Node in the list is told to insert the newly created node, on line 105. Note that the program doesn't know--or care--how Node is inserted or how the list is maintained. That is entirely up to the Node object itself. The call to Insert() causes program execution to jump to line 57. Insert() is always called on pHead first. The test in line 59 fails the first time a new Node is added. Therefore, pHead is pointed at the first new Node. In the output, this is the node with a CAT whose itsAge value was set to 1.When the second CAT object's itsAge variable is set to 9, pHead is called again. This time, its member variable itsNext isn't null, and the else statement in lines 61 to 74 is invoked. Three local variables--NextCatsAge, NewAge, and ThisNodeAge--are filled with the values of The current Node's age--the age of pHead's CAT is 0 The age of the CAT held by the new Node--in this case, 9 The age of the CAT object held by the next node in line--in this case, 1 The test in line 67 could have been written as if ( newNode->GetCat()->GetAge() > itsCat->GetAge() && \\ newNode->GetCat()->GetAge()< itsNext->GetCat()->GetAge()) which would have eliminated the three temporary variables while creating code that is more confusing and harder to read. Some C++ programmers see this as macho--until they have a bug and can't figure out which one of the values is wrong. If the new CAT's age is greater than the current CAT's age and less than the next CAT's age, the proper place to insert the new CAT's age is immediately after the current Node. In this case, the if statement is true. The new Node is set to point to what the current Node points to, and the current Node is set to point to the new Node. Figure 11.6 illustrates this. Figure 11.6. Inserting a Node. If the test fails, this isn't the proper place to insert the Node, and Insert() is called on the next Node in the list. Note that the current call to Insert() doesn't return until after the recursive call to Insert() returns. Therefore, these calls pile up on the stack. If the list gets too long, it will blow the stack and crash the program. There are other ways to do this that aren't so stack-intensive, but they are beyond the scope of this book. Once the user is finished adding CAT objects, display is called on the first Node: pHead. The CAT object's age is displayed if the current Node points to a CAT (pHead does not). Then, if the current Node points to another Node, display() is called on that Node. Finally, delete is called on pHead. Because the destructor deletes the pointer to the next Node, delete is called on that Node as well. It walks the entire list, eliminating each Node and freeing the memory of itsCat. Note that the last Node has its member variable itsNext set to zero, and delete is called on that pointer as well. It is always safe to call delete on zero, for it has no effect. Array Classes Writing your own Array class has many advantages over using the built-in arrays. For starters, you can prevent array overruns. You might also consider making your array class dynamically sized: Atcreation it might have only one member, growing as needed during the course of the program. You might want to sort or otherwise order the members of the array. There are a number of powerful Array variants you might consider. Among the most popular are: )') Ordered collection: Each member is in sorted order. $/+ Set: No member appears more than once. /). Dictionary: This uses matched pairs in which one value acts as a key to retrieve the other value. )'! &), Sparse array: Indices are permitted for a large set, but only those values actually added to the array consume memory. Thus, you can ask for SparseArray[5] or SparseArray[200], but it is possible that memory is allocated only for a small number of entries. Bag: An unordered collection that is added to and retrieved in random order. By overloading the index operator ([ ]), you can turn a linked list into an ordered collection. By excluding duplicates, you can turn a collection into a set. If each object in the list has a pair of matched values, you can use a linked list to build a dictionary or a sparse array. Summary Today you learned how to create arrays in C++. An array is a fixed-size collection of objects that are all of the same type. Arrays don't do bounds checking. Therefore it is legal--even if disastrous--to read or write past the end of an array. Arrays count from 0. A common mistake is to write to offset n of an array of n members. Arrays can be one dimensional or multidimensional. In either case, the members of the array can be initialized, as long as the array contains either built-in types, such as int, or objects of a class that has a default constructor. Arrays and their contents can be on the free store or on the stack. If you delete an array on the free store, remember to use the brackets in the call to delete. Array names are constant pointers to the first elements of the array. Pointers and arrays use pointer arithmetic to find the next element of an array. You can create linked lists to manage collections whose size you won't know at compile time. From linked lists, you can create any number of more complex data structures. Strings are arrays of characters, or chars. C++ provides special features for managing char arrays, including the ability to initialize them with quoted strings.Q&A Q. What happens if I write to element 25 in a 24-member array? A. You will write to other memory, with potentially disastrous effects on your program. Q. What is in an uninitialized array element? A. Whatever happens to be in memory at a given time. The results of using this member without assigning a value are unpredictable. Q. Can I combine arrays? A. Yes. With simple arrays you can use pointers to combine them into a new, larger array. With strings you can use some of the built-in functions, such as strcat, to combine strings. Q. Why should I create a linked list if an array will work? A. An array must have a fixed size, whereas a linked list can be sized dynamically at runtime. Q. Why would I ever use built-in arrays if I can make a better array class? A. Built-in arrays are quick and easy to use. Q. Must a string class use a char * to hold the contents of the string? A. No. It can use any memory storage the designer thinks is best. Workshop The Workshop provides quiz questions to help you solidify your understanding of the material covered and exercises to provide you with experience in using what you've learned. Try to answer the quiz and exercise questions before checking the answers in Appendix D, and make sure you understand the answers before continuing to the next chapter. Quiz 1. What are the first and last elements in SomeArray[25]? 2. How do you declare a multidimensional array? 3. Initialize the members of the array in Question 2. 4. How many elements are in the array SomeArray[10][5][20]? 5. What is the maximum number of elements that you can add to a linked list?6. Can you use subscript notation on a linked list? 7. What is the last character in the string "Brad is a nice guy"? Exercises 1. Declare a two-dimensional array that represents a tic-tac-toe game board. 2. Write the code that initializes all the elements in the array you created in Exercise 1 to the value 0. 3. Write the declaration for a Node class that holds unsigned short integers. 4. BUG BUSTERS: What is wrong with this code fragment? unsigned short SomeArray[5][4]; for (int i = 0; i<4; i++) for (int j = 0; j<5; j++) SomeArray[i][j] = i+j; 5. BUG BUSTERS: What is wrong with this code fragment? unsigned short SomeArray[5][4]; for (int i = 0; i<=5; i++) for (int j = 0; j<=4; j++) SomeArray[i][j] = 0; Day 12 Inheritance What Is Inheritance? What is a dog? When you look at your pet, what do you see? A biologist sees a network of interacting organs, a physicist sees atoms and forces at work, and a taxonomist sees a representative of the species canine domesticus. It is that last assessment that interests us at the moment. A dog is a kind of canine, a canine is a kind of mammal, and so forth. Taxonomists divide the world of living things into Kingdom, Phylum, Class, Order, Family, Genus, and Species. This hierarchy establishes an is-a relationship. A dog is a kind of canine. We see this relationship everywhere: A Toyota is a kind of car, which is a kind of vehicle. A sundae is a kind of dessert, which is a kind of food. What do we mean when we say something is a kind of something else? We mean that it is a specialization of that thing. That is, a car is a special kind of vehicle. Inheritance and DerivationThe concept dog inherits, that is, it automatically gets, all the features of a mammal. Because it is a mammal, we know that it moves and that it breathes air--all mammals move and breathe air by definition. The concept of a dog adds the idea of barking, wagging its tail, and so forth to that definition. We can further divide dogs into hunting dogs and terriers, and we can divide terriers into Yorkshire Terriers, Dandie Dinmont Terriers, and so forth. A Yorkshire Terrier is a kind of terrier, therefore it is a kind of dog, therefore a kind of mammal, therefore a kind of animal, and therefore a kind of living thing. This hierarchy is represented in Figure 12.1. Figure 12.1.Hierarchy of Animals. C++ attempts to represent these relationships by enabling you to define classes that derive from one another. Derivation is a way of expressing the is-a relationship. You derive a new class, Dog, from the class Mammal. You don't have to state explicitly that dogs move, because they inherit that from Mammal. New Term: A class which adds new functionality to an existing class is said to derive from that original class. The original class is said to be the new class's base class. If the Dog class derives from the Mammal class, then Mammal is a base class of Dog. Derived classes are supersets of their base classes. Just as dog adds certain features to the idea of mammal, the Dog class will add certain methods or data to the Mammal class. Typically, a base class will have more than one derived class. Just as dogs, cats, and horses are all types of mammals, their classes would all derive from the Mammal class. The Animal Kingdom To facilitate the discussion of derivation and inheritance, this chapter will focus on the relationships among a number of classes representing animals. You can imagine that you have been asked to design a children's game--a simulation of a farm. In time you will develop a whole set of farm animals, including horses, cows, dogs, cats, sheep, and so forth. You will create methods for these classes so that they can act in the ways the child might expect, but for now you'll stub-out each method with a simple print statement. Stubbing-out a function means you'll write only enough to show that the function was called, leaving the details for later when you have more time. Please feel free to extend the minimal code provided in this chapter to enable the animals to act more realistically. The Syntax of DerivationWhen you declare a class, you can indicate what class it derives from by writing a colon after the class name, the type of derivation (public or otherwise), and the class from which it derives. The following is an example. class Dog : public Mammal The type of derivation will be discussed later in this chapter. For now, always use public. The class from which you derive must have been declared earlier, or you will get a compiler error. Listing 12.1 illustrates how to declare a Dog class that is derived from a Mammal class. Listing 12.1. Simple inheritance. 1: 2: 3: 4: }; 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: //Listing 12.1 Simple inheritance #include enum BREED { YORKIE, CAIRN, DANDIE, SHETLAND, DOBERMAN, LAB class Mammal { public: // constructors Mammal(); ~Mammal(); //accessors int GetAge()const; void SetAge(int); int GetWeight() const; void SetWeight(); //Other methods void Speak(); void Sleep(); protected: int itsAge; int itsWeight; }; class Dog : public Mammal { public:33: // Constructors 34: Dog(); 35: ~Dog(); 36: 37: // Accessors 38: BREED GetBreed() const; 39: void SetBreed(BREED); 40: 41: // Other methods 42: // WagTail(); 43: // BegForFood(); 44: 45: protected: 46: BREED itsBreed; 47: }; This program has no output because it is only a set of class declarations without their implementations. Nonetheless, there is much to see here. Analysis: On lines 6-27, the Mammal class is declared. Note that in this example, Mammal does not derive from any other class. In the real world, mammals do derive--that is, mammals are kinds of animals. In a C++ program, you can represent only a fraction of the information you have about any given object. Reality is far too complex to capture all of it, so every C++ hierarchy is an arbitrary representation of the data available. The trick of good design is to represent the areas that you care about in a way that maps back to reality in a reasonably faithful manner. The hierarchy has to begin somewhere; this program begins with Mammal. Because of this decision, some member variables that might properly belong in a higher base class are now represented here. For example, certainly all animals have an age and weight, so if Mammal is derived from Animal, we might expect to inherit those attributes. As it is, the attributes appear in the Mammal class. To keep the program reasonably simple and manageable, only six methods have been put in the Mammal class--four accessor methods, Speak(), and Sleep(). The Dog class inherits from Mammal, as indicated on line 29. Every Dog object will have three member variables: itsAge, itsWeight, and itsBreed. Note that the class declaration for Dog does not include the member variables itsAge and itsWeight. Dog objects inherit these variables from the Mammal class, along with all of Mammal's methods except the copy operator and the constructors and destructor. Private Versus Protected You may have noticed that a new access keyword, protected, has been introduced on lines 24 and 45 of Listing 12.1. Previously, class data had been declared private. However, private membersare not available to derived classes. You could make itsAge and itsWeight public, but that is not desirable. You don't want other classes accessing these data members directly. What you want is a designation that says, "Make these visible to this class and to classes that derive from this class." That designation is protected. Protected data members and functions are fully visible to derived classes, but are otherwise private. There are, in total, three access specifiers: public, protected, and private. If a function has an object of your class, it can access all the public member data and functions. The member functions, in turn, can access all private data members and functions of their own class, and all protected data members and functions of any class from which they derive. Thus, the function Dog::WagTail() can access the private data itsBreed and can access the protected data in the Mammal class. Even if other classes are layered between Mammal and Dog (for example, DomesticAnimals), the Dog class will still be able to access the protected members of Mammal, assuming that these other classes all use public inheritance. Private inheritance is discussed on Day 15, "Advanced Inheritance." Listing 12.2 demonstrates how to create objects of type Dog and access the data and functions of that type. Listing 12.2. Using a derived object. 1: 2: 3: 4: }; 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: //Listing 12.2 Using a derived object #include enum BREED { YORKIE, CAIRN, DANDIE, SHETLAND, DOBERMAN, LAB class Mammal { public: // constructors Mammal():itsAge(2), itsWeight(5){} ~Mammal(){} //accessors int GetAge()const { return itsAge; } void SetAge(int age) { itsAge = age; } int GetWeight() const { return itsWeight; } void SetWeight(int weight) { itsWeight = weight; } //Other methods void Speak()const { cout << "Mammal sound!\n"; }21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: } void Sleep()const { cout << "shhh. I'm sleeping.\n"; } protected: int itsAge; int itsWeight; }; class Dog : public Mammal { public: // Constructors Dog():itsBreed(YORKIE){} ~Dog(){} // Accessors BREED GetBreed() const { return itsBreed; } void SetBreed(BREED breed) { itsBreed = breed; } // Other methods void WagTail() { cout << "Tail wagging...\n"; } void BegForFood() { cout << "Begging for food...\n"; } private: BREED itsBreed; }; int main() { Dog fido; fido.Speak(); fido.WagTail(); cout << "Fido is " << fido.GetAge() << " years old\n"; return 0; Output: Mammal sound! Tail wagging... Fido is 2 years old Analysis: On lines 6-27, the Mammal class is declared (all of its functions are inline to save space here). On lines 29-47, the Dog class is declared as a derived class of Mammal. Thus, by these declarations, all Dogs have an age, a weight, and a breed.On line 51, a Dog is declared: Fido. Fido inherits all the attributes of a Mammal, as well as all the attributes of a Dog. Thus, Fido knows how to WagTail(), but also knows how to Speak() and Sleep(). Constructors and Destructors Dog objects are Mammal objects. This is the essence of the is-a relationship. When Fido is created, his base constructor is called first, creating a Mammal. Then the Dog constructor is called, completing the construction of the Dog object. Because we gave Fido no parameters, the default constructor was called in each case. Fido doesn't exist until he is completely constructed, which means that both his Mammal part and his Dog part must be constructed. Thus, both constructors must be called. When Fido is destroyed, first the Dog destructor will be called and then the destructor for the Mammal part of Fido. Each destructor is given an opportunity to clean up after its own part of Fido. Remember to clean up after your Dog! Listing 12.3 demonstrates this. Listing 12.3. Constructors and destructors called. 1: 2: 3: 4: }; 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: //Listing 12.3 Constructors and destructors called. #include enum BREED { YORKIE, CAIRN, DANDIE, SHETLAND, DOBERMAN, LAB class Mammal { public: // constructors Mammal(); ~Mammal(); //accessors int GetAge() const { return itsAge; } void SetAge(int age) { itsAge = age; } int GetWeight() const { return itsWeight; } void SetWeight(int weight) { itsWeight = weight; } //Other methods void Speak() const { cout << "Mammal sound!\n"; } void Sleep() const { cout << "shhh. I'm sleeping.\n"; } protected: int itsAge; int itsWeight;27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: }; class Dog : public Mammal { public: // Constructors Dog(); ~Dog(); // Accessors BREED GetBreed() const { return itsBreed; } void SetBreed(BREED breed) { itsBreed = breed; } // Other methods void WagTail() { cout << "Tail wagging...\n"; } void BegForFood() { cout << "Begging for food...\n"; } private: BREED itsBreed; }; Mammal::Mammal(): itsAge(1), itsWeight(5) { cout << "Mammal constructor...\n"; } Mammal::~Mammal() { cout << "Mammal destructor...\n"; } Dog::Dog(): itsBreed(YORKIE) { cout << "Dog constructor...\n"; } Dog::~Dog() { cout << "Dog destructor...\n"; } int main() {73: 74: 75: 76: 77: 78: } Dog fido; fido.Speak(); fido.WagTail(); cout << "Fido is " << fido.GetAge() << " years old\n"; return 0; Output: Mammal constructor... Dog constructor... Mammal sound! Tail wagging... Fido is 1 years old Dog destructor... Mammal destructor... Analysis: Listing 12.3 is just like Listing 12.2, except that the constructors and destructors now print to the screen when called. Mammal's constructor is called, then Dog's. At that point the Dog fully exists, and its methods can be called. When fido goes out of scope, Dog's destructor is called, followed by a call to Mammal's destructor. Passing Arguments to Base Constructors It is possible that you'll want to overload the constructor of Mammal to take a specific age, and that you'll want to overload the Dog constructor to take a breed. How do you get the age and weight parameters passed up to the right constructor in Mammal? What if Dogs want to initialize weight but Mammals don't? Base class initialization can be performed during class initialization by writing the base class name, followed by the parameters expected by the base class. Listing 12.4 demonstrates this. Listing 12.4. Overloading constructors in derived classes. 1: 2: 3: 4: }; 5: 6: 7: 8: 9: 10: 11: //Listing 12.4 Overloading constructors in derived classes #include enum BREED { YORKIE, CAIRN, DANDIE, SHETLAND, DOBERMAN, LAB class Mammal { public: // constructors Mammal(); Mammal(int age);12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: ~Mammal(); //accessors int GetAge() const { return itsAge; } void SetAge(int age) { itsAge = age; } int GetWeight() const { return itsWeight; } void SetWeight(int weight) { itsWeight = weight; } //Other methods void Speak() const { cout << "Mammal sound!\n"; } void Sleep() const { cout << "shhh. I'm sleeping.\n"; } protected: int itsAge; int itsWeight; }; class Dog : public Mammal { public: // Constructors Dog(); Dog(int age); Dog(int age, int weight); Dog(int age, BREED breed); Dog(int age, int weight, BREED breed); ~Dog(); // Accessors BREED GetBreed() const { return itsBreed; } void SetBreed(BREED breed) { itsBreed = breed; } // Other methods void WagTail() { cout << "Tail wagging...\n"; } void BegForFood() { cout << "Begging for food...\n"; } private: BREED itsBreed; }; Mammal::Mammal(): itsAge(1), itsWeight(5) {58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: cout << "Mammal constructor...\n"; } Mammal::Mammal(int age): itsAge(age), itsWeight(5) { cout << "Mammal(int) constructor...\n"; } Mammal::~Mammal() { cout << "Mammal destructor...\n"; } Dog::Dog(): Mammal(), itsBreed(YORKIE) { cout << "Dog constructor...\n"; } Dog::Dog(int age): Mammal(age), itsBreed(YORKIE) { cout << "Dog(int) constructor...\n"; } Dog::Dog(int age, int weight): Mammal(age), itsBreed(YORKIE) { itsWeight = weight; cout << "Dog(int, int) constructor...\n"; } Dog::Dog(int age, int weight, BREED breed): Mammal(age), itsBreed(breed) { itsWeight = weight; cout << "Dog(int, int, BREED) constructor...\n"; } Dog::Dog(int age, BREED breed):104: Mammal(age), 105: itsBreed(breed) 106: { 107: cout << "Dog(int, BREED) constructor...\n"; 108: } 109: 110: Dog::~Dog() 111: { 112: cout << "Dog destructor...\n"; 113: } 114: int main() 115: { 116: Dog fido; 117: Dog rover(5); 118: Dog buster(6,8); 119: Dog yorkie (3,YORKIE); 120: Dog dobbie (4,20,DOBERMAN); 121: fido.Speak(); 122: rover.WagTail(); 123: cout << "Yorkie is " << yorkie.GetAge() << " years old\n"; 124: cout << "Dobbie weighs "; 125: cout << dobbie.GetWeight() << " pounds\n"; 126: return 0; 127: } NOTE: The output has been numbered here so that each line can be referred to in the analysis. Output: 1: Mammal constructor... 2: Dog constructor... 3: Mammal(int) constructor... 4: Dog(int) constructor... 5: Mammal(int) constructor... 6: Dog(int, int) constructor... 7: Mammal(int) constructor... 8: Dog(int, BREED) constructor.... 9: Mammal(int) constructor... 10: Dog(int, int, BREED) constructor... 11: Mammal sound! 12: Tail wagging... 13: Yorkie is 3 years old. 14: Dobbie weighs 20 pounds. 15: Dog destructor. . .16: 17: 18: 19: 20: 21: 22: 23: 24: Mammal destructor... Dog destructor... Mammal destructor... Dog destructor... Mammal destructor... Dog destructor... Mammal destructor... Dog destructor... Mammal destructor... Analysis: In Listing 12.4, Mammal's constructor has been overloaded on line 11 to take an integer, the Mammal's age. The implementation on lines 61-66 initializes itsAge with the value passed into the constructor and initializes itsWeight with the value 5. Dog has overloaded five constructors, on lines 35-39. The first is the default constructor. The second takes the age, which is the same parameter that the Mammal constructor takes. The third constructor takes both the age and the weight, the fourth takes the age and breed, and the fifth takes the age, weight, and breed. Note that on line 74 Dog's default constructor calls Mammal's default constructor. Although it is not strictly necessary to do this, it serves as documentation that you intended to call the base constructor, which takes no parameters. The base constructor would be called in any case, but actually doing so makes your intentions explicit. The implementation for the Dog constructor, which takes an integer, is on lines 80-85. In its initialization phase (lines 81-82), Dog initializes its base class, passing in the parameter, and then it initializes its breed. Another Dog constructor is on lines 87-93. This one takes two parameters. Once again it initializes its base class by calling the appropriate constructor, but this time it also assigns weight to its base class's variable itsWeight. Note that you cannot assign to the base class variable in the initialization phase. Because Mammal does not have a constructor that takes this parameter, you must do this within the body of the Dog's constructor. Walk through the remaining constructors to make sure you are comfortable with how they work. Note what is initialized and what must wait for the body of the constructor. The output has been numbered so that each line can be referred to in this analysis. The first two lines of output represent the instantiation of Fido, using the default constructor. In the output, lines 3 and 4 represent the creation of rover. Lines 5 and 6 represent buster. Note that the Mammal constructor that was called is the constructor that takes one integer, but the Dog constructor is the constructor that takes two integers.After all the objects are created, they are used and then go out of scope. As each object is destroyed, first the Dog destructor and then the Mammal destructor is called, five of each in total. Overriding Functions A Dog object has access to all the member functions in class Mammal, as well as to any member functions, such as WagTail(), that the declaration of the Dog class might add. It can also override a base class function. Overriding a function means changing the implementation of a base class function in a derived class. When you make an object of the derived class, the correct function is called. New Term: When a derived class creates a function with the same return type and signature as a member function in the base class, but with a new implementation, it is said to be overriding that method. When you override a function, it must agree in return type and in signature with the function in the base class. The signature is the function prototype other than the return type: that is, the name, the parameter list, and the keyword const if used. New Term: The signature of a function is its name, as well as the number and type of its parameters. The signature does not include the return type. Listing 12.5 illustrates what happens if the Dog class overrides the Speak() method in Mammal. To save room, the accessor functions have been left out of these classes. Listing 12.5. Overriding a base class methodin a derived class. 1: class 2: 3: 4: }; 5: 6: 7: 8: 9: 10: 11: 12: 13: //Listing 12.5 Overriding a base class method in a derived #include enum BREED { YORKIE, CAIRN, DANDIE, SHETLAND, DOBERMAN, LAB class Mammal { public: // constructors Mammal() { cout << "Mammal constructor...\n"; } ~Mammal() { cout << "Mammal destructor...\n"; } //Other methods14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: } void Speak()const { cout << "Mammal sound!\n"; } void Sleep()const { cout << "shhh. I'm sleeping.\n"; } protected: int itsAge; int itsWeight; }; class Dog : public Mammal { public: // Constructors Dog(){ cout << "Dog constructor...\n"; } ~Dog(){ cout << "Dog destructor...\n"; } // Other methods void WagTail() { cout << "Tail wagging...\n"; } void BegForFood() { cout << "Begging for food...\n"; } void Speak()const { cout << "Woof!\n"; } private: BREED itsBreed; }; int main() { Mammal bigAnimal; Dog fido; bigAnimal.Speak(); fido.Speak(); return 0; Output: Mammal constructor... Mammal constructor... Dog constructor... Mammal sound! Woof! Dog destructor... Mammal destructor... Mammal destructor... Analysis: On line 34, the Dog class overrides the Speak() method, causing Dog objects to sayWoof! when the Speak() method is called. On line 42, a Mammal object, bigAnimal, is created, causing the first line of output when the Mammal constructor is called. On line 43, a Dog object, fido, is created, causing the next two lines of output, where the Mammal constructor and then the Dog constructor are called. On line 44, the Mammal object calls its Speak() method, then on line 45, the Dog object calls its Speak() method. The output reflects that the correct methods were called. Finally, the two objects go out of scope and the destructors are called. Overloading Versus Overriding These terms are similar, and they do similar things. When you overload a method, you create more than one method with the same name, but with a different signature. When you override a method, you create a method in a derived class with the same name as a method in the base class and the same signature. Hiding the Base Class Method In the previous listing, the Dog class's Speak() method hides the base class's method. This is just what is wanted, but it can have unexpected results. If Mammal has a method, Move(), which is overloaded, and Dog overrides that method, the Dog method will hide all of the Mammal methods with that name. If Mammal overloads Move() as three methods--one that takes no parameters, one that takes an integer, and one that takes an integer and a direction--and Dog overrides just the Move() method that takes no parameters, it will not be easy to access the other two methods using a Dog object. Listing 12.6 illustrates this problem. Listing 12.6. Hiding methods. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: //Listing 12.6 Hiding methods #include class Mammal { public: void Move() const { cout << "Mammal move one step\n"; } void Move(int distance) const { cout << "Mammal move "; cout << distance <<" _steps.\n"; } protected:15: int itsAge; 16: int itsWeight; 17: }; 18: 19: class Dog : public Mammal 20: { 21: public: 22: // You may receive a warning that you are hiding a function! 23: void Move() const { cout << "Dog move 5 steps.\n"; } 24: }; 25: 26: int main() 27: { 28: Mammal bigAnimal; 29: Dog fido; 30: bigAnimal.Move(); 31: bigAnimal.Move(2); 32: fido.Move(); 33: // fido.Move(10); 34: return 0; 35: } Output: Mammal move one step Mammal move 2 steps. Dog move 5 steps. Analysis: All of the extra methods and data have been removed from these classes. On lines 8 and 9, the Mammal class declares the overloaded Move() methods. On line 18, Dog overrides the version of Move() with no parameters. These are invoked on lines 30-32, and the output reflects this as executed. Line 33, however, is commented out, as it causes a compile-time error. While the Dog class could have called the Move(int) method if it had not overridden the version of Move() without parameters, now that it has done so it must override both if it wishes to use both. This is reminiscent of the rule that if you supply any constructor, the compiler will no longer supply a default constructor. It is a common mistake to hide a base class method when you intend to override it, by forgetting to include the keyword const. const is part of the signature, and leaving it off changes the signature and thus hides the method rather than overriding it. Overriding Versus Hiding In the next section, virtual methods are described. Overriding a virtual method supports polymorphism--hiding it undermines polymorphism. You'll see more on this very soon.Calling the Base Method If you have overridden the base method, it is still possible to call it by fully qualifying the name of the method. You do this by writing the base name, followed by two colons and then the method name. For example: Mammal::Move(). It would have been possible to rewrite line 28 in Listing 12.6 so that it would compile, by writing 28: fido.Mammal::Move(10); This calls the Mammal method explicitly. Listing 12.7 fully illustrates this idea. Listing 12.7. Calling base method from overridden method. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: //Listing 12.7 Calling base method from overridden method. #include class Mammal { public: void Move() const { cout << "Mammal move one step\n"; } void Move(int distance) const { cout << "Mammal move " << distance; cout << " steps.\n"; } protected: int itsAge; int itsWeight; }; class Dog : public Mammal { public: void Move()const; }; void Dog::Move() const { cout << "In dog move...\n"; Mammal::Move(3); }32: 33: 34: 35: 36: 37: 38: 39: 40: } int main() { Mammal bigAnimal; Dog fido; bigAnimal.Move(2); fido.Mammal::Move(6); return 0; Output: Mammal move 2 steps. Mammal move 6 steps. Analysis: On line 35, a Mammal, bigAnimal, is created, and on line 36, a Dog, fido, is created. The method call on line 37 invokes the Move() method of Mammal, which takes an int. The programmer wanted to invoke Move(int) on the Dog object, but had a problem. Dog overrides the Move() method, but does not overload it and does not provide a version that takes an int. This is solved by the explicit call to the base class Move(int) method on line 33. DO extend the functionality of tested classes by deriving. DO change the behavior of certain functions in the derived class by overriding the base class methods. DON'T hide a base class function by changing the function signature. Virtual Methods This chapter has emphasized the fact that a Dog object is a Mammal object. So far that has meant only that the Dog object has inherited the attributes (data) and capabilities (methods) of its base class. In C++ the is-a relationship runs deeper than that, however. C++ extends its polymorphism to allow pointers to base classes to be assigned to derived class objects. Thus, you can write Mammal* pMammal = new Dog; This creates a new Dog object on the heap and returns a pointer to that object, which it assigns to a pointer to Mammal. This is fine, because a dog is a mammal. NOTE: This is the essence of polymorphism. For example, you could create many different types of windows, including dialog boxes, scrollable windows, and list boxes, and give them each a virtual draw() method. By creating a pointer to a window and assigning dialog boxes and other derived types to that pointer, you can call draw()without regard to the actual run-time type of the object pointed to. The correct draw() function will be called. You can then use this pointer to invoke any method on Mammal. What you would like is for those methods that are overridden in Dog() to call the correct function. Virtual functions let you do that. Listing 12.8 illustrates how this works, and what happens with non-virtual methods. Listing 12.8. Using virtual methods. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: } //Listing 12.8 Using virtual methods #include class Mammal { public: Mammal():itsAge(1) { cout << "Mammal constructor...\n"; } ~Mammal() { cout << "Mammal destructor...\n"; } void Move() const { cout << "Mammal move one step\n"; } virtual void Speak() const { cout << "Mammal speak!\n"; } protected: int itsAge; }; class Dog : public Mammal { public: Dog() { cout << "Dog Constructor...\n"; } ~Dog() { cout << "Dog destructor...\n"; } void WagTail() { cout << "Wagging Tail...\n"; } void Speak()const { cout << "Woof!\n"; } void Move()const { cout << "Dog moves 5 steps...\n"; } }; int main() { Mammal *pDog = new Dog; pDog->Move(); pDog->Speak(); return 0;Output: Mammal constructor... Dog Constructor... Mammal move one step Woof! Analysis: On line 11, Mammal is provided a virtual method--speak(). The designer of this class thereby signals that she expects this class eventually to be another class's base type. The derived class will probably want to override this function. On line 30, a pointer to Mammal is created (pDog), but it is assigned the address of a new Dog object. Because a dog is a mammal, this is a legal assignment. The pointer is then used to call the Move() function. Because the compiler knows pDog only to be a Mammal, it looks to the Mammal object to find the Move() method. On line 32, the pointer then calls the Speak() method. Because Speak() is virtual, the Speak() method overridden in Dog is invoked. This is almost magical. As far as the calling function knew, it had a Mammal pointer, but here a method on Dog was called. In fact, if you had an array of pointers to Mammal, each of which pointed to a subclass of Mammal, you could call each in turn and the correct function would be called. Listing 12.9 illustrates this idea. Listing 12.9. Multiple virtual functions called in turn. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: //Listing 12.9 Multiple virtual functions called in turn #include class Mammal { public: Mammal():itsAge(1) { } ~Mammal() { } virtual void Speak() const { cout << "Mammal speak!\n"; } protected: int itsAge; }; class Dog : public Mammal { public: void Speak()const { cout << "Woof!\n"; } };21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: class Cat : public Mammal { public: void Speak()const { cout << "Meow!\n"; } }; class Horse : public Mammal { public: void Speak()const { cout << "Winnie!\n"; } }; class Pig : public Mammal { public: void Speak()const { cout << "Oink!\n"; } }; int main() { Mammal* theArray[5]; Mammal* ptr; int choice, i; for ( i = 0; i<5; i++) { cout << "(1)dog (2)cat (3)horse (4)pig: "; cin >> choice; switch (choice) { case 1: ptr = new Dog; break; case 2: ptr = new Cat; break; case 3: ptr = new Horse; break; case 4: ptr = new Pig; break; default: ptr = new Mammal; break; } theArray[i] = ptr; } for (i=0;i<5;i++) theArray[i]->Speak();67: 68: } return 0; Output: (1)dog (2)cat (3)horse (1)dog (2)cat (3)horse (4)pig: (1)dog (2)cat (3)horse (4)pig: (1)dog (2)cat (3)horse (4)pig: (1)dog (2)cat (3)horse (4)pig: Woof! Meow! Winnie! Oink! Mammal speak! (4)pig: 1 2 3 4 5 Analysis: This stripped-down program, which provides only the barest functionality to each class, illustrates virtual functions in their purest form. Four classes are declared; Dog, Cat, Horse, and Pig are all derived from Mammal. On line 10, Mammal's Speak() function is declared to be virtual. On lines 18, 25, 32, and 38, the four derived classes override the implementation of Speak(). The user is prompted to pick which objects to create, and the pointers are added to the array on lines 46-64. NOTE: At compile time, it is impossible to know which objects will be created, and thus which Speak() methods will be invoked. The pointer ptr is bound to its object at runtime. This is called dynamic binding, or run-time binding, as opposed to static binding, or compile-time binding. How Virtual Functions Work When a derived object, such as a Dog object, is created, first the constructor for the base class is called and then the constructor for the derived class is called. Figure 12.2 shows what the Dog object looks like after it is created. Note that the Mammal part of the object is contiguous in memory with the Dog part. Figure 12.2. The Dog object after it is created. When a virtual function is created in an object, the object must keep track of that function. Many compilers build a virtual function table, called a v-table. One of these is kept for each type, and each object of that type keeps a virtual table pointer (called a vptr or v-pointer), which points to that table. While implementations vary, all compilers must accomplish the same thing, so you won't be toowrong with this description. Figure 12.3. The v-table of a Mammal. Each object's vptr points to the v-table which, in turn, has a pointer to each of the virtual functions. (Note, pointers to functions will be discussed in depth on Day 14, "Special Classes and Functions.") When the Mammal part of the Dog is created, the vptr is initialized to point to the correct part of the v-table, as shown in Figure 12.3. Figure 12.4. The v-table of a Dog. When the Dog constructor is called, and the Dog part of this object is added, the vptr is adjusted to point to the virtual function overrides (if any) in the Dog object (see Figure 12.4) . When a pointer to a Mammal is used, the vptr continues to point to the correct function, depending on the "real" type of the object. Thus, when Speak() is invoked, the correct function is invoked. You Cant Get There from Here If the Dog object had a method, WagTail(), which is not in the Mammal, you could not use the pointer to Mammal to access that method (unless you cast it to be a pointer to Dog). Because WagTail() is not a virtual function, and because it is not in a Mammal object, you can't get there without either a Dog object or a Dog pointer. Although you can transform the Mammal pointer into a Dog pointer, there are usually far better and safer ways to call the WagTail() method. C++ frowns on explicit casts because they are error- prone. This subject will be addressed in depth when multiple inheritance is covered tomorrow, and again when templates are covered on Day 20, "Exceptions and Error Handling." Slicing Note that the virtual function magic operates only on pointers and references. Passing an object by value will not enable the virtual functions to be invoked. Listing 12.10 illustrates this problem. Listing 12.10. Data slicing when passing by value. 1: 2: 3: 4: 5: 6: 7: 8: 9: //Listing 12.10 Data slicing with passing by value #include enum BOOL { FALSE, TRUE }; class Mammal { public: Mammal():itsAge(1) { }10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: ~Mammal() { } virtual void Speak() const { cout << "Mammal speak!\n"; } protected: int itsAge; }; class Dog : public Mammal { public: void Speak()const { cout << "Woof!\n"; } }; class Cat : public Mammal { public: void Speak()const { cout << "Meow!\n"; } }; void ValueFunction (Mammal); void PtrFunction (Mammal*); void RefFunction (Mammal&); int main() { Mammal* ptr=0; int choice; while (1) { BOOL fQuit = FALSE; cout << "(1)dog (2)cat (0)Quit: "; cin >> choice; switch (choice) { case 0: fQuit = TRUE; break; case 1: ptr = new Dog; break; case 2: ptr = new Cat; break; default: ptr = new Mammal; break; } if (fQuit) break; PtrFunction(ptr); RefFunction(*ptr); ValueFunction(*ptr);56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: } } return 0; } void ValueFunction (Mammal MammalValue) { MammalValue.Speak(); } void PtrFunction (Mammal * pMammal) { pMammal->Speak(); } void RefFunction (Mammal & rMammal) { rMammal.Speak(); Output: (1)dog (2)cat (0)Quit: 1 Woof Woof Mammal Speak! (1)dog (2)cat (0)Quit: 2 Meow! Meow! Mammal Speak! (1)dog (2)cat (0)Quit: 0 Analysis: On lines 6-26, stripped-down versions of the Mammal, Dog, and Cat classes are declared. Three functions are declared--PtrFunction(), RefFunction(), and ValueFunction(). They take a pointer to a Mammal, a Mammal reference, and a Mammal object, respectively. All three functions then do the same thing--they call the Speak() method. The user is prompted to choose a Dog or Cat, and based on the choice he makes, a pointer to the correct type is created on lines 44-49. In the first line of the output, the user chooses Dog. The Dog object is created on the free store on line 44. The Dog is then passed as a pointer, as a reference, and by value to the three functions. The pointer and references all invoke the virtual functions, and the Dog->Speak() member function is invoked. This is shown on the first two lines of output after the user's choice. The dereferenced pointer, however, is passed by value. The function expects a Mammal object, and so the compiler slices down the Dog object to just the Mammal part. At that point, the Mammal Speak() method is called, as reflected in the third line of output after the user's choice.This experiment is then repeated for the Cat object, with similar results. Virtual Destructors It is legal and common to pass a pointer to a derived object when a pointer to a base object is expected. What happens when that pointer to a derived subject is deleted? If the destructor is virtual, as it should be, the right thing happens--the derived class's destructor is called. Because the derived class's destructor will automatically invoke the base class's destructor, the entire object will be properly destroyed. The rule of thumb is this: If any of the functions in your class are virtual, the destructor should be as well. Virtual Copy Constructors As previously stated, no constructor can be virtual. Nonetheless, there are times when your program desperately needs to be able to pass in a pointer to a base object and have a copy of the correct derived object that is created. A common solution to this problem is to create a Clone() method in the base class and to make that be virtual. The Clone() method creates a new object copy of the current class, and returns that object. Because each derived class overrides the Clone() method, a copy of the derived class is created. Listing 12.11 illustrates how this is used. Listing 12.11. Virtual copy constructor. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: //Listing 12.11 Virtual copy constructor #include class Mammal { public: Mammal():itsAge(1) { cout << "Mammal constructor...\n"; } ~Mammal() { cout << "Mammal destructor...\n"; } Mammal (const Mammal & rhs); virtual void Speak() const { cout << "Mammal speak!\n"; } virtual Mammal* Clone() { return new Mammal(*this); } int GetAge()const { return itsAge; } protected: int itsAge; }; Mammal::Mammal (const Mammal & rhs):itsAge(rhs.GetAge())19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: { cout << "Mammal Copy Constructor...\n"; } class Dog : public Mammal { public: Dog() { cout << "Dog constructor...\n"; } ~Dog() { cout << "Dog destructor...\n"; } Dog (const Dog & rhs); void Speak()const { cout << "Woof!\n"; } virtual Mammal* Clone() { return new Dog(*this); } }; Dog::Dog(const Dog & rhs): Mammal(rhs) { cout << "Dog copy constructor...\n"; } class Cat : public Mammal { public: Cat() { cout << "Cat constructor...\n"; } ~Cat() { cout << "Cat destructor...\n"; } Cat (const Cat &); void Speak()const { cout << "Meow!\n"; } virtual Mammal* Clone() { return new Cat(*this); } }; Cat::Cat(const Cat & rhs): Mammal(rhs) { cout << "Cat copy constructor...\n"; } enum ANIMALS { MAMMAL, DOG, CAT}; const int NumAnimalTypes = 3; int main() { Mammal *theArray[NumAnimalTypes]; Mammal* ptr; int choice, i; for ( i = 0; i> choice; switch (choice) { case DOG: ptr = new Dog; break; case CAT: ptr = new Cat; break; default: ptr = new Mammal; break; } theArray[i] = ptr; } Mammal *OtherArray[NumAnimalTypes]; for (i=0;iSpeak(); OtherArray[i] = theArray[i]->Clone(); } for (i=0;iSpeak(); return 0; (1)dog (2)cat (3)Mammal: 1 Mammal constructor... Dog constructor... (1)dog (2)cat (3)Mammal: 2 Mammal constructor... Cat constructor... (1)dog (2)cat (3)Mammal: 3 Mammal constructor... Woof! Mammal Copy Constructor... Dog copy constructor... Meow! Mammal Copy Constructor... Cat copy constructor... Mammal speak! Mammal Copy Constructor... Woof! Meow! Mammal speak! Analysis: Listing 12.11 is very similar to the previous two listings, except that a new virtual method has been added to the Mammal class: Clone(). This method returns a pointer to a new Mammal object by calling the copy constructor, passing in itself (*this) as a const reference.Dog and Cat both override the Clone() method, initializing their data and passing in copies of themselves to their own copy constructors. Because Clone() is virtual, this will effectively create a virtual copy constructor, as shown on line 81. The user is prompted to choose dogs, cats, or mammals, and these are created on lines 62-74. A pointer to each choice is stored in an array on line 75. As the program iterates over the array, each object has its Speak() and its Clone() methods called, in turn, on lines 80 and 81. The result of the Clone() call is a pointer to a copy of the object, which is then stored in a second array on line 81. On line 1 of the output, the user is prompted and responds with 1, choosing to create a dog. The Mammal and Dog constructors are invoked. This is repeated for Cat and for Mammal on lines 4-8 of the constructor. Line 9 of the constructor represents the call to Speak() on the first object, the Dog. The virtual Speak() method is called, and the correct version of Speak() is invoked. The Clone() function is then called, and as this is also virtual, Dog's Clone() method is invoked, causing the Mammal constructor and the Dog copy constructor to be called. The same is repeated for Cat on lines 12-14, and then for Mammal on lines 15 and 16. Finally, the new array is iterated, and each of the new objects has Speak() invoked. The Cost of Virtual Methods Because objects with virtual methods must maintain a v-table, there is some overhead in having virtual methods. If you have a very small class from which you do not expect to derive other classes, there may be no reason to have any virtual methods at all. Once you declare any methods virtual, you've paid most of the price of the v-table (although each entry does add a small memory overhead). At that point, you'll want the destructor to be virtual, and the assumption will be that all other methods probably will be virtual as well. Take a long hard look at any non-virtual methods, and be certain you understand why they are not virtual. DO use virtual methods when you expect to derive from a class. DO use a virtual destructor if any methods are virtual. DON'T mark the constructor as virtual. Summary Today you learned how derived classes inherit from base classes. This chapter discussed public inheritance and virtual functions. Classes inherit all the public and protected data and functions from their base classes.Protected access is public to derived classes and private to all other objects. Even derived classes cannot access private data or functions in their base classes. Constructors can be initialized before the body of the constructor. It is at this time that base constructors are invoked and parameters can be passed to the base class. Functions in the base class can be overridden in the derived class. If the base class functions are virtual, and if the object is accessed by pointer or reference, the derived class's functions will be invoked, based on the run-time type of the object pointed to. Methods in the base class can be invoked by explicitly naming the function with the prefix of the base class name and two colons. For example, if Dog inherits from Mammal, Mammal's walk() method can be called with Mammal::walk(). In classes with virtual methods, the destructor should almost always be made virtual. A virtual destructor ensures that the derived part of the object will be freed when delete is called on the pointer. Constructors cannot be virtual. Virtual copy constructors can be effectively created by making a virtual member function that calls the copy constructor. Q&A Q. Are inherited members and functions passed along to subsequent generations? If Dog derives from Mammal, and Mammal derives from Animal, does Dog inherit Animal's functions and data? A. Yes. As derivation continues, derived classes inherit the sum of all the functions and data in all their base classes. Q. If, in the example above, Mammal overrides a function in Animal, which does Dog get, the original or the overridden function? A. If Dog inherits from Mammal, it gets the function in the state Mammal has it: the overridden function. Q. Can a derived class make a public base function private? A. Yes, and it remains private for all subsequent derivation. Q. Why not make all class functions virtual? A. There is overhead with the first virtual function in the creation of a v-table. After that, the overhead is trivial. Many C++ programmers feel that if one function is virtual, all others should be. Other programmers disagree, feeling that there should always be a reason for what you do. Q. If a function (SomeFunc()) is virtual in a base class and is also overloaded, so as totake either an integer or two integers, and the derived class overrides the form taking one integer, what is called when a pointer to a derived object calls the two-integer form? A. The overriding of the one-int form hides the entire base class function, and thus you will get a compile error complaining that that function requires only one int. Workshop The Workshop provides quiz questions to help you solidify your understanding of the material that was covered, and exercises to provide you with experience in using what you've learned. Try to answer the quiz and exercise questions before checking the answers in Appendix D, and make sure you understand the answers before continuing to the next chapter. Quiz 1. What is a v-table? 2. What is a virtual destructor? 3. How do you show the declaration of a virtual constructor? 4. How can you create a virtual copy constructor? 5. How do you invoke a base member function from a derived class in which you've overridden that function? 6. How do you invoke a base member function from a derived class in which you have not overridden that function? 7. If a base class declares a function to be virtual, and a derived class does not use the term virtual when overriding that class, is it still virtual when inherited by a third-generation class? 8. What is the protected keyword used for? Exercises 1. Show the declaration of a virtual function that takes an integer parameter and returns void. 2. Show the declaration of a class Square, which derives from Rectangle, which in turn derives from Shape. 3. If, in Exercise 2, Shape takes no parameters, Rectangle takes two (length and width), but Square takes only one (length), show the constructor initialization for Square. 4. Write a virtual copy constructor for the class Square (in Exercise 3).5. BUG BUSTERS: What is wrong with this code snippet? void SomeFunction (Shape); Shape * pRect = new Rectangle; SomeFunction(*pRect); 6. BUG BUSTERS: What is wrong with this code snippet? class Shape() { public: Shape(); virtual ~Shape(); virtual Shape(const Shape&); }; Problems with Single Inheritance Suppose you've been working with your animal classes for a while and you've divided the class hierarchy into Birds and Mammals. The Bird class includes the member function Fly(). The Mammal class has been divided into a number of types of Mammals, including Horse. The Horse class includes the member functions Whinny() and Gallop(). Suddenly, you realize you need a Pegasus object: a cross between a Horse and a Bird. A Pegasus can Fly(), it can Whinny(), and it can Gallop(). With single inheritance, you're in quite a jam. You can make Pegasus a Bird, but then it won't be able to Whinny() or Gallop(). You can make it a Horse, but then it won't be able to Fly(). Your first solution is to copy the Fly() method into the Pegasus class and derive Pegasus from Horse. This works fine, at the cost of having the Fly() method in two places (Bird and Pegasus). If you change one, you must remember to change the other. Of course, a developer who comes along months or years later to maintain your code must also know to fix both places. Soon, however, you have a new problem. You wish to create a list of Horse objects and a list of Bird objects. You'd like to be able to add your Pegasus objects to either list, but if a Pegasus is a horse, you can't add it to a list of birds.You have a couple of potential solutions. You can rename the Horse method Gallop() to Move(), and then override Move() in your Pegasus object to do the work of Fly(). You would then override Move() in your other horses to do the work of Gallop(). Perhaps Pegasus could be clever enough to gallop short distances and fly longer distances. Pegasus::Move(long distance) { if (distance > veryFar) fly(distance); else gallop(distance); } This is a bit limiting. Perhaps one day Pegasus will want to fly a short distance or gallop a long distance. Your next solution might be to move Fly() up into Horse, as illustrated in Listing 13.1. The problem is that most horses can't fly, so you have to make this method do nothing unless it is a Pegasus. Listing 13.1. If horses could fly... 1: // Listing 13.1. If horses could fly... 2: // Percolating Fly() up into Horse 3: 4: #include 5: 6: class Horse 7: { 8: public: 9: void Gallop(){ cout << "Galloping...\n"; } 10: virtual void Fly() { cout << "Horses can't fly.\n" ; } 11: private: 12: int itsAge; 13: }; 14: 15: class Pegasus : public Horse 16: { 17: public: 18: virtual void Fly() { cout << "I can fly! I can fly! I can fly!\n"; } 19: }; 20: 21: const int NumberHorses = 5; 22: int main() 23: { 24: Horse* Ranch[NumberHorses];25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: } Horse* pHorse; int choice,i; for (i=0; i> choice; if (choice == 2) pHorse = new Pegasus; else pHorse = new Horse; Ranch[i] = pHorse; } cout << "\n"; for (i=0; iFly(); delete Ranch[i]; } return 0; Output: (1)Horse (2)Pegasus: 1 (1)Horse (2)Pegasus: 2 (1)Horse (2)Pegasus: 1 (1)Horse (2)Pegasus: 2 (1)Horse (2)Pegasus: 1 Horses can't I can fly! I Horses can't I can fly! I Horses can't fly. can fly! I can fly! fly. can fly! I can fly! fly. Analysis: This program certainly works, though at the expense of the Horse class having a Fly() method. On line 10, the method Fly() is provided to Horse. In a real-world class, you might have it issue an error, or fail quietly. On line 18, the Pegasus class overrides the Fly() method to "do the right thing," represented here by printing a happy message. The array of Horse pointers on line 24 is used to demonstrate that the correct Fly() method is called based on the runtime binding of the Horse or Pegasus object. Percolating Upward Putting the required function higher in the class hierarchy is a common solution to this problem and results in many functions "percolating up" into the base class. The base class is then in grave danger of becoming a global namespace for all the functions that might be used by any of the derived classes.This can seriously undermine the class typing of C++, and can create a large and cumbersome base class. In general, you want to percolate shared functionality up the hierarchy, without migrating the interface of each class. This means that if two classes that share a common base class (for example, Horse and Bird both share Animal) have a function in common (both birds and horses eat, for example), you'll want to move that functionality up into the base class and create a virtual function. What you'll want to avoid, however, is percolating an interface (like Fly up where it doesn't belong), just so you can call that function only on some derived classes. Casting Down An alternative to this approach, still within single inheritance, is to keep the Fly() method within Pegasus, and only call it if the pointer is actually pointing to a Pegasus object. To make this work, you'll need to be able to ask your pointer what type it is really pointing to. This is known as Run Time Type Identification (RTTI). Using RTTI has only recently become an official part of C++. If your compiler does not support RTTI, you can mimic it by putting a method that returns an enumerated type in each of the classes. You can then test that type at runtime and call Fly() if it returns Pegasus. NOTE: Beware of adding RTTI to your classes. Use of it may be an indication of poor design. Consider using virtual functions, templates, or multiple inheritance instead. In order to call Fly() however, you must cast the pointer, telling it that the object it is pointing to is a Pegasus object, not a Horse. This is called casting down, because you are casting the Horse object down to a more derived type. C++ now officially, though perhaps reluctantly, supports casting down using the new dynamic_cast operator. Here's how it works. If you have a pointer to a base class such as Horse, and you assign to it a pointer to a derived class, such as Pegasus, you can use the Horse pointer polymorphically. If you then need to get at the Pegasus object, you create a Pegasus pointer and use the dynamic_cast operator to make the conversion. At runtime, the base pointer will be examined. If the conversion is proper, your new Pegasus pointer will be fine. If the conversion is improper, if you didn't really have a Pegasus object after all, then your new pointer will be null. Listing 13.2 illustrates this point. Listing 13.2. Casting down.1: // Listing 13.2 Using dynamic_cast. 2: // Using rtti 3: 4: #include 5: enum TYPE { HORSE, PEGASUS }; 6: 7: class Horse 8: { 9: public: 10: virtual void Gallop(){ cout << "Galloping...\n"; } 11: 12: private: 13: int itsAge; 14: }; 15: 16: class Pegasus : public Horse 17: { 18: public: 19: 20: virtual void Fly() { cout << "I can fly! I can fly! I can fly!\n"; } 21: }; 22: 23: const int NumberHorses = 5; 24: int main() 25: { 26: Horse* Ranch[NumberHorses]; 27: Horse* pHorse; 28: int choice,i; 29: for (i=0; i> choice; 33: if (choice == 2) 34: pHorse = new Pegasus; 35: else 36: pHorse = new Horse; 37: Ranch[i] = pHorse; 38: } 39: cout << "\n"; 40: for (i=0; i (Ranch[i]); 42: if (pPeg) 43: pPeg->Fly(); 44: else45: 46: 47: 48: 49: 50: cout << "Just a horse\n"; delete Ranch[i]; } return 0; Output: (1)Horse (2)Pegasus: 1 (1)Horse (2)Pegasus: 2 (1)Horse (2)Pegasus: 1 (1)Horse (2)Pegasus: 2 (1)Horse (2)Pegasus: 1 Just a horse I can fly! I can fly! I can fly! Just a horse I can fly! I can fly! I can fly! Just a horse Analysis: This solution also works. Fly() is kept out of Horse, and is not called on Horse objects. When it is called on Pegasus objects, however, they must be explicitly cast; Horse objects don't have the method Fly(), so the pointer must be told it is pointing to a Pegasus object before being used. The need for you to cast the Pegasus object is a warning that something may be wrong with your design. This program effectively undermines the virtual function polymorphism, because it depends on casting the object to its real runtime type. Adding to Two Lists The other problem with these solutions is that you've declared Pegasus to be a type of Horse, so you cannot add a Pegasus object to a list of Birds. You've paid the price of either moving Fly() up into Horse, or casting down the pointer, and yet you still don't have the full functionality you need. One final single inheritance solution presents itself. You can push Fly(), Whinny(), and Gallop() all up into a common base class of both Bird and Horse: Animal. Now, instead of having a list of Birds and a list of Horses, you can have one unified list of Animals. This works, but percolates more functionality up into the base classes. Alternatively, you can leave the methods where they are, but cast down Horses and Birds and Pegasus objects, but that is even worse! DO move functionality up the inheritance hierarchy. DON'T move interface up the inheritance hierarchy. DO avoid switching on the runtime type of the object--use virtualmethods, templates, and multiple inheritance. DON'T cast pointers to base objects down to derived objects. Multiple Inheritance It is possible to derive a new class from more than one base class. This is called Multiple Inheritance. To derive from more than the base class, you separate each base class by commas in the class designation. Listing 13.3 illustrates how to declare Pegasus so that it derives from both Horses and Birds. The program then adds Pegasus objects to both types of lists. Listing 13.3. Multiple inheritance. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: // Listing 13.3. Multiple inheritance. // Multiple Inheritance #include class Horse { public: Horse() { cout << "Horse constructor... "; } virtual ~Horse() { cout << "Horse destructor... "; } virtual void Whinny() const { cout << "Whinny!... "; } private: int itsAge; }; class Bird { public: Bird() { cout << "Bird constructor... "; } virtual ~Bird() { cout << "Bird destructor... "; } virtual void Chirp() const { cout << "Chirp... "; } virtual void Fly() const { cout << "I can fly! I can fly! I can fly! "; } private: int itsWeight; }; class Pegasus : public Horse, public Bird { public:33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: void Chirp() const { Whinny(); } Pegasus() { cout << "Pegasus constructor... "; } ~Pegasus() { cout << "Pegasus destructor... "; } }; const int MagicNumber = 2; int main() { Horse* Ranch[MagicNumber]; Bird* Aviary[MagicNumber]; Horse * pHorse; Bird * pBird; int choice,i; for (i=0; i> choice; if (choice == 2) pHorse = new Pegasus; else pHorse = new Horse; Ranch[i] = pHorse; } for (i=0; i> choice; if (choice == 2) pBird = new Pegasus; else pBird = new Bird; Aviary[i] = pBird; } cout << "\n"; for (i=0; iWhinny(); delete Ranch[i]; } for (i=0; iChirp();79: 80: 81: 82: 83: } Aviary[i]->Fly(); delete Aviary[i]; } return 0; Output: (1)Horse (2)Pegasus: 1 Horse constructor... (1)Horse (2)Pegasus: 2 Horse constructor... Bird constructor... Pegasus constructor... (1)Bird (2)Pegasus: 1 Bird constructor... (1)Bird (2)Pegasus: 2 Horse constructor... Bird constructor... Pegasus constructor... Ranch[0]: Whinny!... Horse destructor... Ranch[1]: Whinny!... Pegasus destructor... Bird destructor... Horse destructor... Aviary[0]: Chirp... I can fly! I can fly! I can fly! Bird destructor... Aviary[1]: Whinny!... I can fly! I can fly! I can fly! Pegasus destructor... Bird destructor... Horse destructor... Aviary[0]: Chirp... I can fly! I can fly! I can fly! Bird destructor... Aviary[1]: Whinny!... I can fly! I can fly! I can fly! Pegasus destructor.. Bird destructor... Horse destructor... Analysis: On lines 6-14, a Horse class is declared. The constructor and destructor print out a message, and the Whinny() method prints the word Whinny! On lines 16-25, a Bird class is declared. In addition to its constructor and destructor, this class has two methods: Chirp() and Fly(), both of which print identifying messages. In a real program these might, for example, activate the speaker or generate animated images. Finally, on lines 30-36, the class Pegasus is declared. It derives both from Horse and from Bird. The Pegasus class overrides the Chirp() method to call the Whinny() method, which it inherits from Horse. Two lists are created, a Ranch with pointers to Horse on line 41, and an Aviary with pointers to Bird on line 42. On lines 46-55, Horse and Pegasus objects are added to the Ranch. On lines 56- 65, Bird and Pegasus objects are added to the Aviary. Invocations of the virtual methods on both the Bird pointers and the Horse pointers do the right things for Pegasus objects. For example, on line 78 the members of the Aviary array are used to call Chirp() on the objects to which they point. The Bird class declares this to be a virtual method, so the right function is called for each object.Note that each time a Pegasus object is created, the output reflects that both the Bird part and the Horse part of the Pegasus object is also created. When a Pegasus object is destroyed, the Bird and Horse parts are destroyed as well, thanks to the destructors being made virtual. Declaring Multiple Inheritance Declare an object to inherit from more than one class by listing the base classes following the colon after the class name. Separate the base classes by commas. Example 1: class Pegasus : public Horse, public Bird Example 2: class Schnoodle : public Schnauzer, public Poodle The Parts of a Multiply Inherited Object When the Pegasus object is created in memory, both of the base classes form part of the Pegasus object, as illustrated in Figure 13.1. Figure 13.1. Multiply inherited objects. A number of issues arise with objects with multiple base classes. For example, what happens if two base classes that happen to have the same name have virtual functions or data? How are multiple base class constructors initialized? What happens if multiple base classes both derive from the same class? The next sections will answer these questions, and explore how multiple inheritance can be put to work. Constructors in Multiply Inherited Objects If Pegasus derives from both Horse and Bird, and each of the base classes has constructors that take parameters, the Pegasus class initializes these constructors in turn. Listing 13.4 illustrates how this is done. Listing 13.4. Calling multiple constructors. 1: 2: 3: 4: 5: ; 6: // Listing 13.4 // Calling multiple constructors #include typedef int HANDS; enum COLOR { Red, Green, Blue, Yellow, White, Black, Brown } enum BOOL { FALSE, TRUE };7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: } 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: class Horse { public: Horse(COLOR color, HANDS height); virtual ~Horse() { cout << "Horse destructor...\n"; } virtual void Whinny()const { cout << "Whinny!... "; } virtual HANDS GetHeight() const { return itsHeight; } virtual COLOR GetColor() const { return itsColor; } private: HANDS itsHeight; COLOR itsColor; }; Horse::Horse(COLOR color, HANDS height): itsColor(color),itsHeight(height) { cout << "Horse constructor...\n"; } class Bird { public: Bird(COLOR color, BOOL migrates); virtual ~Bird() {cout << "Bird destructor...\n"; } virtual void Chirp()const { cout << "Chirp... "; } virtual void Fly()const { cout << "I can fly! I can fly! I can fly! "; } virtual COLOR GetColor()const { return itsColor; } virtual BOOL GetMigration() const { return itsMigration; private: COLOR itsColor; BOOL itsMigration; }; Bird::Bird(COLOR color, BOOL migrates): itsColor(color), itsMigration(migrates) { cout << "Bird constructor...\n"; } class Pegasus : public Horse, public Bird52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: } { public: void Chirp()const { Whinny(); } Pegasus(COLOR, HANDS, BOOL,long); ~Pegasus() {cout << "Pegasus destructor...\n";} virtual long GetNumberBelievers() const { return itsNumberBelievers; } private: long itsNumberBelievers; }; Pegasus::Pegasus( COLOR aColor, HANDS height, BOOL migrates, long NumBelieve): Horse(aColor, height), Bird(aColor, migrates), itsNumberBelievers(NumBelieve) { cout << "Pegasus constructor...\n"; } int main() { Pegasus *pPeg = new Pegasus(Red, 5, TRUE, 10); pPeg->Fly(); pPeg->Whinny(); cout << "\nYour Pegasus is " << pPeg->GetHeight(); cout << " hands tall and "; if (pPeg->GetMigration()) cout << "it does migrate."; else cout << "it does not migrate."; cout << "\nA total of " << pPeg->GetNumberBelievers(); cout << " people believe it exists.\n"; delete pPeg; return 0; Output: Horse constructor... Bird constructor... Pegasus constructor...I can fly! I can fly! I can fly! Whinny!... Your Pegasus is 5 hands tall and it does migrate. A total of 10 people believe it exists. Pegasus destructor... Bird destructor... Horse destructor... Analysis: On lines 8-19, the Horse class is declared. The constructor takes two parameters, both using enumerations declared on lines 5 and 6. The implementation of the constructor on lines 21-25 simply initializes the member variables and prints a message. On lines 27-43, the Bird class is declared, and the implementation of its constructor is on lines 45- 49. Again, the Bird class takes two parameters. Interestingly, the Horse constructor takes color (so that you can detect horses of different colors), and the Bird constructor takes the color of the feathers (so those of one feather can stick together). This leads to a problem when you want to ask the Pegasus for its color, which you'll see in the next example. The Pegasus class itself is declared on lines 51-64, and its constructor is on lines 66-72. The initialization of the Pegasus object includes three statements. First, the Horse constructor is initialized with color and height. Then the Bird constructor is initialized with color and the Boolean. Finally, the Pegasus member variable itsNumberBelievers is initialized. Once all that is accomplished, the body of the Pegasus constructor is called. In the main() function, a Pegasus pointer is created and used to access the member functions of the base objects. Ambiguity Resolution In Listing 13.4, both the Horse class and the Bird class have a method GetColor(). You may need to ask the Pegasus object to return its color, but you have a problem: the Pegasus class inherits from both Bird and Horse. They both have a color, and their methods for getting that color have the same names and signature. This creates an ambiguity for the compiler, which you must resolve. If you simply write COLOR currentColor = pPeg->GetColor(); you will get a compiler error: Member is ambiguous: `Horse::GetColor' and `Bird::GetColor' You can resolve the ambiguity with an explicit call to the function you wish to invoke: COLOR currentColor = pPeg->Horse::GetColor();Anytime you need to resolve which class a member function or member data inherits from, you can fully qualify the call by prepending the class name to the base class data or function. Note that if Pegasus were to override this function, the problem would be moved, as it should be, into the Pegasus member function: virtual COLOR GetColor()const { return Horse::itsColor; } This hides the problem from clients of the Pegasus class, and encapsulates within Pegasus the knowledge of which base class it wishes to inherit its color from. A client is still free to force the issue by writing: COLOR currentColor = pPeg->Bird::GetColor(); Inheriting from Shared Base Class What happens if both Bird and Horse inherit from a common base class, such as Animal? Figure 13.2 illustrates what this looks like. As you can see in Figure 13.2, two base class objects exist. When a function or data member is called in the shared base class, another ambiguity exists. For example, if Animal declares itsAge as a member variable and GetAge() as a member function, and you call pPeg->GetAge(), did you mean to call the GetAge() function you inherit from Animal by way of Horse, or by way of Bird? You must resolve this ambiguity as well, as illustrated in Listing 13.5. Figure 13.2. Common base classes. Listing 13.5. Common base classes. 1: 2: 3: 4: 5: 6: ; 7: 8: 9: 10: 11: 12: 13: 14: // Listing 13.5 // Common base classes #include typedef int HANDS; enum COLOR { Red, Green, Blue, Yellow, White, Black, Brown } enum BOOL { FALSE, TRUE }; class Animal // common base to both horse and bird { public: Animal(int); virtual ~Animal() { cout << "Animal destructor...\n"; } virtual int GetAge() const { return itsAge; }15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: } 56: 57: 58: 59: virtual void SetAge(int age) { itsAge = age; } private: int itsAge; }; Animal::Animal(int age): itsAge(age) { cout << "Animal constructor...\n"; } class Horse : public Animal { public: Horse(COLOR color, HANDS height, int age); virtual ~Horse() { cout << "Horse destructor...\n"; } virtual void Whinny()const { cout << "Whinny!... "; } virtual HANDS GetHeight() const { return itsHeight; } virtual COLOR GetColor() const { return itsColor; } protected: HANDS itsHeight; COLOR itsColor; }; Horse::Horse(COLOR color, HANDS height, int age): Animal(age), itsColor(color),itsHeight(height) { cout << "Horse constructor...\n"; } class Bird : public Animal { public: Bird(COLOR color, BOOL migrates, int age); virtual ~Bird() {cout << "Bird destructor...\n"; } virtual void Chirp()const { cout << "Chirp... "; } virtual void Fly()const { cout << "I can fly! I can fly! I can fly! "; } virtual COLOR GetColor()const { return itsColor; } virtual BOOL GetMigration() const { return itsMigration; protected: COLOR itsColor; BOOL itsMigration; };60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: } Bird::Bird(COLOR color, BOOL migrates, int age): Animal(age), itsColor(color), itsMigration(migrates) { cout << "Bird constructor...\n"; } class Pegasus : public Horse, public Bird { public: void Chirp()const { Whinny(); } Pegasus(COLOR, HANDS, BOOL, long, int); ~Pegasus() {cout << "Pegasus destructor...\n";} virtual long GetNumberBelievers() const { return itsNumberBelievers; } virtual COLOR GetColor()const { return Horse::itsColor; } virtual int GetAge() const { return Horse::GetAge(); } private: long itsNumberBelievers; }; Pegasus::Pegasus( COLOR aColor, HANDS height, BOOL migrates, long NumBelieve, int age): Horse(aColor, height,age), Bird(aColor, migrates,age), itsNumberBelievers(NumBelieve) { cout << "Pegasus constructor...\n"; } int main() { Pegasus *pPeg = new Pegasus(Red, 5, TRUE, 10, 2); int age = pPeg->GetAge(); cout << "This pegasus is " << age << " years old.\n"; delete pPeg; return 0; Output: Animal constructor... Horse constructor...Animal constructor... Bird constructor... Pegasus constructor... This pegasus is 2 years old. Pegasus destructor... Bird destructor... Animal destructor... Horse destructor... Animal destructor... Analysis: There are a number of interesting features to this listing. The Animal class is declared on lines 9-18. Animal adds one member variable, itsAge and an accessor, SetAge(). On line 26, the Horse class is declared to derive from Animal. The Horse constructor now has a third parameter, age, which it passes to its base class, Animal. Note that the Horse class does not override GetAge(), it simply inherits it. On line 46, the Bird class is declared to derive from Animal. Its constructor also takes an age and uses it to initialize its base class, Animal. It also inherits GetAge() without overriding it. Pegasus inherits from both Bird and from Animal, and so has two Animal classes in its inheritance chain. If you were to call GetAge() on a Pegasus object, you would have to disambiguate, or fully qualify, the method you want if Pegasus did not override the method. This is solved on line 76 when the Pegasus object overrides GetAge() to do nothing more than to chain up--that is, to call the same method in a base class. Chaining up is done for two reasons: either to disambiguate which base class to call, as in this case, or to do some work and then let the function in the base class do some more work. At times, you may want to do work and then chain up, or chain up and then do the work when the base class function returns. The Pegasus constructor takes five parameters: the creature's color, its height (in HANDS), whether or not it migrates, how many believe in it, and its age. The constructor initializes the Horse part of the Pegasus with the color, height, and age on line 88. It initializes the Bird part with color, whether it migrates, and age on line 89. Finally, it initializes itsNumberBelievers on line 90. The call to the Horse constructor on line 88 invokes the implementation shown on line 39. The Horse constructor uses the age parameter to initialize the Animal part of the Horse part of the Pegasus. It then goes on to initialize the two member variables of Horse--itsColor and itsAge. The call to the Bird constructor on line 89 invokes the implementation shown on line 46. Here too, the age parameter is used to initialize the Animal part of the Bird.Note that the color parameter to the Pegasus is used to initialize member variables in each of Bird and Horse. Note also that the age is used to initialize itsAge in the Horse's base Animal and in the Bird's base Animal. Virtual Inheritance In Listing 13.5, the Pegasus class went to some lengths to disambiguate which of its Animal base classes it meant to invoke. Most of the time, the decision as to which one to use is arbitrary--after all, the Horse and the Bird have exactly the same base class. It is possible to tell C++ that you do not want two copies of the shared base class, as shown in Figure 13.2, but rather to have a single shared base class, as shown in Figure 13.3. You accomplish this by making Animal a virtual base class of both Horse and Bird. The Animal class does not change at all. The Horse and Bird classes change only in their use of the term virtual in their declarations. Pegasus, however, changes substantially. Normally, a class's constructor initializes only its own variables and its base class. Virtually inherited base classes are an exception, however. They are initialized by their most derived class. Thus, Animal is initialized not by Horse and Bird, but by Pegasus. Horse and Bird have to initialize Animal in their constructors, but these initializations will be ignored when a Pegasus object is created. Listing 13.6 rewrites Listing 13.5 to take advantage of virtual derivation. Figure 13.3. A diamond inheritance. Listing 13.6. Illustration of the use of virtual inheritance. 1: 2: 3: 4: 5: 6: ; 7: 8: 9: 10: 11: 12: 13: 14: 15: // Listing 13.6 // Virtual inheritance #include typedef int HANDS; enum COLOR { Red, Green, Blue, Yellow, White, Black, Brown } enum BOOL { FALSE, TRUE }; class Animal // common base to both horse and bird { public: Animal(int); virtual ~Animal() { cout << "Animal destructor...\n"; } virtual int GetAge() const { return itsAge; } virtual void SetAge(int age) { itsAge = age; }16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: } 56: 57: 58: 59: 60: private: int itsAge; }; Animal::Animal(int age): itsAge(age) { cout << "Animal constructor...\n"; } class Horse : virtual public Animal { public: Horse(COLOR color, HANDS height, int age); virtual ~Horse() { cout << "Horse destructor...\n"; } virtual void Whinny()const { cout << "Whinny!... "; } virtual HANDS GetHeight() const { return itsHeight; } virtual COLOR GetColor() const { return itsColor; } protected: HANDS itsHeight; COLOR itsColor; }; Horse::Horse(COLOR color, HANDS height, int age): Animal(age), itsColor(color),itsHeight(height) { cout << "Horse constructor...\n"; } class Bird : virtual public Animal { public: Bird(COLOR color, BOOL migrates, int age); virtual ~Bird() {cout << "Bird destructor...\n"; } virtual void Chirp()const { cout << "Chirp... "; } virtual void Fly()const { cout << "I can fly! I can fly! I can fly! "; } virtual COLOR GetColor()const { return itsColor; } virtual BOOL GetMigration() const { return itsMigration; protected: COLOR itsColor; BOOL itsMigration; };61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: } Bird::Bird(COLOR color, BOOL migrates, int age): Animal(age), itsColor(color), itsMigration(migrates) { cout << "Bird constructor...\n"; } class Pegasus : public Horse, public Bird { public: void Chirp()const { Whinny(); } Pegasus(COLOR, HANDS, BOOL, long, int); ~Pegasus() {cout << "Pegasus destructor...\n";} virtual long GetNumberBelievers() const { return itsNumberBelievers; } virtual COLOR GetColor()const { return Horse::itsColor; } private: long itsNumberBelievers; }; Pegasus::Pegasus( COLOR aColor, HANDS height, BOOL migrates, long NumBelieve, int age): Horse(aColor, height,age), Bird(aColor, migrates,age), Animal(age*2), itsNumberBelievers(NumBelieve) { cout << "Pegasus constructor...\n"; } int main() { Pegasus *pPeg = new Pegasus(Red, 5, TRUE, 10, 2); int age = pPeg->GetAge(); cout << "This pegasus is " << age << " years old.\n"; delete pPeg; return 0; Output: Animal constructor... Horse constructor... Bird constructor...Pegasus constructor... This pegasus is 4 years old. Pegasus destructor... Bird destructor... Horse destructor... Animal destructor... Analysis: On line 26, Horse declares that it inherits virtually from Animal, and on line 46, Bird makes the same declaration. Note that the constructors for both Bird and Animal still initialize the Animal object. Pegasus inherits from both Bird and Animal, and as the most derived object of Animal, it also initializes Animal. It is Pegasus' initialization which is called, however, and the calls to Animal's constructor in Bird and Horse are ignored. You can see this because the value 2 is passed in, and Horse and Bird pass it along to Animal, but Pegasus doubles it. The result, 4, is reflected in the printout on line 99 and as shown in the output. Pegasus no longer has to disambiguate the call to GetAge(), and so is free to simply inherit this function from Animal. Note that Pegasus must still disambiguate the call to GetColor(), as this function is in both of its base classes and not in Animal. Declaring Classes for Virtual Inheritance To ensure that derived classes have only one instance of common base classes, declare the intermediate classes to inherit virtually from the base class. Example 1: class Horse : virtual public Animal class Bird : virtual public Animal class Pegasus : public Horse, public Bird Example 2: class Schnauzer : virtual public Dog class Poodle : virtual public Dog class Schnoodle : public Schnauzer, public Poodle Problems with Multiple Inheritance Although multiple inheritance offers a number of advantages over single inheritance, there are many C++ programmers who are reluctant to use it. The problems they cite are that many compilers don't support it yet, that it makes debugging harder, and that nearly everything that can be done with multiple inheritance can be done without it. These are valid concerns, and you will want to be on your guard against installing needlesscomplexity into your programs. Some debuggers have a hard time with multiple inheritance, and some designs are needlessly made complex by using multiple inheritance when it is not needed. DO use multiple inheritance when a new class needs functions and features from more than one base class. DO use virtual inheritance when the most derived classes must have only one instance of the shared base class. DO initialize the shared base class from the most derived class when using virtual base classes. DON'T use multiple inheritance when single inheritance will do. Mixins and Capabilities Classes One way to strike a middle ground between multiple inheritance and single inheritance is to use what are called mixins. Thus, you might have your Horse class derive from Animal and from Displayable. Displayable would just add a few methods for displaying any object onscreen. New Term: A mixin , or capability class, is a class that adds functionality without adding much or any data. Capability classes are mixed into a derived class like any other class might be, by declaring the derived class to inherit publicly from them. The only difference between a capability class and any other class is that the capability class has little or no data. This is an arbitrary distinction, of course, and is just a shorthand way of noting that at times all you want to do is mix in some additional capabilities without complicating the derived class. This will, for some debuggers, make it easier to work with mixins than with more complex multiply inherited objects. There is also less likelihood of ambiguity in accessing the data in the other principal base class. For example, if Horse derives from Animal and from Displayable, Displayable would have no data. Animal would be just as it always was, so all the data in Horse would derive from Animal, but the functions in Horse would derive from both. The term mixin comes from an ice-cream store in Sommerville, Massachusetts, where candies and cakes were mixed into the basic ice-cream flavors. This seemed like a good metaphor to some of the object-oriented programmers who used to take a summer break there, especially while working with the object-oriented programming language SCOOPS. Abstract Data Types Often, you will create a hierarchy of classes together. For example, you might create a Shape class, and derive from that Rectangle and Circle. From Rectangle, you might derive Square, as aspecial case of Rectangle. Each of the derived classes will override the Draw() method, the GetArea() method, and so forth. Listing 13.7 illustrates a bare-bones implementation of the Shape class and its derived Circle and Rectangle classes. Listing 13.7. Shape classes. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: //Listing 13.7. Shape classes. #include enum BOOL { FALSE, TRUE }; class Shape { public: Shape(){} ~Shape(){} virtual long GetArea() { return -1; } // error virtual long GetPerim() { return -1; } virtual void Draw() {} private: }; class Circle : public Shape { public: Circle(int radius):itsRadius(radius){} ~Circle(){} long GetArea() { return 3 * itsRadius * itsRadius; } long GetPerim() { return 9 * itsRadius; } void Draw(); private: int itsRadius; int itsCircumference; }; void Circle::Draw() { cout << "Circle drawing routine here!\n"; } class Rectangle : public Shape38: { 39: public: 40: Rectangle(int len, int width): 41: itsLength(len), itsWidth(width){} 42: ~Rectangle(){} 43: virtual long GetArea() { return itsLength * itsWidth; } 44: virtual long GetPerim() {return 2*itsLength + 2*itsWidth; } 45: virtual int GetLength() { return itsLength; } 46: virtual int GetWidth() { return itsWidth; } 47: virtual void Draw(); 48: private: 49: int itsWidth; 50: int itsLength; 51: }; 52: 53: void Rectangle::Draw() 54: { 55: for (int i = 0; i> choice; 95: 96: switch (choice) 97: { 98: case 1: sp = new Circle(5); 99: break; 100: case 2: sp = new Rectangle(4,6); 101: break; 102: case 3: sp = new Square(5); 103: break; 104: default: fQuit = TRUE; 105: break; 106: } 107: if (fQuit) 108: break; 109: 110: sp->Draw(); 111: cout << "\n"; 112: } 113: return 0; 114: } Output: x x x x x x x x x x x x x x x x (1)Circle (2)Rectangle (3)Square (0)Quit: 2 x x x x x x x x (1)Circle (2)Rectangle (3)Square (0)Quit:3 x x x x x x x x x x x x x x x x x x x x x x x x x(1)Circle (2)Rectangle (3)Square (0)Quit:0 Analysis: On lines 7-16, the Shape class is declared. The GetArea() and GetPerim() methods return an error value, and Draw() takes no action. After all, what does it mean to draw a Shape? Only types of shapes (circles, rectangle, and so on) can be drawn, Shapes as an abstraction cannot be drawn. Circle derives from Shape and overrides the three virtual methods. Note that there is no reason to add the word "virtual," as that is part of their inheritance. But there is no harm in doing so either, as shown in the Rectangle class on lines 43, 44, and 47. It is a good idea to include the term virtual as a reminder, a form of documentation. Square derives from Rectangle, and it too overrides the GetPerim() method, inheriting the rest of the methods defined in Rectangle. It is troubling, though, that a client might try to instantiate a Shape object, and it might be desirable to make that impossible. The Shape class exists only to provide an interface for the classes derived from it; as such it is an Abstract Data Type, or ADT. New Term: An Abstract Data Type represents a concept (like shape) rather than an object (like circle). In C++, an ADT is always the base class to other classes, and it is not valid to make an instance of an ADT. Pure Virtual Functions C++ supports the creation of abstract data types with pure virtual functions. A virtual function ismade pure by initializing it with zero, as in virtual void Draw() = 0; Any class with one or more pure virtual functions is an ADT, and it is illegal to instantiate an object of a class that is an ADT. Trying to do so will cause a compile-time error. Putting a pure virtual function in your class signals two things to clients of your class: &&" Don't make an object of this class, derive from it. &"' Make sure you override the pure virtual function. Any class that derives from an ADT inherits the pure virtual function as pure, and so must override every pure virtual function if it wants to instantiate objects. Thus, if Rectangle inherits from Shape, and Shape has three pure virtual functions, Rectangle must override all three or it too will be an ADT. Listing 13.8 rewrites the Shape class to be an abstract data type. To save space, therest of Listing 13.7 is not reproduced here. Replace the declaration of Shape in Listing 13.7, lines 7- 16, with the declaration of Shape in Listing 13.8 and run the program again. Listing 13.8. Abstract Data Types. 1: 2: 3: 4: 5: 6: 7: 8: 9: class Shape { public: Shape(){} ~Shape(){} virtual long GetArea() = 0; // error virtual long GetPerim()= 0; virtual void Draw() = 0; private: 10: }; Output: x x x x x x x x x x x x x x x x (1)Circle (2)Rectangle (3)Square (0)Quit: 2 x x x x x x x x (1)Circle (2)Rectangle (3)Square (0)Quit: 3 x x x x x x x x x x x x x x x x x x x x x x x x x (1)Circle (2)Rectangle (3)Square (0)Quit: 0 Analysis: As you can see, the workings of the program are totally unaffected. The only difference is that it would now be impossible to make an object of class Shape. Abstract Data Types Declare a class to be an abstract data type by including one or more pure virtual functions in the class declaration. Declare a pure virtual function by writing = 0 after the function declaration. Example: class Shape { virtual void Draw() = 0; }; // pure virtualImplementing Pure Virtual Functions Typically, the pure virtual functions in an abstract base class are never implemented. Because no objects of that type are ever created, there is no reason to provide implementations, and the ADT works purely as the definition of an interface to objects which derive from it. It is possible, however, to provide an implementation to a pure virtual function. The function can then be called by objects derived from the ADT, perhaps to provide common functionality to all the overridden functions. Listing 13.9 reproduces Listing 13.7, this time with Shape as an ADT and with an implementation for the pure virtual function Draw(). The Circle class overrides Draw(), as it must, but it then chains up to the base class function for additional functionality. In this example, the additional functionality is simply an additional message printed, but one can imagine that the base class provides a shared drawing mechanism, perhaps setting up a window that all derived classes will use. Listing 13.9. Implementing pure virtual functions. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: //Implementing pure virtual functions #include enum BOOL { FALSE, TRUE }; class Shape { public: Shape(){} ~Shape(){} virtual long GetArea() = 0; // error virtual long GetPerim()= 0; virtual void Draw() = 0; private: }; void Shape::Draw() { cout << "Abstract drawing mechanism!\n"; } class Circle : public Shape { public: Circle(int radius):itsRadius(radius){} ~Circle(){}28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: long GetArea() { return 3 * itsRadius * itsRadius; } long GetPerim() { return 9 * itsRadius; } void Draw(); private: int itsRadius; int itsCircumference; }; void Circle::Draw() { cout << "Circle drawing routine here!\n"; Shape::Draw(); } class Rectangle : public Shape { public: Rectangle(int len, int width): itsLength(len), itsWidth(width){} ~Rectangle(){} long GetArea() { return itsLength * itsWidth; } long GetPerim() {return 2*itsLength + 2*itsWidth; } virtual int GetLength() { return itsLength; } virtual int GetWidth() { return itsWidth; } void Draw(); private: int itsWidth; int itsLength; }; void Rectangle::Draw() { for (int i = 0; i> choice; switch (choice) { case 1: sp = new break; case 2: sp = new break; case 3: sp = new break; default: fQuit = break; } if (fQuit) break; sp->Draw(); cout << "\n"; Circle(5); Rectangle(4,6); Square (5); TRUE;120: 121: 122: } } return 0; Output: (1)Circle (2)Rectangle (3)Square (0)Quit: 2 x x x x x x x x x x x x x x x x x x x x x x x x Abstract drawing mechanism! (1)Circle (2)Rectangle (3)Square (0)Quit: 3 x x x x x x x x x x x x x x x x x x x x x x x x x Abstract drawing mechanism! (1)Circle (2)Rectangle (3)Square (0)Quit: 0 Analysis: On lines 7-16, the abstract data type Shape is declared, with all three of its accessor methods declared to be pure virtual. Note that this is not necessary. If any one were declared pure virtual, the class would have been an ADT. The GetArea() and GetPerim() methods are not implemented, but Draw() is. Circle and Rectangle both override Draw(), and both chain up to the base method, taking advantage of shared functionality in the base class. Complex Hierarchies of Abstraction At times, you will derive ADTs from other ADTs. It may be that you will want to make some of the derived pure virtual functions non-pure, and leave others pure. If you create the Animal class, you may make Eat(), Sleep(), Move(), and Reproduce() all be pure virtual functions. Perhaps from Animal you derive Mammal and Fish. On examination, you decide that every Mammal will reproduce in the same way, and so you make Mammal::Reproduce() be non-pure, but you leave Eat(), Sleep(), and Move() as pure virtual functions. From Mammal you derive Dog, and Dog must override and implement the three remaining pure virtual functions so that you can make objects of type Dog. What you've said, as class designer, is that no Animals or Mammals can be instantiated, but that all Mammals may inherit the provided Reproduce() method without overriding it.Listing 13.10 illustrates this technique with a bare-bones implementation of these classes. Listing 13.10. Deriving ADTs from other ADTs. 1: 2: 3: 4: 5: ; 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: // Listing 13.10 // Deriving ADTs from other ADTs #include enum COLOR { Red, Green, Blue, Yellow, White, Black, Brown } enum BOOL { FALSE, TRUE }; class Animal // common base to both horse and bird { public: Animal(int); virtual ~Animal() { cout << "Animal destructor...\n"; } virtual int GetAge() const { return itsAge; } virtual void SetAge(int age) { itsAge = age; } virtual void Sleep() const = 0; virtual void Eat() const = 0; virtual void Reproduce() const = 0; virtual void Move() const = 0; virtual void Speak() const = 0; private: int itsAge; }; Animal::Animal(int age): itsAge(age) { cout << "Animal constructor...\n"; } class Mammal : public Animal { public: Mammal(int age):Animal(age) { cout << "Mammal constructor...\n";} ~Mammal() { cout << "Mammal destructor...\n";} virtual void Reproduce() const { cout << "Mammal reproduction depicted...\n"; } };40: class Fish : public Animal 41: { 42: public: 43: Fish(int age):Animal(age) 44: { cout << "Fish constructor...\n";} 45: virtual ~Fish() {cout << "Fish destructor...\n"; } 46: virtual void Sleep() const { cout << "fish snoring...\n"; } 47: virtual void Eat() const { cout << "fish feeding...\n"; } 48: virtual void Reproduce() const 49: { cout << "fish laying eggs...\n"; } 50: virtual void Move() const 51: { cout << "fish swimming...\n"; } 52: virtual void Speak() const { } 53: }; 54: 55: class Horse : public Mammal 56: { 57: public: 58: Horse(int age, COLOR color ): 59: Mammal(age), itsColor(color) 60: { cout << "Horse constructor...\n"; } 61: virtual ~Horse() { cout << "Horse destructor...\n"; } 62: virtual void Speak()const { cout << "Whinny!... \n"; } 63: virtual COLOR GetItsColor() const { return itsColor; } 64: virtual void Sleep() const 65: { cout << "Horse snoring...\n"; } 66: virtual void Eat() const { cout << "Horse feeding...\n"; } 67: virtual void Move() const { cout << "Horse running...\n";} 68: 69: protected: 70: COLOR itsColor; 71: }; 72: 73: class Dog : public Mammal 74: { 75: public: 76: Dog(int age, COLOR color ): 77: Mammal(age), itsColor(color) 78: { cout << "Dog constructor...\n"; } 79: virtual ~Dog() { cout << "Dog destructor...\n"; } 80: virtual void Speak()const { cout << "Whoof!... \n"; } 81: virtual void Sleep() const { cout << "Dog snoring...\n"; }82: 83: } 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: } virtual void Eat() const { cout << "Dog eating...\n"; } virtual void Move() const { cout << "Dog running...\n"; virtual void Reproduce() const { cout << "Dogs reproducing...\n"; } protected: COLOR itsColor; }; int main() { Animal *pAnimal=0; int choice; BOOL fQuit = FALSE; while (1) { cout << "(1)Dog (2)Horse (3)Fish (0)Quit: "; cin >> choice; switch (choice) { case 1: pAnimal = new Dog(5,Brown); break; case 2: pAnimal = new Horse(4,Black); break; case 3: pAnimal = new Fish (5); break; default: fQuit = TRUE; break; } if (fQuit) break; pAnimal->Speak(); pAnimal->Eat(); pAnimal->Reproduce(); pAnimal->Move(); pAnimal->Sleep(); delete pAnimal; cout << "\n"; } return 0Output: (1)Dog (2)Horse (3)Bird (0)Quit: 1 Animal constructor... Mammal constructor... Dog constructor... Whoof!... Dog eating... Dog reproducing.... Dog running... Dog snoring... Dog destructor... Mammal destructor... Animal destructor... (1)Dog (2)Horse (3)Bird (0)Quit: 0 Analysis: On lines 8-22, the abstract data type Animal is declared. Animal has non-pure virtual accessors for itsAge, which are shared by all Animal objects. It has five pure virtual functions, Sleep(), Eat(), Reproduce(), Move(), and Speak(). Mammal is derived from Animal, is declared on lines 30-38, and adds no data. It overrides Reproduce(), however, providing a common form of reproduction for all mammals. Fish must override Reproduce(), because Fish derives directly from Animal and cannot take advantage of Mammalian reproduction (and a good thing, too!). Mammal classes no longer have to override the Reproduce() function, but they are free to do so if they choose, as Dog does on line 84. Fish, Horse, and Dog all override the remaining pure virtual functions, so that objects of their type can be instantiated. In the body of the program, an Animal pointer is used to point to the various derived objects in turn. The virtual methods are invoked, and based on the runtime binding of the pointer, the correct method is called in the derived class. It would be a compile-time error to try to instantiate an Animal or a Mammal, as both are abstract data types. Which Types Are Abstract? In one program, the class Animal is abstract, in another it is not. What determines whether to make a class abstract or not? The answer to this question is decided not by any real-world intrinsic factor, but by what makes sense in your program. If you are writing a program that depicts a farm or a zoo, you may want Animal to be an abstract data type, but Dog to be a class from which you can instantiate objects. On the other hand, if you are making an animated kennel, you may want to keep Dog as an abstractdata type, and only instantiate types of dogs: retrievers, terriers, and so fort. The level of abstraction is a function of how finely you need to distinguish your types. DO use abstract data types to provide common functionality for a number of related classes. DO override all pure virtual functions. DO make pure virtual any function that must be overridden. DON'T try to instantiate an object of an abstract data type. The Observer Pattern A very hot trend in C++ is the creation and dissemination of design patterns. These are well- documented solutions to common problems encountered by C++ programmers. As an example, the observer pattern solves a common problem in inheritance. Imagine you develop a timer class which knows how to count elapsed seconds. Such a class might have a class member itsSeconds which is an integer, and it would have methods to set, get, and increment itsSeconds. Now let's further assume that your program wants to be informed every time the timer's itsSeconds member is incremented. One obvious solution would be to put a notification method into the timer. However, notification is not an intrinsic part of timing, and the complex code for registering those classes which need to be informed when the clock increments doesn't really belong in your timer class. More importantly, once you work out the logic of registering those who are interested in these changes, and then notifying them, you'd like to abstract this out into a class of its own and be able to reuse it with other classes which might be "observed" in this way. Therefore, a better solution is to create an observer class. Make this observer an Abstract Data Type with a pure virtual function Update(). Now create a second abstract data type, called Subject. Subject keeps an array of Observer objects and also provides two methods: register() (which adds observers to its list) and Notify(), which is called when there is something to report. Those classes which wish to be notified of your timer's changes inherit from Observer. The timer itself inherits from Subject. The Observer class registers itself with the Subject class. The Subject class calls Notify when it changes (in this case when the timer updates). Finally, we note that not every client of timer wants to be observable, and thus we create a new class called ObservedTimer, which inherits both from timer and from Subject. This gives the ObservedTimer the timer characteristics and the ability to be observed. A Word About Multiple Inheritance, Abstract Data Types, and JavaMany C++ programmers are aware that Java was based in large part on C++, and yet the creators of Java chose to leave out multiple inheritance. It was their opinion that multiple inheritance introduced complexity that worked against the ease of use of Java. They felt they could meet 90% of the multiple inheritance functionality using what are called interfaces. New Term: An interface is much like an Abstract Data Type in that it defines a set of functions that can only be implemented in a derived class. However, with interfaces, you don't directly derive from the interface, you derive from another class and implement the interface, much like multiple inheritance. Thus, this marriage of an abstract data type and multiple inheritance gives you something akin to a capability class without the complexity or overhead of multiple inheritance. In addition, because interfaces cannot have implementations nor data members, the need for virtual inheritance is eliminated. Whether this is a bug or a feature is in the eyes of the beholder. In either case, if you understand multiple inheritance and Abstract Data Types in C++ you will be in a good position to move on to using some of the more advanced features of Java should you decide to learn that language as well. The observer pattern and how it is implemented both in Java and C++ is covered in detail in Robert Martin's article "C++ and Java: A Critical Comparison," in the January 1997 issue of C++ Report. Summary Today you learned how to overcome some of the limitations in single inheritance. You learned about the danger of percolating interfaces up the inheritance hierarchy, and the risks in casting down the inheritance hierarchy. You also learned how to use multiple inheritance, what problems multiple inheritance can create and how to solve them using virtual inheritance. You also learned what Abstract Data Types are and how to create Abstract classes using pure virtual functions. You learned how to implement pure virtual functions and when and why you might do so. Finally, you saw how to implement the Observer Pattern using multiple inheritance and Abstract Data types. Q&A Q. What does percolating functionality upwards mean? A. This refers to the idea of moving shared functionality upwards into a common base class. If more than one class shares a function, it is desirable to find a common base class in which that function can be stored. Q. Is percolating upwards always a good thing?A. Yes, if you are percolating shared functionality upwards. No, if all you are moving is interface. That is, if all the derived classes can't use the method, it is a mistake to move it up into a common base class. If you do, you'll have to switch on the runtime type of the object before deciding if you can invoke the function. Q. Why is switching on the runtime type of an object bad? A. With large programs, the switch statements become big and hard to maintain. The point of virtual functions is to let the virtual table, rather than the programmer, determine the runtime type of the object. Q. Why is casting bad? A. Casting isn't bad if it is done in a way that is type-safe. If a function is called that knows that the object must be of a particular type, casting to that type is fine. Casting can be used to undermine the strong type checking in C++, and that is what you want to avoid. If you are switching on the runtime type of the object and then casting a pointer, that may be a warning sign that something is wrong with your design. Q. Why not make all functions virtual? A. Virtual functions are supported by a virtual function table, which incurs runtime overhead, both in the size of the program and in the performance of the program. If you have very small classes that you don't expect to subclass, you may not want to make any of the functions virtual. Q. When should the destructor be made virtual? A. Anytime you think the class will be subclassed, and a pointer to the base class will be used to access an object of the subclass. As a general rule of thumb, if you've made any functions in your class virtual, be sure to make the destructor virtual as well. Q. Why bother making an Abstract Data Type--why not just make it non-abstract and avoid creating any objects of that type? A. The purpose of many of the conventions in C++ is to enlist the compiler in finding bugs, so as to avoid runtime bugs in code that you give your customers. Making a class abstract, that is, giving it pure virtual functions, causes the compiler to flag any objects created of that abstract type as errors. Workshop The Workshop provides quiz questions to help you solidify your understanding of the material covered, and exercises to provide you with experience in using what you've learned. Try to answer the quiz and exercise questions before checking the answers in Appendix D, and make sure you understand the answers before continuing to the next chapter.Quiz 1. What is a down cast? 2. What is the v-ptr? 3. If a round-rectangle has straight edges and rounded corners, and your RoundRect class inherits both from Rectangle and from Circle, and they in turn both inherit from Shape, how many Shapes are created when you create a RoundRect? 4. If Horse and Bird inherit from Animal using public virtual inheritance, do their constructors initialize the Animal constructor? If Pegasus inherits from both Horse and Bird, how does it initialize Animal's constructor? 5. Declare a class vehicle, and make it an abstract data type. 6. If a base class is an ADT, and it has three pure virtual functions, how many of these must be overridden in its derived classes? Exercises 1. Show the declaration for a class JetPlane, which inherits from Rocket and Airplane. 2. Show the declaration for 747, which inherits from the JetPlane class described in Exercise 1. 3. Write a program that derives Car and Bus from the class Vehicle. Make Vehicle be an ADT with two pure virtual functions. Make Car and Bus not be ADTs. 4. Modify the program in Exercise 3 so that Car is an ADT, and derive SportsCar, Wagon, and Coupe from Car. In the Car class, provide an implementation for one of the pure virtual functions in Vehicle and make it non-pure. Day 14 How to work with arrays of pointers to functions. Static Member Data Until now, you have probably thought of the data in each object as unique to that object and not shared among objects in a class. For example, if you have five Cat objects, each has its own age, weight, and other data. The age of one does not affect the age of another. There are times, however, when you'll want to keep track of a pool of data. For example, you might want to know how many objects for a specific class have been created in your program, and how many are still in existence. Static member variables are shared among all instances of a class. They are a compromise between global data, which is available to all parts of your program, and member data, which is usually available only to each object. You can think of a static member as belonging to the class rather than to the object. Normal member data is one per object, but static members are one per class. Listing 14.1 declares a Cat object with a static data member, HowManyCats. This variable keeps track of how many Cat objects have been created. This is done by incrementing the static variable, HowManyCats, with each construction and decrementing it with each destruction. Listing 14.1. Static member data. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: //Listing 14.1 static data members #include class Cat { public: Cat(int virtual virtual virtual age):itsAge(age){HowManyCats++; } ~Cat() { HowManyCats--; } int GetAge() { return itsAge; } void SetAge(int age) { itsAge = age; }12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: } static int HowManyCats; private: int itsAge; }; int Cat::HowManyCats = 0; int main() { const int MaxCats = 5; int i; Cat *CatHouse[MaxCats]; for (i = 0; iGetAge(); cout << " years old\n"; delete CatHouse[i]; CatHouse[i] = 0; } return 0; Output: There are 5 cats left! Deleting the one which is 0 years There are 4 cats left! Deleting the one which is 1 years There are 3 cats left! Deleting the one which is 2 years There are 2 cats left! Deleting the one which is 3 years There are 1 cats left! Deleting the one which is 4 years old old old old old Analysis: On lines 5 to 17 the simplified class Cat is declared. On line 12, HowManyCats is declared to be a static member variable of type int. The declaration of HowManyCats does not define an integer; no storage space is set aside. Unlike the non-static member variables, no storage space is set aside by instantiating a Cat object, becausethe HowManyCats member variable is not in the object. Thus, on line 19 the variable is defined and initialized. It is a common mistake to forget to define the static member variables of classes. Don't let this happen to you! Of course, if it does, the linker will catch it with a pithy error message such as the following: undefined symbol Cat::HowManyCats You don't need to do this for itsAge, because it is a non-static member variable and is defined each time you make a Cat object, which you do here on line 26. The constructor for Cat increments the static member variable on line 8. The destructor decrements it on line 9. Thus, at any moment, HowManyCats has an accurate measure of how many Cat objects were created but not yet destroyed. The driver program on lines 21-40 instantiates five Cats and puts them in an array. This calls five Cat constructors, and thus HowManyCats is incremented five times from its initial value of 0. The program then loops through each of the five positions in the array and prints out the value of HowManyCats before deleting the current Cat pointer. The printout reflects that the starting value is 5 (after all, 5 are constructed), and that each time the loop is run, one fewer Cat remains. Note that HowManyCats is public and is accessed directly by main(). There is no reason to expose this member variable in this way. It is preferable to make it private along with the other member variables and provide a public accessor method, as long as you will always access the data through an instance of Cat. On the other hand, if you'd like to access this data directly without necessarily having a Cat object available, you have two options: keep it public, as shown in Listing 14.2, or provide a static member function, as discussed later in this chapter. Listing 14.2. Accessing static members without an object. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: //Listing 14.2 static data members #include class Cat { public: Cat(int age):itsAge(age){HowManyCats++; } virtual ~Cat() { HowManyCats--; } virtual int GetAge() { return itsAge; } virtual void SetAge(int age) { itsAge = age; } static int HowManyCats; private:15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: } int itsAge; }; int Cat::HowManyCats = 0; void TelepathicFunction(); int main() { const int MaxCats = 5; int i; Cat *CatHouse[MaxCats]; for (i = 0; i class Cat { public: Cat(int virtual virtual virtual virtual age):itsAge(age){HowManyCats++; } ~Cat() { HowManyCats--; } int GetAge() { return itsAge; } void SetAge(int age) { itsAge = age; } int GetHowMany() { return HowManyCats; } private: int itsAge; static int HowManyCats; }; int Cat::HowManyCats = 0; int main() { const int MaxCats = 5; int i; Cat *CatHouse[MaxCats]; for (i = 0; iGetHowMany(); cout << " cats left!\n"; cout << "Deleting the one which is "; cout << CatHouse[i]->GetAge()+2;36: 37: 38: 39: 40: 41: } cout << " years old\n"; delete CatHouse[i]; CatHouse[i] = 0; } return 0; Output: There are 5 cats left! Deleting the one which is 2 years There are 4 cats left! Deleting the one which is 3 years There are 3 cats left! Deleting the one which is 4 years There are 2 cats left! Deleting the one which is 5 years There are 1 cats left! Deleting the one which is 6 years old old old old old Analysis: On line 17, the static member variable HowManyCats is declared to have private access. Now you cannot access this variable from non-member functions, such as TelepathicFunction from the previous listing. Even though HowManyCats is static, it is still within the scope of the class. Any class function, such as GetHowMany(), can access it, just as member functions can access any member data. However, for a function to call GetHowMany(), it must have an object on which to call the function. DO use static member variables to share data among all instances of a class. DO make static member variables protected or private if you wish to restrict access to them. DON'T use static member variables to store data for one object. Static member data is shared among all objects of its class. Static Member Functions Static member functions are like static member variables: they exist not in an object but in the scope of the class. Thus, they can be called without having an object of that class, as illustrated in Listing 14.4. Listing 14.4. Static member functions. 1: 2: 3: 4: //Listing 14.4 static data members #include 5: class Cat 6: { 7: public: 8: Cat(int age):itsAge(age){HowManyCats++; } 9: virtual ~Cat() { HowManyCats--; } 10: virtual int GetAge() { return itsAge; } 11: virtual void SetAge(int age) { itsAge = age; } 12: static int GetHowMany() { return HowManyCats; } 13: private: 14: int itsAge; 15: static int HowManyCats; 16: }; 17: 18: int Cat::HowManyCats = 0; 19: 20: void TelepathicFunction(); 21: 22: int main() 23: { 24: const int MaxCats = 5; 25: Cat *CatHouse[MaxCats]; int i; 26: for (i = 0; i void void void void Square (int&,int&); Cube (int&, int&); Swap (int&, int &); GetVals(int&, int&);9: void PrintVals(int, int); 10: enum BOOL { FALSE, TRUE }; 11: 12: int main() 13: { 14: void (* pFunc) (int &, int &); 15: BOOL fQuit = FALSE; 16: 17: int valOne=1, valTwo=2; 18: int choice; 19: while (fQuit == FALSE) 20: { 21: cout << "(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: "; 22: cin >> choice; 23: switch (choice) 24: { 25: case 1: pFunc = GetVals; break; 26: case 2: pFunc = Square; break; 27: case 3: pFunc = Cube; break; 28: case 4: pFunc = Swap; break; 29: default : fQuit = TRUE; break; 30: } 31: 32: if (fQuit) 33: break; 34: 35: PrintVals(valOne, valTwo); 36: pFunc(valOne, valTwo); 37: PrintVals(valOne, valTwo); 38: } 39: return 0; 40: } 41: 42: void PrintVals(int x, int y) 43: { 44: cout << "x: " << x << " y: " << y << endl; 45: } 46: 47: void Square (int & rX, int & rY) 48: { 49: rX *= rX; 50: rY *= rY; 51: } 52: 53: void Cube (int & rX, int & rY)54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: } { int tmp; tmp = rX; rX *= rX; rX = rX * tmp; tmp = rY; rY *= rY; rY = rY * tmp; } void Swap(int & rX, int & rY) { int temp; temp = rX; rX = rY; rY = temp; } void GetVals (int & rValOne, int & rValTwo) { cout << "New value for ValOne: "; cin >> rValOne; cout << "New value for ValTwo: "; cin >> rValTwo; Output: (0)Quit (1)Change Values (2)Square x: 1 y: 2 New value for ValOne: 2 New value for ValTwo: 3 x: 2 y: 3 (0)Quit (1)Change Values (2)Square (3)Cube x: 2 y: 3 x: 8 y: 27 (0)Quit (1)Change Values (2)Square (3)Cube x: 8 y: 27 x: 64 y: 729 (0)Quit (1)Change Values (2)Square (3)Cube x: 64 y: 729 x: 729 y: 64 (0)Quit (1)Change Values (2)Square (3)Cube (3)Cube (4)Swap: 1 (4)Swap: 3 (4)Swap: 2 (4)Swap: 4 (4)Swap: 0 Analysis: On lines 5-8, four functions are declared, each with the same return type and signature, returning void and taking two references to integers.On line 14, pFunc is declared to be a pointer to a function that returns void and takes two integer reference parameters. Any of the previous functions can be pointed to by pFunc. The user is repeatedly offered the choice of which functions to invoke, and pFunc is assigned accordingly. On lines 35-36, the current value of the two integers is printed, the currently assigned function is invoked, and then the values are printed again. Pointer to Function A pointer to function is invoked exactly like the functions it points to, except that the function pointer name is used instead of the function name. Assign a pointer to function to a specific function by assigning to the function name without the parentheses. The function name is a constant pointer to the function itself. Use the pointer to function just as you would the function name. The pointer to function must agree in return value and signature with the function to which you assign it. Example long (*pFuncOne) (int, int); long SomeFunction (int, int); pFuncOne = SomeFunction; pFuncOne(5,7); Why Use Function Pointers? You certainly could write the program in Listing 14.5 without function pointers, but the use of these pointers makes the intent and use of the program explicit: pick a function from a list, and then invoke it. Listing 14.6 uses the function prototypes and definitions from Listing 14.5, but the body of the program does not use a function pointer. Examine the differences between these two listings. NOTE: To compile this program, place lines 41-80 from Listing 14.5 immediately after line 56. Listing 14.6. Rewriting Listing 14.5 without the pointer to function. 1: 2: 3: 4: 5: 6: 7: 8: 9: // Listing 14.6 Without function pointers #include void void void void void Square (int&,int&); Cube (int&, int&); Swap (int&, int &); GetVals(int&, int&); PrintVals(int, int);10: enum BOOL { FALSE, TRUE }; 11: 12: int main() 13: { 14: BOOL fQuit = FALSE; 15: int valOne=1, valTwo=2; 16: int choice; 17: while (fQuit == FALSE) 18: { 19: cout << "(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: "; 20: cin >> choice; 21: switch (choice) 22: { 23: case 1: 24: PrintVals(valOne, valTwo); 25: GetVals(valOne, valTwo); 26: PrintVals(valOne, valTwo); 27: break; 28: 29: case 2: 30: PrintVals(valOne, valTwo); 31: Square(valOne,valTwo); 32: PrintVals(valOne, valTwo); 33: break; 34: 35: case 3: 36: PrintVals(valOne, valTwo); 37: Cube(valOne, valTwo); 38: PrintVals(valOne, valTwo); 39: break; 40: 41: case 4: 42: PrintVals(valOne, valTwo); 43: Swap(valOne, valTwo); 44: PrintVals(valOne, valTwo); 45: break; 46: 47: default : 48: fQuit = TRUE; 49: break; 50: } 51: 52: if (fQuit) 53: break; 54: }55: 56: } return 0; Output: (0)Quit (1)Change Values (2)Square x: 1 y: 2 New value for ValOne: 2 New value for ValTwo: 3 (0)Quit (1)Change Values (2)Square (3)Cube x: 2 y: 3 x: 8 y: 27 (0)Quit (1)Change Values (2)Square (3)Cube x: 8 y: 27 x: 64 y: 729 (0)Quit (1)Change Values (2)Square (3)Cube x: 64 y: 729 x: 729 y: 64 (0)Quit (1)Change Values (2)Square (3)Cube (3)Cube (4)Swap: 1 (4)Swap: 3 (4)Swap: 2 (4)Swap: 4 (4)Swap: 0 Analysis: The implementation of the functions has been left out, because it is identical to that provided in Listing 14.5. As you can see, the output is unchanged, but the body of the program has expanded from 27 lines to 38. The calls to PrintVals() must be repeated for each case. It was tempting to put PrintVals() at the top of the while loop and again at the bottom, rather than in each case statement. This would have called PrintVals() even for the exit case, however, and that was not part of the specification. Setting aside the increased size of the code and the repeated calls to do the same thing, the overall clarity is somewhat diminished. This is an artificial case, however, created to show how pointers to functions work. In real-world conditions the advantages are even clearer: pointers to functions can eliminate duplicate code, clarify your program, and allow you to make tables of functions to call based on runtime conditions. Shorthand Invocation The pointer to function does not need to be dereferenced, though you are free to do so. Therefore, if pFunc is a pointer to a function taking an integer and returning a variable of type long, and you assign pFunc to a matching function, you can invoke that function with either pFunc(x); or (*pFunc)(x); The two forms are identical. The former is just a shorthand version of the latter.Arrays of Pointers to Functions Just as you can declare an array of pointers to integers, you can declare an array of pointers to functions returning a specific value type and with a specific signature. Listing 14.7 again rewrites Listing 14.5, this time using an array to invoke all the choices at once. NOTE: To compile this program, place lines 41-80 of Listing 14.5 immediately after line 39. Listing 14.7. Demonstrates use of an array of pointers to functions. 1: // Listing 14.7 demonstrates use of an array of pointers to functions 2: 3: #include 4: 5: void Square (int&,int&); 6: void Cube (int&, int&); 7: void Swap (int&, int &); 8: void GetVals(int&, int&); 9: void PrintVals(int, int); 10: enum BOOL { FALSE, TRUE }; 11: 12: int main() 13: { 14: int valOne=1, valTwo=2; 15: int choice, i; 16: const MaxArray = 5; 17: void (*pFuncArray[MaxArray])(int&, int&); 18: 19: for (i=0;i> choice; 23: switch (choice) 24: { 25: case 1:pFuncArray[i] = GetVals; break; 26: case 2:pFuncArray[i] = Square; break; 27: case 3:pFuncArray[i] = Cube; break; 28: case 4:pFuncArray[i] = Swap; break; 29: default:pFuncArray[i] = 0;30: 31: 32: 33: 34: 35: 36: 37: 38: 39: } } } for (i=0;i 4: 5: void Square (int&,int&); 6: void Cube (int&, int&); 7: void Swap (int&, int &); 8: void GetVals(int&, int&); 9: void PrintVals(void (*)(int&, int&),int&, int&); 10: enum BOOL { FALSE, TRUE }; 11: 12: int main() 13: { 14: int valOne=1, valTwo=2; 15: int choice; 16: BOOL fQuit = FALSE; 17: 18: void (*pFunc)(int&, int&); 19: 20: while (fQuit == FALSE) 21: { 22: cout << "(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: "; 23: cin >> choice; 24: switch (choice) 25: { 26: case 1:pFunc = GetVals; break; 27: case 2:pFunc = Square; break; 28: case 3:pFunc = Cube; break; 29: case 4:pFunc = Swap; break; 30: default:fQuit = TRUE; break; 31: } 32: if (fQuit == TRUE) 33: break; 34: PrintVals ( pFunc, valOne, valTwo); 35: } 36: 37: return 0; 38: } 39: 40: void PrintVals( void (*pFunc)(int&, int&),int& x, int& y) 41: { 42: cout << "x: " << x << " y: " << y << endl;43: 44: 45: } pFunc(x,y); cout << "x: " << x << " y: " << y << endl; Output: (0)Quit (1)Change Values (2)Square x: 1 y: 2 New value for ValOne: 2 New value for ValTwo: 3 x: 2 y: 3 (0)Quit (1)Change Values (2)Square (3)Cube x: 2 y: 3 x: 8 y: 27 (0)Quit (1)Change Values (2)Square (3)Cube x: 8 y: 27 x: 64 y: 729 (0)Quit (1)Change Values (2)Square (3)Cube x: 64 y: 729 x: 729 y:64 (0)Quit (1)Change Values (2)Square (3)Cube (3)Cube (4)Swap: 1 (4)Swap: 3 (4)Swap: 2 (4)Swap: 4 (4)Swap: 0 Analysis: On line 18, pFunc is declared to be a pointer to a function returning void and taking two parameters, both integer references. On line 9, PrintVals is declared to be a function taking three parameters. The first is a pointer to a function that returns void but takes two integer reference parameters, and the second and third arguments to PrintVals are integer references. The user is again prompted for which functions to call, and then on line 34 PrintVals is called. Go find a C++ programmer and ask him what this declaration means: void PrintVals(void (*)(int&, int&),int&, int&); This is the kind of declaration that you use infrequently and probably look up in the book each time you need it, but it will save your program on those rare occasions when it is exactly the required construct. Using typedef with Pointers to Functions The construct void (*)(int&, int&) is cumbersome, at best. You can use typedef to simplify this, by declaring a type VPF as a pointer to a function returning void and taking two integer references. Listing 14.9 rewrites Listing 14.8 using this typedef statement. NOTE: To compile this program, place lines 46-80 of Listing 14.5 immediately after line 45.Listing 14.9. Using typedef to make pointers to functions more readable. 1: more 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: "; 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: // Listing 14.9. Using typedef to make pointers to functions _readable #include void Square (int&,int&); void Cube (int&, int&); void Swap (int&, int &); void GetVals(int&, int&); typedef void (*VPF) (int&, int&) ; void PrintVals(VPF,int&, int&); enum BOOL { FALSE, TRUE }; int main() { int valOne=1, valTwo=2; int choice; BOOL fQuit = FALSE; VPF pFunc; while (fQuit == FALSE) { cout << "(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: cin >> choice; switch (choice) { case 1:pFunc = GetVals; break; case 2:pFunc = Square; break; case 3:pFunc = Cube; break; case 4:pFunc = Swap; break; default:fQuit = TRUE; break; } if (fQuit == TRUE) break; PrintVals ( pFunc, valOne, valTwo); } return 0; } void PrintVals( VPF pFunc,int& x, int& y) { cout << "x: " << x << " y: " << y << endl;43: pFunc(x,y); 44: cout << "x: " << x << " y: " << y << endl; 45: } Output: (0)Quit (1)Change Values (2)Square x: 1 y: 2 New value for ValOne: 2 New value for ValTwo: 3 x: 2 y: 3 (0)Quit (1)Change Values (2)Square (3)Cube x: 2 y: 3 x: 8 y: 27 (0)Quit (1)Change Values (2)Square (3)Cube x: 8 y: 27 x: 64 y: 729 (0)Quit (1)Change Values (2)Square (3)Cube x: 64 y: 729 x: 729 y: 64 (0)Quit (1)Change Values (2)Square (3)Cube (3)Cube (4)Swap: 1 (4)Swap: 3 (4)Swap: 2 (4)Swap: 4 (4)Swap: 0 Analysis: On line 9, typedef is used to declare VPF to be of the type "function that returns void and takes two parameters, both integer references." On line 10, the function PrintVals() is declared to take three parameters: a VPF and two integer references. On line 19, pFunc is now declared to be of type VPF. Once the type VPF is defined, all subsequent uses to declare pFunc and PrintVals() are much cleaner. As you can see, the output is identical. Pointers to Member Functions Up until this point, all of the function pointers you've created have been for general, non-class functions. It is also possible to create pointers to functions that are members of classes. To create a pointer to member function, use the same syntax as with a pointer to function, but include the class name and the scoping operator (::). Thus, if pFunc points to a member function of the class Shape, which takes two integers and returns void, the declaration for pFunc is the following: void (Shape::*pFunc) (int, int); Pointers to member functions are used in exactly the same way as pointers to functions, except that they require an object of the correct class on which to invoke them. Listing 14.10 illustrates the use of pointers to member functions. Listing 14.10. Pointers to member functions.1: //Listing 14.10 Pointers to member functions using virtual methods 2: 3: #include 4: 5: enum BOOL {FALSE, TRUE}; 6: class Mammal 7: { 8: public: 9: Mammal():itsAge(1) { } 10: ~Mammal() { } 11: virtual void Speak() const = 0; 12: virtual void Move() const = 0; 13: protected: 14: int itsAge; 15: }; 16: 17: class Dog : public Mammal 18: { 19: public: 20: void Speak()const { cout << "Woof!\n"; } 21: void Move() const { cout << "Walking to heel...\n"; } 22: }; 23: 24: 25: class Cat : public Mammal 26: { 27: public: 28: void Speak()const { cout << "Meow!\n"; } 29: void Move() const { cout << "slinking...\n"; } 30: }; 31: 32: 33: class Horse : public Mammal 34: { 35: public: 36: void Speak()const { cout << "Winnie!\n"; } 37: void Move() const { cout << "Galloping...\n"; } 38: }; 39: 40: 41: int main() 42: { 43: void (Mammal::*pFunc)() const =0; 44: Mammal* ptr =0;45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: } int Animal; int Method; BOOL fQuit = FALSE; while (fQuit == FALSE) { cout << "(0)Quit (1)dog (2)cat (3)horse: "; cin >> Animal; switch (Animal) { case 1: ptr = new Dog; break; case 2: ptr = new Cat; break; case 3: ptr = new Horse; break; default: fQuit = TRUE; break; } if (fQuit) break; cout << "(1)Speak (2)Move: "; cin >> Method; switch (Method) { case 1: pFunc = Mammal::Speak; break; default: pFunc = Mammal::Move; break; } (ptr->*pFunc)(); delete ptr; } return 0; Output: (0)Quit (1)dog (2)cat (3)horse: 1 (1)Speak (2)Move: 1 Woof! (0)Quit (1)dog (2)cat (3)horse: 2 (1)Speak (2)Move: 1 Meow! (0)Quit (1)dog (2)cat (3)horse: 3 (1)Speak (2)Move: 2 Galloping (0)Quit (1)dog (2)cat (3)horse: 0 Analysis: On lines 6-15, the abstract data type Mammal is declared with two pure virtual methods, Speak() and Move(). Mammal is subclassed into Dog, Cat, and Horse, each of which overrides Speak() and Move().The driver program in main() asks the user to choose which type of animal to create, and then a new subclass of Animal is created on the free store and assigned to ptr on lines 55-57. The user is then prompted for which method to invoke, and that method is assigned to the pointer pFunc. On line 71, the method chosen is invoked by the object created, by using the pointer ptr to access the object and pFunc to access the function. Finally, on line 72, delete is called on the pointer ptr to return the memory set aside for the object to the free store. Note that there is no reason to call delete on pFunc because this is a pointer to code, not to an object on the free store. In fact, attempting to do so will generate a compile-time error. Arrays of Pointers to Member Functions As with pointers to functions, pointers to member functions can be stored in an array. The array can be initialized with the addresses of various member functions, and these can be invoked by offsets into the array. Listing 14.11 illustrates this technique. Listing 14.11. Array of pointers to member functions. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: Little 17: 18: 19: 20: 21: 22: 23: 24: //Listing 14.11 Array of pointers to member functions #include enum BOOL {FALSE, TRUE}; class Dog { public: void Speak()const { cout << "Woof!\n"; } void Move() const { cout << "Walking to heel...\n"; } void Eat() const { cout << "Gobbling food...\n"; } void Growl() const { cout << "Grrrrr\n"; } void Whimper() const { cout << "Whining noises...\n"; } void RollOver() const { cout << "Rolling over...\n"; } void PlayDead() const { cout << "Is this the end of Caeser?\n"; } }; typedef void (Dog::*PDF)()const ; int main() { const int MaxFuncs = 7; PDF DogFunctions[MaxFuncs] = { Dog::Speak,25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: } Dog::Move, Dog::Eat, Dog::Growl, Dog::Whimper, Dog::RollOver, Dog::PlayDead }; Dog* pDog =0; int Method; BOOL fQuit = FALSE; while (!fQuit) { cout << "(0)Quit (1)Speak (2)Move (3)Eat (4)Growl"; cout << " (5)Whimper (6)Roll Over (7)Play Dead: "; cin >> Method; if (Method == 0) { fQuit = TRUE; break; } else { pDog = new Dog; (pDog->*DogFunctions[Method-1])(); delete pDog; } } return 0; Output: (0)Quit (1)Speak (2)Move (3)Eat (4)Growl (5)Whimper (6)Roll Over (7)Play Dead: 1 Woof! (0)Quit (1)Speak (2)Move (3)Eat (4)Growl (5)Whimper (6)Roll Over (7)Play Dead: 4 Grrr (0)Quit (1)Speak (2)Move (3)Eat (4)Growl (5)Whimper (6)Roll Over (7)Play Dead: 7 Is this the end of Little Caeser? (0)Quit (1)Speak (2)Move (3)Eat (4)Growl (5)Whimper (6)Roll Over (7)Play Dead: 0 Analysis: On lines 7-17, the class Dog is created, with 7 member functions all sharing the same return type and signature. On line 19, a typedef declares PDF to be a pointer to a member function of Dog that takes no parameters and returns no values, and that is const: the signature of the 7 memberfunctions of Dog. On lines 23-30, the array DogFunctions is declared to hold 7 such member functions, and it is initialized with the addresses of these functions. On lines 38 and 39, the user is prompted to pick a method. Unless they pick Quit, a new Dog is created on the heap, and then the correct method is invoked on the array on line 49. Here's another good line to show to the hotshot C++ programmers in your company; ask them what this does: (pDog->*DogFunctions[Method-1])(); Once again, this is a bit esoteric, but when you need a table built from member functions, it can make your program far easier to read and understand. DO invoke pointers to member functions on a specific object of a class. DO use typedef to make pointer to member function declarations easier to read. DON'T use pointer to member functions when there are simpler solutions. Summary Today you learned how to create static member variables in your class. Each class, rather than each object, has one instance of the static member variable. It is possible to access this member variable without an object of the class type by fully qualifying the name, assuming you've declared the static member to have public access. Static member variables can be used as counters across instances of the class. Because they are not part of the object, the declaration of static member variables does not allocate memory, and static member variables must be defined and initialized outside the declaration of the class. Static member functions are part of the class in the same way that static member variables are. They can be accessed without a particular object of the class, and can be used to access static member data. Static member functions cannot be used to access non-static member data because they do not have a this pointer. Because static member functions do not have a this pointer, they also cannot be made const. const in a member function indicates that the this pointer is const. You also learned how to declare and use pointers to functions and pointers to member functions. You saw how to create arrays of these pointers and how to pass them to functions. Pointers to functions and pointers to member functions can be used to create tables of functions that can be selected from at runtime. This can give your program flexibility that is not easily achievedwithout these pointers. Q&A Q. Why use static data when you can use global data? A. Static data is scoped to the class. In this manner, static data are available only through an object of the class, through an explicit call using the class name if they are public, or by using a static member function. Static data are typed to the class type, however, and the restricted access and strong typing makes static data safer than global data. Q. Why use static member functions when you can use global functions? A. Static member functions are scoped to the class, and can be called only by using an object of the class or an explicit full specification (such as ClassName::FunctionName()). Q. Is it common to use many pointers to functions and pointers to member functions? A. No, these have their special uses, but are not common constructs. Many complex and powerful programs have neither. Workshop The Workshop contains quiz questions to help solidify your understanding of the material covered and exercises to provide you with experience in using what you've learned. Try to answer the quiz and exercise questions before checking the answers in Appendix D, and make sure you understand the answers before going to the next chapter. Quiz 1. Can static member variables be private? 2. Show the declaration for a static member variable. 3. Show the declaration for a static function pointer. 4. Show the declaration for a pointer to function returning long and taking an integer parameter. 5. Modify the pointer in Question 4 so it's a pointer to member function of class Car. 6. Show the declaration for an array of 10 pointers as defined in Question 5. Exercises 1. Write a short program declaring a class with one member variable and one static membervariable. Have the constructor initialize the member variable and increment the static member variable. Have the destructor decrement the member variable. 2. Using the program from Exercise 1, write a short driver program that makes three objects and then displays their member variables and the static member variable. Then destroy each object and show the effect on the static member variable. 3. Modify the program from Exercise 2 to use a static member function to access the static member variable. Make the static member variable private. 4. Write a pointer to member function to access the non-static member data in the program in Exercise 3, and use that pointer to print the value of that data. 5. Add two more member variables to the class from the previous questions. Add accessor functions that get the value of these values, and give all the member functions the same return values and signatures. Use the pointer to member function to access these functions.In Review The Week in Review program for Week 2 brings together many of the skills you've acquired over the past fortnight and produces a powerful program. This demonstration of linked lists utilizes virtual functions, pure virtual functions, function overriding, polymorphism, public inheritance, function overloading, forever loops, pointers, references, and more. The goal of this program is to create a linked list. The nodes on the list are designed to hold parts, as might be used in a factory. While this is not the final form of this program, it does make a good demonstration of a fairly advanced data structure. The code list is 311 lines. Try to analyze the code on your own before reading the analysis that follows the output. Listing R2.1. Week 2 in Review listing. 0: // ************************************************** 1: // 2: // Title: Week 2 in Review 3: // 4: // File: Week2 5: // 6: // Description: Provide a linked list demonstration program 7: // 8: // Classes: PART - holds part numbers and potentially other 9: // information about parts 10: // 11: // PartNode - acts as a node in a PartsList 12: // 13: // PartsList - provides the mechanisms for a linked list ."of parts 14: // 15: // Author: Jesse Liberty (jl) 16: // 17: // Developed: 486/66 32mb RAM MVC 1.5 18: // 19: // Target: Platform independent 20: //21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: // Rev History: 9/94 - First release (jl) // // ************************************************** #include typedef unsigned long ULONG; typedef unsigned short USHORT; // **************** Part ************ // Abstract base class of parts class Part { public: Part():itsPartNumber(1) {} Part(ULONG PartNumber):itsPartNumber(PartNumber){} virtual ~Part(){}; ULONG GetPartNumber() const { return itsPartNumber; } virtual void Display() const =0; // must be overridden private: ULONG itsPartNumber; }; // implementation of pure virtual function so that // derived classes can chain up void Part::Display() const { cout << "\nPart Number: " << itsPartNumber << endl; } // **************** Car Part ************ class CarPart : public Part { public: CarPart():itsModelYear(94){} CarPart(USHORT year, ULONG partNumber); virtual void Display() const { Part::Display(); cout << "Model Year: "; cout << itsModelYear << endl; } private: USHORT itsModelYear;67: }; 68: 69: CarPart::CarPart(USHORT year, ULONG partNumber): 70: itsModelYear(year), 71: Part(partNumber) 72: {} 73: 74: 75: // **************** AirPlane Part ************ 76: 77: class AirPlanePart : public Part 78: { 79: public: 80: AirPlanePart():itsEngineNumber(1){}; 81: AirPlanePart(USHORT EngineNumber, ULONG PartNumber); 82: virtual void Display() const 83: { 84: Part::Display(); cout << "Engine No.: "; 85: cout << itsEngineNumber << endl; 86: } 87: private: 88: USHORT itsEngineNumber; 89: }; 90: 91: AirPlanePart::AirPlanePart(USHORT EngineNumber, ULONG PartNumber): 92: itsEngineNumber(EngineNumber), 93: Part(PartNumber) 94: {} 95: 96: // **************** Part Node ************ 97: class PartNode 98: { 99: public: 100: PartNode (Part*); 101: ~PartNode(); 102: void SetNext(PartNode * node) { itsNext = node; } 103: PartNode * GetNext() const; 104: Part * GetPart() const; 105: private: 106: Part *itsPart; 107: PartNode * itsNext; 108: }; 109: 110: // PartNode Implementations... 111:112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: const; 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: PartNode::PartNode(Part* pPart): itsPart(pPart), itsNext(0) {} PartNode::~PartNode() { delete itsPart; itsPart = 0; delete itsNext; itsNext = 0; } // Returns NULL if no next PartNode PartNode * PartNode::GetNext() const { return itsNext; } Part * PartNode::GetPart() const { if (itsPart) return itsPart; else return NULL; //error } // **************** Part List ************ class PartsList { public: PartsList(); ~PartsList(); // needs copy constructor and operator equals! Part* Find(ULONG & position, ULONG PartNumber) ULONG Part* static GetCount() const { return itsCount; } GetFirst() const; PartsList& GetGlobalPartsList() { return GlobalPartsList; } void void Part* private: Insert(Part *); Iterate(void (Part::*f)()const) const; operator[](ULONG) const;157: PartNode * pHead; 158: ULONG itsCount; 159: static PartsList GlobalPartsList; 160: }; 161: 162: PartsList PartsList::GlobalPartsList; 163: 164: // Implementations for Lists... 165: 166: PartsList::PartsList(): 167: pHead(0), 168: itsCount(0) 169: {} 170: 171: PartsList::~PartsList() 172: { 173: delete pHead; 174: } 175: 176: Part* PartsList::GetFirst() const 177: { 178: if (pHead) 179: return pHead->GetPart(); 180: else 181: return NULL; // error catch here 182: } 183: 184: Part * PartsList::operator[](ULONG offSet) const 185: { 186: PartNode* pNode = pHead; 187: 188: if (!pHead) 189: return NULL; // error catch here 190: 191: if (offSet > itsCount) 192: return NULL; // error 193: 194: for (ULONG i=0;iGetNext(); 196: 197: return pNode->GetPart(); 198: } 199: 200: Part* PartsList::Find(ULONG & position, ULONG PartNumber) const 201: {202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: PartNode * pNode = 0; for (pNode = pHead, position = 0; pNode!=NULL; pNode = pNode->GetNext(), position++) { if (pNode->GetPart()->GetPartNumber() == PartNumber) break; } if (pNode == NULL) return NULL; else return pNode->GetPart(); } void PartsList::Iterate(void (Part::*func)()const) const { if (!pHead) return; PartNode* pNode = pHead; do (pNode->GetPart()->*func)(); while (pNode = pNode->GetNext()); } void PartsList::Insert(Part* pPart) { PartNode * pNode = new PartNode(pPart); PartNode * pCurrent = pHead; PartNode * pNext = 0; ULONG New = pPart->GetPartNumber(); ULONG Next = 0; itsCount++; if (!pHead) { pHead = pNode; return; } // if this one is smaller than head // this one is the new head if (pHead->GetPart()->GetPartNumber() > New) { pNode->SetNext(pHead); pHead = pNode;248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: return; } for (;;) { // if there is no next, append this new one if (!pCurrent->GetNext()) { pCurrent->SetNext(pNode); return; } // if this goes after this one and before the next // then insert it here, otherwise get the next pNext = pCurrent->GetNext(); Next = pNext->GetPart()->GetPartNumber(); if (Next > New) { pCurrent->SetNext(pNode); pNode->SetNext(pNext); return; } pCurrent = pNext; } } int main() { PartsList pl = PartsList::GetGlobalPartsList(); Part * pPart = 0; ULONG PartNumber; USHORT value; ULONG choice; while (1) { cout << "(0)Quit (1)Car (2)Plane: "; cin >> choice; if (!choice) break; cout << "New PartNumber?: "; cin >> PartNumber; if (choice == 1)294: 295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: } { cout << "Model Year?: "; cin >> value; pPart = new CarPart(value,PartNumber); } else { cout << "Engine Number?: "; cin >> value; pPart = new AirPlanePart(value,PartNumber); } pl.Insert(pPart); } void (Part::*pFunc)()const = Part::Display; pl.Iterate(pFunc); return 0; Output: (0)Quit (1)Car (2)Plane: 1 New PartNumber?: 2837 Model Year? 90 (0)Quit (1)Car (2)Plane: 2 New PartNumber?: 378 Engine Number?: 4938 (0)Quit (1)Car (2)Plane: 1 New PartNumber?: 4499 Model Year? 94 (0)Quit (1)Car (2)Plane: 1 New PartNumber?: 3000 Model Year? 93 (0)Quit (1)Car (2)Plane: 0 Part Number: 378 Engine No.: 4938 Part Number: 2837 Model Year: 90 Part Number: 3000 Model Year: 93 Part Number: 4499 Model Year: 94 Analysis: The Week 2 in Review listing provides a linked list implementation for Part objects. A linked list is a dynamic data structure; that is, it is like an array but it is sized to fit as objects areadded and deleted. This particular linked list is designed to hold objects of class Part, where Part is an abstract data type serving as a base class to any objects with a part number. In this example, Part has been subclassed into CarPart and AirPlanePart. Class Part is declared on lines 34-44, and consists of a part number and some accessors. Presumably this class could be fleshed out to hold other important information about the parts, such as what components they are used in, how many are in stock, and so forth. Part is an abstract data type, enforced by the pure virtual function Display(). Note that Display() does have an implementation, on lines 48-51. It is the designer's intention that derived classes will be forced to create their own Display() method, but may chain up to this method as well. Two simple derived classes, CarPart and AirPlanePart, are provided on lines 55-67 and 77-89, respectively. Each provides an overridden Display() method, which does in fact chain up to the base class Display() method. The class PartNode serves as the interface between the Part class and the PartList class. It contains a pointer to a part and a pointer to the next node in the list. Its only methods are to get and set the next node in the list and to return the Part to which it points. The intelligence of the list is, appropriately, in the class PartsList, whose declaration is on lines 140-160. PartsList keeps a pointer to the first element in the list (pHead) and uses that to access all other methods by walking the list. Walking the list means asking each node in the list for the next node, until you reach a node whose next pointer is NULL. This is only a partial implementation; a fully developed list would provide either greater access to its first and last nodes, or would provide an iterator object, which allows clients to easily walk the list. PartsList nonetheless provides a number of interesting methods, which are listed in alphabetical order. This is often a good idea, as it makes finding the functions easier. Find() takes a PartNumber and a ULONG. If the part corresponding to PartNumber is found, it returns a pointer to the Part and fills the ULONG with the position of that part in the list. If PartNumber is not found, it returns NULL, and the position is meaningless. GetCount() returns the number of elements in the list. PartsList keeps this number as a member variable, itsCount, though it could, of course, compute this number by walking the list. GetFirst() returns a pointer to the first Part in the list, or returns NULL if the list is empty. GetGlobalPartsList() returns a reference to the static member variable GlobalPartsList. This is a static instance of this class; every program with a PartsList also has one GlobalPartsList, though, of course, it is free to make other PartsLists as well. A fullimplementation of this idea would modify the constructor of Part to ensure that every part is created on the GlobalPartsList. Insert takes a pointer to a Part, creates a PartNode for it, and adds the Part to the list, ordered by PartNumber. Iterate takes a pointer to a member function of Part, which takes no parameters, returns void, and is const. It calls that function for every Part object in the list. In the example program this is called on Display(), which is a virtual function, so the appropriate Display() method will be called based on the runtime type of the Part object called. Operator[] allows direct access to the Part at the offset provided. Rudimentary bounds checking is provided; if the list is NULL or if the offset requested is greater than the size of the list, NULL is returned as an error condition. Note that in a real program these comments on the functions would be written into the class declaration. The driver program is on lines 274-311. A pointer to PartsList is declared on line 266 and initialized with GlobalPartsList. Note that GlobalPartsList is initialized on line 162. This is necessary as the declaration of a static member variable does not define it; definition must be done outside the declaration of the class. On lines 282-307, the user is repeatedly prompted to choose whether to enter a car part or an airplane part. Depending on the choice the right value is requested, and the appropriate part is created. Once created, the part is inserted into the list on line 306. The implementation for the Insert() method of PartsList is on lines 226-272. When the first part number is entered, 2837, a CarPart with that part number and the model year 90 is created and passed in to LinkedList::Insert(). On line 228, a new PartNode is created with that part, and the variable New is initialized with the part number. The PartsList's itsCount member variable is incremented on line 234. On line 236, the test that pHead is NULL will evaluate TRUE. Since this is the first node, it is true that the PartsList's pHead pointer has zero. Thus, on line 238, pHead is set to point to the new node and this function returns. The user is prompted to enter a second part, and this time an AirPlane part with part number 378 and engine number 4938 is entered. Once again PartsList::Insert() is called, and once again pNode is initialized with the new node. The static member variable itsCount is incremented to 2, and pHead is tested. Since pHead was assigned last time, it is no longer null and the test fails. On line 244, the part number held by pHead, 2837, is compared against the current part number, 378. Since the new one is smaller than the one held by pHead, the new one must become the newhead pointer, and the test on line 244 is true. On line 246, the new node is set to point to the node currently pointed to by pHead. Note that this does not point the new node to pHead, but rather to the node that pHead was pointing to! On line 247, pHead is set to point to the new node. The third time through the loop, the user enters the part number 4499 for a Car with model year 94. The counter is incremented and the number this time is not less than the number pointed to by pHead, so the for loop that begins on line 251 is entered. The value pointed to by pHead is 378. The value pointed to by the second node is 2837. The current value is 4499. The pointer pCurrent points to the same node as pHead and so has a next value; pCurrent points to the second node, and so the test on line 254 fails. The pointer pCurrent is set to point to the next node and the loop repeats. This time the test on line 254 succeeds. There is no next item, so the current node is told to point to the new node on line 256, and the insert is finished. The fourth time through, the part number 3000 is entered. This proceeds just like the previous iteration, but this time when the current node is pointing to 2837 and the next node has 4499, the test on line 264 returns TRUE and the new node is inserted into position. When the user finally presses 0, the test on line 287 evaluates true and the while(1) loop breaks. On line 308, the member function Display() is assigned to the pointer to member function pFunc. In a real program this would be assigned dynamically, based on the user's choice of method. The pointer to member function is passed to the PartsList Iterate() method. On line 216, the Iterate() method ensures that the list is not empty. Then, on lines 221-223, each Part on the list is called using the pointer to member function. This calls the appropriate Display() method for the Part, as shown in the output Containment As you have seen in previous examples, it is possible for the member data of a class to include objects of another class. C++ programmers say that the outer class contains the inner class. Thus, an Employee class might contain string objects (for the name of the employee), as well as integers (for the employee's salary and so forth). Listing 15.1 describes an incomplete, but still useful, String class. This listing does not produce any output. Instead Listing 15.1 will be used with later listings. Listing 15.1. The String class. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: #include #include class String { public: // constructors String(); String(const char *const); String(const String &); ~String(); // overloaded operators char & operator[](int offset); char operator[](int offset) const; String operator+(const String&); void operator+=(const String&); String & operator= (const String &); // General accessors int GetLen()const { return itsLen; }22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: const char * GetString() const { return itsString; } // static int ConstructorCount; private: String (int); // private constructor char * itsString; unsigned short itsLen; }; // default constructor creates string of 0 bytes String::String() { itsString = new char[1]; itsString[0] = `\0'; itsLen=0; // cout << "\tDefault string constructor\n"; // ConstructorCount++; } // private (helper) constructor, used only by // class methods for creating a new string of // required size. Null filled. String::String(int len) { itsString = new char[len+1]; for (int i = 0; i<=len; i++) itsString[i] = `\0'; itsLen=len; // cout << "\tString(int) constructor\n"; // ConstructorCount++; } // Converts a character array to a String String::String(const char * const cString) { itsLen = strlen(cString); itsString = new char[itsLen+1]; for (int i = 0; i itsLen) return itsString[itsLen-1]; else return itsString[offset]; }114: // constant offset operator for use 115: // on const objects (see copy constructor!) 116: char String::operator[](int offset) const 117: { 118: if (offset > itsLen) 119: return itsString[itsLen-1]; 120: else 121: return itsString[offset]; 122: } 123: 124: // creates a new string by adding current 125: // string to rhs 126: String String::operator+(const String& rhs) 127: { 128: int totalLen = itsLen + rhs.GetLen(); 129: String temp(totalLen); 130: int i, j; 131: for (i = 0; i typedef unsigned long ULONG; typedef unsigned short USHORT; // **************** Part ************8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: // Abstract base class of parts class Part { public: Part():itsPartNumber(1) {} Part(ULONG PartNumber): itsPartNumber(PartNumber){} virtual ~Part(){} ULONG GetPartNumber() const { return itsPartNumber; } virtual void Display() const =0; private: ULONG itsPartNumber; }; // implementation of pure virtual function so that // derived classes can chain up void Part::Display() const { cout << "\nPart Number: " << itsPartNumber << endl; } // **************** Car Part ************ class CarPart : public Part { public: CarPart():itsModelYear(94){} CarPart(USHORT year, ULONG partNumber); virtual void Display() const { Part::Display(); cout << "Model Year: "; cout << itsModelYear << endl; } private: USHORT itsModelYear; }; CarPart::CarPart(USHORT year, ULONG partNumber): itsModelYear(year), Part(partNumber) {} // **************** AirPlane Part ************54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: class AirPlanePart : public Part { public: AirPlanePart():itsEngineNumber(1){}; AirPlanePart (USHORT EngineNumber, ULONG PartNumber); virtual void Display() const { Part::Display(); cout << "Engine No.: "; cout << itsEngineNumber << endl; } private: USHORT itsEngineNumber; }; AirPlanePart::AirPlanePart (USHORT EngineNumber, ULONG PartNumber): itsEngineNumber(EngineNumber), Part(PartNumber) {} // **************** Part Node ************ class PartNode { public: PartNode (Part*); ~PartNode(); void SetNext(PartNode * node) { itsNext = node; } PartNode * GetNext() const; Part * GetPart() const; private: Part *itsPart; PartNode * itsNext; }; // PartNode Implementations... PartNode::PartNode(Part* pPart): itsPart(pPart), itsNext(0) {} PartNode::~PartNode() {100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: const; 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: delete itsPart; itsPart = 0; delete itsNext; itsNext = 0; } // Returns NULL if no next PartNode PartNode * PartNode::GetNext() const { return itsNext; } Part * PartNode::GetPart() const { if (itsPart) return itsPart; else return NULL; //error } // **************** Part List ************ class PartsList { public: PartsList(); ~PartsList(); // needs copy constructor and operator equals! void Iterate(void (Part::*f)()const) const; Part* Find(ULONG & position, ULONG PartNumber) Part* GetFirst() const; void Insert(Part *); Part* operator[](ULONG) const; ULONG GetCount() const { return itsCount; } static PartsList& GetGlobalPartsList() { return GlobalPartsList; } private: PartNode * pHead; ULONG itsCount; static PartsList GlobalPartsList; };145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: PartsList PartsList::GlobalPartsList; PartsList::PartsList(): pHead(0), itsCount(0) {} PartsList::~PartsList() { delete pHead; } Part* PartsList::GetFirst() const { if (pHead) return pHead->GetPart(); else return NULL; // error catch here } Part * PartsList::operator[](ULONG offSet) const { PartNode* pNode = pHead; if (!pHead) return NULL; // error catch here if (offSet > itsCount) return NULL; // error for (ULONG i=0;iGetNext(); return pNode->GetPart(); } Part* PartsList::Find( ULONG & position, ULONG PartNumber) const { PartNode * pNode = 0; for (pNode = pHead, position = 0; pNode!=NULL; pNode = pNode->GetNext(), position++) {191: if (pNode->GetPart()->GetPartNumber() == PartNumber) 192: break; 193: } 194: if (pNode == NULL) 195: return NULL; 196: else 197: return pNode->GetPart(); 198: } 199: 200: void PartsList::Iterate(void (Part::*func)()const) const 201: { 202: if (!pHead) 203: return; 204: PartNode* pNode = pHead; 205: do 206: (pNode->GetPart()->*func)(); 207: while (pNode = pNode->GetNext()); 208: } 209: 210: void PartsList::Insert(Part* pPart) 211: { 212: PartNode * pNode = new PartNode(pPart); 213: PartNode * pCurrent = pHead; 214: PartNode * pNext = 0; 215: 216: ULONG New = pPart->GetPartNumber(); 217: ULONG Next = 0; 218: itsCount++; 219: 220: if (!pHead) 221: { 222: pHead = pNode; 223: return; 224: } 225: 226: // if this one is smaller than head 227: // this one is the new head 228: if (pHead->GetPart()->GetPartNumber() > New) 229: { 230: pNode->SetNext(pHead); 231: pHead = pNode; 232: return; 233: } 234: 235: for (;;)236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: } 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: { // if there is no next, append this new one if (!pCurrent->GetNext()) { pCurrent->SetNext(pNode); return; } // if this goes after this one and before the next // then insert it here, otherwise get the next pNext = pCurrent->GetNext(); Next = pNext->GetPart()->GetPartNumber(); if (Next > New) { pCurrent->SetNext(pNode); pNode->SetNext(pNext); return; } pCurrent = pNext; } } class PartsCatalog { public: void Insert(Part *); ULONG Exists(ULONG PartNumber); Part * Get(int PartNumber); operator+(const PartsCatalog &); void ShowAll() { thePartsList.Iterate(Part::Display); private: PartsList thePartsList; }; void PartsCatalog::Insert(Part * newPart) { ULONG partNumber = newPart->GetPartNumber(); ULONG offset; if (!thePartsList.Find(offset, partNumber)) thePartsList.Insert(newPart); else281: { 282: cout << partNumber << " was the "; 283: switch (offset) 284: { 285: case 0: cout << "first "; break; 286: case 1: cout << "second "; break; 287: case 2: cout << "third "; break; 288: default: cout << offset+1 << "th "; 289: } 290: cout << "entry. Rejected!\n"; 291: } 292: } 293: 294: ULONG PartsCatalog::Exists(ULONG PartNumber) 295: { 296: ULONG offset; 297: thePartsList.Find(offset,PartNumber); 298: return offset; 299: } 300: 301: Part * PartsCatalog::Get(int PartNumber) 302: { 303: ULONG offset; 304: Part * thePart = thePartsList.Find(offset, PartNumber); 305: return thePart; 306: } 307: 308: 309: int main() 310: { 311: PartsCatalog pc; 312: Part * pPart = 0; 313: ULONG PartNumber; 314: USHORT value; 315: ULONG choice; 316: 317: while (1) 318: { 319: cout << "(0)Quit (1)Car (2)Plane: "; 320: cin >> choice; 321: 322: if (!choice) 323: break; 324: 325: cout << "New PartNumber?: ";326: 344: } cin >> PartNumber; if (choice == 1) { cout << "Model Year?: "; cin >> value; pPart = new CarPart(value,PartNumber); } else { cout << "Engine Number?: "; cin >> value; pPart = new AirPlanePart(value,PartNumber); } pc.Insert(pPart); } pc.ShowAll(); return 0; Output: (0)Quit (1)Car (2)Plane: 1 New PartNumber?: 1234 Model Year?: 94 (0)Quit (1)Car (2)Plane: 1 New PartNumber?: 4434 Model Year?: 93 (0)Quit (1)Car (2)Plane: 1 New PartNumber?: 1234 Model Year?: 94 1234 was the first entry. Rejected! (0)Quit (1)Car (2)Plane: 1 New PartNumber?: 2345 Model Year?: 93 (0)Quit (1)Car (2)Plane: 0 Part Number: 1234 Model Year: 94 Part Number: 2345 Model Year: 93 Part Number: 4434 Model Year: 93 Analysis: Listing 15.7 reproduces the interface to the Part, PartNode, and PartList classes from Week 2 in Review, but to save room it does not reproduce the implementation of their methods.A new class, PartsCatalog, is declared on lines 260-270. PartsCatalog has a PartsList as its data member, to which it delegates list management. Another way to say this is that the PartsCatalog is implemented in terms of this PartsList. Note that clients of the PartsCatalog do not have access to the PartsList directly. The interface is through the PartsCatalog, and as such the behavior of the PartsList is dramatically changed. For example, the PartsCatalog::Insert() method does not allow duplicate entries into the PartsList. The implementation of PartsCatalog::Insert() starts on line 272. The Part that is passed in as a parameter is asked for the value of its itsPartNumber member variable. This value is fed to the PartsList's Find() method, and if no match is found the number is inserted; otherwise an informative error message is printed. Note that PartsCatalog does the actual insert by calling Insert() on its member variable, pl, which is a PartsList. The mechanics of the actual insertion and the maintenance of the linked list, as well as searching and retrieving from the linked list, are maintained in the contained PartsList member of PartsCatalog. There is no reason for PartsCatalog to reproduce this code; it can take full advantage of the well-defined interface. This is the essence of reusability within C++: PartsCatalog can reuse the PartsList code, and the designer of PartsCatalog is free to ignore the implementation details of PartsList. The interface to PartsList (that is, the class declaration) provides all the information needed by the designer of the PartsCatalog class. Private Inheritance If PartsCatalog needed access to the protected members of LinkedList (in this case there are none), or needed to override any of the LinkedList methods, then PartsCatalog would be forced to inherit from PartsList. Since a PartsCatalog is not a PartsList object, and since you don't want to expose the entire set of functionality of PartsList to clients of PartsCatalog, you need to use private inheritance. The first thing to know about private inheritance is that all of the base member variables and functions are treated as if they were declared to be private, regardless of their actual access level in the base. Thus, to any function that is not a member function of PartsCatalog, every function inherited from PartsList is inaccessible. This is critical: private inheritance does not involve inheriting interface, just implementation. To clients of the PartsCatalog class, the PartsList class is invisible. None of its interface is available: you can't call any of its methods. You can call PartsCatalog methods, however, and they can access all of LinkedLists, because they are derived from LinkedLists.The important thing here is that the PartsCatalog isn't a PartsList, as would have been implied by public inheritance. It is implemented in terms of a PartsList, just as would have been the case with containment. The private inheritance is just a convenience. Listing 15.6 demonstrates the use of private inheritance by rewriting the PartsCatalog class as privately derived from PartsList. NOTE: To compile this program, replace lines 260-344 of Listing 15.5 with Listing 15.6 and recompile. Listing 15.6. Private inheritance. 1: //listing 15.6 demonstrates private inheritance 2: 3: //rewrites PartsCatalog from listing 15.5 4: 5: //see attached notes on compiling 6: 7: class PartsCatalog : private PartsList 8: { 9: public: 10: void Insert(Part *); 11: ULONG Exists(ULONG PartNumber); 12: Part * Get(int PartNumber); 13: operator+(const PartsCatalog &); 14: void ShowAll() { Iterate(Part::Display); } 15: private: 16: }; 17: 18: void PartsCatalog::Insert(Part * newPart) 19: { 20: ULONG partNumber = newPart->GetPartNumber(); 21: ULONG offset; 22: 23: if (!Find(offset, partNumber)) 24: PartsList::Insert(newPart); 25: else 26: { 27: cout << partNumber << " was the "; 28: switch (offset) 29: { 30: case 0: cout << "first "; break; 31: case 1: cout << "second "; break;32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: case 2: cout << "third "; break; default: cout << offset+1 << "th "; } cout << "entry. Rejected!\n"; } } ULONG PartsCatalog::Exists(ULONG PartNumber) { ULONG offset; Find(offset,PartNumber); return offset; } Part * PartsCatalog::Get(int PartNumber) { ULONG offset; return (Find(offset, PartNumber)); } int main() { PartsCatalog pc; Part * pPart = 0; ULONG PartNumber; USHORT value; ULONG choice; while (1) { cout << "(0)Quit (1)Car (2)Plane: "; cin >> choice; if (!choice) break; cout << "New PartNumber?: "; cin >> PartNumber; if (choice == 1) { cout << "Model Year?: "; cin >> value; pPart = new CarPart(value,PartNumber); }78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: } else { cout << "Engine Number?: "; cin >> value; pPart = new AirPlanePart(value,PartNumber); } pc.Insert(pPart); } pc.ShowAll(); return 0; Output: (0)Quit (1)Car (2)Plane: 1 New PartNumber?: 1234 Model Year?: 94 (0)Quit (1)Car (2)Plane: 1 New PartNumber?: 4434 Model Year?: 93 (0)Quit (1)Car (2)Plane: 1 New PartNumber?: 1234 Model Year?: 94 1234 was the first entry. Rejected! (0)Quit (1)Car (2)Plane: 1 New PartNumber?: 2345 Model Year?: 93 (0)Quit (1)Car (2)Plane: 0 Part Number: 1234 Model Year: 94 Part Number: 2345 Model Year: 93 Part Number: 4434 Model Year: 93 Analysis: Listing 15.6 shows only the changed interface to PartsCatalog and the rewritten driver program. The interfaces to the other classes are unchanged from Listing 15.5. On line 7 of Listing 15.6, PartsCatalog is declared to derive privately from PartsList. The interface to PartsCatalog doesn't change from Listing 15.5, though of course it no longer needs an object of type PartsList as member data. The PartsCatalog ShowAll() function calls PartsList Iterate() with the appropriate pointer to member function of class Part. ShowAll() acts as a public interface to Iterate(), providing the correct information but preventing client classes from calling Iterate() dir-ectly.Although PartsList might allow other functions to be passed to Iterate(), PartsCatalog does not. The Insert() function has changed as well. Note, on line 23, that Find() is now called directly, because it is inherited from the base class. The call on line 24 to Insert() must be fully qualified, of course, or it would endlessly recurse into itself. In short, when methods of PartsCatalog want to call PartsList methods, they may do so directly. The only exception is when PartsCatalog has overridden the method and the PartsList version is needed, in which case the function name must be qualified fully. Private inheritance allows the PartsCatalog to inherit what it can use, but still provide mediated access to Insert and other methods to which client classes should not have direct access. DO inherit publicly when the derived object is a kind of the base class. DO use containment when you want to delegate functionality to another class, but you don't need access to its protected members. DO use private inheritance when you need to implement one class in terms of another, and you need access to the base class's protected members. DON'T use private inheritance when you need to use more than one of the base class. You must use containment. For example, if PartsCatalog needed two PartsLists, you could not have used private inheritance. DON'T use public inheritance when members of the base class should not be available to clients of the derived class. Friend Classes Sometimes you will create classes together, as a set. For example, PartNode and PartsList were tightly coupled, and it would have been convenient if PartsList could have read PartNode's Part pointer, itsPart, directly. You wouldn't want to make itsPart public, or even protected, because this is an implementation detail of PartNode and you want to keep it private. You do want to expose it to PartsList, however. If you want to expose your private member data or functions to another class, you must declare that class to be a friend. This extends the interface of your class to include the friend class. Once PartsNode declares PartsList to be a friend, all of PartsNode's member data and functions are public as far as PartsList is concerned. It is important to note that friendship cannot be transferred. Just because you are my friend and Joe is your friend doesn't mean Joe is my friend. Friendship is not inherited either. Again, just because you are my friend and I'm willing to share my secrets with you doesn't mean I'm willing to share mysecrets with your children. Finally, friendship is not commutative. Assigning Class One to be a friend of Class Two does not make Class Two a friend of Class One. Just because you are willing to tell me your secrets doesn't mean I am willing to tell you mine. Listing 15.7 illustrates friendship by rewriting the example from Listing 15.6, making PartsList a friend of PartNode. Note that this does not make PartNode a friend of PartsList. Listing 15.7. Friend class illustrated. 0: 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: #include typedef unsigned long ULONG; typedef unsigned short USHORT; // **************** Part ************ // Abstract base class of parts class Part { public: Part():itsPartNumber(1) {} Part(ULONG PartNumber): itsPartNumber(PartNumber){} virtual ~Part(){} ULONG GetPartNumber() const { return itsPartNumber; } virtual void Display() const =0; private: ULONG itsPartNumber; }; // implementation of pure virtual function so that // derived classes can chain up void Part::Display() const { cout << "\nPart Number: "; cout << itsPartNumber << endl; } // **************** Car Part ************ class CarPart : public Part34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: { public: CarPart():itsModelYear(94){} CarPart(USHORT year, ULONG partNumber); virtual void Display() const { Part::Display(); cout << "Model Year: "; cout << itsModelYear << endl; } private: USHORT itsModelYear; }; CarPart::CarPart(USHORT year, ULONG partNumber): itsModelYear(year), Part(partNumber) {} // **************** AirPlane Part ************ class AirPlanePart : public Part { public: AirPlanePart():itsEngineNumber(1){}; AirPlanePart (USHORT EngineNumber, ULONG PartNumber); virtual void Display() const { Part::Display(); cout << "Engine No.: "; cout << itsEngineNumber << endl; } private: USHORT itsEngineNumber; }; AirPlanePart::AirPlanePart (USHORT EngineNumber, ULONG PartNumber): itsEngineNumber(EngineNumber), Part(PartNumber) {} // **************** Part Node ************ class PartNode80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: { public: friend class PartsList; PartNode (Part*); ~PartNode(); void SetNext(PartNode * node) { itsNext = node; } PartNode * GetNext() const; Part * GetPart() const; private: Part *itsPart; PartNode * itsNext; }; PartNode::PartNode(Part* pPart): itsPart(pPart), itsNext(0) {} PartNode::~PartNode() { delete itsPart; itsPart = 0; delete itsNext; itsNext = 0; } // Returns NULL if no next PartNode PartNode * PartNode::GetNext() const { return itsNext; } Part * PartNode::GetPart() const { if (itsPart) return itsPart; else return NULL; //error } // **************** Part List ************ class PartsList {126: 127: 128: 129: 130: 131: const; 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: public: PartsList(); ~PartsList(); // needs copy constructor and operator equals! void Iterate(void (Part::*f)()const) const; Part* Find(ULONG & position, ULONG PartNumber) Part* void Part* ULONG static GetFirst() const; Insert(Part *); operator[](ULONG) const; GetCount() const { return itsCount; } PartsList& GetGlobalPartsList() { return GlobalPartsList; } private: PartNode * pHead; ULONG itsCount; static PartsList GlobalPartsList; }; PartsList PartsList::GlobalPartsList; // Implementations for Lists... PartsList::PartsList(): pHead(0), itsCount(0) {} PartsList::~PartsList() { delete pHead; } Part* PartsList::GetFirst() const { if (pHead) return pHead->itsPart; else return NULL; // error catch here } Part * PartsList::operator[](ULONG offSet) const { PartNode* pNode = pHead;171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: const 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: if (!pHead) return NULL; // error catch here if (offSet > itsCount) return NULL; // error for (ULONG i=0;iitsNext; return pNode->itsPart; } Part* PartsList::Find(ULONG & position, ULONG PartNumber) { PartNode * pNode = 0; for (pNode = pHead, position = 0; pNode!=NULL; pNode = pNode->itsNext, position++) { if (pNode->itsPart->GetPartNumber() == PartNumber) break; } if (pNode == NULL) return NULL; else return pNode->itsPart; } void PartsList::Iterate(void (Part::*func)()const) const { if (!pHead) return; PartNode* pNode = pHead; do (pNode->itsPart->*func)(); while (pNode = pNode->itsNext); } void PartsList::Insert(Part* pPart) { PartNode * pNode = new PartNode(pPart); PartNode * pCurrent = pHead; PartNode * pNext = 0;216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: ULONG New = pPart->GetPartNumber(); ULONG Next = 0; itsCount++; if (!pHead) { pHead = pNode; return; } // if this one is smaller than head // this one is the new head if (pHead->itsPart->GetPartNumber() > New) { pNode->itsNext = pHead; pHead = pNode; return; } for (;;) { // if there is no next, append this new one if (!pCurrent->itsNext) { pCurrent->itsNext = pNode; return; } // if this goes after this one and before the next // then insert it here, otherwise get the next pNext = pCurrent->itsNext; Next = pNext->itsPart->GetPartNumber(); if (Next > New) { pCurrent->itsNext = pNode; pNode->itsNext = pNext; return; } pCurrent = pNext; } } class PartsCatalog : private PartsList { public: void Insert(Part *);262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: ULONG Exists(ULONG PartNumber); Part * Get(int PartNumber); operator+(const PartsCatalog &); void ShowAll() { Iterate(Part::Display); } private: }; void PartsCatalog::Insert(Part * newPart) { ULONG partNumber = newPart->GetPartNumber(); ULONG offset; if (!Find(offset, partNumber)) PartsList::Insert(newPart); else { cout << partNumber << " was the "; switch (offset) { case 0: cout << "first "; break; case 1: cout << "second "; break; case 2: cout << "third "; break; default: cout << offset+1 << "th "; } cout << "entry. Rejected!\n"; } } ULONG PartsCatalog::Exists(ULONG PartNumber) { ULONG offset; Find(offset,PartNumber); return offset; } Part * PartsCatalog::Get(int PartNumber) { ULONG offset; return (Find(offset, PartNumber)); } int main() { PartsCatalog pc; Part * pPart = 0;308: 339: } ULONG PartNumber; USHORT value; ULONG choice; while (1) { cout << "(0)Quit (1)Car (2)Plane: "; cin >> choice; if (!choice) break; cout << "New PartNumber?: "; cin >> PartNumber; if (choice == 1) { cout << "Model Year?: "; cin >> value; pPart = new CarPart(value,PartNumber); } else { cout << "Engine Number?: "; cin >> value; pPart = new AirPlanePart(value,PartNumber); } pc.Insert(pPart); } pc.ShowAll(); return 0; Output: (0)Quit (1)Car (2)Plane: 1 New PartNumber?: 1234 Model Year?: 94 (0)Quit (1)Car (2)Plane: 1 New PartNumber?: 4434 Model Year?: 93 (0)Quit (1)Car (2)Plane: 1 New PartNumber?: 1234 Model Year?: 94 1234 was the first entry. Rejected! (0)Quit (1)Car (2)Plane: 1 New PartNumber?: 2345 Model Year?: 93(0)Quit (1)Car (2)Plane: 0 Part Number: 1234 Model Year: 94 Part Number: 2345 Model Year: 93 Part Number: 4434 Model Year: 93 Analysis: On line 82, the class PartsList is declared to be a friend to the PartNode class. Because PartsList has not yet been declared, the compiler would complain that this type is not known. This listing places the friend declaration in the public section, but this is not required; it can be put anywhere in the class declaration without changing the meaning of the statement. Because of this statement, all the private member data and functions are available to any member function of class PartsList. On line 160, the implementation of the member function GetFirst() reflects this change. Rather than returning pHead->GetPart, this function can now return the otherwise private member data by writing pHead->itsPart. Similarly, the Insert() function can now write pNode- >itsNext = pHead, rather than writing pNode->SetNext(pHead). Admittedly these are trivial changes, and there is not a good enough reason to make PartsList a friend of PartNode, but they do serve to illustrate how the keyword friend works. Declarations of friend classes should be used with extreme caution. If two classes are inextricably entwined, and one must frequently access data in the other, there may be good reason to use this declaration. But use it sparingly; it is often just as easy to use the public accessor methods, and doing so allows you to change one class without having to recompile the other. NOTE: You will often hear novice C++ programmers complain that friend declarations "undermine" the encapsulation so important to object-oriented programming. This is, frankly, errant nonsense. The friend declaration makes the declared friend part of the class interface, and is no more an undermining of encapsulation than is public derivation. Friend Class Declare one class to be a friend of another by putting the word friend into the class granting the access rights. That is, I can declare you to be my friend, but you can't declare yourself to be my friend.Example: class PartNode{ public: friend class PartList; PartNode }; // declares PartList to be a friend of Friend Functions At times you will want to grant this level of access not to an entire class, but only to one or two functions of that class. You can do this by declaring the member functions of the other class to be friends, rather than declaring the entire class to be a friend. In fact, you can declare any function, whether or not it is a member function of another class, to be a friend function. Friend Functions and Operator Overloading Listing 15.1 provided a String class that overrode the operator+. It also provided a constructor that took a constant character pointer, so that string objects could be created from C-style strings. This allowed you to create a string and add to it with a C-style string. NOTE: C-style strings are null-terminated character arrays, such as char myString[] = "Hello World." What you could not do, however, was create a C-style string (a character string) and add to it using a string object, as shown in this example: char cString[] = {"Hello"}; String sString(" World"); String sStringTwo = cString + sString; //error! C-style strings don't have an overloaded operator+. As discussed on Day 10, "Advanced Functions," when you say cString + sString; what you are really calling is cString.operator+(sString). Since you can't call operator+() on a C-style string, this causes a compile-time error. You can solve this problem by declaring a friend function in String, which overloads operator+ but takes two string objects. The C-style string will be converted to a string object by the appropriate constructor, and then operator+ will be called using the two string objects. NOTE: To compile this listing, copy lines 33-123 from Listing 15.1 after line 33 ofListing 15.8. Listing 15.8. Friendly operator+. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: //Listing 15.8 - friendly operators #include #include // Rudimentary string class class String { public: // constructors String(); String(const char *const); String(const String &); ~String(); // overloaded operators char & operator[](int offset); char operator[](int offset) const; String operator+(const String&); friend String operator+(const String&, const String&); void operator+=(const String&); String & operator= (const String &); // General accessors int GetLen()const { return itsLen; } const char * GetString() const { return itsString; } private: String (int); // private constructor char * itsString; unsigned short itsLen; }; // creates a new string by adding current // string to rhs String String::operator+(const String& rhs) { int totalLen = itsLen + rhs.GetLen(); String temp(totalLen); for (int i = 0; i #include class String { public: // constructors String(); String(const char *const); String(const String &); ~String(); // overloaded operators char & operator[](int offset); char operator[](int offset) const; String operator+(const String&); void operator+=(const String&); String & operator= (const String &); friend ostream& operator<< ( ostream& theStream,String& theString); // General accessors int GetLen()const { return itsLen; } const char * GetString() const { return itsString; } // static int ConstructorCount; private: String (int); // private constructor char * itsString; unsigned short itsLen; }; ostream& operator<< ( ostream& theStream,String& theString) { theStream << theString.GetString(); return theStream; } int main()38: { 39: String theString("Hello world."); 40: cout << theString; 41: return 0; 42: } Output: Hello world. Analysis: To save space, the implementation of all of String's methods is left out, as they are unchanged from the previous examples. On line 19, operator<< is declared to be a friend function that takes an ostream reference and a String reference and then returns an ostream reference. Note that this is not a member function of String. It returns a reference to an ostream so that you can concatenate calls to operator<<, such as this: cout << "myAge: " << itsAge << " years."; The implementation of this friend function is on lines 32-35. All this really does is hide the implementation details of feeding the string to the ostream, and that is just as it should be. You'll see more about overloading this operator and operator>> on Day 16. Summary Today you saw how to delegate functionality to a contained object. You also saw how to implement one class in terms of another by using either containment or private inheritance. Containment is restricted in that the new class does not have access to the protected members of the contained class, and it cannot override the member functions of the contained object. Containment is simpler to use than private inheritance, and should be used when possible. You also saw how to declare both friend functions and friend classes. Using a friend function, you saw how to overload the extraction operator, to allow your new classes to use cout just as the built- in classes do. Remember that public inheritance expresses is-a, containment expresses has-a, and private inheritance expresses implemented in terms of. The relationship delegates to can be expressed using either containment or private inheritance, though containment is more common. Q&A Q. Why is it so important to distinguish between is-a, has-a, and implemented in terms of? A. The point of C++ is to implement well-designed, object-oriented programs. Keeping these relationships straight helps to ensure that your design corresponds to the reality of what you aremodeling. Furthermore, a well-understood design will more likely be reflected in well- designed code. Q. Why is containment preferred over private inheritance? A. The challenge in modern programming is to cope with complexity. The more you can use objects as black boxes, the fewer details you have to worry about and the more complexity you can manage. Contained classes hide their details; private inheritance exposes the implementation details. Q. Why not make all classes friends of all the classes they use? A. Making one class a friend of another exposes the implementation details and reduces encapsulation. The ideal is to keep as many of the details of each class hidden from all other classes as possible. Q. If a function is overloaded, do you need to declare each form of the function to be a friend? A. Yes, if you overload a function and declare it to be a friend of another class, you must declare friend for each form that you wish to grant this access to. Workshop The Workshop contains quiz questions to help solidify your understanding of the material covered and exercises to provide you with experience in using what you've learned. Try to answer the quiz and exercise questions before checking the answers in Appendix D, and make sure you understand the answers before going to the next chapter. Quiz 1. How do you establish an is-a relationship? 2. How do you establish a has-a relationship? 3. What is the difference between containment and delegation? 4. What is the difference between delegation and implemented in terms of? 5. What is a friend function? 6. What is a friend class? 7. If Dog is a friend of Boy, is Boy a friend of Dog? 8. If Dog is a friend of Boy, and Terrier derives from Dog, is Terrier a friend of Boy? 9. If Dog is a friend of Boy and Boy is a friend of House, is Dog a friend of House?10. Where must the declaration of a friend function appear? Exercises 1. Show the declaration of a class, Animal, that contains a datamember that is a string object. 2. Show the declaration of a class, BoundedArray, that is an array. 3. Show the declaration of a class, Set, that is declared in terms of an array. 4. Modify Listing 15.1 to provide the String class with an extraction operator (>>). 5. BUG BUSTERS: What is wrong with this program? 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: #include class Animal; void setValue(Animal& , int); class Animal { public: int GetWeight()const { return itsWeight; } int GetAge() const { return itsAge; } private: int itsWeight; int itsAge; }; void setValue(Animal& theAnimal, int theWeight) { friend class Animal; theAnimal.itsWeight = theWeight; } int main() { Animal peppy; setValue(peppy,5);28: } 6. Fix the listing in Exercise 5 so it compiles. 7. BUG BUSTERS: What is wrong with this code?1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: #include class Animal; void setValue(Animal& , int); void setValue(Animal& ,int,int); class Animal { friend void setValue(Animal& ,int);11: int itsWeight; int itsAge; }; private: void setValue(Animal& theAnimal, int theWeight) { theAnimal.itsWeight = theWeight; } void setValue(Animal& theAnimal, int theWeight, int theAge) { theAnimal.itsWeight = theWeight; theAnimal.itsAge = theAge; } int main() { Animal peppy; setValue(peppy,5); setValue(peppy,7,9); } 8. Fix Exercise 7 so it compiles. Binary Versus Text Files Listing 16.18. Writing a class to a file. Command-Line Processing Listing 16.19. Using command-line arguments. Listing 16.20. Using command-line arguments. Summary Q&A Workshop C++ does not, as part of the language, define how data is written to the screen or to a file, nor how data is read into a program. These are clearly essential parts of working with C++, however, and the standard C++ library now includes the iostream library, which facilitates input and output (I/O). The advantage of having the input and output kept apart from the language and handled in libraries is that it is easier to make the language "platform-independent." That is, you can write C++ programs on a PC and then recompile them and run them on a Sun Workstation. The compiler manufacturer just supplies the right library, and everything works. At least that's the theory. NOTE: A library is a collection of OBJ files that can be linked to your program to provide additional functionality. This is the most basic form of code reuse, and has been around since ancient programmers chiseled 1s and 0s into the walls of caves. Encapsulation The iostream classes view the flow of data from your program to the screen as being a stream of data, one byte following another. If the destination of the stream is a file or the screen, the source is usually some part of your program. If the stream is reversed, the data can come from the keyboard or a disk file and be "poured" into your data variables. One principal goal of streams is to encapsulate the problems of getting the data to and from the disk or the screen. Once a stream is created, your program works with the stream and the stream sweats the details. Figure 16.1 illustrates this fundamental idea. Figure 16.1. Encapsulation through streams. Buffering Writing to the disk (and to a lesser extent the screen) is very "expensive." It takes a long time (relatively speaking) to write data to the disk or to read data from the disk, and execution of the program is generally blocked by disk writes and reads. To solve this problem, streams provide "buffering." Data is written into the stream, but it is not written back out to the disk immediately. Instead, the stream's buffer fills and fills, and when it is full it writes to the disk all at once. Picture water trickling into the top of a tank, and the tank filling and filling, but no water running out of the bottom. Figure 16.2 illustrates this idea. When the water (data) reaches the top, the valve opens and all the water flows out in a rush. Figure 16.3 illustrates this. Once the buffer is empty, the bottom valve closes, the top valve opens, and more water flows into thebuffer tank. Figure 16.4 illustrates this. Every once in a while you need to get the water out of the tank even before it is full. This is called "flushing the buffer." Figure 16.5 illustrates this idea. Figure 16.2. Filling the buffer. Figure 16.3. Emptying the buffer. Figure 16.4. Refilling the buffer. Figure 16.5. Flushing the buffer. Streams and Buffers As you might expect, C++ takes an object-oriented view toward implementing streams and buffers. !(- $!. /(. '(# &*" The streambuf class manages the buffer, and its member functions provide the capability to fill, empty, flush, and otherwise manipulate the buffer. The ios class is the base class to the input and output stream classes. The ios class has a streambuf object as a member variable. The istream and ostream classes derive from the ios class and specialize input and output stream behavior, respectively. The iostream class is derived from both the istream and the ostream classes and provides input and output methods for writing to the screen. The fstream classes provide input and output from files. Standard I/O Objects When a C++ program that includes the iostream classes starts, four objects are created and initialized: NOTE: The iostream class library is added automatically to your program by the compiler. All you need to do to use these functions is to put the appropriate include statement at the top of your program listing. /(" cin (pronounced "see-in") handles input from the standard input, the keyboard.-!' &,$ $%* cou (pronounced "see-out") handles output to the standard output, the screen. cer (pronounced "see-err") handles unbuffered output to the standard error device, the screen. Because this is unbuffered, everything sent to cerr is written to the standard error device immediately, without waiting for the buffer to fill or for a flush command to be received. clo (pronounced "see-log") handles buffered error messages that are output to the standard error device, the screen. It is common for this to be "redirected" to a log file, as described in the following section. Redirection Each of the standard devices, input, output, and error, can be redirected to other devices. Standard error is often redirected to a file, and standard input and output can be piped to files using operating system commands. New Term: Redirecting refers to sending output (or input) to a place different than the default. The redirection operators for DOS and UNIX are (<) redirect input and (>) redirect output. Piping refers to using the output of one program as the input of another. DOS provides rudimentary redirection commands, such as redirect output (>) and (>)redirect input (<). UNIX provides more advanced redirection capabilities, but the general idea is the same: Take the output intended for the screen and write it to a file, or pipe it into another program. Alternatively, the input for a program can be extracted from a file rather than from the keyboard. Redirection is more a function of the operating system than of the iostream libraries. C++ just provides access to the four standard devices; it is up to the user to redirect the devices to whatever alternatives are needed. Input Using cin The global object cin is responsible for input and is made available to your program when you include iostream.h. In previous examples, you used the overloaded extraction operator (>>) to put data into your program's variables. How does this work? The syntax, as you may remember, is the following: int someVariable; cout << "Enter a number: "; cin >> someVariable; The global object cout is discussed later today; for now, focus on the third line, cin >>someVariable;. What can you guess about cin? Clearly it must be a global object, because you didn't define it in your own code. You know from previous operator experience that cin has overloaded the extraction operator (>>) and that the effect is to write whatever data cin has in its buffer into your local variable, someVariable. What may not be immediately obvious is that cin has overloaded the extraction operator for a great variety of parameters, among them int&, short&, long&, double&, float&, char&, char*, and so forth. When you write cin >> someVariable;, the type of someVariable is assessed. In the example above, someVariable is an integer, so the following function is called: istream & operator>> (int &) Note that because the parameter is passed by reference, the extraction operator is able to act on the original variable. Listing 16.1 illustrates the use of cin. Listing 16.1. cin handles different data types. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: //Listing 16.1 -- character strings and cin #include int main() { int myInt; long myLong; double myDouble; float myFloat; unsigned int myUnsigned; cout << "int: "; cin >> myInt; cout << "Long: "; cin >> myLong; cout << "Double: "; cin >> myDouble; cout << "Float: "; cin >> myFloat; cout << "Unsigned: "; cin >> myUnsigned; cout cout cout cout << << << << "\n\nInt:\t" << myInt << endl; "Long:\t" << myLong << endl; "Double:\t" << myDouble << endl; "Float:\t" << myFloat << endl;28: 29: 30: } cout << "Unsigned:\t" << myUnsigned << endl; return 0; Output: int: 2 Long: 70000 Double: 987654321 Float: 3.33 Unsigned: 25 Int: 2 Long: 70000 Double: 9.87654e+08 Float: 3.33 Unsigned: 25 Analysis: On lines 7-11, variables of various types are declared. On lines 13-22, the user is prompted to enter values for these variables, and the results are printed (using cout) on lines 24-28. The output reflects that the variables were put into the right "kinds" of variables, and the program works as you might expect. Strings cin can also handle character pointer (char*) arguments; thus, you can create a character buffer and use cin to fill it. For example, you can write this: char YourName[50] cout << "Enter your name: "; cin >> YourName; If you enter Jesse, the variable YourName will be filled with the characters J, e, s, s, e, \0. The last character is a null; cin automatically ends the string with a null character, and you must have enough room in the buffer to allow for the entire string plus the null. The null signals "end of string" to the standard library functions discussed on Day 21, "What's Next." String Problems After all this success with cin, you might be surprised when you try to enter a full name into a string. cin believes that white space is a separator. When it sees a space or a new line, it assumes the input for the parameter is complete, and in the case of strings it adds a null character right then and there. Listing 16.2 illustrates this problem. Listing 16.2. Trying to write more than one word to cin.1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: } //Listing 16.2 -- character strings and cin #include int main() { char YourName[50]; cout << "Your first name: "; cin >> YourName; cout << "Here it is: " << YourName << endl; cout << "Your entire name: "; cin >> YourName; cout << "Here it is: " << YourName << endl; return 0; Output: Your first name: Jesse Here it is: Jesse Your entire name: Jesse Liberty Here it is: Jesse Analysis: On line 7, a character array is created to hold the user's input. On line 8, the user is prompted to enter one name, and that name is stored properly, as shown in the output. On line 11, the user is again prompted, this time for a full name. cin reads the input, and when it sees the space between the names, it puts a null character after the first word and terminates input. This is not exactly what was intended. To understand why this works this way, examine Listing 16.3, which shows input for a number of fields. Listing 16.3. Multiple input. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: //Listing 16.3 - character strings and cin #include int main() { int myInt; long myLong; double myDouble; float myFloat; unsigned int myUnsigned; char myWord[50];14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: } cout << "int: "; cin >> myInt; cout << "Long: "; cin >> myLong; cout << "Double: "; cin >> myDouble; cout << "Float: "; cin >> myFloat; cout << "Word: "; cin >> myWord; cout << "Unsigned: "; cin >> myUnsigned; cout cout cout cout cout cout << << << << << << "\n\nInt:\t" << myInt << endl; "Long:\t" << myLong << endl; "Double:\t" << myDouble << endl; "Float:\t" << myFloat << endl; "Word: \t" << myWord << endl; "Unsigned:\t" << myUnsigned << endl; cout << "\n\nInt, Long, Double, Float, Word, Unsigned: "; cin >> myInt >> myLong >> myDouble; cin >> myFloat >> myWord >> myUnsigned; cout << "\n\nInt:\t" << myInt << endl; cout << "Long:\t" << myLong << endl; cout << "Double:\t" << myDouble << endl; cout << "Float:\t" << myFloat << endl; cout << "Word: \t" << myWord << endl; cout << "Unsigned:\t" << myUnsigned << endl; return 0; Output: Int: 2 Long: 30303 Double: 393939397834 Float: 3.33 Word: Hello Unsigned: 85 Int: Long: Double: Float: Word: 2 30303 3.93939e+11 3.33 HelloUnsigned: 85 Int, Long, Double, Float, Word, Unsigned: 3 304938 393847473 6.66 bye -2 Int: Long: Double: Float: Word: 3 304938 3.93847e+08 6.66 bye Unsigned: 65534 Analysis: Once again, a number of variables are created, this time including a char array. The user is prompted for input and the output is faithfully printed. On line 34, the user is prompted for all the input at once, and then each "word" of input is assigned to the appropriate variable. It is in order to facilitate this kind of multiple assignment that cin must consider each word in the input to be the full input for each variable. If cin was to consider the entire input to be part of one variable's input, this kind of concatenated input would be impossible. Note that on line 35 the last object requested was an unsigned integer, but the user entered -2. Because cin believes it is writing to an unsigned integer, the bit pattern of -2 was evaluated as an unsigned integer, and when written out by cout, the value 65534 was displayed. The unsigned value 65534 has the exact bit pattern of the signed value -2. Later in this chapter you will see how to enter an entire string into a buffer, including multiple words. For now, the question arises, "How does the extraction operator manage this trick of concatenation?" operator>> Returns a Reference to an istream Object The return value of cin is a reference to an istream object. Because cin itself is an istream object, the return value of one extraction operation can be the input to the next extraction. int VarOne, varTwo, varThree; cout << "Enter three numbers: " cin >> VarOne >> varTwo >> varThree; When you write cin >> VarOne >> varTwo >> varThree;, the first extraction is evaluated (cin >> VarOne). The return value from this is another istream object, and that object's extraction operator gets the variable varTwo. It is as if you had written this: ((cin >> varOne) >> varTwo) >> varThree; You'll see this technique repeated later when cout is discussed.Other Member Functions of cin In addition to overloading operator>>, cin has a number of other member functions. These are used when finer control over the input is required. Single Character Input operator>> taking a character reference can be used to get a single character from the standard input. The member function get() can also be used to obtain a single character, and can do so in two ways. get() can be used with no parameters, in which case the return value is used, or it can be used with a reference to a character. Using get() with No Parameters The first form of get() is without parameters. This returns the value of the character found, and will return EOF (end of file) if the end of the file is reached. get() with no parameters is not often used. It is not possible to concatenate this use of get() for multiple input, because the return value is not an iostream object. Thus, the following won't work: cin.get() >>myVarOne >> myVarTwo; // illegal The return value of (cin.get() >> myVarOne) is an integer, not an iostream object. A common use of get() with no parameters is illustrated in Listing 16.4. Listing 16.4. Using get() with no parameters. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: } // Listing 16.4 - Using get() with no parameters #include int main() { char ch; while ( (ch = cin.get()) != EOF) { cout << "ch: " << ch << endl; } cout << "\nDone!\n"; return 0; NOTE: To exit this program, you must send end of file from the keyboard. On DOS computers use Ctrl+Z; on UNIX units use Ctrl+D. Output: Helloch: ch: ch: ch: ch: ch: H e l l o World ch: W ch: o ch: r ch: l ch: d ch: (ctrl-z) Done! Analysis: On line 6, a local character variable is declared. The while loop assigns the input received from cin.get() to ch, and if it is not EOF the string is printed out. This output is buffered until an end of line is read, however. Once EOF is encountered (by pressing Ctrl+Z on a DOS machine, or Ctrl+D on a UNIX machine), the loop exits. Note that not every implementation of istream supports this version of get(). Using get() with a Character Reference Parameter When a character is passed as input to get(), that character is filled with the next character in the input stream. The return value is an iostream object, and so this form of get() can be concatenated, as illustrated in Listing 16.5. Listing 16.5 Using get() with parameters. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: endl; 13: 14: } // Listing 16.5 - Using get() with parameters #include int main() { char a, b, c; cout << "Enter three letters: "; cin.get(a).get(b).get(c); cout << "a: " << a << "\nb: " << b << "\nc: " << c << return 0;Output: Enter three letters: one a: o b: n c: e Analysis: On line 6, three character variables are created. On line 10, cin.get() is called three times, concatenated. First cin.get(a) is called. This puts the first letter into a and returns cin so that when it is done, cin.get(b) is called, putting the next letter into b. The end result of this is that cin.get(c) is called and the third letter is put in c. Because cin.get(a) evaluates to cin, you could have written this: cin.get(a) >> b; In this form, cin.get(a) evaluates to cin, so the second phrase is cin >> b;. DO use the extraction operator (>>) when you need to skip over white space. DO use get() with a character parameter when you need to examine every character, including white space. DON'T use get() with no parameters at all; it is more or less obsolete. Getting Strings from Standard Input The extraction operator (>>) can be used to fill a character array, as can the member functions get() and getline(). The final form of get() takes three parameters. The first parameter is a pointer to a character array, the second parameter is the maximum number of characters to read plus one, and the third parameter is the termination character. If you enter 20 as the second parameter, get() will read 19 characters and then will null-terminate the string, which it will store in the first parameter. The third parameter, the termination character, defaults to newline (`\n'). If a termination character is reached before the maximum number of characters is read, a null is written and the termination character is left in the buffer. Listing 16.6 illustrates the use of this form of get(). Listing 16.6. Using get() with a character array. 1: 2: 3: 4: // Listing 16.6 - Using get() with a character array #include int main()5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: } { char stringOne[256]; char stringTwo[256]; cout << "Enter string one: "; cin.get(stringOne,256); cout << "stringOne: " << stringOne << endl; cout << "Enter string two: "; cin >> stringTwo; cout << "StringTwo: " << stringTwo << endl; return 0; Output: Enter string one: Now is the time stringOne: Now is the time Enter string two: For all good StringTwo: For Analysis: On lines 6 and 7, two character arrays are created. On line 9, the user is prompted to enter a string, and cin.get() is called on line 10. The first parameter is the buffer to fill, and the second is one more than the maximum number for get() to accept (the extra position being given to the null character, (`\0')). The defaulted third parameter is a newline. The user enters Now is the time. Because the user ends the phrase with a newline, that phrase is put into stringOne, followed by a terminating null. The user is prompted for another string on line 13, and this time the extraction operator is used. Because the extraction operator takes everything up to the first white space, the string For, with a terminating null character, is stored in the second string, which of course is not what was intended. Another way to solve this problem is to use getline(), as illustrated in Listing 16.7. Listing 16.7. Using getline(). 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: // Listing 16.7 - Using getline() #include int main() { char stringOne[256]; char stringTwo[256]; char stringThree[256]; cout << "Enter string one: ";11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: } cin.getline(stringOne,256); cout << "stringOne: " << stringOne << endl; cout << "Enter string two: "; cin >> stringTwo; cout << "stringTwo: " << stringTwo << endl; cout << "Enter string three: "; cin.getline(stringThree,256); cout << "stringThree: " << stringThree << endl; return 0; Output: Enter string one: one two three stringOne: one two three Enter string two: four five six stringTwo: four Enter string three: stringThree: five six Analysis: This example warrants careful examination; there are some potential surprises. On lines 6-8, three character arrays are declared. On line 10, the user is prompted to enter a string, and that string is read by getline(). Like get(), getline() takes a buffer and a maximum number of characters. Unlike get(), however, the terminating newline is read and thrown away. With get() the terminating newline is not thrown away. It is left in the input buffer. On line 14, the user is prompted again, and this time the extraction operator is used. The user enters four five six, and the first word, four, is put in stringTwo. The string Enter string three is then displayed, and getline() is called again. Because five six is still in the input buffer, it is immediately read up to the newline; getline() terminates and the string in stringThree is printed on line 20. The user has no chance to enter string three, because the second getline() call is fulfilled by the string remaining in the input buffer after the call to the extraction operator on line 15. The extraction operator (>>) reads up to the first white space and puts the word into the character array. The member function get() is overloaded. In one version, it takes no parameters and returns the value of the character it receives. In the second version, it takes a single character reference and returns the istream object by reference. In the third and final version, get() takes a character array, a number of characters to get, and a termination character (which defaults to newline). This version of get() reads characters into the array until it gets to one fewer than its maximum number of characters or it encounters the terminationcharacter, whichever comes first. If get() encounters the termination character, it leaves that character in the input buffer and stops reading characters. The member function getline() also takes three parameters: the buffer to fill, one more than the maximum number of characters to get, and the termination character. getline()functions exactly like get() does with these parameters, except getline() throws away the terminating character. Using cin.ignore() At times you want to ignore the remaining characters on a line until you hit either end of line (EOL) or end of file (EOF). The member function ignore() serves this purpose. ignore() takes two parameters, the maximum number of characters to ignore and the termination character. If you write ignore(80,'\n'), up to 80 characters will be thrown away until a newline character is found. The newline is then thrown away and the ignore() statement ends. Listing 16.8 illustrates the use of ignore(). Listing 16.8. Using ignore(). 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: // Listing 16.8 - Using ignore() #include int main() { char stringOne[255]; char stringTwo[255]; cout << "Enter string one:"; cin.get(stringOne,255); cout << "String one" << stringOne << endl; cout << "Enter string two: "; cin.getline(stringTwo,255); cout << "String two: " << stringTwo << endl; cout << "\n\nNow try again...\n"; cout << "Enter string one: "; cin.get(stringOne,255); cout << "String one: " << stringOne<< endl; cin.ignore(255,'\n'); cout << "Enter string two: "; cin.getline(stringTwo,255); cout << "String Two: " << stringTwo<< endl;28: 29: } return 0; Output: Enter string one:once upon a time String oneonce upon a time Enter string two: String two: Now try again... Enter string one: once upon a time String one: once upon a time Enter string two: there was a String Two: there was a Analysis: On lines 6 and 7, two character arrays are created. On line 9, the user is prompted for input and types once upon a time, followed by Enter. On line 10, get() is used to read this string. get() fills stringOne and terminates on the newline, but leaves the newline character in the input buffer. On line 13, the user is prompted again, but the getline() on line 14 reads the newline that is already in the buffer and terminates immediately, before the user can enter any input. On line 19, the user is prompted again and puts in the same first line of input. This time, however, on line 23, ignore() is used to "eat" the newline character. Thus, when the getline() call on line 26 is reached, the input buffer is empty, and the user can input the next line of the story. peek() and putback() The input object cin has two additional methods that can come in rather handy: peek(), which looks at but does not extract the next character, and putback(), which inserts a character into the input stream. Listing 16.9 illustrates how these might be used. Listing 16.9. Using peek() and putback(). 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: // Listing 16.9 - Using peek() and putback() #include int main() { char ch; cout << "enter a phrase: "; while ( cin.get(ch) ) { if (ch == `!') cin.putback(`$'); else13: 14: 15: 16: 17: 18: } cout << ch; while (cin.peek() == `#') cin.ignore(1,'#'); } return 0; Output: enter a phrase: Now!is#the!time#for!fun#! Now$isthe$timefor$fun$ Analysis: On line 6, a character variable, ch, is declared, and on line 7, the user is prompted to enter a phrase. The purpose of this program is to turn any exclamation marks (!) into dollar signs ($) and to remove any pound symbols (#). The program loops as long as it is getting characters other than the end of file (remember that cin.get() returns 0 for end of file). If the current character is an exclamation point, it is thrown away and the $ symbol is put back into the input buffer; it will be read the next time through. If the current item is not an exclamation point, it is printed. The next character is "peeked" at, and when pound symbols are found, they are removed. This is not the most efficient way to do either of these things (and it won't find a pound symbol if it is the first character), but it does illustrate how these methods work. They are relatively obscure, so don't spend a lot of time worrying about when you might really use them. Put them into your bag of tricks; they'll come in handy sooner or later. TIP: peek() and putback() are typically used for parsing strings and other data, such as when writing a compiler. Output with cout You have used cout along with the overloaded insertion operator (<<) to write strings, integers, and other numeric data to the screen. It is also possible to format the data, aligning columns and writing the numeric data in decimal and hexadecimal. This section will show you how. Flushing the Output You've already seen that using endl will flush the output buffer. endl calls cout's member function flush(), which writes all of the data it is buffering. You can call the flush() method directly, either by calling the flush() member method or by writing the following: cout << flush This can be convenient when you need to ensure that the output buffer is emptied and that the contentsare written to the screen. Related Functions Just as the extraction operator can be supplemented with get() and getline(), the insertion operator can be supplemented with put() and write(). The function put() is used to write a single character to the output device. Because put() returns an ostream reference, and because cout is an ostream object, you can concatenate put() just as you do the insertion operator. Listing 16.10 illustrates this idea. Listing 16.10. Using put(). 1: // Listing 16.10 - Using put() 2: #include 3: 4: int main() 5: { 6: cout.put(`H').put(`e').put(`l').put(`l').put(`o').put(`\n'); 7: return 0; 8: } Output: Hello Analysis: Line 6 is evaluated like this: cout.put(`H') writes the letter H to the screen and returns the cout object. This leaves the following: cout.put(`e').put(`l').put(`l').put(`o').put(`\n'); The letter e is written, leaving cout.put(`l'). This process repeats, each letter being written and the cout object returned until the final character (`\n') is written and the function returns. The function write() works just like the insertion operator (<<), except that it takes a parameter that tells the function the maximum number of characters to write. Listing 16.11 illustrates its use. Listing 16.11. Using write(). 1: 2: 3: 4: 5: 6: 7: // Listing 16.11 - Using write() #include #include int main() { char One[] = "One if by land";8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: } int fullLength = strlen(One); int tooShort = fullLength -4; int tooLong = fullLength + 6; cout.write(One,fullLength) << "\n"; cout.write(One,tooShort) << "\n"; cout.write(One,tooLong) << "\n"; return 0; Output: One if by land One if by One if by land i?! NOTE: The last line of output may look different on your computer. Analysis: On line 7, one phrase is created. On line 11, the integer fullLength is set to the length of the phrase and tooShort is set to that length minus four, while tooLong is set to fullLength plus six. On line 15, the complete phrase is printed using write(). The length is set to the actual length of the phrase, and the correct phrase is printed. On line 16, the phrase is printed again, but is four characters shorter than the full phrase, and that is reflected in the output. On line 17, the phrase is printed again, but this time write() is instructed to write an extra six characters. Once the phrase is written, the next six bytes of contiguous memory are written. Manipulators, Flags, and Formatting Instructions The output stream maintains a number of state flags, determining which base (decimal or hexadecimal) to use, how wide to make the fields, and what character to use to fill in fields. A state flag is just a byte whose individual bits are each assigned a special meaning. Manipulating bits in this way is discussed on Day 21. Each of ostream's flags can be set using member functions and manipulators. Using cout.width() The default width of your output will be just enough space to print the number, character, or string inthe output buffer. You can change this by using width(). Because width() is a member function, it must be invoked with a cout object. It only changes the width of the very next output field and then immediately reverts to the default. Listing 16.12 illustrates its use. Listing 16.12. Adjusting the width of output. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: } // Listing 16.12 - Adjusting the width of output #include int main() { cout << "Start >"; cout.width(25); cout << 123 << "< End\n"; cout << "Start >"; cout.width(25); cout << 123<< "< Next >"; cout << 456 << "< End\n"; cout << "Start >"; cout.width(4); cout << 123456 << "< End\n"; return 0; Output: Start > Start > Start >123456< End 123< End 123< Next >456< End Analysis: The first output, on lines 6-8, prints the number 123 within a field whose width is set to 25 on line 7. This is reflected in the first line of output. The second line of output first prints the value 123 in the same field whose width is set to 25, and then prints the value 456. Note that 456 is printed in a field whose width is reset to just large enough; as stated, the effect of width() lasts only as long as the very next output. The final output reflects that setting a width that is smaller than the output is exactly like setting a width that is just large enough. Setting the Fill Characters Normally cout fills the empty field created by a call to width() with spaces, as shown above. At times you may want to fill the area with other characters, such as asterisks. To do this, you callfill() and pass in as a parameter the character you want used as a fill character. Listing 16.13 illustrates this. Listing 16.13. Using fill(). 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: } // Listing 16.3 - fill() #include int main() { cout << "Start >"; cout.width(25); cout << 123 << "< End\n"; cout << "Start >"; cout.width(25); cout.fill(`*'); cout << 123 << "< End\n"; return 0; Output: Start > 123< End Start >******************123< End Analysis: Lines 7-9 repeat the functionality from the previous example. Lines 12-15 repeat this again, but this time, on line 14, the fill character is set to asterisks, as reflected in the output. Set Flags The iostream objects keep track of their state by using flags. You can set these flags by calling setf() and passing in one or another of the predefined enumerated constants. New Term: Objects are said to have state when some or all of their data represents a condition that can change during the course of the program. For example, you can set whether or not to show trailing zeros (so that 20.00 does not become truncated to 20). To turn trailing zeros on, call setf(ios::showpoint). The enumerated constants are scoped to the iostream class (ios) and thus are called with the full qualification ios::flagname, such as ios::showpoint.You can turn on the plus sign (+) before positive numbers by using ios::showpos. You can change the alignment of the output by using ios::left, ios::right, or ios::internal. Finally, you can set the base of the numbers for display by using ios::dec (decimal), ios::oct (octal--base eight), or ios::hex (hexadecimal--base sixteen). These flags can also be concatenated into the insertion operator. Listing 16.14 illustrates these settings. As a bonus, Listing 16.14 also introduces the setw manipulator, which sets the width but can also be concatenated with the insertion operator. Listing 16.14. Using setf. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: endl; 30: 31: } // Listing 16.14 - Using setf #include #include int main() { const int number = 185; cout << "The number is " << number << endl; cout << "The number is " << hex << number << endl; cout.setf(ios::showbase); cout << "The number is " << hex << number << endl; cout << "The number is " ; cout.width(10); cout << hex << number << endl; cout << "The number is " ; cout.width(10); cout.setf(ios::left); cout << hex << number << endl; cout << "The number is " ; cout.width(10); cout.setf(ios::internal); cout << hex << number << endl; cout << "The number is:" << setw(10) << hex << number << return 0; Output: The number is 185 The number is b9The The The The The number number number number number is 0xb9 is is 0xb9 is 0x is:0x 0xb9 b9 b9 Analysis: On line 7, the constant int number is initialized to the value 185. This is displayed on line 8. The value is displayed again on line 10, but this time the manipulator hex is concatenated, causing the value to be displayed in hexadecimal as b9. (b=11; 11*16=176+9=185). On line 12, the flag showbase is set. This causes the prefix 0x to be added to all hexadecimal numbers, as reflected in the output. On line 16, the width is set to 10, and the value is pushed to the extreme right. On line 20, the width is again set to 10, but this time the alignment is set to the left, and the number is again printed flush left. On line 25, once again the width is set to 10, but this time the alignment is internal. Thus the 0x is printed flush left, but the value, b9, is printed flush right. Finally, on line 29, the concatenation operator setw() is used to set the width to 10, and the value is printed again. Streams Versus the printf() Function Most C++ implementations also provide the standard C I/O libraries, including the printf() statement. Although printf() is in some ways easier to use than cout, it is far less desirable. printf() does not provide type safety, so it is easy to inadvertently tell it to display an integer as if it was a character and vice versa. printf() also does not support classes, and so it is not possible to teach it how to print your class data; you must feed each class member to printf() one by one. On the other hand, printf() does make formatting much easier, because you can put the formatting characters directly into the printf() statement. Because printf() has its uses and many programmers still make extensive use of it, this section will briefly review its use. To use printf(), be sure to include the STDIO.H header file. In its simplest form, printf() takes a formatting string as its first parameter and then a series of values as its remaining parameters. The formatting string is a quoted string of text and conversion specifiers. All conversion specifiers must begin with the percent symbol (%). The common conversion specifiers are presented in Table 16.1.Table 16.1. The Common Conversion Specifiers . Specifier %s %d %l %ld %f Used For strings integers long integer long integers float Each of the conversion specifiers can also provide a width statement and a precision statement, expressed as a float, where the digits to the left of the decimal are used for the total width, and the digits to the right of the decimal provide the precision for floats. Thus, %5d is the specifier for a five-digit-wide integer, and %15.5f is the specifier for a 15-digit-wide float, of which the final five digits are dedicated to the decimal portion. Listing 16.15 illustrates various uses of printf(). Listing 16.15. Printing with printf(). 1: #include 2: int main() 3: { 4: printf("%s","hello world\n"); 5: 6: char *phrase = "Hello again!\n"; 7: printf("%s",phrase); 8: 9: int x = 5; 10: printf("%d\n",x); 11: 12: char *phraseTwo = "Here's some values: "; 13: char *phraseThree = " and also these: "; 14: int y = 7, z = 35; 15: long longVar = 98456; 16: float floatVar = 8.8; 17: 18: printf("%s %d %d %s %ld %f\n",phraseTwo,y,z,phraseThree,longVar,floatVar); 19: 20: char *phraseFour = "Formatted: "; 21: printf("%s %5d %10d %10.5f\n",phraseFour,y,z,floatVar); 22: return 0; 23: } Output: hello worldHello again! 5 Here's some values: 7 35 and also these: 98456 8.800000 Formatted: 7 35 8.800000 Analysis: The first printf() statement, on line 4, uses the standard form: the term printf, followed by a quoted string with a conversion specifier (in this case %s), followed by a value to insert into the conversion specifier. The %s indicates that this is a string, and the value for the string is, in this case, the quoted string "hello world". The second printf() statement is just like the first, but this time a char pointer is used, rather than quoting the string right in place in the printf() statement. The third printf(), on line 10, uses the integer conversion specifier, and for its value the integer variable x. The fourth printf() statement, on line 18, is more complex. Here six values are concatenated. Each conversion specifier is supplied, and then the values are provided, separated by commas. Finally, on line 21, format specifications are used to specify width and precision. As you can see, all of this is somewhat easier than using manipulators. As stated previously, however, the limitation here is that there is no type checking and printf() cannot be declared a friend or member function of a class. So if you want to print the various member data of a class, you must feed each accessor method to the printf() statement explicitly. File Input and Output Streams provide a uniform way of dealing with data coming from the keyboard or the hard disk and going out to the screen or hard disk. In either case, you can use the insertion and extraction operators or the other related functions and manipulators. To open and close files, you create ifstream and ofstream objects as described in the next few sections. ofstream The particular objects used to read from or write to files are called ofstream objects. These are derived from the iostream objects you've been using so far. To get started with writing to a file, you must first create an ofstream object, and then associate that object with a particular file on your disk. To use ofstream objects, you must be sure to include fstream.h in your program.NOTE: Because fstream.h includes iostream.h, there is no need for you to include iostream explicitly. Condition States The iostream objects maintain flags that report on the state of your input and output. You can check each of these flags using the Boolean functions eof(), bad(), fail(), and good(). The function eof() returns true if the iostream object has encountered EOF, end of file. The function bad() returns TRUE if you attempt an invalid operation. The function fail() returns TRUE anytime bad() is true or an operation fails. Finally, the function good() returns TRUE anytime all three of the other functions are FALSE. Opening Files for Input and Output To open the file myfile.cpp with an ofstream object, declare an instance of an ofstream object and pass in the filename as a parameter: ofstream fout("myfile.cpp"); Opening this file for input works exactly the same way, except it uses an ifstream object: ifstream fin("myfile.cpp"); Note that fout and fin are names you assign; here fout has been used to reflect its similarity to cout, and fin has been used to reflect its similarity to cin. One important file stream function that you will need right away is close(). Every file stream object you create opens a file for either reading or writing (or both). It is important to close() the file after you finish reading or writing; this ensures that the file won't be corrupted and that the data you've written is flushed to the disk. Once the stream objects are associated with files, they can be used like any other stream objects. Listing 16.16 illustrates this. Listing 16.16. Opening files for read and write. 1: 2: 3: 4: 5: 6: 7: #include int main() { char fileName[80]; char buffer[255]; // for user input cout << "File name: "; cin >> fileName;8: 9: 10: 11: 12: name 13: 14: 15: reopen 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: } ofstream fout(fileName); // open for writing fout << "This line written directly to the file...\n"; cout << "Enter text for the file: "; cin.ignore(1,'\n'); // eat the newline after the file cin.getline(buffer,255); fout << buffer << "\n"; fout.close(); // get the user's input // and write it to the file // close the file, ready for ifstream fin(fileName); // reopen for reading cout << "Here's the contents of the file:\n"; char ch; while (fin.get(ch)) cout << ch; cout << "\n***End of file contents.***\n"; fin.close(); return 0; // always pays to be tidy Output: File name: test1 Enter text for the file: This text is written to the file! Here's the contents of the file: This line written directly to the file... This text is written to the file! ***End of file contents.*** Analysis: On line 4, a buffer is set aside for the filename, and on line 5 another buffer is set aside for user input. The user is prompted to enter a filename on line 6, and this response is written to the fileName buffer. On line 9, an ofstream object is created, fout, which is associated with the new filename. This opens the file; if the file already exists, its contents are thrown away. On line 10, a string of text is written directly to the file. On line 11, the user is prompted for input. The newline character left over from the user's input of the filename is eaten on line 12, and the user's input is stored into buffer on line 13. That input is written to the file along with a newline character on line 14, and then the file is closed on line 15. On line 17, the file is reopened, this time in input mode, and the contents are read, one character at a time, on lines 20 and 21. Changing the Default Behavior of ofstream on OpenThe default behavior upon opening a file is to create the file if it doesn't yet exist and to truncate the file (that is, delete all its contents) if it does exist. If you don't want this default behavior, you can explicitly provide a second argument to the constructor of your ofstream object. Valid arguments include: %+( ios::app--Appends to the end of existing files rather than truncating them. &#! ios::at--Places you at the end of the file, but you can write data anywhere in the file. *'( ios::trun--The default. Causes existing files to be truncated. (-/ ios::nocreat--If the file does not exist, the open fails. &$' ios::noreplac--If the file does already exist, the open fails. Note that app is short for append; ate is short for at end, and trunc is short for truncate. Listing 16.17 illustrates using append by reopening the file from Listing 16.16 and appending to it. Listing 16.17. Appending to the end of a file. 1: #include 2: int main() // returns 1 on error 3: { 4: char fileName[80]; 5: char buffer[255]; 6: cout << "Please re-enter the file name: "; 7: cin >> fileName; 8: 9: ifstream fin(fileName); 10: if (fin) // already exists? 11: { 12: cout << "Current file contents:\n"; 13: char ch; 14: while (fin.get(ch)) 15: cout << ch; 16: cout << "\n***End of file contents.***\n"; 17: } 18: fin.close(); 19: 20: cout << "\nOpening " << fileName << " in append mode...\n"; 21: 22: ofstream fout(fileName,ios::app); 23: if (!fout)24: { 25: cout << "Unable to open " << fileName << " for appending.\n"; 26: return(1); 27: } 28: 29: cout << "\nEnter text for the file: "; 30: cin.ignore(1,'\n'); 31: cin.getline(buffer,255); 32: fout << buffer << "\n"; 33: fout.close(); 34: 35: fin.open(fileName); // reassign existing fin object! 36: if (!fin) 37: { 38: cout << "Unable to open " << fileName << " for reading.\n"; 39: return(1); 40: } 41: cout << "\nHere's the contents of the file:\n"; 42: char ch; 43: while (fin.get(ch)) 44: cout << ch; 45: cout << "\n***End of file contents.***\n"; 46: fin.close(); 47: return 0; 48: } Output: Please re-enter the file name: test1 Current file contents: This line written directly to the file... This text is written to the file! ***End of file contents.*** Opening test1 in append mode... Enter text for the file: More text for the file! Here's the contents of the file: This line written directly to the file... This text is written to the file! More text for the file! ***End of file contents.*** Analysis: The user is again prompted to enter the filename. This time an input file stream object iscreated on line 9. That open is tested on line 10, and if the file already exists, its contents are printed on lines 12 to 16. Note that if(fin) is synonymous with if (fin.good()). The input file is then closed, and the same file is reopened, this time in append mode, on line 22. After this open (and every open), the file is tested to ensure that the file was opened properly. Note that if(!fout) is the same as testing if (fout.fail()). The user is then prompted to enter text, and the file is closed again on line 33. Finally, as in Listing 16.16, the file is reopened in read mode; however, this time fin does not need to be redeclared. It is just reassigned to the same filename. Again the open is tested, on line 36, and if all is well, the contents of the file are printed to the screen and the file is closed for the final time. DO test each open of a file to ensure that it opened successfully. DO reuse existing ifstream and ofstream objects. DO close all fstream objects when you are done using them. DON'T try to close or reassign cin or cout. Binary Versus Text Files Some operating systems, such as DOS, distinguish between text files and binary files. Text files store everything as text (as you might have guessed), so large numbers such as 54,325 are stored as a string of numerals (`5', `4', `,', `3', `2', `5'). This can be inefficient, but has the advantage that the text can be read using simple programs such as the DOS program type. To help the file system distinguish between text and binary files, C++ provides the ios::binary flag. On many systems, this flag is ignored because all data is stored in binary format. On some rather prudish systems, the ios::binary flag is illegal and won't compile! Binary files can store not only integers and strings, but entire data structures. You can write all the data at one time by using the write() method of fstream. If you use write(), you can recover the data using read(). Each of these functions expects a pointer to character, however, so you must cast the address of your class to be a pointer to character. The second argument to these functions is the number of characters to write, which you can determine using sizeof(). Note that what is being written is just the data, not the methods. What is recovered is just data. Listing 16.18 illustrates writing the contents of a class to a file. Listing 16.18. Writing a class to a file. 1: 2: 3: #include class Animal4: { 5: public: 6: Animal(int weight, long days):itsWeight(weight),itsNumberDaysAlive(days){} 7: ~Animal(){} 8: 9: int GetWeight()const { return itsWeight; } 10: void SetWeight(int weight) { itsWeight = weight; } 11: 12: long GetDaysAlive()const { return itsNumberDaysAlive; } 13: void SetDaysAlive(long days) { itsNumberDaysAlive = days; } 14: 15: private: 16: int itsWeight; 17: long itsNumberDaysAlive; 18: }; 19: 20: int main() // returns 1 on error 21: { 22: char fileName[80]; 23: char buffer[255]; 24: 25: cout << "Please enter the file name: "; 26: cin >> fileName; 27: ofstream fout(fileName,ios::binary); 28: if (!fout) 29: { 30: cout << "Unable to open " << fileName << " for writing.\n"; 31: return(1); 32: } 33: 34: Animal Bear(50,100); 35: fout.write((char*) &Bear,sizeof Bear); 36: 37: fout.close(); 38: 39: ifstream fin(fileName,ios::binary); 40: if (!fin) 41: { 42: cout << "Unable to open " << fileName << " for reading.\n"; 43: return(1); 44: } 45:46: 47: 48: endl; 49: endl; 50: 51: 52: 53: endl; 54: endl; 55: 56: Animal BearTwo(1,1); cout << "BearTwo weight: " << BearTwo.GetWeight() << cout << "BearTwo days: " << BearTwo.GetDaysAlive() << fin.read((char*) &BearTwo, sizeof BearTwo); cout << "BearTwo weight: " << BearTwo.GetWeight() << cout << "BearTwo days: " << BearTwo.GetDaysAlive() << fin.close(); return 0; 57: } Output: BearTwo BearTwo BearTwo BearTwo Please enter the file name: Animals weight: 1 days: 1 weight: 50 days: 100 Analysis: On lines 3-18, a stripped-down Animal class is declared. On lines 22-32, a file is created and opened for output in binary mode. An animal whose weight is 50 and who is 100 days old is created on line 34, and its data is written to the file on line 35. The file is closed on line 37 and reopened for reading in binary mode on line 39. A second animal is created on line 46 whose weight is 1 and who is only one day old. The data from the file is read into the new animal object on line 51, wiping out the existing data and replacing it with the data from the file. Command-Line Processing Many operating systems, such as DOS and UNIX, enable the user to pass parameters to your program when the program starts. These are called command-line options, and are typically separated by spaces on the command line. For example: SomeProgram Param1 Param2 Param3 These parameters are not passed to main() directly. Instead, every program's main() function is passed two parameters. The first is an integer count of the number of arguments on the command line. The program name itself is counted, so every program has at least one parameter. The example command line shown previously has four. (The name SomeProgram plus the three parameters makea total of four command-line arguments.) The second parameter passed to main() is an array of pointers to character strings. Because an array name is a constant pointer to the first element of the array, you can declare this argument to be a pointer to a pointer to char, a pointer to an array of char, or an array of arrays of char. Typically, the first argument is called argc (argument count), but you may call it anything you like. The second argument is often called argv (argument vector), but again this is just a convention. It is common to test argc to ensure you've received the expected number of arguments, and to use argv to access the strings themselves. Note that argv[0] is the name of the program, and argv[1] is the first parameter to the program, represented as a string. If your program takes two numbers as arguments, you will need to translate these numbers to strings. On Day 21 you will see how to use the standard library conversions. Listing 16.19 illustrates how to use the command-line arguments. Listing 16.19. Using command-line arguments. 1: 2: 3: 4: 5: 6: 7: 8: } #include int main(int argc, char **argv) { cout << "Received " << argc << " arguments...\n"; for (int i=0; i 2: int main(int argc, char *argv[]) // returns 1 on error 3: { 4: if (argc != 2) 5: { 6: cout << "Usage: " << argv[0] << " " << endl; 7: return(1); 8: } 9: 10: ofstream fout(argv[1],ios::binary); 11: if (!fout) 12: { 13: cout << "Unable to open " << argv[1] << " for writing.\n"; 14: return(1); 15: } 16: 17: Animal Bear(50,100); 18: fout.write((char*) &Bear,sizeof Bear); 19: 20: fout.close(); 21: 22: ifstream fin(argv[1],ios::binary); 23: if (!fin) 24: { 25: cout << "Unable to open " << argv[1] << " for reading.\n"; 26: return(1); 27: } 28: 29: Animal BearTwo(1,1); 30: 31: cout << "BearTwo weight: " << BearTwo.GetWeight() << endl;32: endl; 33: 34: 35: 36: endl; 37: endl; 38: 39: 40: } Output: BearTwo BearTwo BearTwo cout << "BearTwo days: " << BearTwo.GetDaysAlive() << fin.read((char*) &BearTwo, sizeof BearTwo); cout << "BearTwo weight: " << BearTwo.GetWeight() << cout << "BearTwo days: " << BearTwo.GetDaysAlive() << fin.close(); return 0; BearTwo weight: 1 days: 1 weight: 50 days: 100 Analysis: The declaration of the Animal class is the same as in Listing 16.18, and so is left out of this example. This time, however, rather than prompting the user for the filename, command-line arguments are used. On line 2, main() is declared to take two parameters: the count of the command- line arguments and a pointer to the array of command-line argument strings. On lines 4-8, the program ensures that the expected number of arguments (exactly two) is received. If the user fails to supply a single filename, an error message is printed: Usage TestProgram Then the program exits. Note that by using argv[0] rather than hard-coding a program name, you can compile this program to have any name, and this usage statement will work automatically. On line 10, the program attempts to open the supplied filename for binary output. There is no reason to copy the filename into a local temporary buffer. It can be used directly by accessing argv[1]. This technique is repeated on line 22 when the same file is reopened for input, and is used in the error condition statements when the files cannot be opened, on lines 13 and 25. Summary Today streams were introduced, and the global objects cout and cin were described. The goal of the istream and ostream objects is to encapsulate the work of writing to device drivers and buffering input and output. There are four standard stream objects created in every program: cout, cin, cerr, and clog. Each of these can be "redirected" by many operating systems.The istream object cin is used for input, and its most common use is with the overloaded extraction operator (>>). The ostream object cout is used for output, and its most common use is with the overloaded insertion operator (<<). Each of these objects has a number of other member functions, such as get() and put(). Because the common forms of each of these methods returns a reference to a stream object, it is easy to concatenate each of these operators and functions. The state of the stream objects can be changed by using manipulators. These can set the formatting and display characteristics and various other attributes of the stream objects. File I/O can be accomplished by using the fstream classes, which derive from the stream classes. In addition to supporting the normal insertion and extraction operators, these objects also support read() and write() for storing and retrieving large binary objects. Q&A Q. How do you know when to use the insertion and extraction operators and when to use the other member functions of the stream classes? A. In general, it is easier to use the insertion and extraction operators, and they are preferred when their behavior is what is needed. In those unusual circumstances when these operators don't do the job (such as reading in a string of words), the other functions can be used. Q. What is the difference between cerr and clog? A. cerr is not buffered. Everything written to cerr is immediately written out. This is fine for errors to be written to the screen, but may have too high a performance cost for writing logs to disk. clog buffers its output, and thus can be more efficient. Q. Why were streams created if printf() works well? A. printf() does not support the strong type system of C++, and it does not support user- defined classes. Q. When would you ever use putback()? A. When one read operation is used to determine whether or not a character is valid, but a different read operation (perhaps by a different object) needs the character to be in the buffer. This is most often used when parsing a file; for example, the C++ compiler might use putback(). Q. When would you use ignore()? A. A common use of this is after using get(). Because get() leaves the terminating character in the buffer, it is not uncommon to immediately follow a call to get() with a call toignore(1,'\n');. Once again, this is often used in parsing. Q. My friends use printf() in their C++ programs. Can I? A. Sure. You'll gain some convenience, but you'll pay by sacrificing type safety. Workshop The Workshop contains quiz questions to help solidify your understanding of the material covered and exercises to provide you with experience in using what you've learned. Try to answer the quiz and exercise questions before checking the answers in Appendix D, and make sure you understand the answers before going to the next chapter. Quiz 1. What is the insertion operator, and what does it do? 2. What is the extraction operator, and what does it do? 3. What are the three forms of cin.get(), and what are their differences? 4. What is the difference between cin.read() and cin.getline()? 5. What is the default width for outputting a long integer using the insertion operator? 6. What is the return value of the insertion operator? 7. What parameter does the constructor to an ofstream object take? 8. What does the ios::ate argument do? Exercises 1. Write a program that writes to the four standard iostream objects: cin, cout, cerr, and clog. 2. Write a program that prompts the user to enter her full name and then displays it on the screen. 3. Rewrite Listing 16.9 to do the same thing, but without using putback() or ignore(). 4. Write a program that takes a filename as a parameter and opens the file for reading. Read every character of the file and display only the letters and punctuation to the screen. (Ignore all nonprinting characters.) Then close the file and exit. 5. Write a program that displays its command-line arguments in reverse order and does not display the program name. The Preprocessor and the Compiler Every time you run your compiler, your preprocessor runs first. The preprocessor looks for preprocessor instructions, each of which begins with a pound symbol (#). The effect of each of these instructions is a change to the text of the source code. The result is a new source code file, a temporary file that you normally don't see, but that you can instruct the compiler to save so that you can examine it if you want to. The compiler does not read your original source code file; it reads the output of the preprocessor and compiles that file. You've seen the effect of this already with the #include directive. This instructs the preprocessor to find the file whose name follows the #include directive, and to write it into the intermediate file at that location. It is as if you had typed that entire file right into your source code, and by the time the compiler sees the source code, the included file is there. Seeing the Intermediate Form Just about every compiler has a switch that you can set either in the integrated development environment (IDE) or at the command line, and that instructs the compiler to save the intermediate file. Check your compiler manual for the right switches to set for your compiler, if you'd like to examine this file. Using #defineThe #define command defines a string substitution. If you write #define BIG 512 you have instructed the precompiler to substitute the string 512 wherever it sees the string BIG. This is not a string in the C++ sense. The characters 512 are substituted in your source code wherever the token BIG is seen. A token is a string of characters that can be used wherever a string or constant or other set of letters might be used. Thus, if you write #define BIG 512 int myArray[BIG]; The intermediate file produced by the precompiler will look like this: int myArray[512]; Note that the #define statement is gone. Precompiler statements are all removed from the intermediate file; they do not appear in the final source code at all. Using #define for Constants One way to use #define is as a substitute for constants. This is almost never a good idea, however, as #define merely makes a string substitution and does no type checking. As explained in the section on constants, there are tremendous advantages to using the const keyword rather than #define. Using #define for Tests A second way to use #define, however, is simply to declare that a particular character string is defined. Therefore, you could write #define BIG Later, you can test whether BIG has been defined and take action accordingly. The precompiler commands to test whether a string has been defined are #ifdef and #ifndef. Both of these must be followed by the command #endif before the block ends (before the next closing brace). #ifdef evaluates to TRUE if the string it tests has been defined already. So, you can write #ifdef DEBUG cout << "Debug defined"; #endif When the precompiler reads the #ifdef, it checks a table it has built to see if you've defined DEBUG.If you have, the #ifdef evaluates to TRUE, and everything to the next #else or #endif is written into the intermediate file for compiling. If it evaluates to FALSE, nothing between #ifdef DEBUG and #endif will be written into the intermediate file; it will be as if it were never in the source code in the first place. Note that #ifndef is the logical reverse of #ifdef. #ifndef evaluates to TRUE if the string has not been defined up to that point in the file. The #else Precompiler Command As you might imagine, the term #else can be inserted between either #ifdef or #ifndef and the closing #endif. Listing 17.1 illustrates how these terms are used. Listing 17.1. Using #define. 1: #define DemoVersion 2: #define DOS_VERSION 5 3: #include 4: 5: 6: int main() 7: { 8: 9: cout << "Checking on the definitions of DemoVersion, DOS_VERSION (" _and WINDOWS_VERSION...\n"; 10: 11: #ifdef DemoVersion 12: cout << "DemoVersion defined.\n"; 13: #else 14: cout << "DemoVersion not defined.\n"; 15: #endif 16: 17: #ifndef DOS_VERSION 18: cout << "DOS_VERSION not defined!\n"; 19: #else 20: cout << "DOS_VERSION defined as: " << DOS_VERSION << endl; 21: #endif 22: 23: #ifdef WINDOWS_VERSION 24: cout << "WINDOWS_VERSION defined!\n"; 25: #else 26: cout << "WINDOWS_VERSION was not defined.\n"; 27: #endif 28:29: 30: 31: } cout << "Done.\n"; return 0; Output: Checking on the definitions of DemoVersion, DOS_VERSION (" _and WINDOWS_VERSION...\n"; DemoVersion defined. DOS_VERSION defined as: 5 WINDOWS_VERSION was not defined. Done. Analysis: On lines 1 and 2, DemoVersion and DOS_VERSION are defined, with DOS_VERSION defined with the string 5. On line 11, the definition of DemoVersion is tested, and because DemoVersion is defined (albeit with no value), the test is true and the string on line 12 is printed. On line 17 is the test that DOS_VERSION is not defined. Because DOS_VERSION is defined, this test fails and execution jumps to line 20. Here the string 5 is substituted for the word DOS_VERSION; this is seen by the compiler as cout << "DOS_VERSION defined as: " << 5 << endl; Note that the first word DOS_VERSION is not substituted because it is in a quoted string. The second DOS_VERSION is substituted, however, and thus the compiler sees 5 as if you had typed 5 there. Finally, on line 23, the program tests for WINDOWS_VERSION. Because you did not define WINDOWS_VERSION, the test fails and the message on line 24 is printed. Inclusion and Inclusion Guards You will create projects with many different files. You will probably organize your directories so that each class has its own header file (HPP) with the class declaration, and its own implementation file (CPP) with the source code for the class methods. Your main() function will be in its own CPP file, and all the CPP files will be compiled into OBJ files, which will then be linked together into a single program by the linker. Because your programs will use methods from many classes, many header files will be included in each file. Also, header files often need to include one another. For example, the header file for a derived class's declaration must include the header file for its base class. Imagine that the Animal class is declared in the file ANIMAL.HPP. The Dog class (which derives from Animal) must include the file ANIMAL.HPP in DOG.HPP, or Dog will not be able to derive from Animal. The Cat header also includes ANIMAL.HPP for the same reason. If you create a method that uses both a Cat and a Dog, you will be in danger of including ANIMAL.HPP twice. This will generate a compile-time error, because it is not legal to declare a class(Animal) twice, even though the declarations are identical. You can solve this problem with inclusion guards. At the top of your ANIMAL header file, you write these lines: #ifndef ANIMAL_HPP #define ANIMAL_HPP ... #endif // the whole file goes here This says, if you haven't defined the term ANIMAL_HPP, go ahead and define it now. Between the #define statement and the closing #endif are the entire contents of the file. The first time your program includes this file, it reads the first line and the test evaluates to TRUE; that is, you have not yet defined ANIMAL_HPP. So, it goes ahead and defines it and then includes the entire file. The second time your program includes the ANIMAL.HPP file, it reads the first line and the test evaluates to FALSE; ANIMAL.HPP has been defined. It therefore skips to the next #else (there isn't one) or the next #endif (at the end of the file). Thus, it skips the entire contents of the file, and the class is not declared twice. The actual name of the defined symbol (ANIMAL_HPP) is not important, although it is customary to use the filename in all uppercase with the dot (.) changed to an underscore. This is purely convention, however. NOTE: It never hurts to use inclusion guards. Often they will save you hours of debugging time. Defining on the Command Line Almost all C++ compilers will let you #define values either from the command line or from the integrated development environment (and usually both). Thus you can leave out lines 1 and 2 from Listing 17.1, and define DemoVersion and BetaTestVersion from the command line for some compilations, and not for others. It is common to put in special debugging code surrounded by #ifdef DEBUG and #endif. This allows all the debugging code to be easily removed from the source code when you compile the final version; just don't define the term DEBUG. Undefining If you have a name defined and you'd like to turn it off from within your code, you can use #undef. This works as the antidote to #define. Listing 17.2 provides an illustration of its use.Listing 17.2. Using #undef. 1: #define DemoVersion 2: #define DOS_VERSION 5 3: #include 4: 5: 6: int main() 7: { 8: 9: cout << "Checking on the definitions of DemoVersion, DOS_VERSION !/ _and WINDOWS_VERSION...\n"; 10: 11: #ifdef DemoVersion 12: cout << "DemoVersion defined.\n"; 13: #else 14: cout << "DemoVersion not defined.\n"; 15: #endif 16: 17: #ifndef DOS_VERSION 18: cout << "DOS_VERSION not defined!\n"; 19: #else 20: cout << "DOS_VERSION defined as: " << DOS_VERSION << endl; 21: #endif 22: 23: #ifdef WINDOWS_VERSION 24: cout << "WINDOWS_VERSION defined!\n"; 25: #else 26: cout << "WINDOWS_VERSION was not defined.\n"; 27: #endif 28: 29: #undef DOS_VERSION 30: 31: #ifdef DemoVersion 32: cout << "DemoVersion defined.\n"; 33: #else 34: cout << "DemoVersion not defined.\n"; 35: #endif 36: 37: #ifndef DOS_VERSION 38: cout << "DOS_VERSION not defined!\n"; 39: #else 40: cout << "DOS_VERSION defined as: " << DOS_VERSION << endl; 41: #endif42: 43: 44: 45: 46: 47: 48: 49: 50: 51: } #if_Tz'WINDOWS_VERSION cout << "WINDOWS_VERSION defined!\n"; #else cout << "WINDOWS_VERSION was not defined.\n"; #endif cout << "Done.\n"; return 0; Output: Checking on the definitions of DemoVersion, DOS_VERSION #% _and WINDOWS_VERSION...\n"; DemoVersion defined. DOS_VERSION defined as: 5 WINDOWS_VERSION was not defined. DemoVersion defined. DOS_VERSION not defined! WINDOWS_VERSION was not defined. Done. Analysis: Listing 17.2 is the same as Listing 17.1 until line 29, when #undef DOS_VERSION is called. This removes the definition of the term DOS_VERSION without changing the other defined terms (in this case, DemoVersion). The rest of the listing just repeats the printouts. The tests for DemoVersion and WINDOWS_VERSION act as they did the first time, but the test for DOS_VERSION now evaluates TRUE. In this second case DOS_VERSION does not exist as a defined term. Conditional Compilation By combining #define or command-line definitions with #ifdef, #else, and #ifndef, you can write one program that compiles different code, depending on what is already #defined. This can be used to create one set of source code to compile on two different platforms, such as DOS and Windows. Another common use of this technique is to conditionally compile in some code based on whether debug has been defined, as you'll see in a few moments. DO use conditional compilation when you need to create more than one version of your code at the same time. DON'T let your conditions get too complex to manage. DO use #undef as often as possible to avoid leaving stray definitions in your code. DO use inclusion guards!Macro Functions The #define directive can also be used to create macro functions. A macro function is a symbol created using #define and that takes an argument, much like a function does. The preprocessor will substitute the substitution string for whatever argument it is given. For example, you can define the macro TWICE as #define TWICE(x) ( (x) * 2 ) and then in your code you write TWICE(4) The entire string TWICE(4) will be removed, and the value 8 will be substituted! When the precompiler sees the 4, it will substitute ( (4) * 2 ), which will then evaluate to 4 * 2 or 8. A macro can have more than one parameter, and each parameter can be used repeatedly in the replacement text. Two common macros are MAX and MIN: #define MAX(x,y) ( (x) > (y) ? (x) : (y) ) #define MIN(x,y) ( (x) < (y) ? (x) : (y) ) Note that in a macro function definition, the opening parenthesis for the parameter list must immediately follow the macro name, with no spaces. The preprocessor is not as forgiving of white space as is the compiler. If you were to write #define MAX (x,y) ( (x) > (y) ? (x) : (y) ) and then tried to use MAX like this, int x = 5, y = 7, z; z = MAX(x,y); the intermediate code would be int x = 5, y = 7, z; z = (x,y) ( (x) > (y) ? (x) : (y) ) (x,y) A simple text substitution would be done, rather than invoking the macro function. Thus the token MAX would have substituted for it (x,y) ( (x) > (y) ? (x) : (y) ), and then that would be followed by the (x,y) which followed Max.By removing the space between MAX and (x,y), however, the intermediate code becomes: int x = 5, y = 7, z; z =7; Why All the Parentheses? You may be wondering why there are so many parentheses in many of the macros presented so far. The preprocessor does not demand that parentheses be placed around the arguments in the substitution string, but the parentheses help you to avoid unwanted side effects when you pass complicated values to a macro. For example, if you define MAX as #define MAX(x,y) x > y ? x : y and pass in the values 5 and 7, the macro works as intended. But if you pass in a more complicated expression, you'll get unintended results, as shown in Listing 17.3. Listing 17.3. Using parentheses in macros. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: } // Listing 17.3 Macro Expansion #include #define CUBE(a) ( (a) * (a) * (a) ) #define THREE(a) a * a * a int main() { long x = 5; long y = CUBE(x); long z = THREE(x); cout << "y: " << y << endl; cout << "z: " << z << endl; long a = 5, b = 7; y = CUBE(a+b); z = THREE(a+b); cout << "y: " << y << endl; cout << "z: " << z << endl; return 0; Output: y: 125 z: 125y: 1728 z: 82 Analysis: On line 4, the macro CUBE is defined, with the argument x put into parentheses each time it is used. On line 5, the macro THREE is defined, without the parentheses. In the first use of these macros, the value 5 is given as the parameter, and both macros work fine. CUBE(5) expands to ( (5) * (5) * (5) ), which evaluates to 125, and THREE(5) expands to 5 * 5 * 5, which also evaluates to 125. In the second use, on lines 16-18, the parameter is 5 + 7. In this case, CUBE(5+7) evaluates to ( (5+7) * (5+7) * (5+7) ) which evaluates to ( (12) * (12) * (12) ) which in turn evaluates to 1728. THREE(5+7), however, evaluates to 5 + 7 * 5 + 7 * 5 + 7 Because multiplication has a higher precedence than addition, this becomes 5 + (7 * 5) + (7 * 5) + 7 which evaluates to 5 + (35) + (35) + 7 which finally evaluates to 82. Macros Versus Functions and Templates Macros suffer from four problems in C++. The first is that they can be confusing if they get large, because all macros must be defined on one line. You can extend that line by using the backslash character (\), but large macros quickly become difficult to manage. The second problem is that macros are expanded inline each time they are used. This means that if a macro is used a dozen times, the substitution will appear 12 times in your program, rather than appear once as a function call will. On the other hand, they are usually quicker than a function call because the overhead of a function call is avoided. The fact that they are expanded inline leads to the third problem, which is that the macro does not appear in the intermediate source code used by the compiler, and therefore is unavailable in mostdebuggers. This makes debugging macros tricky. The final problem, however, is the biggest: macros are not type-safe. While it is convenient that absolutely any argument may be used with a macro, this completely undermines the strong typing of C++ and so is anathema to C++ programmers. However, there is a way to overcome this problem, as you'll see on Day 19, "Templates." Inline Functions It is often possible to declare an inline function rather than a macro. For example, Listing 17.4 creates a CUBE function, which accomplishes the same thing as the CUBE macro in Listing 17.3, but does so in a type-safe way. Listing 17.4. Using inline rather than a macro. 1: 2: 3: } 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: } #include inline unsigned long Square(unsigned long a) { return a * a; inline unsigned long Cube(unsigned long a) { return a * a * a; } int main() { unsigned long x=1 ; for (;;) { cout << "Enter a number (0 to quit): "; cin >> x; if (x == 0) break; cout << "You entered: " << x; cout << ". Square(" << x << "): "; cout << Square(x); cout<< ". Cube(" _<< x << "): "; cout << Cube(x) << "." << endl; } return 0; Output: Enter a number (0 to quit): 1 You entered: 1. Square(1): 1. Cube(1): 1. Enter a number (0 to quit): 2 You entered: 2. Square(2): 4. Cube(2): 8. Enter a number (0 to quit): 3 You entered: 3. Square(3): 9. Cube(3): 27.Enter a number (0 to quit): You entered: 4. Square(4): Enter a number (0 to quit): You entered: 5. Square(5): Enter a number (0 to quit): You entered: 6. Square(6): Enter a number (0 to quit): 4 16. Cube(4): 64. 5 25. Cube(5): 125. 6 36. Cube(6): 216. 0 Analysis: On lines 3 and 4, two inline functions are declared: Square() and Cube(). Each is declared to be inline, so like a macro function these will be expanded in place for each call, and there will be no function call overhead. As a reminder, expanded inline means that the content of the function will be placed into the code wherever the function call is made (for example, on line 16). Because the function call is never made, there is no overhead of putting the return address and the parameters on the stack. On line 16, the function Square is called, as is the function Cube. Again, because these are inline functions, it is exactly as if this line had been written like this: 16: cout << ". Square(" << x << "): " Cube(" << x << '*"): " << x * x * x << "." << endl; << x * x << ". String Manipulation The preprocessor provides two special operators for manipulating strings in macros. The stringizing operator (#) substitutes a quoted string for whatever follows the stringizing operator. The concatenation operator bonds two strings together into one. Stringizing The stringizing operator puts quotes around any characters following the operator, up to the next white space. Thus, if you write #define WRITESTRING(x) cout << #x and then call WRITESTRING(This is a string); the precompiler will turn it into cout << "This is a string"; Note that the string This is a string is put into quotes, as required by cout.Concatenation The concatenation operator allows you to bond together more than one term into a new word. The new word is actually a token that can be used as a class name, a variable name, an offset into an array, or anywhere else a series of letters might appear. Assume for a moment that you have five functions, named fOnePrint, fTwoPrint, fThreePrint, fFourPrint, and fFivePrint. You can then declare: #define fPRINT(x) f ## x ## Print and then use it with fPRINT(Two) to generate fTwoPrint and with fPRINT(Three) to generate fThreePrint. At the conclusion of Week 2, a PartsList class was developed. This list could only handle objects of type List. Let's say that this list works well, and you'd like to be able to make lists of animals, cars, computers, and so forth. One approach would be to create AnimalList, CarList, ComputerList, and so on, cutting and pasting the code in place. This will quickly become a nightmare, as every change to one list must be written to all the others. An alternative is to use macros and the concatenation operator. For example, you could define #define Listof(Type) { \ public: \ Type##List(){} \ private: \ int itsLength; \ }; class Type##List \ This example is overly sparse, but the idea would be to put in all the necessary methods and data. When you were ready to create an AnimalList, you would write Listof(Animal) and this would be turned into the declaration of the AnimalList class. There are some problems with this approach, all of which are discussed in detail on Day 19, when templates are discussed. Predefined Macros Many compilers predefine a number of useful macros, including __DATE__, __TIME__,__LINE__, and __FILE__. Each of these names is surrounded by two underscore characters to reduce the likelihood that the names will conflict with names you've used in your program. When the precompiler sees one of these macros, it makes the appropriate substitutes. For __DATE__, the current date is substituted. For __TIME__, the current time is substituted. __LINE__ and __FILE__ are replaced with the source code line number and filename, respectively. You should note that this substitution is made when the source is precompiled, not when the program is run. If you ask the program to print __DATE__, you will not get the current date; instead, you will get the date the program was compiled. These defined macros are very useful in debugging. assert() Many compilers offer an assert() macro. The assert() macro returns TRUE if its parameter evaluates TRUE and takes some kind of action if it evaluates FALSE. Many compilers will abort the program on an assert() that fails; others will throw an exception (see Day 20, "Exceptions and Error Handling"). One powerful feature of the assert() macro is that the preprocessor collapses it into no code at all if DEBUG is not defined. It is a great help during development, and when the final product ships there is no performance penalty nor increase in the size of the executable version of the program. Rather than depending on the compiler-provided assert(), you are free to write your own assert() macro. Listing 17.5 provides a simple assert() macro and shows its use. Listing 17.5. A simple assert() macro. 1: // Listing 17.5 ASSERTS 2: #define DEBUG 3: #include 4: 5: #ifndef DEBUG 6: #define ASSERT(x) 7: #else 8: #define ASSERT(x) \ 9: if (! (x)) \ 10: { \ 11: cout << "ERROR!! Assert " << #x << " failed\n"; \ 12: cout << " on line " << __LINE__ << "\n"; \ 13: cout << " in file " << __FILE__ << "\n"; \ 14: } 15: #endif 16: 17: 18: int main()19: 20: 21: 22: 23: 24: 25: 26: 27: } { int x = 5; cout << "First assert: \n"; ASSERT(x==5); cout << "\nSecond assert: \n"; ASSERT(x != 5); cout << "\nDone.\n"; return 0; Output: First assert: Second assert: ERROR!! Assert x !=5 failed on line 24 in file test1704.cpp Done. Analysis: On line 2, the term DEBUG is defined. Typically, this would be done from the command line (or the IDE) at compile time, so you can turn this on and off at will. On lines 8-14, the assert() macro is defined. Typically, this would be done in a header file, and that header (ASSERT.HPP) would be included in all your implementation files. On line 5, the term DEBUG is tested. If it is not defined, assert() is defined to create no code at all. If DEBUG is defined, the functionality defined on lines 8-14 is applied. The assert() itself is one long statement, split across seven source code lines, as far as the precompiler is concerned. On line 9, the value passed in as a parameter is tested; if it evaluates FALSE, the statements on lines 11-13 are invoked, printing an error message. If the value passed in evaluates TRUE, no action is taken. Debugging with assert() When writing your program, you will often know deep down in your soul that something is true: a function has a certain value, a pointer is valid, and so forth. It is the nature of bugs that what you know to be true might not be so under some conditions. For example, you know that a pointer is valid, yet the program crashes. assert() can help you find this type of bug, but only if you make it a regular practice to use assert() liberally in your code. Every time you assign or are passed a pointer as a parameter or function return value, be sure to assert that the pointer is valid. Any time your code depends on a particular value being in a variable, assert() that that is true. There is no penalty for frequent use of assert(); it is removed from the code when you undefine debugging. It also provides good internal documentation, reminding the reader of what you believe is true at any given moment in the flow of the code. assert() Versus ExceptionsOn Day 20, you will learn how to work with exceptions to handle error conditions. It is important to note that assert() is not intended to handle runtime error conditions such as bad data, out-of- memory conditions, unable to open file, and so forth. assert() is created to catch programming errors only. That is, if an assert() "fires," you know you have a bug in your code. This is critical, because when you ship your code to your customers, instances of assert() will be removed. You can't depend on an assert() to handle a runtime problem, because the assert() won't be there. It is a common mistake to use assert() to test the return value from a memory assignment: Animal *pCat = new Cat; Assert(pCat); // bad use of assert pCat->SomeFunction(); This is a classic programming error; every time the programmer runs the program, there is enough memory and the assert() never fires. After all, the programmer is running with lots of extra RAM to speed up the compiler, debugger, and so forth. The programmer then ships the executable, and the poor user, who has less memory, reaches this part of the program and the call to new fails and returns NULL. The assert(), however, is no longer in the code and there is nothing to indicate that the pointer points to NULL. As soon as the statement pCat->SomeFunction() is reached, the program crashes. Getting NULL back from a memory assignment is not a programming error, although it is an exceptional situation. Your program must be able to recover from this condition, if only by throwing an exception. Remember: The entire assert() statement is gone when DEBUG is undefined. Exceptions are covered in detail on Day 20. Side Effects It is not uncommon to find that a bug appears only after the instances of assert() are removed. This is almost always due to the program unintentionally depending on side effects of things done in assert() and other debug-only code. For example, if you write ASSERT (x = 5) when you mean to test whether x == 5, you will create a particularly nasty bug. Let's say that just prior to this assert() you called a function that set x equal to 0. With this assert() you think you are testing whether x is equal to 5; in fact, you are setting x equal to 5. The test returns TRUE, because x = 5 not only sets x to 5, but returns the value 5, and because 5 is non- zero it evaluates as TRUE. Once you pass the assert() statement, x really is equal to 5 (you just set it!). Your program runsjust fine. You're ready to ship it, so you turn off debugging. Now the assert() disappears, and you are no longer setting x to 5. Because x was set to 0 just before this, it remains at 0 and your program breaks. In frustration, you turn debugging back on, but hey! Presto! The bug is gone. Once again, this is rather funny to watch, but not to live through, so be very careful about side effects in debugging code. If you see a bug that only appears when debugging is turned off, take a look at your debugging code with an eye out for nasty side effects. Class Invariants Most classes have some conditions that should always be true whenever you are finished with a class member function. These class invariants are the sine qua non of your class. For example, it may be true that your CIRCLE object should never have a radius of zero, or that your ANIMAL should always have an age greater than zero and less than 100. It can be very helpful to declare an Invariants() method that returns TRUE only if each of these conditions is still true. You can then ASSERT(Invariants()) at the start and completion of every class method. The exception would be that your Invariants() would not expect to return TRUE before your constructor runs or after your destructor ends. Listing 17.6 demonstrates the use of the Invariants() method in a trivial class. Listing 17.6. Using Invariants(). 0: #define DEBUG 1: #define SHOW_INVARIANTS 2: #include 3: #include 4: 5: #ifndef DEBUG 6: #define ASSERT(x) 7: #else 8: #define ASSERT(x) \ 9: if (! (x)) \ 10: { \ 11: cout << "ERROR!! Assert " << #x << " failed\n"; \ 12: cout << " on line " << __LINE__ << "\n"; \ 13: cout << " in file " << __FILE__ << "\n"; \ 14: } 15: #endif 16: 17: 18: const int FALSE = 0; 19: const int TRUE = 1;20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: typedef int BOOL; class String { public: // constructors String(); String(const char *const); String(const String &); ~String(); char & operator[](int offset); char operator[](int offset) const; String & operator= (const String &); int GetLen()const { return itsLen; } const char * GetString() const { return itsString; } BOOL Invariants() const; private: String (int); // private constructor char * itsString; // unsigned short itsLen; int itsLen; }; // default constructor creates string of 0 bytes String::String() { itsString = new char[1]; itsString[0] = `\0'; itsLen=0; ASSERT(Invariants()); } // private (helper) constructor, used only by // class methods for creating a new string of // required size. Null filled. String::String(int len) { itsString = new char[len+1]; for (int i = 0; i<=len; i++) itsString[i] = `\0'; itsLen=len; ASSERT(Invariants());66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: } // Converts a character array to a String String::String(const char * const cString) { itsLen = strlen(cString); itsString = new char[itsLen+1]; for (int i = 0; i itsLen) return itsString[itsLen-1]; else return itsString[offset]; ASSERT(Invariants()); } // constant offset operator for use // on const objects (see copy constructor!) char String::operator[](int offset) const { ASSERT(Invariants()); if (offset > itsLen) return itsString[itsLen-1]; else return itsString[offset]; ASSERT(Invariants()); } BOOL String::Invariants() const { #ifdef SHOW_INVARIANTS cout << " String OK "; #endif return ( (itsLen && itsString) || (!itsLen && !itsString) ); } class Animal { public: Animal():itsAge(1),itsName("John Q. Animal") {ASSERT(Invariants());} Animal(int, const String&); ~Animal(){} int GetAge() { ASSERT(Invariants()); return itsAge;}158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: "; 200: 201: 202: void SetAge(int Age) { ASSERT(Invariants()); itsAge = Age; ASSERT(Invariants()); } String& GetName() { ASSERT(Invariants()); return itsName; } void SetName(const String& name) { ASSERT(Invariants()); itsName = name; ASSERT(Invariants()); } BOOL Invariants(); private: int itsAge; String itsName; }; Animal::Animal(int age, const String& name): itsAge(age), itsName(name) { ASSERT(Invariants()); } BOOL Animal::Invariants() { #ifdef SHOW_INVARIANTS cout << " Animal OK "; #endif return (itsAge > 0 && itsName.GetLen()); } int main() { Animal sparky(5,"Sparky"); cout << "\n" << sparky.GetName().GetString() << " is cout << sparky.GetAge() << " years old."; sparky.SetAge(8); cout << "\n" << sparky.GetName().GetString() << " is"; 203: 204: 205: } cout << sparky.GetAge() << " years old."; return 0; Output: String OK String OK String OK String OK String OK String OK String OK Animal OK String OK Animal OK Sparky is Animal OK 5 years old. Animal OK Animal OK Animal OK Sparky is Animal OK 8 years old. String OK Analysis: On lines 6-16, the assert() macro is defined. If DEBUG is defined, this will write out an error message when the assert() macro evaluates FALSE. On line 38, the String class member function Invariants() is declared; it is defined on lines 141-148. The constructor is declared on lines 48-54, and on line 53, after the object is fully constructed, Invariants() is called to confirm proper construction. This pattern is repeated for the other constructors, and the destructor calls Invariants() only before it sets out to destroy the object. The remaining class functions call Invariants() both before taking any action and then again before returning. This both affirms and validates a fundamental principal of C++: Member functions other than constructors and destructors should work on valid objects and should leave them in a valid state. On line 175, class Animal declares its own Invariants() method, implemented on lines 188- 194. Note on lines 154, 157, 160, and 162 that inline functions can call the Invariants() method. Printing Interim Values In addition to asserting that something is true using the assert() macro, you may want to print the current value of pointers, variables, and strings. This can be very helpful in checking your assumptions about the progress of your program, and in locating off-by-one bugs in loops. Listing 17.7 illustrates this idea. Listing 17.7. Printing values in DEBUG mode. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: // Listing 17.7 - Printing values in DEBUG mode #include #define DEBUG #ifndef #define #else #define cout #endif DEBUG PRINT(x) PRINT(x) \ << #x << ":\t" << x << endl;12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: } enum BOOL { FALSE, TRUE } ; Output: i: i: i: i: i: y: "Hi.": px: *px: int main() { int x = 5; long y = 73898l; PRINT(x); for (int i = 0; i < x; i++) { PRINT(i); } PRINT (y); PRINT("Hi."); int *px = &x; PRINT(px); PRINT (*px); return 0; x: 5 0 1 2 3 4 73898 Hi. 0x2100 (You may receive a value other than 0x2100) 5 Analysis: The macro on lines 5-10 provides printing of the current value of the supplied parameter. Note that the first thing fed to cout is the stringized version of the parameter; that is, if you pass in x, cout receives "x". Next, cout receives the quoted string ":\t", which prints a colon and then a tab. Third, cout receives the value of the parameter (x), and then finally, endl, which writes a new line and flushes the buffer. Debugging Levels In large, complex projects, you may want more control than simply turning DEBUG on and off. You can define debug levels and test for these levels when deciding which macros to use and which to strip out. To define a level, simply follow the #define DEBUG statement with a number. While you can haveany number of levels, a common system is to have four levels: HIGH, MEDIUM, LOW, and NONE. Listing 17.8 illustrates how this might be done, using the String and Animal classes from Listing 17.6. The definitions of the class methods other than Invariants() have been left out to save space because they are unchanged from Listing 17.6. NOTE: To compile this code, copy lines 43-136 of Listing 17.6 between lines 64 and 65 of this listing. Listing 17.8. Levels of debugging. 0: 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: enum LEVEL { NONE, LOW, MEDIUM, HIGH }; const int FALSE = 0; const int TRUE = 1; typedef int BOOL; #define DEBUGLEVEL HIGH #include #include #if DEBUGLEVEL < LOW // must be medium or high #define ASSERT(x) #else #define ASSERT(x) \ if (! (x)) \ { \ cout << "ERROR!! Assert " << #x << " failed\n"; \ cout << " on line " << __LINE__ << "\n"; \ cout << " in file " << __FILE__ << "\n"; \ } #endif #if DEBUGLEVEL < MEDIUM #define EVAL(x) #else #define EVAL(x) \ cout << #x << ":\t" << x << endl; #endif #if DEBUGLEVEL < HIGH #define PRINT(x) #else #define PRINT(x) \ cout << x << endl;34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: #endif class String { public: // constructors String(); String(const char *const); String(const String &); ~String(); char & operator[](int offset); char operator[](int offset) const; String & operator= (const String &); int GetLen()const { return itsLen; } const char * GetString() const { return itsString; } BOOL Invariants() const; private: String (int); // private constructor char * itsString; unsigned short itsLen; }; BOOL String::Invariants() const { PRINT("(String Invariants Checked)"); return ( (BOOL) (itsLen && itsString) || (!itsLen && !itsString) ); } class Animal { public: Animal():itsAge(1),itsName("John Q. Animal") {ASSERT(Invariants());} Animal(int, const String&); ~Animal(){} int GetAge() { ASSERT(Invariants());80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: return itsAge; } void SetAge(int Age) { ASSERT(Invariants()); itsAge = Age; ASSERT(Invariants()); } String& GetName() { ASSERT(Invariants()); return itsName; } void SetName(const String& name) { ASSERT(Invariants()); itsName = name; ASSERT(Invariants()); } BOOL Invariants(); private: int itsAge; String itsName; }; BOOL Animal::Invariants() { PRINT("(Animal Invariants Checked)"); return (itsAge > 0 && itsName.GetLen()); } int main() { const int AGE = 5; EVAL(AGE); Animal sparky(AGE,"Sparky"); cout << "\n" << sparky.GetName().GetString(); cout << " is "; cout << sparky.GetAge() << " years old."; sparky.SetAge(8); cout << "\n" << sparky.GetName().GetString(); cout << " is "; cout << sparky.GetAge() << " years old.";126: 127: } return 0; Output: AGE: 5 (String Invariants (String Invariants (String Invariants (String Invariants (String Invariants (String Invariants (String Invariants (String Invariants (String Invariants (String Invariants Checked) Checked) Checked) Checked) Checked) Checked) Checked) Checked) Checked) Checked) Sparky is (Animal Invariants Checked) 5 Years old. (Animal Invariants Checked) (Animal Invariants Checked) (Animal Invariants Checked) Sparky is (Animal Invariants Checked) 8 years old. (String Invariants Checked) (String Invariants Checked) // run again with DEBUG = MEDIUM AGE: 5 Sparky is 5 years old. Sparky is 8 years old. Analysis: On lines 10 to 20, the assert() macro is defined to be stripped if DEBUGLEVEL is less than LOW (that is, DEBUGLEVEL is NONE). If any debugging is enabled, the assert() macro will work. On line 23, EVAL is declared to be stripped if DEBUG is less than MEDIUM; if DEBUGLEVEL is NONE or LOW, EVAL is stripped. Finally, on lines 29-34, the PRINT macro is declared to be stripped if DEBUGLEVEL is less than HIGH. PRINT is used only when DEBUGLEVEL is HIGH; you can eliminate this macro by setting DEBUGLEVEL to MEDIUM and still maintain your use of EVAL and assert(). PRINT is used within the Invariants() methods to print an informative message. EVAL is used on line 117 to evaluate the current value of the constant integer AGE. DO use CAPITALS for your macro names. This is a pervasive convention, and other programmers will be confused if you don't. DON'T allow your macros to have side effects. Don't increment variables or assign values from within a macro. DO surround all arguments with parentheses in macro functions.Summary Today you learned more details about working with the preprocessor. Each time you run the compiler, the preprocessor runs first and translates your preprocessor directives such as #define and #ifdef. The preprocessor does text substitution, although with the use of macros these can be somewhat complex. By using #ifdef, #else, and #ifndef, you can accomplish conditional compilation, compiling in some statements under one set of conditions and in another set of statements under other conditions. This can assist in writing programs for more than one platform and is often used to conditionally include debugging information. Macro functions provide complex text substitution based on arguments passed at compile time to the macro. It is important to put parentheses around every argument in the macro to ensure the correct substitution takes place. Macro functions, and the preprocessor in general, are less important in C++ than they were in C. C++ provides a number of language features, such as const variables and templates, that offer superior alternatives to use of the preprocessor. Q&A Q. If C++ offers better alternatives than the preprocessor, why is this option still available? A. First, C++ is backward-compatible with C, and all significant parts of C must be supported in C++. Second, there are some uses of the preprocessor that are still used frequently in C++, such as inclusion guards. Q. Why use macro functions when you can use a regular function? A. Macro functions are expanded inline and are used as a substitute for repeatedly typing the same commands with minor variations. Again, though, templates offer a better alternative. Q. How do you know when to use a macro versus an inline function? A. Often it doesn't matter much; use whichever is simpler. However, macros offer character substitution, stringizing, and concatenation. None of these is available with functions. Q. What is the alternative to using the preprocessor to print interim values during debugging? A. The best alternative is to use watch statements within a debugger. For information on watch statements, consult your compiler or debugger documentation.Q. How do you decide when to use an assert() and when to throw an exception? A. If the situation you're testing can be true without your having committed a programming error, use an exception. If the only reason for this situation to ever be true is a bug in your program, use an assert(). Workshop The Workshop provides quiz questions to help you solidify your understanding of the material covered and exercises to provide you with experience in using what you've learned. Try to answer the quiz and exercise questions before checking the answers in Appendix D, and make sure you understand the answers before continuing to the next chapter. Quiz 1. What is an inclusion guard? 2. How do you instruct your compiler to print the contents of the intermediate file showing the effects of the preprocessor? 3. What is the difference between #define debug 0 and #undef debug? 4. Name four predefined macros. 5. Why can't you call Invariants() as the first line of your constructor? Exercises 1. Write the inclusion guard statements for the header file STRING.H. 2. Write an assert() macro that prints an error message and the file and line number if debug level is 2, just a message (without file and line number) if the level is 1, and does nothing if the level is 0. 3. Write a macro DPrint that tests if DEBUG is defined and, if it is, prints the value passed in as a parameter. 4. Write a function that prints an error message. The function should print the line number and filename where the error occurred. Note that the line number and filename are passed in to this function. 5. How would you call the preceding error function? 6. Write an assert() macro that uses the error function from Exercise 4, and write a driver program that calls this assert() macro. The Development Cycle Many volumes have been written about the development cycle. Some propose a "waterfall" method, in which designers determine what the program should do; architects determine how the program will be built, what classes will be used, and so forth; and then programmers implement the design and architecture. By the time the design and architecture is given to the programmer, it is complete; all the programmer needs to do is implement the required functionality. Even if the waterfall method worked, it would probably be a poor method for writing good programs. As the programmer proceeds, there is a necessary and natural feedback between what has been written so far and what remains to be done. While it is true that good C++ programs are designed in great detail before a line of code is written, it is not true that that design remains unchanged throughout the cycle. The amount of design that must be finished "up front," before programming begins, is a function of the size of the program. A highly complex effort, involving dozens of programmers working for many months, will require a more fully articulated architecture than a quick-and-dirty utility written in one day by a single programmer. This chapter will focus on the design of large, complex programs which will be expanded and enhanced over many years. Many programmers enjoy working at the bleeding edge of technology; they tend to write programs whose complexity pushes at the limits of their tools and understanding. In many ways, C++ was designed to extend the complexity that a programmer or team of programmers could manage. This chapter will examine a number of design problems from an object-oriented perspective. The goal will be to review the analysis process, and then to understand how you apply the syntax of C++ to implement these design objectives. Simulating an Alarm SystemA simulation is a computer model of a part of a real-world system. There are many reasons to build a simulation, but a good design must start with an understanding of what questions you hope the simulation will answer. As a starting point, examine this problem: You have been asked to simulate the alarm system for a house. The house is a center hall colonial with four bedrooms, a finished basement, and an under-the- house garage. The downstairs has the following windows: three in the kitchen, four in the dining room, one in the half-bathroom, two each in the living room and the family room, and two small windows next to the door. All four bedrooms are upstairs, each of which has two windows except for the master bedroom, which has four. There are two baths, each with one window. Finally, there are four half-windows in the basement, and one window in the garage. Normal access to the house is through the front door. Additionally, the kitchen has a sliding glass door, and the garage has two doors for the cars and one door for easy access to the basement. There is also a cellar door in the backyard. All the windows and doors are alarmed, and there are panic buttons on each phone and next to the bed. The grounds are alarmed as well, though these are carefully calibrated so that they are not set off by small animals or birds. There is a central alarm system in the basement, which sounds a warning chirp when the alarm has been tripped. If the alarm is not disabled within a setable amount of time, the police are called. If a panic button is pushed, the police are called immediately. The alarm is also wired into the fire and smoke detectors and the sprinkler system, and the alarm system itself is fault tolerant, has its own internal backup power supply, and is encased in a fireproof box. Preliminary Design You begin by asking, "What questions might this simulation answer?" For example, you might be able to use the simulation to answer the questions, "How long might a sensor be broken before anyone notices?" or "Is there a way to defeat the window alarms without the police being notified?" Once you understand the purpose of the simulation you will know what parts of the real system the program must model. Once that is well understood, it becomes much easier to design the program itself. What Are the Objects? One way to approach this problem is to set aside issues relating to the user interface and to focus onlyon the components of the "problem space." A first approximation of an object-oriented design might be to list the objects that you need to simulate, and then to examine what these objects "know" and "do." New Term: The problem space is the set of problems and issues your program is trying to solve. The solution space is the set of possible solutions to the problems. For example, clearly you have sensors of various types, a central alarm system, buttons, wires, and telephones. Further thought convinces you that you must also simulate rooms, perhaps floors, and possibly groups of people such as owners and police. The sensors can be divided into motion detectors, trip wires, sound detectors, smoke detectors, and so forth. All of these are types of sensors, though there is no such thing as a sensor per se. This is a good indication that sensor is an abstract data type (ADT). As an ADT, the class sensor would provide the complete interface for all types of sensors, and each derived type would provide the implementation. Clients of the various sensors would use them without regard to which type of sensor they are, and they would each "do the right thing" based on their real type. To create a good ADT, you need to have a complete understanding of what sensors do (rather than how they work). For example, are sensors passive devices or are they active? Do they wait for some element to heat up, a wire to break, or a piece of caulk to melt, or do they probe their environment? Perhaps some sensors have only a binary state (alarm state or okay), but others have a more analog state (what is the current temperature?). The interface to the abstract data type should be sufficiently complete to handle all the anticipated needs of the myriad derived classes. Other Objects The design continues in this way, teasing out the various other classes that will be required to meet the specification. For example, if a log is to be kept, probably a timer will be needed; should the timer poll each sensor or should each sensor file its own report periodically? The user is going to need to be able to set up, disarm, and program the system, and so a terminal of some sort will be required. You may want a separate object in your simulation for the alarm program itself. What Are the Classes? As you solve these problems, you will begin to design your classes. For example, you already have an indication that HeatSensor will derive from Sensor. If the sensor is to make periodic reports, it may also derive via multiple inheritance from Timer, or it may have a timer as a member variable.The HeatSensor will probably have member functions such as CurrentTemp() and SetTempLimit() and will probably inherit functions such as SoundAlarm() from its base class, Sensor. A frequent issue in object-oriented design is that of encapsulation. You could imagine a design in which the alarm system has a setting for MaxTemp. The alarm system asks the heat sensor what the current temperature is, compares it to the maximum temperature, and sounds the alarm if it is too hot. One could argue that this violates the principle of encapsulation. Perhaps it would be better if the alarm system didn't know or care what the details are of temperature analysis; arguably that should be in the HeatSensor. Whether or not you agree with that argument, it is the kind of decision you want to focus on during the analysis of the problem. To continue this analysis, one could argue that only the Sensor and the Log object should know any details of how sensor activity is logged; the Alarm object shouldn't know or care. Good encapsulation is marked by each class having a coherent and complete set of responsibilities, and no other class having the same responsibilities. If the sensor is responsible for noting the current temperature, no other class should have that responsibility. On the other hand, other classes might help deliver the necessary functionality. For example, while it might be the responsibility of the Sensor class to note and log the current temperature, it might implement that responsibility by delegating to a Log object the job of actually recording the data. Maintaining a firm division of responsibilities makes your program easier to extend and maintain. When you decide to change the alarm system for an enhanced module, its interface to the log and to the sensors will be narrow and well defined. Changes to the alarm system should not affect the Sensor classes, and vice versa. Should the HeatSensor have a ReportAlarm() function? All sensors will need the ability to report an alarm. This is a good indication that ReportAlarm() should be a virtual method of Sensor, and that Sensor may be an abstract base class. It is possible that HeatSensor will chain up to Sensor's more general ReportAlarm() method; the overridden function would just fill in the details it is uniquely qualified to supply. How Are Alarms Reported? When your sensors report an alarm condition, they will want to provide a lot of information to the object that phones the police and to the log. It may well be that you'll want to create a Condition class, whose constructor takes a number of measurements. Depending on the complexity of the measurements, these too might be objects, or they might be simple scalar values such as integers. It is possible that Condition objects are passed to the central Alarm object, or that Condition objects are subclassed into Alarm objects, which themselves know how to take emergency action. Perhaps there is no central object; instead there might be sensors, which know how to createCondition objects. Some Condition objects would know how to log themselves; others might know how to contact the police. A well-designed event-driven system need not have a central coordinator. One can imagine the sensors all independently receiving and sending message objects to one another, setting parameters, taking readings, and monitoring the house. When a fault is detected, an Alarm object is created, which logs the problem (by sending a message to the Log object) and takes the appropriate action. Event Loops To simulate such an event-driven system, your program needs to create an event loop. An event loop is typically an infinite loop such as while(1), which gets messages from the operating system (mouse clicks, keyboard presses, and so on) and dispatches them one by one, returning to the loop until an exit condition is satisfied. Listing 18.1 shows a rudimentary event loop. Listing 18.1. A simple event loop. 1: // Listing 18.1 2: 3: #include 4: 5: class Condition 6: { 7: public: 8: Condition() { } 9: virtual ~Condition() {} 10: virtual void Log() = 0; 11: }; 12: 13: class Normal : public Condition 14: { 15: public: 16: Normal() { Log(); } 17: virtual ~Normal() {} 18: virtual void Log() { cout << "Logging normal conditions...\n"; } 19: }; 20: 21: class Error : public Condition 22: { 23: public: 24: Error() {Log();} 25: virtual ~Error() {} 26: virtual void Log() { cout << "Logging error!\n"; } 27: };28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: class Alarm : public Condition { public: Alarm (); virtual ~Alarm() {} virtual void Warn() { cout << "Warning!\n"; } virtual void Log() { cout << "General Alarm log\n"; } virtual void Call() = 0; }; Alarm::Alarm() { Log(); Warn(); } class FireAlarm : public Alarm { public: FireAlarm(){Log();}; virtual ~FireAlarm() {} virtual void Call() { cout<< "Calling Fire Dept.!\n"; } virtual void Log() { cout << "Logging fire call.\n"; } }; int main() { int input; int okay = 1; Condition * pCondition; while (okay) { cout << "(0)Quit (1)Normal (2)Fire: "; cin >> input; okay = input; switch (input) { case 0: break; case 1: pCondition = new Normal; delete pCondition; break; case 2: pCondition = new FireAlarm; delete pCondition;74: 75: 76: 77: 78: 79: 80: 81: 82: 83: } break; default: pCondition = new Error; delete pCondition; okay = 0; break; } } return 0; Output: (0)Quit (1)Normal (2)Fire: 1 Logging normal conditions... (0)Quit (1)Normal (2)Fire: 2 General Alarm log Warning! Logging fire call. (0)Quit (1)Normal (2)Fire: 0 Analysis: The simple loop created on lines 59-80 allows the user to enter input simulating a normal report from a sensor and a report of a fire. Note that the effect of this report is to spawn a Condition object whose constructor calls various member functions. Calling virtual member functions from a constructor can cause confusing results if you are not mindful of the order of construction of objects. For example, when the FireAlarm object is created on line 72, the order of construction is Condition, Alarm, FireAlarm. The Alarm constructor calls Log, but it is Alarm's Log(), not FireAlarm's, that is invoked, despite Log() being declared virtual. This is because at the time Alarm's constructor runs, there is no FireAlarm object. Later, when FireAlarm itself is constructed, its constructor calls Log() again, and this time FireAlarm::Log() is called. PostMaster Here's another problem on which to practice your object-oriented analysis: You have been hired by Acme Software, Inc., to start a new software project and to hire a team of C++ programmers to implement your program. Jim Grandiose, vice-president of new product development, is your new boss. He wants you to design and build PostMaster, a utility to read electronic mail from various unrelated e-mail providers. The potential customer is a businessperson who uses more than one e-mail product, for example Interchange, CompuServe, Prodigy, America Online, Delphi, Internet Mail, Lotus Notes, AppleMail, cc:Mail, and so forth. The customer will be able to "teach" PostMaster how to dial up or otherwise connect to each of the e- mail providers, and PostMaster will get the mail and then present it in a uniform manner, allowing the customer to organize the mail, reply, forward letters among services, and so forth.PostMasterProfessional, to be developed as version 2 of PostMaster, is already anticipated. It will add an Administrative Assistant mode, which will allow the user to designate another person to read some or all of the mail, to handle routine correspondence, and so forth. There is also speculation in the marketing department that an artificial intelligence component might add the capability for PostMaster to pre-sort and prioritize the mail based on subject and content keywords and associations. Other enhancements have been talked about, including the ability to handle not only mail but discussion groups such as Interchange discussions, CompuServe forums, Internet newsgroups, and so forth. It is obvious that Acme has great hopes for PostMaster, and you are under severe time constraints to bring it to market, though you seem to have a nearly unlimited budget. Measure Twice, Cut Once You set up your office and order your equipment, and then your first order of business is to get a good specification for the product. After examining the market, you decide to recommend that development be focused on a single platform, and you set out to decide among DOS; UNIX; the Macintosh; and Windows, Windows NT, and OS/2. You have many painful meetings with Jim Grandiose, and it becomes clear that there is no right choice, and so you decide to separate the front end, that is the user interface or UI, from the back end, the communications and database part. To get things going quickly, you decide to write for DOS first, followed by Win32, the Mac, and then UNIX and OS/2. This simple decision has enormous ramifications for your project. It quickly becomes obvious that you will need a class library or a series of libraries to handle memory management, the various user interfaces, and perhaps also the communications and database components. Mr. Grandiose believes strongly that projects live or die by having one person with a clear vision, so he asks that you do the initial architectural analysis and design before hiring any programmers. You set out to analyze the problem. Divide and Conquer It quickly becomes obvious that you really have more than one problem to solve. You divide the project into these significant sub-projects: 1. Communications: the ability for the software to dial into the e-mail provider via modem, or to connect over a network. 2. Database: the ability to store data and to retrieve it from disk. 3. E-mail: the ability to read various e-mail formats and to write new messages to each system. 4. Editing: providing state-of-the-art editors for the creation and manipulation of messages.5. Platform issues: the various UI issues presented by each platform (DOS, Macintosh, and so on). 6. Extensibility: planning for growth and enhancements. 7. Organization and scheduling: managing the various developers and their code interdependencies. Each group must devise and publish schedules, and then be able to plan accordingly. Senior management and marketing need to know when the product will be ready. You decide to hire a manager to handle item 7, organization and scheduling. You then hire senior developers to help you analyze and design, and then to manage the implementation of the remaining areas. These senior developers will create the following teams: 1. Communications: responsible for both dial-up and network communications. They deal with packets, streams, and bits, rather than with e-mail messages per se. 2. Message format: responsible for converting messages from each e-mail provider to a canonical form (PostMaster standard) and back. It is also their job to write these messages to disk and to get them back off the disk as needed. 3. Message editors: This group is responsible for the entire UI of the product, on each platform. It is their job to ensure that the interface between the back end and the front end of the product is sufficiently narrow that extending the product to other platforms does not require duplication of code. Message Format You decide to focus on the message format first, setting aside the issues relating to communications and user interface. These will follow once you understand more fully what it is you are dealing with. There is little sense in worrying about how to present the information to the user until you understand what information you are dealing with. An examination of the various e-mail formats reveals that they have many things in common, despite their various differences. Each e-mail message has a point of origination, a destination, and a creation date. Nearly all such messages have a title or subject line and a body which may consist of simple text, rich text (text with formatting), graphics, and perhaps even sound or other fancy additions. Most such e-mail services also support attachments, so that users can send programs and other files. You confirm your early decision that you will read each mail message out of its original format and into PostMaster format. This way you will only have to store one record format, and writing to and reading from the disk will be simplified. You also decide to separate the "header" information (sender, recipient, date, title, and so on) from the body of the message. Often the user will want to scan the headers without necessarily reading the contents of all the messages. You anticipate that a time may come when users will want to download only the headers from the message provider, without getting the text at all, but for now you intend that version 1 of PostMaster will always get the full message, although it may not display it to the user.Initial Class Design This analysis of the messages leads you to design the Message class. In anticipation of extending the program to non-e-mail messages, you derive EmailMessage from the abstract base Message. From EmailMessage you derive PostMasterMessage, InterchangeMessage, CISMessage, ProdigyMessage, and so forth. Messages are a natural choice for objects in a program handling mail messages, but finding all the right objects in a complex system is the single greatest challenge of object-oriented programming. In some cases, such as with messages, the primary objects seem to "fall out" of your understanding of the problem. More often, however, you have to think long and hard about what you are trying to accomplish to find the right objects. Don't despair. Most designs are not perfect the first time. A good starting point is to describe the problem out loud. Make a list of all the nouns and verbs you use when describing the project. The nouns are good candidates for objects. The verbs might be the methods of those objects (or they may be objects in their own right). This is not a foolproof method, but it is a good technique to use when getting started on your design. That was the easy part. Now the question arises, "Should the message header be a separate class from the body?" If so, do you need parallel hierarchies, CompuServeBody and CompuServeHeader, as well as ProdigyBody and ProdigyHeader? Parallel hierarchies are often a warning sign of a bad design. It is a common error in object-oriented design to have a set of objects in one hierarchy, and a matching set of "managers" of those objects in another. The burden of keeping these hierarchies up-to-date and in sync with each other soon becomes overwhelming: a classic maintenance nightmare. There are no hard-and-fast rules, of course, and at times such parallel hierarchies are the most efficient way to solve a particular problem. Nonetheless, if you see your design moving in this direction, you should rethink the problem; there may be a more elegant solution available. When the messages arrive from the e-mail provider, they will not necessarily be separated into header and body; many will be one large stream of data, which your program will have to disentangle. Perhaps your hierarchy should reflect that idea directly. Further reflection on the tasks at hand leads you to try to list the properties of these messages, with an eye towards introducing capabilities and data storage at the right level of abstraction. Listing properties of your objects is a good way to find the data members, as well as to "shake out" other objects you might need. Mail messages will need to be stored, as will the user's preferences, phone numbers, and so forth. Storage clearly needs to be high up in the hierarchy. Should the mail messages necessarily share a base class with the preferences?Rooted Hierarchies Versus Non-Rooted Hierarchies There are two overall approaches to inheritance hierarchies: you can have all, or nearly all, of your classes descend from a common root class, or you can have more than one inheritance hierarchy. An advantage of a common root class is that you often can avoid multiple inheritance; a disadvantage is that many times implementation will percolate up into the base class. New Term: A set of classes is rooted if all share a common ancestor. Non-rooted hierarchies do not all share a common base class. Because you know that your product will be developed on many platforms, and because multiple inheritance is complex and not necessarily well supported by all compilers on all platforms, your first decision is to use a rooted hierarchy and single inheritance. You decide to identify those places where multiple inheritance might be used in the future, and to design so that breaking apart the hierarchy and adding multiple inheritance at a later time need not be traumatic to your entire design. You decide to prefix the name of all of your internal classes with the letter p so that you can easily and quickly tell which classes are yours and which are from other libraries. On Day 21, "What's Next," you'll learn about name spaces, which can reinforce this idea, but for now the initial will do nicely. Your root class will be pObject; virtually every class you create will descend from this object. pObject itself will be kept fairly simple; only that data which absolutely every item shares will appear in this class. If you want a rooted hierarchy, you'll want to give the root class a fairly generic name (like pObject) and few capabilities. The point of a root object is to be able to create collections of all its descendants and refer to them as instances of pObject. The trade-off is that rooted hierarchies often percolate interface up into the root class. You will pay the price; by percolating these interfaces up into the root object, other descendants will have interfaces that are inappropriate to their design. The only good solution to this problem, in single inheritance, is to use templates. Templates are discussed tomorrow. The next likely candidates for top of the hierarchy status are pStored and pWired. pStored objects are saved to disk at various times (for example when the program is not in use), and pWired objects are sent over the modem or network. Because nearly all of your objects will need to be stored to disk, it makes sense to push this functionality up high in the hierarchy. Because all the objects that are sent over the modem must be stored, but not all stored objects must be sent over the wire, it makes sense to derive pWired from pStored. Each derived class acquires all the knowledge (data) and functionality (methods) of its base class, and each should add one discrete additional ability. Thus, pWired may add various methods, but all arein service of adding the ability to be transferred over the modem. It is possible that all wired objects are stored, or that all stored objects are wired, or that neither of these statements is true. If only some wired objects are stored, and only some stored objects are wired, you will be forced either to use multiple inheritance or to "hack around" the problem. A potential "hack" for such a situation would be to inherit, for example, Wired from Stored, and then for those objects that are sent via modem, but are never stored, to make the stored methods do nothing or return an error. In fact, you realize that some stored objects clearly are not wired: for example, user preferences. All wired objects, however, are stored, and so your inheritance hierarchy so far is as reflected in Figure 18.1. Figure 18.1. Initial inheritance hierarchy. Designing the Interfaces It is important at this stage of designing your product to avoid being concerned with implementation. You want to focus all of your energies on designing a clean interface among the classes and then delineating what data and methods each class will need. It is often a good idea to have a solid understanding of the base classes before trying to design the more derived classes, so you decide to focus on pObject, pStored, and pWired. The root class, pObject, will only have the data and methods that are common to everything on your system. Perhaps every object should have a unique identification number. You could create pID (PostMaster ID) and make that a member of pObject; but first you must ask yourself, "Does any object that is not stored and not wired need such a number?" That begs the question, "Are there any objects that are not stored, but that are part of this hierarchy?" If there are no such objects, you may want to consider collapsing pObject and pStored into one class; after all, if all objects are stored, what is the point of the differentiation? Thinking this through, you realize that there may be some objects, such as address objects, that it would be beneficial to derive from pObject, but that will never be stored on their own; if they are stored, they will be as part of some other object. That says that for now having a separate pObject class would be useful. One can imagine that there will be an address book that would be a collection of pAddress objects, and while no pAddress will ever be stored on its own, there would be utility in having each one have its own unique identification number. You tentatively assign pID to pObject, and this means that pObject, at a minimum, will look like this: class pObject { public:pObject(); ~pObject(); pID GetID()const; void SetID(); private: pID itsID; } There are a number of things to note about this class declaration. First, this class is not declared to derive from any other; this is your root class. Second, there is no attempt to show implementation, even for methods such as GetID() that are likely to have inline implementation when you are done. Third, const methods are already identified; this is part of the interface, not the implementation. Finally, a new data type is implied: pID. Defining pID as a type, rather than using, for example, unsigned long, puts greater flexibility into your design. If it turns out that you don't need an unsigned long, or that an unsigned long is not sufficiently large, you can modify pID. That modification will affect every place pID is used, and you won't have to track down and edit every file with a pID in it. For now, you will use typedef to declare pID to be ULONG, which in turn you will declare to be unsigned long. This raises the question: Where do these declarations go? When programming a large project, an overall design of the files is needed. A standard approach, one which you will follow for this project, is that each class appears in its own header file, and the implementation for the class methods appears in an associated CPP file. Thus, you will have a file called OBJECT.HPP and another called OBJECT.CPP. You anticipate having other files such as MSG.HPP and MSG.CPP, with the declaration of pMessage and the implementation of its methods, respectively. NOTE: Buy it or write it? One question that you will confront throughout the design phase of your program is which routines might you buy and which must you write yourself. It is entirely possible that you can take advantage of existing commercial libraries to solve some or all of your communications issues. Licensing fees and other non-technical concerns must also be resolved. It is often advantageous to purchase such a library, and to focus your energies on your specific program, rather than to "reinvent the wheel" about secondary technical issues. You might even want to consider purchasing libraries that were not necessarily intended for use with C++, if they can provide fundamental functionality you'd otherwise have to engineer yourself. This can be instrumental in helping you hit your deadlines. Building a PrototypeFor a project as large as PostMaster, it is unlikely that your initial design will be complete and perfect. It would be easy to become overwhelmed by the sheer scale of the problem, and trying to create all the classes and to complete their interface before writing a line of working code is a recipe for disaster. There are a number of good reasons to try out your design on a prototype--a quick-and-dirty working example of your core ideas. There are a number of different types of prototypes, however, each meeting different needs. An interface design prototype provides the chance to test the look and feel of your product with potential users. A functionality prototype might be designed that does not have the final user interface, but allows users to try out various features, such as forwarding messages or attaching files without worrying about the final interface. Finally, an architecture prototype might be designed to give you a chance to develop a smaller version of the program and to assess how easily your design decisions will "scale up," as the program is fleshed out. It is imperative to keep your prototyping goals clear. Are you examining the user interface, experimenting with functionality, or building a scale model of your final product? A good architecture prototype makes a poor user interface prototype, and vice versa. It is also important to keep an eye on over-engineering the prototype, or becoming so concerned with the investment you've made in the prototype that you are reluctant to tear the code down and redesign as you progress. The 80/80 Rule A good design rule of thumb at this stage is to design for those things that 80 percent of the people want to do 80 percent of the time, and to set aside your concerns about the remaining 20 percent. The "boundary conditions" will need to be addressed sooner or later, but the core of your design should focus on the 80/80. In the face of this, you might decide to start by designing the principal classes, setting aside the need for the secondary classes. Further, when you identify multiple classes that will have similar designs with only minor refinements, you might choose to pick one representative class and focus on that, leaving until later the design and implementation of its close cousins. NOTE: There is another rule, the 80/20 rule, which states that "the first 20% of your program will take 80% of your time to code, and the remaining 80% of your program will take the other 80% of your time!"Designing the PostMasterMessage Class In keeping with these considerations, you decide to focus on PostMasterMessage. This is the class that is most under your direct control. As part of its interface, PostMasterMessage will need to talk with other types of messages, of course. You hope to be able to work closely with the other message providers and to get their message format specifications, but for now you can make some smart guesses just by observing what is sent to your computer as you use their services. In any case, you know that every PostMasterMessage will have a sender, a recipient, a date, and a subject, as well as the body of the message and perhaps attached files. This tells you that you'll need accessor methods for each of these attributes, as well as methods to report on the size of the attached files, the size of the messages, and so forth. Some of the services to which you will connect will use rich text--that is, text with formatting instructions to set the font, character size, and attributes, such as bold and italic. Other services do not support these attributes, and those that do may or may not use their own proprietary scheme for managing rich text. Your class will need conversion methods for turning rich text into plain ASCII, and perhaps for turning other formats into PostMaster formats. Application Program Interface An Application Program Interface (API) is a set of documentation and routines for using a service. Many of the mail providers will give you an API so that PostMaster mail will be able to take advantage of their more advanced features, such as rich text and embedding files. PostMaster will also want to publish its own API so that other providers can plan for working with PostMaster in the future. Your PostMasterMessage class will want to have a well-designed public interface, and the conversion functions will be a principal component of PostMaster's API. Listing 18.2 illustrates what PostMasterMessage's interface looks like so far. Listing 18.2. PostMasterMessages interface 1: 2: 3: 4: 5: 6: 7: 8: class PostMasterMessage : public MailMessage { public: PostMasterMessage(); PostMasterMessage( pAddress Sender, pAddress Recipient, pString Subject,9: pDate creationDate); 10: 11: // other constructors here 12: // remember to include copy constructor 13: // as well as constructor from storage 14: // and constructor from wire format 15: // Also include constructors from other formats 16: ~PostMasterMessage(); 17: pAddress& GetSender() const; 18: void SetSender(pAddress&); 19: // other member accessors 20: 21: // operator methods here, including operator equals 22: // and conversion routines to turn PostMaster messages 23: // into messages of other formats. 24: 25: private: 26: pAddress itsSender; 27: pAddress itsRecipient; 28: pString itsSubject; 29: pDate itsCreationDate; 30: pDate itsLastModDate; 31: pDate itsReceiptDate; 32: pDate itsFirstReadDate; 33: pDate itsLastReadDate; 34: }; Output: None. Analysis: Class PostMasterMessage is declared to derive from MailMessage. A number of constructors will be provided, facilitating the creation of PostMasterMessages from other types of mail messages. A number of accessor methods are anticipated for reading and setting the various member data, as well as operators for turning all or part of this message into other message formats. You anticipate storing these messages to disk and reading them from the wire, so accessor methods are needed for those purposes as well. Programming in Large Groups Even this preliminary architecture is enough to indicate how the various development groups ought to proceed. The communications group can go ahead and start work on the communications back end, negotiating a narrow interface with the message format group. The message format group will probably lay out the general interface to the Message classes, as wasbegun above, and then will turn its attention to the question of how to write data to the disk and read it back. Once this disk interface is well understood, they will be in a good position to negotiate the interface to the communications layer. The message editors will be tempted to create editors with an intimate knowledge of the internals of the Message class, but this would be a bad design mistake. They too must negotiate a very narrow interface to the Message class; message editor objects should know very little about the internal structure of messages. Ongoing Design Considerations As the project continues, you will repeatedly confront this basic design issue: In which class should you put a given set of functionality (or information)? Should the Message class have this function, or should the Address class? Should the editor store this information, or should the message store it itself? Your classes should operate on a "need to know" basis, much like secret agents. They shouldn't share any more knowledge than is absolutely necessary. Design Decisions As you progress with your program, you will face hundreds of design issues. They will range from the more global questions, "What do we want this to do?" to the more specific, "How do we make this work?" While the details of your implementation won't be finalized until you ship the code, and some of the interfaces will continue to shift and change as you work, you must ensure that your design is well understood early in the process. It is imperative that you know what you are trying to build before you write the code. The single most frequent cause of software dying on the vine must be that there was not sufficient agreement early enough in the process about what was being built. Decisions, Decisions To get a feel for what the design process is like, examine this question, "What will be on the menu?" For PostMaster, the first choice is probably "new mail message," and this immediately raises another design issue: When the user presses New Message, what happens? Does an editor get created, which in turn creates a mail message, or does a new mail message get created, which then creates the editor? The command you are working with is "new mail message," so creating a new mail message seems like the obvious thing to do. But what happens if the user hits Cancel after starting to write the message? Perhaps it would be cleaner to first create the editor and have it create (and own) the new message.The problem with this approach is that the editor will need to act differently if it is creating a message than if it is editing the message, whereas if the message is created first and then handed to the editor, only one set of code need exist: Everything is an edit of an existing message. If a message is created first, who creates it? Is it created by the menu command code? If so, does the menu also tell the message to edit itself, or is this part of the constructor method of the message? It makes sense for the constructor to do this at first glance; after all, every time you create a message you'll probably want to edit it. Nonetheless, this is not a good design idea. First, it is very possible that the premise is wrong: You may well create "canned" messages (that is, error messages mailed to the system operator) that are not put into an editor. Second, and more important, a constructor's job is to create an object; it should do no more and no less than that. Once a mail message is created, the constructor's job is done; adding a call to the edit method just confuses the role of the constructor and makes the mail message vulnerable to failures in the editor. What is worse, the edit method will call another class, the editor, causing its constructor to be called. Yet the editor is not a base class of the message, nor is it contained within the message; it would be unfortunate if the construction of the message depended on successful construction of the editor. Finally, you won't want to call the editor at all if the message can't be successfully created; yet successful creation would, in this scenario, depend on calling the editor! Clearly you want to fully return from the message's constructor before calling Message::Edit(). DO look for objects that arise naturally out of your design. DO redesign as your understanding of the problem space improves. DON'T share more information among the classes than is absolutely necessary. DO look for opportunities to take advantage of C++'s polymorphism. Working with Driver Programs One approach to surfacing design issues is to create a driver program early in the process. For example, the driver program for PostMaster might offer a very simple menu, which will create PostMasterMessage objects, manipulate them, and otherwise exercise some of the design. New Term: A driver program is a function that exists only to demonstrate or test other functions. Listing 18.3 illustrates a somewhat more robust definition of the PostMasterMessage class and a simple driver program.Listing 18.3. A driver program for PostMasterMessage. 1: #include 2: #include 3: 4: typedef unsigned long pDate; 5: enum SERVICE 6: { PostMaster, Interchange, CompuServe, Prodigy, AOL, Internet }; 7: class String 8: { 9: public: 10: // constructors 11: String(); 12: String(const char *const); 13: String(const String &); 14: ~String(); 15: 16: // overloaded operators 17: char & operator[](int offset); 18: char operator[](int offset) const; 19: String operator+(const String&); 20: void operator+=(const String&); 21: String & operator= (const String &); 22: friend ostream& operator<< 23: ( ostream& theStream,String& theString); 24: // General accessors 25: int GetLen()const { return itsLen; } 26: const char * GetString() const { return itsString; } 27: // static int ConstructorCount; 28: private: 29: String (int); // private constructor 30: char * itsString; 31: unsigned short itsLen; 32: 33: }; 34: 35: // default constructor creates string of 0 bytes 36: String::String() 37: { 38: itsString = new char[1]; 39: itsString[0] = `\0'; 40: itsLen=0; 41: // cout << "\tDefault string constructor\n"; 42: // ConstructorCount++; 43: }44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: // private (helper) constructor, used only by // class methods for creating a new string of // required size. Null filled. String::String(int len) { itsString = new char[len+1]; for (int i = 0; i<=len; i++) itsString[1] = `\0'; itsLen=len; // cout << "\tString(int) constructor\n"; // ConstructorCount++; } // Converts a character array to a String String::String(const char * const cString) { itsLen = strlen(cString); itsString = new char[itsLen+1]; for (int i = 0; i itsLen) return itsString[itsLen-1]; else return itsString[offset]; } // constant offset operator for use // on const objects (see copy constructor!) char String::operator[](int offset) const { if (offset > itsLen) return itsString[itsLen-1]; else return itsString[offset]; } // creates a new string by adding current // string to rhs String String::operator+(const String& rhs) { int totalLen = itsLen + rhs.GetLen(); int i,j; String temp(totalLen); for ( i = 0; i // declare the template and the parameter class Array // the class being parameterized { public: Array(); // full class declaration here }; The keyword template is used at the beginning of every declaration and definition of a template class. The parameters of the template are after the keyword template. The parameters are the things that will change with each instance. For example, in the array template shown previously, the type of the objects stored in the array will change. One instance might store an array of integers, while another might store an array of Animals. In this example, the keyword class is used, followed by the identifier T. The keyword class indicates that this parameter is a type. The identifier T is used throughout the rest of the template definition to refer to the parameterized type. One instance of this class will substitute int everywhere T appears, and another will substitute Cat. To declare an int and a Cat instance of the parameterized Array class, you would write Array anIntArray; Array aCatArray; The object anIntArray is of the type array of integers; the object aCatArray is of the type array of cats. You can now use the type Array anywhere you would normally use a type--as the return value from a function, as a parameter to a function, and so forth. Listing 19.1 provides the full declaration of this stripped-down Array template. NOTE: Listing 19.1 is not a complete program!Listing 19.1. A template of an Array class 1: Listing 19.1 A template of an array class 2: #include 3: const int DefaultSize = 10; 4: 5: template // declare the template and the parameter 6: class Array // the class being parameterized 7: { 8: public: 9: // constructors 10: Array(int itsSize = DefaultSize); 11: Array(const Array &rhs); 12: ~Array() { delete [] pType; } 13: 14: // operators 15: Array& operator=(const Array&); 16: T& operator[](int offSet) { return pType[offSet]; } 17: 18: // accessors 19: int getSize() { return itsSize; } 20: 21: private: 22: T *pType; 23: int itsSize; 24: }; Output: There is no output. This is an incomplete program. Analysis: The definition of the template begins on line 5, with the keyword template followed by the parameter. In this case, the parameter is identified to be a type by the keyword class, and the identifier T is used to represent the parameterized type. From line 6 until the end of the template on line 24, the rest of the declaration is like any other class declaration. The only difference is that wherever the type of the object would normally appear, the identifier T is used instead. For example, operator[] would be expected to return a reference to an object in the array, and in fact it is declared to return a reference to a T. When an instance of an integer array is declared, the operator= that is provided to that array will return a reference to an integer. When an instance of an Animal array is declared, the operator=provided to the Animal array will return a reference to an Animal. Using the Name Within the class declaration, the word Array may be used without further qualification. Elsewhere in the program, this class will be referred to as Array. For example, if you do not write the constructor within the class declaration, you must write template Array::Array(int size): itsSize = size { pType = new T[size]; for (int i = 0; i, and the function name is Array(int size). The remainder of the function is exactly the same as it would be for a non-template function. It is a common and preferred method to get the class and its functions working as a simple declaration before turning it into a template. Implementing the Template The full implementation of the Array template class requires implementation of the copy constructor, operator=, and so forth. Listing 19.2 provides a simple driver program to exercise this template class. NOTE: Some older compilers do not support templates. Templates are, however, part of the emerging C++ standard. All major compiler vendors have committed to supporting templates in their next release, if they have not already done so. If you have an older compiler, you won't be able to compile and run the exercises in this chapter. It's still a good idea to read through the entire chapter, however, and return to this material when you upgrade your compiler. Listing 19.2. The implementation of the template array. 1: 2: 3: 4: #include const int DefaultSize = 10;5: // declare a simple Animal class so that we can 6: // create an array of animals 7: 8: class Animal 9: { 10: public: 11: Animal(int); 12: Animal(); 13: ~Animal() {} 14: int GetWeight() const { return itsWeight; } 15: void Display() const { cout << itsWeight; } 16: private: 17: int itsWeight; 18: }; 19: 20: Animal::Animal(int weight): 21: itsWeight(weight) 22: {} 23: 24: Animal::Animal(): 25: itsWeight(0) 26: {} 27: 28: 29: template // declare the template and the parameter 30: class Array // the class being parameterized 31: { 32: public: 33: // constructors 34: Array(int itsSize = DefaultSize); 35: Array(const Array &rhs); 36: ~Array() { delete [] pType; } 37: 38: // operators 39: Array& operator=(const Array&); 40: T& operator[](int offSet) { return pType[offSet]; } 41: const T& operator[](int offSet) const 42: { return pType[offSet]; } 43: // accessors 44: int GetSize() const { return itsSize; } 45: 46: private: 47: T *pType; 48: int itsSize; 49: };50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: // implementations follow... // implement the Constructor template Array::Array(int size = DefaultSize): itsSize(size) { pType = new T[size]; for (int i = 0; i Array::Array(const Array &rhs) { itsSize = rhs.GetSize(); pType = new T[itsSize]; for (int i = 0; i Array& Array::operator=(const Array &rhs) { if (this == &rhs) return *this; delete [] pType; itsSize = rhs.GetSize(); pType = new T[itsSize]; for (int i = 0; i theArray; Array theZoo; Animal *pAnimal; // an array of integers // an array of Animals // fill the arrays for (int i = 0; i < theArray.GetSize(); i++)96: { 97: theArray[i] = i*2; 98: pAnimal = new Animal(i*3); 99: theZoo[i] = *pAnimal; 100: delete pAnimal; 101: } 102: // print the contents of the arrays 103: for (int j = 0; j < theArray.GetSize(); j++) 104: { 105: cout << "theArray[" << j << "]:\t"; 106: cout << theArray[j] << "\t\t"; 107: cout << "theZoo[" << j << "]:\t"; 108: theZoo[j].Display(); 109: cout << endl; 110: } 111: 112: for (int k = 0; k < theArray.GetSize(); k++) 113: delete &theZoo[j]; 114: return 0; 115: } Output: theArray[0]: 0 theZoo[0]: 0 theArray[1]: 2 theZoo[1]: 3 theArray[2]: 4 theZoo[2]: 6 theArray[3]: 6 theZoo[3]: 9 theArray[4]: 8 theZoo[4]: 12 theArray[5]: 10 theZoo[5]: 15 theArray[6]: 12 theZoo[6]: 18 theArray[7]: 14 theZoo[7]: 21 theArray[8]: 16 theZoo[8]: 24 theArray[9]: 18 theZoo[9]: 27 Analysis: Lines 8 to 26 provide a stripped-down Animal class, created here so that there are objects of a user-defined type to add to the array. Line 29 declares that what follows is a template, and that the parameter to the template is a type, designated as T. The Array class has two constructors as shown, the first of which takes a size and defaults to the constant integer DefaultSize. The assignment and offset operators are declared, with the latter declaring both a const and a non- const variant. The only accessor provided is GetSize(), which returns the size of the array. One can certainly imagine a fuller interface, and, for any serious Array program, what has been supplied here would be inadequate. At a minimum, operators to remove elements, to expand the array, to pack the array, and so forth would be required. The private data consists of the size of the array and a pointer to the actual in-memory array ofobjects. Template Functions If you want to pass an array object to a function, you must pass a particular instance of the array, not a template. Therefore, if SomeFunction() takes an integer array as a parameter, you may write void SomeFunction(Array&); // ok but you may not write void SomeFunction(Array&); // error! because there is no way to know what a T& is. You also may not write void SomeFunction(Array &); // error! because there is no class Array--only the template and the instances. To accomplish the more general approach, you must declare a template function. template void MyTemplateFunction(Array&); // ok Here the function MyTemplateFunction() is declared to be a template function by the declaration on the top line. Note that template functions can have any name, just as other functions can. Template functions can also take instances of the template, in addition to the parameterized form. The following is an example: template void MyOtherFunction(Array&, Array&); // ok Note that this function takes two arrays: a parameterized array and an array of integers. The former can be an array of any object, but the latter is always an array of integers. Templates and Friends Template classes can declare three types of friends: ++- A non-template friend class or function../. A general template friend class or function. *#' A type-specific template friend class or function. Non-Template Friend Classes and Functions It is possible to declare any class or function to be a friend to your template class. Each instance of the class will treat the friend properly, as if the declaration of friendship had been made in that particular instance. Listing 19.3 adds a trivial friend function, Intrude(), to the template definition of the Array class, and the driver program invokes Intrude(). Because it is a friend, Intrude() can then access the private data of the Array. Because this is not a template function, it can only be called on Arrays of int. NOTE: To use Listing 19.3, copy lines 1-26 of Listing 19.2 after line 1 of this listing, and then copy lines 51-86 of Listing 19.2 after line 37 of this listing. Listing 19.3. Non-template friend function. 1: // Listing 19.3 - Type specific friend functions in templates 2: 3: template // declare the template and the parameter 4: class Array // the class being parameterized 5: { 6: public: 7: // constructors 8: Array(int itsSize = DefaultSize); 9: Array(const Array &rhs); 10: ~Array() { delete [] pType; } 11: 12: // operators 13: Array& operator=(const Array&); 14: T& operator[](int offSet) { return pType[offSet]; } 15: const T& operator[](int offSet) const 16: { return pType[offSet]; } 17: // accessors 18: int GetSize() const { return itsSize; } 19: 20: // friend function 21: friend void Intrude(Array); 22: 23: private:24: T *pType; 25: int itsSize; 26: }; 27: 28: // friend function. Not a template, can only be used 29: // with int arrays! Intrudes into private data. 30: void Intrude(Array theArray) 31: { 32: cout << "\n*** Intrude ***\n"; 33: for (int i = 0; i < theArray.itsSize; i++) 34: cout << "i: " << theArray.pType[i] << endl; 35: cout << "\n"; 36: } 37: 38: // driver program 39: int main() 40: { 41: Array theArray; // an array of integers 42: Array theZoo; // an array of Animals 43: Animal *pAnimal; 44: 45: // fill the arrays 46: for (int i = 0; i < theArray.GetSize(); i++) 47: { 48: theArray[i] = i*2; 49: pAnimal = new Animal(i*3); 50: theZoo[i] = *pAnimal; 51: } 52: 53: int j, k; 54: for (j = 0; j < theArray.GetSize(); j++) 55: { 56: cout << "theZoo[" << j << "]:\t"; 57: theZoo[j].Display(); 58: cout << endl; 59: } 60: cout << "Now use the friend function to "; 61: cout << "find the members of Array"; 62: Intrude(theArray); 63: 63: // return the allocated memory before the arrays are destroyed. 64: for (k = 0; k < theArray.GetSize(); k++) 65: delete &theZoo[j]; 66: 67: cout << "\n\nDone.\n";68: 69: } return 0; Output: theZoo[0]: 0 theZoo[1]: 3 theZoo[2]: 6 theZoo[3]: 9 theZoo[4]: 12 theZoo[5]: 15 theZoo[6]: 18 theZoo[7]: 21 theZoo[8]: 24 theZoo[9]: 27 Now use the friend function to find the members of Array *** Intrude *** i: 0 i: 2 i: 4 i: 6 i: 8 i: 10 i: 12 i: 14 i: 16 i: 18 Done. Analysis: The declaration of the Array template has been extended to include the friend function Intrude(). This declares that every instance of an array will consider Intrude() to be a friend function; thus, Intrude() will have access to the private member data and functions of the array instance. On line 33, Intrude() accesses itsSize directly, and on line 34 it accesses pType directly. This trivial use of these members was unnecessary because the Array class provides public accessors for this data, but it serves to demonstrate how friend functions can be declared with templates. General Template Friend Class or Function It would be helpful to add a display operator to the Array class. One approach would be to declare a display operator for each possible type of Array, but this would undermine the whole point of having made Array a template. What is needed is an insertion operator that works for any possible type of Array. ostream& operator<< (ostream& Array&);To make this work, we need to declare operator<< to be a template function. template ostream& operator<< (ostream&, Array&) Now that operator<< is a template function, you need only to provide an implementation. Listing 19.4 shows the Array template extended to include this declaration and provides the implementation for the operator<<. NOTE: To compile this listing, copy lines 8-26 of Listing 19.2 and insert them between lines 3 and 4. Also copy lines 51-86 of Listing 19.2 and insert them between lines 37 and 38. Listing 19.4. Using operator ostream. 1: #include 2: 3: const int DefaultSize = 10; 4: 5: template // declare the template and the parameter 6: class Array // the class being parameterized 7: { 8: public: 9: // constructors 10: Array(int itsSize = DefaultSize); 11: Array(const Array &rhs); 12: ~Array() { delete [] pType; } 13: 14: // operators 15: Array& operator=(const Array&); 16: T& operator[](int offSet) { return pType[offSet]; } 17: const T& operator[](int offSet) const 18: { return pType[offSet]; } 19: // accessors 20: int GetSize() const { return itsSize; } 21: 22: friend ostream& operator<< (ostream&, Array&); 23: 24: private: 25: T *pType; 26: int itsSize; 27: };28: 29: template 30: ostream& operator<< (ostream& output, Array& theArray) 31: { 32: for (int i = 0; i theArray; 43: 44: while (!Stop) 45: { 46: cout << "Enter an offset (0-9) "; 47: cout << "and a value. (-1 to stop): " ; 47: cin >> offset >> value; 48: 49: if (offset < 0) 50: break; 51: 52: if (offset > 9) 53: { 54: cout << "***Please use values between 0 and 9.***\n"; 55: continue; 56: } 57: 58: theArray[offset] = value; 59: } 60: 61: cout << "\nHere's the entire array:\n"; 62: cout << theArray << endl; 63: return 0; 64: } Output: Enter an offset (0-9) and a value. (-1 to stop): 1 10 Enter an offset (0-9) and a value. (-1 to stop): 2 20 Enter an offset (0-9) and a value. (-1 to stop): 3 30 Enter an offset (0-9) and a value. (-1 to stop): 4 40 Enter an offset (0-9) and a value. (-1 to stop): 5 50Enter an offset (0-9) and a value. Enter an offset (0-9) and a value. Enter an offset (0-9) and a value. Enter an offset (0-9) and a value. Enter an offset (0-9) and a value. ***Please use values between 0 and Enter an offset (0-9) and a value. (-1 to (-1 to (-1 to (-1 to (-1 to 9.*** (-1 to stop): stop): stop): stop): stop): 6 60 7 70 8 80 9 90 10 10 stop): -1 -1 Here's the entire array: [0] 0 [1] 10 [2] 20 [3] 30 [4] 40 [5] 50 [6] 60 [7] 70 [8] 80 [9] 90 Analysis: On line 22, the function template operator<<() is declared to be a friend of the Array class template. Because operator<<() is implemented as a template function, every instance of this parameterized array type will automatically have an operator<<(). The implementation for this operator starts on line 29. Every member of an array is called in turn. This only works if there is an operator<< defined for every type of object stored in the array. A Type-Specific Template Friend Class or Function Although the insertion operator shown in Listing 19.4 works, it is still not quite what is needed. Because the declaration of the friend operator on line 29 declares a template, it will work for any instance of Array and any insertion operator taking an array of any type. The insertion operator template shown in Listing 19.4 makes all instances of the insertion operator<< a friend of any instance of Array, whether the instance of the insertion operator is an integer, an Animal, or a Car. It makes no sense, however, for an Animal insertion operator to be a friend to the insertion operator for an integer array. What is needed is for the insertion operator for an array of int to be a friend to the Array of int class, and for the insertion operator of an array of Animals to be a friend to the Array of animals instance. To accomplish this, modify the declaration of the insertion operator on line 29 of Listing 19.4, and remove the words template . That is, change line 30 to read friend ostream& operator<< (ostream&, Array&);This will use the type (T) declared in the template of Array. Thus, the operator<< for an integer will only work with an array of integers, and so forth. Using Template Items You can treat template items as you would any other type. You can pass them as parameters, either by reference or by value, and you can return them as the return values of functions, also by value or by reference. Listing 19.5 demonstrates how to pass template objects. Listing 19.5. Passing template objects to and from functions. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: } 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28 29: 30: 31: 32: 33: #include const int DefaultSize = 10; // A trivial class for adding to arrays class Animal { public: // constructors Animal(int); Animal(); ~Animal(); // accessors int GetWeight() const { return itsWeight; } void SetWeight(int theWeight) { itsWeight = theWeight; // friend operators friend ostream& operator<< (ostream&, const Animal&); private: int itsWeight; }; // extraction operator for printing animals ostream& operator<< (ostream& theStream, const Animal& theAnimal) { theStream << theAnimal.GetWeight(); return theStream; } Animal::Animal(int weight):34: itsWeight(weight) 35: { 36: // cout << "Animal(int)\n"; 37: } 38: 39: Animal::Animal(): 40: itsWeight(0) 41: { 42: // cout << "Animal()\n"; 43: } 44: 45: Animal::~Animal() 46: { 47: // cout << "Destroyed an animal...\n"; 48: } 49: 50: template // declare the template and the parameter 51: class Array // the class being parameterized 52: { 53: public: 54: Array(int itsSize = DefaultSize); 55: Array(const Array &rhs); 56: ~Array() { delete [] pType; } 57: 58: Array& operator=(const Array&); 59: T& operator[](int offSet) { return pType[offSet]; } 60: const T& operator[](int offSet) const 61: { return pType[offSet]; } 62: int GetSize() const { return itsSize; } 63 64: // friend function 65: friend ostream& operator<< (ostream&, const Array&); 66: 67: private: 68: T *pType; 69: int itsSize; 70: }; 71: 70: template 72: ostream& operator<< (ostream& output, const Array& theArray) 73: { 74: for (int i = 0; i& theArray); 80: void AnimalFillFunction(Array& theArray); 81: enum BOOL {FALSE, TRUE}; 82: 84: int main() 85: { 86: Array intArray; 87: Array animalArray; 88: IntFillFunction(intArray); 87: AnimalFillFunction(animalArray); 89: cout << "intArray...\n" << intArray; 90: cout << "\nanimalArray...\n" << animalArray << endl; 91: return 0; 92: } 93: 94: void IntFillFunction(Array& theArray) 95: { 96: BOOL Stop = FALSE; 97: int offset, value; 98: while (!Stop) 99: { 100: cout << "Enter an offset (0-9) "; 101: cout << "and a value. (-1 to stop): " ; 102: cin >> offset >> value; 103: if (offset < 0) 104: break; 105: if (offset > 9) 106: { 107: cout << "***Please use values between 0 and 9.***\n"; 108: continue; 109: } 110: theArray[offset] = value; 111: } 112: } 113: 114: 115: void AnimalFillFunction(Array& theArray) 116: { 117: Animal * pAnimal; 118: for (int i = 0; iSetWeight(i*100);122: 123: 124: 125: } theArray[i] = *pAnimal; delete pAnimal; // a copy was put in the array } Output: Enter an offset (0-9) and a value. (-1 to stop): 1 10 Enter an offset (0-9) and a value. (-1 to stop): 2 20 Enter an offset (0-9) and a value. (-1 to stop): 3 30 Enter an offset (0-9) and a value. (-1 to stop): 4 40 Enter an offset (0-9) and a value. (-1 to stop): 5 50 Enter an offset (0-9) and a value. (-1 to stop): 6 60 Enter an offset (0-9) and a value. (-1 to stop): 7 70 Enter an offset (0-9) and a value. (-1 to stop): 8 80 Enter an offset (0-9) and a value. (-1 to stop): 9 90 Enter an offset (0-9) and a value. (-1 to stop): 10 10 ***Please use values between 0 and 9.*** Enter an offset (0-9) and a value. (-1 to stop): -1 -1 intArray:... [0] 0 [1] 10 [2] 20 [3] 30 [4] 40 [5] 50 [6] 60 [7] 70 [8] 80 [9] 90 animalArray:... [0] 0 [1] 100 [2] 200 [3] 300 [4] 400 [5] 500 [6] 600 [7] 700 [8] 800 [9] 900 Analysis: Most of the Array class implementation is left out to save space. The Animal class is declared on lines 6-23. Although this is a stripped-down and simplified class, it does provide its own insertion operator (<<) to allow the printing of Animals. Printing simply prints the current weight of the Animal.Note that Animal has a default constructor. This is necessary because, when you add an object to an array, the object's default constructor is used to create the object. This creates some difficulties, as you'll see. On line 79, the function IntFillFunction() is declared. The prototype indicates that this function takes an integer array. Note that this is not a template function. IntFillFunction() expects only one type of an array--an integer array. Similarly, on line 80, AnimalFillFunction() is declared to take an Array of Animal. The implementations for these functions are different from one another, because filling an array of integers does not have to be accomplished in the same way as filling an array of Animals. Specialized Functions If you uncomment the print statements in Animal's constructors and destructor in Listing 19.5, you'll find there are unanticipated extra constructions and destructions of Animals. When an object is added to an array, the object's default constructor is called. The Array constructor, however, goes on to assign 0 to the value of each member of the array, as shown on lines 59 and 60 of Listing 19.2. When you write someAnimal = (Animal) 0;, you call the default operator= for Animal. This causes a temporary Animal object to be created, using the constructor, which takes an integer (zero). That temporary is used as the right-hand side of the operator= and then is destroyed. This is an unfortunate waste of time, because the Animal object was already properly initialized. However, you can't remove this line, because integers are not automatically initialized to a value of 0. The solution is to teach the template not to use this constructor for Animals, but to use a special Animal constructor. You can provide an explicit implementation for the Animal class, as indicated in Listing 19.6. Listing 19.6. Specializing template implementations. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: #include const int DefaultSize = 3; // A trivial class for adding to arrays class Animal { public: // constructors Animal(int);11: Animal(); 12: ~Animal(); 13: 14: // accessors 15: int GetWeight() const { return itsWeight; } 16: void SetWeight(int theWeight) { itsWeight = theWeight; } 17: 18: // friend operators 19: friend ostream& operator<< (ostream&, const Animal&); 20: 21: private: 22: int itsWeight; 23: }; 24: 25: // extraction operator for printing animals 26: ostream& operator<< 27: (ostream& theStream, const Animal& theAnimal) 28: { 29: theStream << theAnimal.GetWeight(); 30: return theStream; 31: } 32: 33: Animal::Animal(int weight): 34: itsWeight(weight) 35: { 36: cout << "animal(int) "; 37: } 38: 39: Animal::Animal(): 40: itsWeight(0) 41: { 42: cout << "animal() "; 43: } 44: 45: Animal::~Animal() 46: { 47: cout << "Destroyed an animal..."; 48: } 49: 50: template // declare the template and the parameter 51: class Array // the class being parameterized 52: { 53: public: 54: Array(int itsSize = DefaultSize);55: 56: 57: 58: 59: 60: 61: 62: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: Array(const Array &rhs); ~Array() { delete [] pType; } // operators Array& operator=(const Array&); T& operator[](int offSet) { return pType[offSet]; } const T& operator[](int offSet) const { return pType[offSet]; } // accessors int GetSize() const { return itsSize; } // friend function friend ostream& operator<< (ostream&, const Array&); private: T *pType; int itsSize; }; template Array::Array(int size = DefaultSize): itsSize(size) { pType = new T[size]; for (int i = 0; i Array& Array::operator=(const Array &rhs) { if (this == &rhs) return *this; delete [] pType; itsSize = rhs.GetSize(); pType = new T[itsSize]; for (int i = 0; i Array::Array(const Array &rhs) { itsSize = rhs.GetSize(); pType = new T[itsSize];100: for (int i = 0; i 106: ostream& operator<< (ostream& output, const Array& theArray) 107: { 108: for (int i = 0; i::Array(int AnimalArraySize): 115: itsSize(AnimalArraySize) 116: { 117: pType = new Animal[AnimalArraySize]; 118: } 119: 120: 121: void IntFillFunction(Array& theArray); 122: void AnimalFillFunction(Array& theArray); 123: enum BOOL {FALSE, TRUE}; 124: 125: int main() 126: { 127: Array intArray; 128: Array animalArray; 129: IntFillFunction(intArray); 130: AnimalFillFunction(animalArray); 131: cout << "intArray...\n" << intArray; 132: cout << "\nanimalArray...\n" << animalArray << endl; 133: return 0; 134: } 135: 136: void IntFillFunction(Array& theArray) 137: { 138: BOOL Stop = FALSE; 139: int offset, value; 140: while (!Stop) 141: { 142: cout << "Enter an offset (0-9) and a value. "; 143: cout << "(-1 to stop): " ; 143: cin >> offset >> value;144: if (offset < 0) 145: break; 146: if (offset > 9) 147: { 148: cout << "***Please use values between 0 and 9.***\n"; 149: continue; 150: } 151: theArray[offset] = value; 152: } 153: } 154: 155: 156: void AnimalFillFunction(Array& theArray) 157: { 158: Animal * pAnimal; 159: for (int i = 0; i>> 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: animal(int) Destroyed an animal(int) Destroyed an animal(int) Destroyed an Enter an offset (0-9) and Enter an offset (0-9) and Enter an offset (0-9) and Enter an offset (0-9) and animal(int) Destroyed an animal... animal(int) Destroyed an animal... animal(int) Destroyed an animal... initArray... [0] 0 [1] 1 [2] 2 animal... animal... animal... a value. (-1 a value. (-1 a value. (-1 a value. (-1 to to to to stop): stop): stop): stop): 0 1 2 3 0 1 2 3 animal array... [0] 0 [1] 10 [2] 20 Destroyed an animal... Destroyed an animal... Destroyed an animal... Analysis: Listing 19.6 reproduces both classes in their entirety, so that you can see the creation and destruction of temporary Animal objects. The value of DefaultSize has been reduced to 3 to simplify the output. The Animal constructors and destructors on lines 33-48 each print a statement indicating when they are called. On lines 74-81, the template behavior of an Array constructor is declared. On lines 114-118, the specialized constructor for an Array of Animals is demonstrated. Note that in this special constructor, the default constructor is allowed to set the initial value for each Animal, and no explicit assignment is done. The first time this program is run, the first set of output is shown. Line 1 of the output shows the three default constructors called by creating the array. The user enters four numbers, and these are entered into the integer array.Execution jumps to AnimalFillFunction(). Here a temporary Animal object is created on the heap on line 161, and its value is used to modify the Animal object in the array on line 162. On line 163, the temporary Animal is destroyed. This is repeated for each member of the array and is reflected in the output on line 6. At the end of the program, the arrays are destroyed, and when their destructors are called, all their objects are destroyed as well. This is reflected in the output on line 16. For the second set of output (lines 18-43), the special implementation of the array of character constructor, shown on lines 114-118 of the program, is commented out. When the program is run again, the template constructor, shown on lines 74-81 of the program, is run when the Animal array is constructed. This causes temporary Animal objects to be called for each member of the array on lines 79 and 80 of the program, and is reflected in the output on lines 18 to 20 of the output. In all other respects, the output for the two runs is identical, as you would expect. Static Members and Templates A template can declare static data members. Each instantiation of the template then has its own set of static data, one per class type. That is, if you add a static member to the Array class (for example, a counter of how many arrays have been created), you will have one such member per type: one for all the arrays of Animals, and another for all the arrays of integers. Listing 19.7 adds a static member and a static function to the Array class. Listing 19.7. Using static member data and functions with templates. 1: #include 2: 3: template // declare the template and the parameter 4: class Array // the class being parameterized 5: { 6: public: 7: // constructors 8: Array(int itsSize = DefaultSize); 9: Array(const Array &rhs); 10: ~Array() { delete [] pType; itsNumberArrays--; } 11: 12: // operators 13: Array& operator=(const Array&); 14: T& operator[](int offSet) { return pType[offSet]; } 15: const T& operator[](int offSet) const16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: { return pType[offSet]; } // accessors int GetSize() const { return itsSize; } static int GetNumberArrays() { return itsNumberArrays; } // friend function friend ostream& operator<< (ostream&, const Array&); private: T *pType; int itsSize; static int itsNumberArrays; }; template int Array::itsNumberArrays = 0; template Array::Array(int size = DefaultSize): itsSize(size) { pType = new T[size]; for (int i = 0; i Array& Array::operator=(const Array &rhs) { if (this == &rhs) return *this; delete [] pType; itsSize = rhs.GetSize(); pType = new T[itsSize]; for (int i = 0; i Array::Array(const Array &rhs) { itsSize = rhs.GetSize(); pType = new T[itsSize]; for (int i = 0; i 67: ostream& operator<< (ostream& output, const Array& theArray) 68: { 69: for (int i = 0; i::Array(int AnimalArraySize): 76: itsSize(AnimalArraySize) 77: { 78: pType = new T[AnimalArraySize]; 79: itsNumberArrays++; 80: } 81: 82: int main() 83: { 84: 85: cout << Array::GetNumberArrays() << " integer arrays\n"; 86: cout << Array::GetNumberArrays(); 87 cout << " animal arrays\n\n"; 88: Array intArray; 89: Array animalArray; 90: 91: cout << intArray.GetNumberArrays() << " integer arrays\n"; 92: cout << animalArray.GetNumberArrays(); 93: cout << " animal arrays\n\n"; 93: 94: Array *pIntArray = new Array; 95: 96: cout << Array::GetNumberArrays() << " integer arrays\n"; 97: cout << Array::GetNumberArrays(); 98: cout << " animal arrays\n\n"; 98: 99: delete pIntArray; 100: 101: cout << Array::GetNumberArrays() << " integerarrays\n"; 102: cout << Array::GetNumberArrays(); 103: cout << " animal arrays\n\n"; 103: return 0; 104: } Output: 0 integer arrays 0 animal arrays 1 integer arrays 1 animal arrays 2 integer arrays 1 animal arrays 1 integer arrays 1 animal arrays Analysis: The declaration of the Animal class has been left out to save space. The Array class has added the static variable itsNumberArrays on line 27, and because this data is private, the static public accessor GetNumberArrays() was added on line 19. Initialization of the static data is accomplished with a full template qualification, as shown on lines 30 and 31. The constructors of Array and the destructor are each modified to keep track of how many arrays exist at any moment. Accessing the static members is exactly like accessing the static members of any class: You can do so with an existing object, as shown on lines 91 and 92, or by using the full class specification, as shown on lines 85 and 86. Note that you must use a specific type of array when accessing the static data. There is one variable for each type. DO use statics with templates as needed. DO specialize template behavior by overriding template functions by type. DO use the parameters to template functions to narrow their instances to be type-safe. The Standard Template Library A new development in C++ is the adoption of the Standard Template Library (STL). All the major compiler vendors now offer the STL as part of their compilers. STL is a library of template-based container classes, including vectors, lists, queues, and stacks. The STL also includes a number of common algorithms, including sorting and searching. The goal of the STL is to give you an alternative to reinventing the wheel for these common requirements. The STL is tested and debugged, offers high performance, and is free. Most important, the STL is reusable; once you understand how to use an STL container, you can use it in all yourprograms without reinventing it. Summary Today you learned how to create and use templates. Templates are a built-in facility of C++, used to create parameterized types--types that change their behavior based on parameters passed in at creation. They are a way to reuse code safely and effectively. The definition of the template determines the parameterized type. Each instance of the template is an actual object, which can be used like any other object--as a parameter to a function, as a return value, and so forth. Template classes can declare three types of friend functions: non-template, general template, and type- specific template. A template can declare static data members, in which case each instance of the template has its own set of static data. If you need to specialize behavior for some template functions based on the actual type, you can override a template function with a particular type. This works for member functions as well. Q&A Q. Why use templates when macros will do? A. Templates are type-safe and built into the language. Q. What is the difference between the parameterized type of a template function and the parameters to a normal function? A. A regular function (non-template) takes parameters on which it may take action. A template function allows you to parameterize the type of a particular parameter to the function. That is, you can pass an Array of Type to a function, and then have the Type determined by the template instance. Q. When do you use templates and when do you use inheritance? A. Use templates when all the behavior, or virtually all the behavior, is unchanged, except in regard to the type of the item on which your class acts. If you find yourself copying a class and changing only the type of one or more of its members, it may be time to consider using a template. Q. When do you use general template friend classes? A. When every instance, regardless of type, should be a friend to this class or function. Q. When do you use type-specific template friend classes or functions?A. When you want to establish a one-to-one relationship between two classes. For example, array should match iterator, but not iterator. Workshop The Workshop provides quiz questions to help you solidify your understanding of the material covered, and exercises to provide you with experience in using what you've learned. Try to answer the quiz and exercise questions before checking the answers in Appendix D, and make sure you understand the answers before continuing to the next chapter. Quiz 1. What is the difference between a template and a macro? 2. What is the difference between the parameter in a template and the parameter in a function? 3. What is the difference between a type-specific template friend class and a general template friend class? 4. Is it possible to provide special behavior for one instance of a template but not for other instances? 5. How many static variables are created if you put one static member into a template class definition? Exercises 1. Create a template based on this List class: class List { private: public: List():head(0),tail(0),theCount(0) {} virtual ~List(); void insert( int value ); void append( int value ); int is_present( int value ) const; int is_empty() const { return head == 0; } int count() const { return theCount; } private: class ListCell { public: ListCell(int value, ListCell *cell =):val(value),next(cell){} int val; ListCell *next; }; ListCell *head; ListCell *tail; int theCount; }; 2. Write the implementation for the List class (non-template) version. 3. Write the template version of the implementations. 4. Declare three list objects: a list of Strings, a list of Cats, and a list of ints. 5. BUG BUSTERS: What is wrong with the following code? (Assume the List template is defined and Cat is the class defined earlier in the book.) List Cat_List; Cat Felix; CatList.append( Felix ); cout << "Felix is " << ( Cat_List.is_present( Felix ) ) ? "" : "not " << "present\n"; HINT (this is tough): What makes Cat different from int? 6. Declare friend operator== for List. 7. Implement friend operator== for List. 8. Does operator== have the same problem as in Exercise 5? 9. Implement a template function for swap, which exchanges two variables. ',. What a debugger is. Bugs, Errors, Mistakes, and Code Rot All programs have bugs. The bigger the program, the more bugs, and many of those bugs actually "get out the door" and into final, released software. That this is true does not make it okay, and making robust, bug-free programs is the number-one priority of anyone serious about programming. The single biggest problem in the software industry is buggy, unstable code. The biggest expense in many major programming efforts is testing and fixing. The person who solves the problem of producing good, solid, bulletproof programs at low cost and on time will revolutionize the software industry. There are a number of discrete kinds of bugs that can trouble a program. The first is poor logic: The program does just what you asked, but you haven't thought through the algorithms properly. The second is syntactic: You used the wrong idiom, function, or structure. These two are the most common, and they are the ones most programmers are on the lookout for. Research and real-world experience have shown beyond a doubt that the later in the development process you find a problem, the more it costs to fix it. The least expensive problems or bugs to fix are the ones you manage to avoid creating. The next cheapest are those the compiler spots. The C++ standards force compilers to put a lot of energy into making more and more bugs show up at compile time. Bugs that get compiled in but are caught at the first test--those that crash every time--are less expensive to find and fix than those that are flaky and only crash once in a while.A bigger problem than logic or syntactic bugs is unnecessary fragility: Your program works just fine if the user enters a number when you ask for one, but it crashes if the user enters letters. Other programs crash if they run out of memory, or if the floppy disk is left out of the drive, or if the modem drops the line. To combat this kind of fragility, programmers strive to make their programs bulletproof. A bulletproof program is one that can handle anything that comes up at runtime, from bizarre user input to running out of memory. It is important to distinguish between bugs, which arise because the programmer made a mistake in syntax; logic errors, which arise because the programmer misunderstood the problem or how to solve it; and exceptions, which arise because of unusual but predictable problems such as running out of resources (memory or disk space). Exceptions Programmers use powerful compilers and sprinkle their code with asserts, as discussed on Day 17, "The Preprocessor," to catch programming errors. They use design reviews and exhaustive testing to find logic errors. Exceptions are different, however. You can't eliminate exceptional circumstances; you can only prepare for them. Your users will run out of memory from time to time, and the only question is what you will do. Your choices are limited to these: !#- Crash the program. /!, Inform the user and exit gracefully. %+* Inform the user and allow the user to try to recover and continue. %," Take corrective action and continue without disturbing the user. While it is not necessary or even desirable for every program you write to automatically and silently recover from all exceptional circumstances, it is clear that you must do better than crashing. C++ exception handling provides a type-safe, integrated method for coping with the predictable but unusual conditions that arise while running a program. A Word About Code Rot Code rot is a well-proven phenomenon. Code rot is when code deteriorates due to being neglected. Perfectly well-written, fully debugged code will develop new and bizarre behavior six months after you release it, and there isn't much you can do to stop it. What you can do, of course, is write your programs so that when you go back to fix the spoilage, you can quickly and easily identify where theproblems are. NOTE: Code rot is somewhat of a programmer's joke used to explain how bug-free code suddenly becomes unreliable. It does, however, teach an important lesson. Programs are enormously complex, and bugs, errors, and mistakes can hide for a long time before turning up. Protect yourself by writing easy-to-maintain code. This means that your code must be commented even if you don't expect anyone else to ever look at it. Six months after you deliver your code, you will read it with the eyes of a total stranger, bewildered by how anyone could ever have written such convoluted and twisty code and expected anything but disaster. Exceptions In C++, an exception is an object that is passed from the area of code where a problem occurs to the part of the code that is going to handle the problem. The type of the exception determines which area of code will handle the problem, and the contents of the object thrown, if any, may be used to provide feedback to the user. The basic idea behind exceptions is fairly straightforward: &$( /)' )+" The actual allocation of resources (for example, the allocation of memory or the locking of a file) is usually done at a very low level in the program. The logic of what to do when an operation fails, memory cannot be allocated, or a file cannot be locked is usually high in the program, with the code for interacting with the user. Exceptions provide an express path from the code that allocates resources to the code that can handle the error condition. If there are intervening layers of functions, they are given an opportunity to clean up memory allocations, but are not required to include code whose only purpose is to pass along the error condition. How Exceptions Are Used try blocks are created to surround areas of code that may have a problem. For example: try { SomeDangerousFunction(); } catch blocks handle the exceptions thrown in the try block. For example:try { SomeDangerousFunction(); } catch(OutOfMemory) { // take some actions } catch(FileNotFound) { // take other action } The basic steps in using exceptions are 1. Identify those areas of the program in which you begin an operation that might raise an exception, and put them in try blocks. 2. Create catch blocks to catch the exceptions if they are thrown, to clean up allocated memory, and to inform the user as appropriate. Listing 20.1 illustrates the use of both try blocks and catch blocks. New Term: Exceptions are objects used to transmit information about a problem. New Term: A try block is a block surrounded by braces in which an exception may be thrown. New Term: A catch block is the block immediately following a try block, in which exceptions are handled. When an exception is thrown (or raised), control transfers to the catch block immediately following the current try block. NOTE: Some older compilers do not support exceptions. Exceptions are, however, part of the emerging C++ standard. All major compiler vendors have committed to supporting exceptions in their next releases, if they have not already done so. If you have an older compiler, you won't be able to compile and run the exercises in this chapter. It's still a good idea to read through the entire chapter, however, and return tothis material when you upgrade your compiler. Listing 20.1. Raising an exception. 0: 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: #include const int DefaultSize = 10; class Array { public: // constructors Array(int itsSize = DefaultSize); Array(const Array &rhs); ~Array() { delete [] pType;} // operators Array& operator=(const Array&); int& operator[](int offSet); const int& operator[](int offSet) const; // accessors int GetitsSize() const { return itsSize; } // friend function friend ostream& operator<< (ostream&, const Array&); class xBoundary {}; private: int *pType; int itsSize; }; // define the exception class Array::Array(int size): itsSize(size) { pType = new int[size]; for (int i = 0; i= 0 && offSet < GetitsSize()) return pType[offSet]; throw xBoundary(); return pType[0]; // appease MSC } const int& Array::operator[](int offSet) const { int mysize = GetitsSize(); if (offSet >= 0 && offSet < GetitsSize()) return pType[offSet]; throw xBoundary(); return pType[0]; // appease MSC } ostream& operator<< (ostream& output, const Array& { for (int i = 0; i>(), all these are on the call stack. A recursive function might be on the call stack many times. The exception is passed up the call stack to each enclosing block. As the stack is unwound, the destructors for local objects on the stack are invoked, and the objects are destroyed. After each try block there is one or more catch statements. If the exception matches one of the catch statements, it is considered to be handled by having that statement execute. If it doesn't match any, the unwinding of the stack continues. If the exception reaches all the way to the beginning of the program (main()) and is still not caught, a built-in handler is called that terminates the program. It is important to note that the exception unwinding of the stack is a one-way street. As it progresses,the stack is unwound and objects on the stack are destroyed. There is no going back: Once the exception is handled, the program continues after the try block of the catch statement that handled the exception. Thus, in Listing 20.1, execution will continue on line 101, the first line after the try block of the catch statement that handled the xBoundary exception. Remember that when an exception is raised, program flow continues after the catch block, not after the point where the exception was thrown. More Than One catch Specification It is possible for more than one condition to cause an exception. In this case, the catch statements can be lined up one after another, much like the conditions in a switch statement. The equivalent to the default statement is the "catch everything" statement, indicated by catch(...). Listing 20.2 illustrates multiple exception conditions. Listing 20.2. Multiple exceptions. 0: 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: #include const int DefaultSize = 10; class Array { public: // constructors Array(int itsSize = DefaultSize); Array(const Array &rhs); ~Array() { delete [] pType;} // operators Array& operator=(const Array&); int& operator[](int offSet); const int& operator[](int offSet) const; // accessors int GetitsSize() const { return itsSize; } // friend function friend ostream& operator<< (ostream&, const Array&); // define the exception classes class xBoundary {}; class xTooBig {}; class xTooSmall{};27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: class xZero {}; class xNegative {}; private: int *pType; int itsSize; }; int& Array::operator[](int offSet) { int size = GetitsSize(); if (offSet >= 0 && offSet < GetitsSize()) return pType[offSet]; throw xBoundary(); return pType[0]; // appease MFC } const int& Array::operator[](int offSet) const { int mysize = GetitsSize(); if (offSet >= 0 && offSet < GetitsSize()) return pType[offSet]; throw xBoundary(); return pType[0]; return pType[0]; // appease MFC } Array::Array(int size): itsSize(size) { if (size == 0) throw xZero(); if (size < 10) throw xTooSmall(); if (size > 30000) throw xTooBig(); if (size < 1) throw xNegative(); pType = new int[size]; for (int i = 0; i const int DefaultSize = 10; class Array { public: // constructors Array(int itsSize = DefaultSize); Array(const Array &rhs); ~Array() { delete [] pType;} // operators Array& operator=(const Array&); int& operator[](int offSet); const int& operator[](int offSet) const; // accessors int GetitsSize() const { return itsSize; } // friend function friend ostream& operator<< (ostream&, const Array&); // define the exception classes class xBoundary {}; class xSize {}; class xTooBig : public xSize {}; class xTooSmall : public xSize {}; class xZero : public xTooSmall {}; class xNegative : public xSize {};30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: private: int *pType; int itsSize; }; Array::Array(int size): itsSize(size) { if (size == 0) throw xZero(); if (size > 30000) throw xTooBig(); if (size <1) throw xNegative(); if (size < 10) throw xTooSmall(); pType = new int[size]; for (int i = 0; i= 0 && offSet < GetitsSize()) return pType[offSet]; throw xBoundary(); return pType[0]; // appease MFC } const int& Array::operator[](int offSet) const { int mysize = GetitsSize(); if (offSet >= 0 && offSet < GetitsSize()) return pType[offSet]; throw xBoundary(); return pType[0]; return pType[0]; // appease MFC } int main() {76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: } try { Array intArray(5); for (int j = 0; j< 100; j++) { intArray[j] = j; cout << "intArray[" << j << "] okay...\n"; } } catch (Array::xBoundary) { cout << "Unable to process your input!\n"; } catch (Array::xTooBig) { cout << "This array is too big...\n"; } catch (Array::xZero) { cout << "You asked for an array"; cout << " of zero objects!\n"; } catch (Array::xTooSmall) { cout << "This array is too small...\n"; } catch (...) { cout << "Something went wrong!\n"; } cout << "Done.\n"; return 0 Output: This array is too small... Done. Analysis: The significant change is on lines 26-29, where the class hierarchy is established. Classes xTooBig, xTooSmall, and xNegative are derived from xSize, and xZero is derived from xTooSmall. The Array is created with size zero, but what's this? The wrong exception appears to be caught! Examine the catch block carefully, however, and you will find that it looks for an exception of typexTooSmall before it looks for an exception of type xZero. Because an xZero object is thrown and an xZero object is an xTooSmall object, it is caught by the handler for xTooSmall. Once handled, the exception is not passed on to the other handlers, so the handler for xZero is never called. The solution to this problem is to carefully order the handlers so that the most specific handlers come first and the less specific handlers come later. In this particular example, switching the placement of the two handlers xZero and xTooSmall will fix the problem. Data in Exceptions and Naming Exception Objects Often you will want to know more than just what type of exception was thrown so you can respond properly to the error. Exception classes are like any other class. You are free to provide data, initialize that data in the constructor, and read that data at any time. Listing 20.4 illustrates how to do this. Listing 20.4. Getting data out of an exception object. 0: 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: #include const int DefaultSize = 10; class Array { public: // constructors Array(int itsSize = DefaultSize); Array(const Array &rhs); ~Array() { delete [] pType;} // operators Array& operator=(const Array&); int& operator[](int offSet); const int& operator[](int offSet) const; // accessors int GetitsSize() const { return itsSize; } // friend function friend ostream& operator<< (ostream&, const Array&); // define the exception classes class xBoundary {}; class xSize { public:28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: xSize(int size):itsSize(size) {} ~xSize(){} int GetSize() { return itsSize; } private: int itsSize; }; class xTooBig : public xSize { public: xTooBig(int size):xSize(size){} }; class xTooSmall : public xSize { public: xTooSmall(int size):xSize(size){} }; class xZero : public xTooSmall { public: xZero(int size):xTooSmall(size){} }; class xNegative : public xSize { public: xNegative(int size):xSize(size){} }; private: int *pType; int itsSize; }; Array::Array(int size): itsSize(size) { if (size == 0) throw xZero(size); if (size > 30000) throw xTooBig(size); if (size <1) throw xNegative(size);74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: endl; 111: 112: 113: 114: 115: 116: 117: 118: if (size < 10) throw xTooSmall(size); pType = new int[size]; for (int i = 0; i= 0 && offSet < GetitsSize()) return pType[offSet]; throw xBoundary(); return pType[0]; } const int& Array::operator[] (int offSet) const { int size = GetitsSize(); if (offSet >= 0 && offSet < GetitsSize()) return pType[offSet]; throw xBoundary(); return pType[0]; } int main() { try { Array intArray(9); for (int j = 0; j< 100; j++) { intArray[j] = j; cout << "intArray[" << j << "] okay..." << } } catch (Array::xBoundary) { cout << "Unable to process your input!\n"; } catch (Array::xZero theException) {119: << endl; 120: endl; 121: 122: 123: 124: 125: endl; 126: 127: 128: 129: 130: endl; 131: 132: 133: 134: what!\n"; 135: 136: 137: 138: } cout << "You asked for an Array of zero objects!" cout << "Received " << theException.GetSize() << } catch (Array::xTooBig theException) { cout << "This Array is too big..." << endl; cout << "Received " << theException.GetSize() << } catch (Array::xTooSmall theException) { cout << "This Array is too small..." << endl; cout << "Received " << theException.GetSize() << } catch (...) { cout << "Something went wrong, but I've no idea } cout << "Done.\n"; return 0; Output: This array is too small... Received 9 Done. Analysis: The declaration of xSize has been modified to include a member variable, itsSize, on line 32 and a member function, GetSize(), on line 30. Additionally, a constructor has been added that takes an integer and initializes the member variable, as shown on line 28. The derived classes declare a constructor that does nothing but initialize the base class. No other functions were declared, in part to save space in the listing. The catch statements on lines 113 to 135 are modified to name the exception they catch, theException, and to use this object to access the data stored in itsSize. NOTE: Keep in mind that if you are constructing an exception, it is because an exception has been raised: Something has gone wrong, and your exception should be careful not to kick off the same problem. Therefore, if you are creating an OutOfMemory exception, you probably don't want to allocate memory in its constructor.It is tedious and error-prone to have each of these catch statements individually print the appropriate message. This job belongs to the object, which knows what type of object it is and what value it received. Listing 20.5 takes a more object-oriented approach to this problem, using virtual functions so that each exception "does the right thing." Listing 20.5.Passing by reference and using virtual functions in exceptions. 0: 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: #include const int DefaultSize = 10; class Array { public: // constructors Array(int itsSize = DefaultSize); Array(const Array &rhs); ~Array() { delete [] pType;} // operators Array& operator=(const Array&); int& operator[](int offSet); const int& operator[](int offSet) const; // accessors int GetitsSize() const { return itsSize; } // friend function friend ostream& operator<< (ostream&, const Array&); // define the exception classes class xBoundary {}; class xSize { public: xSize(int size):itsSize(size) {} ~xSize(){} virtual int GetSize() { return itsSize; } virtual void PrintError() { cout << "Size error. Received: "; cout << itsSize << endl; } protected:38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: int itsSize; }; class xTooBig : public xSize { public: xTooBig(int size):xSize(size){} virtual void PrintError() { cout << "Too big! Received: "; cout << xSize::itsSize << endl; } }; class xTooSmall : public xSize { public: xTooSmall(int size):xSize(size){} virtual void PrintError() { cout << "Too small! Received: "; cout << xSize::itsSize << endl; } }; class xZero : public xTooSmall { public: xZero(int size):xTooSmall(size){} virtual void PrintError() { cout << "Zero!!. Received: " ; cout << xSize::itsSize << endl; } }; class xNegative : public xSize { public: xNegative(int size):xSize(size){} virtual void PrintError() { cout << "Negative! Received: "; cout << xSize::itsSize << endl; } };84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: private: int *pType; int itsSize; }; Array::Array(int size): itsSize(size) { if (size == 0) throw xZero(size); if (size > 30000) throw xTooBig(size); if (size <1) throw xNegative(size); if (size < 10) throw xTooSmall(size); pType = new int[size]; for (int i = 0; i= 0 && offSet < GetitsSize()) return pType[offSet]; throw xBoundary(); return pType[0]; } const int& Array::operator[] (int offSet) const { int size = GetitsSize(); if (offSet >= 0 && offSet < GetitsSize()) return pType[offSet]; throw xBoundary(); return pType[0]; } int main() { try {130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: } Array intArray(9); for (int j = 0; j< 100; j++) { intArray[j] = j; cout << "intArray[" << j << "] okay...\n"; } } catch (Array::xBoundary) { cout << "Unable to process your input!\n"; } catch (Array::xSize& theException) { theException.PrintError(); } catch (...) { cout << "Something went wrong!\n"; } cout << "Done.\n"; return 0; Output: Too small! Received: 9 Done. Analysis: Listing 20.5 declares a virtual method in the xSize class, PrintError(), that prints an error message and the actual size of the class. This is overridden in each of the derived classes. On line 141, the exception object is declared to be a reference. When PrintError() is called with a reference to an object, polymorphism causes the correct version of PrintError() to be invoked. The code is cleaner, easier to understand, and easier to maintain. Exceptions and Templates When creating exceptions to work with templates, you have a choice: you can create an exception for each instance of the template, or you can use exception classes declared outside the template declaration. Listing 20.6 illustrates both approaches. Listing 20.6. Using exceptions with templates. 0: 1: 2: 3: #include const int DefaultSize = 10; class xBoundary {};4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: template class Array { public: // constructors Array(int itsSize = DefaultSize); Array(const Array &rhs); ~Array() { delete [] pType;} // operators Array& operator=(const Array&); T& operator[](int offSet); const T& operator[](int offSet) const; // accessors int GetitsSize() const { return itsSize; } // friend function friend ostream& operator<< (ostream&, const Array&); // define the exception classes class xSize {}; private: int *pType; int itsSize; }; template Array::Array(int size): itsSize(size) { if (size <10 || size > 30000) throw xSize(); pType = new T[size]; for (int i = 0; i Array& Array::operator=(const Array &rhs) { if (this == &rhs) return *this;50: delete [] pType; 51: itsSize = rhs.GetitsSize(); 52: pType = new T[itsSize]; 53: for (int i = 0; i 57: Array::Array(const Array &rhs) 58: { 59: itsSize = rhs.GetitsSize(); 60: pType = new T[itsSize]; 61: for (int i = 0; i 66: T& Array::operator[](int offSet) 67: { 68: int size = GetitsSize(); 69: if (offSet >= 0 && offSet < GetitsSize()) 70: return pType[offSet]; 71: throw xBoundary(); 72: return pType[0]; 73: } 74: 75: template 76: const T& Array::operator[](int offSet) const 77: { 78: int mysize = GetitsSize(); 79: if (offSet >= 0 && offSet < GetitsSize()) 80: return pType[offSet]; 81: throw xBoundary(); 82: } 83: 84: template 85: ostream& operator<< (ostream& output, const Array& theArray) 86: { 87: for (int i = 0; i intArray(9); for (int j = 0; j< 100; j++) { intArray[j] = j; cout << "intArray[" << j << "] okay..." << endl; } } catch (xBoundary) { cout << "Unable to process your input!\n"; } catch (Array::xSize) { cout << "Bad Size!\n"; } cout << "Done.\n"; return 0; Output: Bad Size! Done. Analysis: The first exception, xBoundary, is declared outside the template definition on line 3. The second exception, xSize, is declared from within the definition of the template, on line 27. The exception xBoundary is not tied to the template class, but can be used like any other class. xSize is tied to the template, and must be called based on the instantiated Array. You can see the difference in the syntax for the two catch statements. Line 105 shows catch (xBoundary), but line 109 shows catch (Array::xSize). The latter is tied to the instantiation of an integer Array. Exceptions Without Errors When C++ programmers get together for a virtual beer in the cyberspace bar after work, talk often turns to whether exceptions should be used for routine conditions. Some maintain that by their nature, exceptions should be reserved for those predictable but exceptional circumstances (hence the name!) that a programmer must anticipate, but that are not part of the routine processing of the code. Others point out that exceptions offer a powerful and clean way to return through many layers of function calls without danger of memory leaks. A frequent example is this: The user requests an action in a GUI environment. The part of the code that catches the request must call a member function on a dialog manager, which in turn calls code that processes the request, which calls codethat decides which dialog box to use, which in turn calls code to put up the dialog box, which finally calls code that processes the user's input. If the user presses Cancel, the code must return to the very first calling method, where the original request was handled. One approach to this problem is to put a try block around the original call and catch CancelDialog as an exception, which can be raised by the handler for the Cancel button. This is safe and effective, but pressing Cancel is a routine circumstance, not an exceptional one. This frequently becomes something of a religious argument, but there is a reasonable way to decide the question: Does use of exceptions in this way make the code easier or harder to understand? Are there fewer risks of errors and memory leaks, or more? Will it be harder or easier to maintain this code? These decisions, like so many others, will require an analysis of the trade-offs; there is no single, obvious right answer. Bugs and Debugging You saw on Day 17 how to use assert() to trap runtime bugs during the testing phase, and today you saw how to use exceptions to trap runtime problems. There is one more powerful weapon you'll want to add to your arsenal as you attack bugs: the debugger. Nearly all modern development environments include one or more high-powered debuggers. The essential idea of using a debugger is this: You run the debugger, which loads your source code, and then you run your program from within the debugger. This allows you to see each instruction in your program as it executes, and to examine your variables as they change during the life of your program. All compilers will let you compile with or without symbols. Compiling with symbols tells the compiler to create the necessary mapping between your source code and the generated program; the debugger uses this to point to the line of source code that corresponds to the next action in the program. Full-screen symbolic debuggers make this chore a delight. When you load your debugger, it will read through all your source code and show the code in a window. You can step over function calls or direct the debugger to step into the function, line by line. With most debuggers, you can switch between the source code and the output to see the results of each executed statement. More powerfully, you can examine the current state of each variable, look at complex data structures, examine the value of member data within classes, and look at the actual values in memory of various pointers and other memory locations. You can execute several types of control within a debugger that include setting breakpoints, setting watch points, examining memory, and looking at the assembler code. Breakpoints Breakpoints are instructions to the debugger that when a particular line of code is ready to beexecuted, the program should stop. This allows you to run your program unimpeded until the line in question is reached. Breakpoints help you analyze the current condition of variables just before and after a critical line of code. Watch Points It is possible to tell the debugger to show you the value of a particular variable or to break when a particular variable is read or written to. Watch points allow you to set these conditions, and at times even to modify the value of a variable while the program is running. Examining Memory At times it is important to see the actual values held in memory. Modern debuggers can show values in the form of the actual variable; that is, strings can be shown as characters, longs as numbers rather than as four bytes, and so forth. Sophisticated C++ debuggers can even show complete classes, providing the current value of all the member variables, including the this pointer. Assembler Although reading through the source can be all that is required to find a bug, when all else fails it is possible to instruct the debugger to show you the actual assembly code generated for each line of your source code. You can examine the memory registers and flags, and generally delve as deep into the inner workings of your program as required. Learn to use your debugger. It can be the most powerful weapon in your holy war against bugs. Runtime bugs are the hardest to find and squash, and a powerful debugger can make it possible, if not easy, to find nearly all of them. Summary Today you learned how to create and use exceptions. Exceptions are objects that can be created and thrown at points in the program where the executing code cannot handle the error or other exceptional condition that has arisen. Other parts of the program, higher in the call stack, implement catch blocks that catch the exception and take appropriate action. Exceptions are normal, user-created objects, and as such may be passed by value or by reference. They may contain data and methods, and the catch block may use that data to decide how to deal with the exception. It is possible to create multiple catch blocks, but once an exception matches a catch block's signature, it is considered to be handled and is not given to the subsequent catch blocks. It is important to order the catch blocks appropriately, so that more specific catch blocks have first chance and more general catch blocks handle those not otherwise handled.This chapter also examined some of the fundamentals of symbolic debuggers, including using watch points, breakpoints, and so forth. These tools can help you zero in on the part of your program that is causing the error, and let you see the value of variables as they change during the course of the execution of the program. Q&A Q. Why bother with raising exceptions? Why not handle the error right where it happens? A. Often, the same error can be generated in a number of different parts of the code. Exceptions let you centralize the handling of errors. Additionally, the part of the code that generates the error may not be the best place to determine how to handle the error. Q. Why generate an object? Why not just pass an error code? A. Objects are more flexible and powerful than error codes. They can convey more information, and the constructor/destructor mechanisms can be used for the creation and removal of resources that may be required to properly handle the exceptional condition. Q. Why not use exceptions for non-error conditions? Isn't it convenient to be able to express-train back to previous areas of the code, even when non-exceptional conditions exist? A. Yes, some C++ programmers use exceptions for just that purpose. The danger is that exceptions might create memory leaks as the stack is unwound and some objects are inadvertently left in the free store. With careful programming techniques and a good compiler, this can usually be avoided. Otherwise, it is a matter of personal aesthetic; some programmers feel that by their nature exceptions should not be used for routine conditions. Q. Does an exception have to be caught in the same place where the try block created the exception? A. No, it is possible to catch an exception anywhere in the call stack. As the stack is unwound, the exception is passed up the stack until it is handled. Q. Why use a debugger when you can use cout with conditional (#ifdef debug) compiling? A. The debugger provides a much more powerful mechanism for stepping through your code and watching values change without having to clutter your code with thousands of debugging statements. Workshop The Workshop contains quiz questions to help solidify your understanding of the material covered and exercises to provide you with experience in using what you've learned. Try to answer the quiz andexercise questions before checking the answers in Appendix D, and make sure you understand the answers before going to the next chapter. Quiz 1. What is an exception? 2. What is a try block? 3. What is a catch statement? 4. What information can an exception contain? 5. When are exception objects created? 6. Should you pass exceptions by value or by reference? 7. Will a catch statement catch a derived exception if it is looking for the base class? 8. If there are two catch statements, one for base and one for derived, which should come first? 9. What does catch(...) mean? 10. What is a breakpoint? Exercises 1. Create a try block, a catch statement, and a simple exception. 2. Modify the answer from Exercise 1, put data into the exception, along with an accessor function, and use it in the catch block. 3. Modify the class from Exercise 2 to be a hierarchy of exceptions. Modify the catch block to use the derived objects and the base objects. 4. Modify the program from Exercise 3 to have three levels of function calls. 5. BUG BUSTERS: What is wrong with the following code? class xOutOfMemory { public: xOutOfMemory( const String& message ) : itsMsg( message ){} ~xOutOfMemory(){} virtual const String& Message(){ return itsMsg}; private: String itsMsg;} main() { try { char *var = new char; if ( var == 0 ) throw xOutOfMemory(); } catch( xOutOfMemory& theException ) { cout << theException.Message() << "\n"; } } Whats Next Congratulations! You are nearly done with a full three-week intensive introduction to C++. By now you should have a solid understanding of C++, but in modern programming there is always more to learn. This chapter will fill in some missing details and then set the course for continued study. Today you will learn Every implementation of C++ includes the standard libraries, and most include additional libraries as well. Libraries are sets of functions that can be linked into your code. You've already used a number of standard library functions and classes, most notably from the iostreams library. To use a library, you typically include a header file in your source code, much as you did by writing #include in many of the examples in this book. The angle brackets around the filename are a signal to the compiler to look in the directory where you keep the header files for your compiler's standard libraries.There are dozens of libraries, covering everything from file manipulation to setting the date and time to math functions. Today I will review just a few of the most popular functions and classes in the standard library that have not yet been covered in this book. String The most popular library is almost certainly the string library, with perhaps the function strlen() called most often. strlen() returns the length of a null-terminated string. Listing 21.1 illustrates its use. Listing 21.1. strlen(). 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: } #include #include int main() { char buffer80]; do { cout << "Enter a string up to 80 characters: "; cin.getline(buffer,80); cout << "Your string is " << strlen(buffer); cout << " characters long." << endl; } while (strlen(buffer)); cout << "\nDone." << endl; return 0; Output: Enter a string up to 80 characters: This sentence has 31 characters Your string is 31 characters long. Enter a string up to 80 characters: This sentence no verb Your string is 21 characters long. Enter a string up to 80 characters: Your string is 0 characters long. Done. Analysis: On line 6, a character buffer is created, and on line 9 the user is prompted to enter a string. As long as the user enters a string, the length of the string is reported on line 11. Note the test in the do...while() statement: while (strlen(buffer)). Since strlen() will return 0 when the buffer is empty, and since 0 evaluates FALSE, this while loop will continue as long as there are any characters in the buffer.strcpy() and strncpy() The second most popular function in string.h probably was strcpy(), which copied one string to another. This may now be diminished somewhat as C-style null-terminated strings have become less important in C++; typically, string manipulation is done from within a vendor-supplied or user- written string class. Nonetheless, your string class must support an assignment operator and a copy constructor, and often these are implemented using strcpy(), as illustrated in Listing 21.2. Listing 21.2. Using strcpy. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: } #include #include int main() { char stringOne80]; char stringTwo80]; stringOne0]='\0'; stringTwo0]='\0'; cout << "String One: " << stringOne << endl; cout << "String Two: " << stringTwo << endl; cout << "Enter a string: "; cin.getline(stringOne,80); cout << "\nString One: " << stringOne << endl; cout << "String Two: " << stringTwo << endl; cout << "copying..." << endl; strcpy(stringTwo,stringOne); cout cout cout return << "\nString One: " << stringOne << endl; << "String Two: " << stringTwo << endl; << "\nDone " << endl; 0; Output: String One: String Two: Enter a string: Test of strcpy() String One: String Two: Test of strcpy()copying... String One: String Two: Test of strcpy() Test of strcpy() Done Analysis: Two C-style null-terminated strings are declared on lines 6 and 7. They are initialized to empty on lines 9 and 10, and their values are printed on lines 12 and 13. The user is prompted to enter a string, and the result is put in stringOne; the two strings are printed again, and only stringOne has the input. Strcpy() is then called, and stringOne is copied into stringTwo. Note that the syntax of strcpy() can be read as "copy into the first parameter the string in the second parameter." What happens if the target string (stringTwo) is too small to hold the copied string? This problem and its solution are illustrated in Listing 21.3. Listing 21.3. Using strncpy(). 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: #include #include int main() { char stringOne[80]; char stringTwo[10]; char stringThree[80]; stringOne[0]='\0'; stringTwo[0]='\0'; stringThree[0]='\0'; cout << "String One: " << stringOne << endl; cout << "String Two: " << stringTwo << endl; cout << "String Three: " << stringThree << endl; cout << "Enter a long string: "; cin.getline(stringOne,80); strcpy(stringThree,stringOne); // strcpy(stringTwo,stringOne); cout << "\nString One: " << stringOne << endl; cout << "String Two: " << stringTwo << endl; cout << "String Three: " << stringThree << endl; strncpy(stringTwo,stringOne,9);29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: } cout << "\nString One: " << stringOne << endl; cout << "String Two: " << stringTwo << endl; cout << "String Three: " << stringThree << endl; stringTwo[9]='\0'; cout cout cout cout return << << << << 0; "\nString One: " << stringOne << endl; "String Two: " << stringTwo << endl; "String Three: " << stringThree << endl; "\nDone." << endl; Output: String One: String Two: String Three: Enter a long string: Now is the time for all... String One: Now is the time for all... String Two: String Three: Now is the time for all... String One: Now is the time for all... String Two: Now is th_+|| String Three: Now is the time for all... String One: Now is the time for all... String Two: Now is th String Three: Now is the time for all... Done. Analysis: On lines 6, 7, and 8, three string buffers are declared. Note that stringTwo is declared to be only 10 characters, while the others are 80. All three are initialized to zero length on lines 10 to 12 and are printed on lines 14 to 16. The user is prompted to enter a string, and that string is copied to string three on line 20. Line 21 is commented out; copying this long string to stringTwo caused a crash on my computer because it wrote into memory that was critical to the program. The standard function strcpy() starts copying at the address pointed to by the first parameter (the array name), and it copies the entire string without ensuring that you've allocated room for it! The standard library offers a second, safer function, strncpy(), which copies only a specified number of characters to the target string. The n in the middle of the function name strncpy() stands for number. This is a convention used throughout the standard libraries.On line 27, the first nine characters of stringOne are copied to stringTwo and the result is printed. Because strncpy() does not put a null at the end of the copied string, the result is not what was intended. Note that strcpy() does null-terminate the copied string, but strncpy() does not, just to keep life interesting. The null is added on line 33, and the strings are then printed a final time. strcat() and strncat() Related to strcpy() and strncpy() are the standard functions strcat() and strncat(). The former concatenates one string to another; that is, it appends the string it takes as its second parameter to the end of the string it takes as its first parameter. strncat(), as you might expect, appends the first n characters of one string to the other. Listing 21.4 illustrates their use. Listing 21.4. Using strcat() and strncat(). 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: } #include #include int main() { char stringOne[255]; char stringTwo[255]; stringOne[0]='\0'; stringTwo[0]='\0'; cout << "Enter a string: "; cin.getline(stringOne,80); cout << "Enter a second string: "; cin.getline(stringTwo,80); cout << "String One: " << stringOne << endl; cout << "String Two: " << stringTwo << endl; strcat(stringOne," "); strncat(stringOne,stringTwo,10); cout << "String One: " << stringOne << endl; cout << "String Two: " << stringTwo << endl; return 0;Output: Enter a string: Oh beautiful Enter a second string: for spacious skies for amber waves of grain String One: Oh beautiful String Two: for spacious skies for amber waves of grain String One: Oh beautiful for spacio String Two: for spacious skies for amber waves of grain Analysis: On lines 7 and 8, two character arrays are created, and the user is prompted for two strings, which are put into the two arrays. A space is appended to stringOne on line 22, and on line 23, the first ten characters of stringTwo are appended to stringOne. The result is printed on lines 25 and 26. Other String Functions The string library provides a number of other string functions, including those used to find occurrences of various characters or "tokens" within a string. If you need to find a comma or a particular word as it occurs in a string, look to the string library to see whether the function you need already exists. Time and Date The time library provides a number of functions for obtaining a close approximation of the current time and date, and for comparing times and dates to one another. The center of this library is a structure, tm, which consists of nine integer values for the second, minute, hour, day of the month, number of the month (where January=0), the number of years since 1900, the day (where Sunday=0), the day of the year (0-365), and a Boolean value establishing whether daylight saving time is in effect. (This last may not be supported on some systems.) Most time functions expect a variable of type time_t or a pointer to a variable of this type. There are conversion routines to turn such a variable into a tm data structure. The standard library supplies the function time(), which takes a pointer to a time_t variable and fills it with the current time. It also provides ctime(), which takes the time_t variable filled by time() and returns an ASCII string that can be used for printing. If you need more control over the output, however, you can pass the time_t variable to local_time(), which will return a pointer to a tm structure. Listing 21.5 illustrates these various time functions. Listing 21.5. Using ctime(). 1: 2: 3: #include #include 4: int main() 5: { 6: time_t currentTime; 7: 8: // get and print the current time 9: time (¤tTime); // fill now with the current time 10: cout << "It is now " << ctime(¤tTime) << endl; 11: 12: struct tm * ptm= localtime(¤tTime); 13: 14: cout << "Today is " << ((ptm->tm_mon)+1) << "/"; 15: cout << ptm->tm_mday << "/"; 16: cout << ptm->tm_year << endl; 17: 18: cout << "\nDone."; 19: return 0; 20: } Output: It is now Mon Mar 31 13:50:10 1997 Today is 3/31/97 Done. Analysis: On line 6, CurrentTime is declared to be a variable of type time_t. The address of this variable is passed to the standard time library function time(), and the variable currentTime is set to the current date and time. The address of this variable is then passed to ctime(), which returns an ASCII string that is in turn passed to the cout statement on line 12.The address of currentTime is then passed to the standard time library function localtime(), and a pointer to a tm structure is returned, which is used to initialize the local variable ptm. The member data of this structure is then accessed to print the current month, day of the month, and year. stdlib stdlib is something of a miscellaneous collection of functions that did not fit into the other libraries. It includes simple integer math functions, sorting functions (including qsort(), one of the fastest sorts available), and text conversions for moving from ASCII text to integers, long, float, and so forth. The functions in stdlib you are likely to use most often include atoi(), itoa(), and the family of related functions. atoi() provides ASCII to integer conversion. atoi() takes a single argument: a pointer to a constant character string. It returns an integer (as you might expect). Listing 21.6 illustrates its use. Listing 21.6. Using atoi() and related functions.1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: } #include #include int main() { char buffer[80]; cout << "Enter a number: "; cin >> buffer; int number; // number = buffer; compile error number = atoi(buffer); cout << "Here's the number: " << number << endl; // int sum = buffer + 5; int sum = atoi(buffer) + 5; cout << "Here's sum: " << sum << endl; return 0; Output: Enter a number: 9 Here's the number: 9 Here's sum: 14 Analysis: On line 6 of this simple program, an 80-character buffer is allocated, and on line 7 the user is prompted for a number. The input is taken as text and written into the buffer. On line 10, an int variable, number, is declared, and on line 11 the program attempts to assign the contents of the buffer to the int variable. This generates a compile-time error and is commented out. On line 12, the problem is solved by invoking the standard library function atoi(), passing in the buffer as the parameter. The return value, the integer value of the text string, is assigned to the integer variable number and printed on line 13. On line 15, a new integer variable, sum, is declared, and an attempt is made to assign to it the result of adding the integer constant 5 to the buffer. This, too, fails and is solved by calling the standard function atoi(). NOTE: Some compilers implement standard conversion procedures (such as atoi()) using macros. You can usually use these functions without worrying about how they are implemented. Check your compiler's documentation for details. qsort() At times you may want to sort a table or an array; qsort() provides a quick and easy way to do so.The hard part of using qsort() is setting up the structures to pass in. qsort() takes four arguments. The first is a pointer to the start of the table to be sorted (an array name works just fine), the second is the number of elements in the table, the third is the size of each element, and the fourth is a pointer to a comparison function. The comparison function must return an int, and must take as its parameters two constant void pointers. void pointers aren't used very often in C++, as they diminish the type checking, but they have the advantage that they can be used to point to items of any type. If you were writing your own qsort() function, you might consider using templates instead. Listing 21.7 illustrates how to use the standard qsort() function. Listing 21.7. Using qsort(). 1: /* qsort example */ 2: 3: #include 4: #include 5: 6: // form of sort_function required by qsort 7: int sortFunction( const void *intOne, const void *intTwo); 8: 9: const int TableSize = 10; // array size 10: 11: int main(void) 12: { 13: int i,table[TableSize]; 14: 15: // fill the table with values 16: for (i = 0; i> table[i]; 20: } 21: cout << "\n"; 22: 23: // sort the values 24: qsort((void *)table, TableSize, sizeof(table[0]), sortFunction); 25: 26: // print the results 27: for (i = 0; i < TableSize; i++) 28: cout << "Table [" << i << "]: " << table[i] << endl; 29: 30: cout << "Done." << endl;31: return 0; 32: } 33: 34: int sortFunction( const void *a, const void *b) 35: { 36: int intOne = *((int*)a); 37: int intTwo = *((int*)b); 38: if (intOne < intTwo) 39: return -1; 40: if (intOne == intTwo) 41: return 0; 42: return 1; 43: } Output: Enter a number: 2 Enter a number: 9 Enter a number: 12 Enter a number: 873 Enter a number: 0 Enter a number: 45 Enter a number: 93 Enter a number: 2 Enter a number: 66 Enter a number: 1 Table[0]: Table[1]: Table[2]: Table[3]: Table[4]: Table[5]: Table[6]: Table[7]: Table[8]: Table[9]: Done. 0 1 2 2 9 12 45 66 93 873 Analysis: On line 4, the standard library header is included, which is required by the qsort() function. On line 7, the function sortFunction() is declared, which takes the required four parameters. An array is declared on line 13 and filled by user input on lines 16-20. qsort() is called on line 24, casting the address of the array name table to be a void*. Note that the parameters for sortFunction are not passed to the call to qsort(). The name of the sortFunction, which is itself a pointer to that function, is the parameter to qsort().Once qsort() is running, it will fill the constant void pointers a and b with each value of the array. If the first value is smaller than the second, the comparison function must return -1. If it is equal, the comparison function must return 0. Finally, if the first value is greater than the second value, the comparison function must return 1. This is reflected in the sortFunction(), as shown on lines 34 to 43. Other Libraries Your C++ compiler supplies a number of other libraries, among them the standard input and output libraries and the stream libraries that you've been using throughout this book. It is well worth your time and effort to explore the documentation that came with your compiler to find out what these libraries have to offer. Bit Twiddling Often you will want to set flags in your objects to keep track of the state of your object. (Is it in AlarmState? Has this been initialized yet? Are you coming or going?) You can do this with user-defined Booleans, but when you have many flags, and when storage size is an issue, it is convenient to be able to use the individual bits as flags. Each byte has eight bits, so in a four-byte long you can hold 32 separate flags. A bit is said to be "set" if its value is 1, and clear if its value is 0. When you set a bit, you make its value 1, and when you clear it, you make its value 0. (Set and clear are both adjectives and verbs). You can set and clear bits by changing the value of the long, but that can be tedious and confusing. NOTE: Appendix C, "Binary and Hexadecimal," provides valuable additional information about binary and hexadecimal manipulation. C++ provides bitwise operators that act upon the individual bits.These look like, but are different from, the logical operators, so many novice programmers confuse them. The bitwise operators are presented in Table 21.1. Table 21.1. The Bitwise Operators. symbol & | ^ ~ operator AND OR exclusive OR complementOperator AND The AND operator (&) is a single ampersand, as opposed to the logical AND, which is two ampersands. When you AND two bits, the result is 1 if both bits are 1, but 0 if either or both bits are 0. The way to think of this is: The result is 1 if bit 1 is set and if bit 2 is set. Operator OR The second bitwise operator is OR (|). Again, this is a single vertical bar, as opposed to the logical OR, which is two vertical bars. When you OR two bits, the result is 1 if either bit is set or if both are. Operator Exclusive OR The third bitwise operator is exclusive OR (^). When you exclusive OR two bits, the result is 1 if the two bits are different. The Complement Operator The complement operator (~) clears every bit in a number that is set and sets every bit that is clear. If the current value of the number is 1010 0011, the complement of that number is 0101 1100. Setting Bits When you want to set or clear a particular bit, you use masking operations. If you have a 4-byte flag and you want to set bit 8 TRUE, you need to OR the flag with the value 128. Why? 128 is 1000 0000 in binary; thus the value of the eighth bit is 128. Whatever the current value of that bit (set or clear), if you OR it with the value 128 you will set that bit and not change any of the other bits. Let's assume that the current value of the 8 bits is 1010 0110 0010 0110. ORing 128 to it looks like this: 9 8765 4321 1010 0110 0010 0110 // bit 8 is clear | 0000 0000 1000 0000 // 128 ---------------------- 1010 0110 1010 0110 // bit 8 is set There are a few things to note. First, as usual, bits are counted from right to left. Second, the value 128 is all zeros except for bit 8, the bit you want to set. Third, the starting number 1010 0110 0010 0110 is left unchanged by the OR operation, except that bit 8 was set. Had bit 8 already been set, it would have remained set, which is what you want. Clearing Bits If you want to clear bit 8, you can AND the bit with the complement of 128. The complement of 128 isthe number you get when you take the bit pattern of 128 (1000 0000), set every bit that is clear, and clear every bit that is set (0111 1111). When you AND these numbers, the original number is unchanged, except for the eighth bit, which is forced to zero. 1010 0110 1010 0110 // bit 8 is set & 1111 1111 0111 1111 // ~128 ---------------------- 1010 0110 0010 0110 // bit 8 cleared To fully understand this solution, do the math yourself. Each time both bits are 1, write 1 in the answer. If either bit is 0, write 0 in the answer. Compare the answer with the original number. It should be the same except that bit 8 was cleared. Flipping Bits Finally, if you want to flip bit 8, no matter what its state, you exclusive OR the number with 128. Thus: 1010 0110 1010 0110 // number ^ 0000 0000 1000 0000 // 128 ---------------------- 1010 0110 0010 0110 // bit flipped ^ 0000 0000 1000 0000 // 128 ---------------------- 1010 0110 1010 0110 // flipped back DO set bits by using masks and the OR operator. DO clear bits by using masks and the AND operator. DO flip bits using masks and the exclusive OR operator. Bit Fields There are circumstances under which every byte counts, and saving six or eight bytes in a class can make all the difference. If your class or structure has a series of Boolean variables, or variables that can have only a very small number of possible values, you may save some room using bit fields. Using the standard C++ data types, the smallest type you can use in your class is a type char, which is one byte. You will usually end up using an int, which is two, or more often four, bytes. By using bit fields, you can store eight binary values in a char and 32 such values in a long. Here's how bit fields work: bit fields are named and accessed like any class member. Their type is always declared to be unsigned int. After the bit field name, write a colon followed by a number. The number is an instruction to the compiler as to how many bits to assign to this variable. If you write 1, the bit will represent either the value 0 or 1. If you write 2, the bit can represent 0, 1, 2, or 3,a total of four values. A three-bit field can represent eight values, and so forth. Appendix C reviews binary numbers. Listing 21.8 illustrates the use of bit fields. Listing 21.8. Using bit fields. 0: 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: #include #include enum enum enum enum STATUS { FullTime, PartTime } ; GRADLEVEL { UnderGrad, Grad } ; HOUSING { Dorm, OffCampus }; FOODPLAN { OneMeal, AllMeals, WeekEnds, NoMeals }; class student { public: student(): myStatus(FullTime), myGradLevel(UnderGrad), myHousing(Dorm), myFoodPlan(NoMeals) {} ~student(){} STATUS GetStatus(); void SetStatus(STATUS); unsigned GetPlan() { return myFoodPlan; } private: unsigned unsigned unsigned unsigned }; myStatus : 1; myGradLevel: 1; myHousing : 1; myFoodPlan : 2; STATUS student::GetStatus() { if (myStatus) return FullTime; else return PartTime; } void student::SetStatus(STATUS theStatus) { myStatus = theStatus; }41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: break; 66: 67: break; 68: 69: 70: 71: } int main() { student Jim; if (Jim.GetStatus()== PartTime) cout << "Jim is part time" << endl; else cout << "Jim is full time" << endl; Jim.SetStatus(PartTime); if (Jim.GetStatus()) cout << "Jim is part time" << endl; else cout << "Jim is full time" << endl; cout << "Jim is on the " ; char Plan[80]; switch (Jim.GetPlan()) { case OneMeal: strcpy(Plan,"One meal"); break; case AllMeals: strcpy(Plan,"All meals"); break; case WeekEnds: strcpy(Plan,"Weekend meals"); case NoMeals: strcpy(Plan,"No Meals");break; default : cout << "Something bad went wrong!\n"; } cout << Plan << " food plan." << endl; return 0; Output: Jim is part time Jim is full time Jim is on the No Meals food plan. Analysis: On lines 3 to 7, several enumerated types are defined. These serve to define the possible values for the bit fields within the student class. Student is declared in lines 8-27. While this is a trivial class, it is interesting in that all the data is packed into five bits. The first bit represents the student's status, full-time or part-time. The second bit represents whether or not this is an undergraduate. The third bit represents whether or not the student lives in a dorm. The final two bits represent the four possible food plans.The class methods are written as for any other class, and are in no way affected by the fact that these are bit fields and not integers or enumerated types. The member function GetStatus() reads the Boolean bit and returns an enumerated type, but this is not necessary. It could just as easily have been written to return the value of the bit field directly. The compiler would have done the translation. To prove that to yourself, replace the GetStatus() implementation with this code: STATUS student::GetStatus() { return myStatus; } There should be no change whatsoever to the functioning of the program. It is a matter of clarity when reading the code; the compiler isn't particular. Note that the code on line 46 must check the status and then print the meaningful message. It is tempting to write this: cout << "Jim is " << Jim.GetStatus() << endl; That will simply print this: Jim is 0 The compiler has no way to translate the enumerated constant PartTime into meaningful text. On line 61, the program switches on the food plan, and for each possible value it puts a reasonable message into the buffer, which is then printed on line 69. Note again that the switch statement could have been written as follows: case case case case 0: 1: 2: 3: strcpy(Plan,"One meal"); break; strcpy(Plan,"All meals"); break; strcpy(Plan,"Weekend meals"); break; strcpy(Plan,"No Meals");break; The most important thing about using bit fields is that the client of the class need not worry about the data storage implementation. Because the bit fields are private, you can feel free to change them later and the interface will not need to change. StyleAs stated elsewhere in this book, it is important to adopt a consistent coding style, though in many ways it doesn't matter which style you adopt. A consistent style makes it easier to guess what you meant by a particular part of the code, and you avoid having to look up whether you spelled the function with an initial cap or not the last time you invoked it. The following guidelines are arbitrary; they are based on the guidelines used in projects I've worked on in the past, and they've worked well. You can just as easily make up your own, but these will get you started. As Emerson said, "Foolish consistency is the hobgoblin of small minds," but having some consistency in your code is a good thing. Make up your own, but then treat it as if it were dispensed by the programming gods. Indenting Tab size should be four spaces. Make sure your editor converts each tab to four spaces. Braces How to align braces can be the most controversial topic between C and C++ programmers. Here are the tips I suggest: .)( ,** *!' Matching braces should be aligned vertically. The outermost set of braces in a definition or declaration should be at the left margin. Statements within should be indented. All other sets of braces should be in line with their leading statements. No code should appear on the same line as a brace. For example: if (condition==true) { j = k; SomeFunction(); } m++; Long Lines Keep lines to the width displayable on a single screen. Code that is off to the right is easily overlooked, and scrolling horizontally is annoying. When a line is broken, indent the following lines. Try to break the line at a reasonable place, and try to leave the intervening operator at the end of the previous line (as opposed to the beginning of the following line) so that it is clear that the line does not stand alone and that there is more coming.In C++, functions tend to be far shorter than they were in C, but the old, sound advice still applies. Try to keep your functions short enough to print the entire function on one page. switch Statements Indent switches as follows to conserve horizontal space: switch(variable) { case ValueOne: ActionOne(); break; case ValueTwo: ActionTwo(); break; default: assert("bad Action"); break; } Program Text There are several tips you can use to create code that is easy to read. Code that is easy to read is easy to maintain. /(! ,'( (#+ )'' Use whitespace to help readability. Objects and arrays are really referring to one thing. Don't use spaces within object references (., ->, []). Unary operators are associated with their operands, so don't put a space between them. Do put a space on the side away from the operand. Unary operators include !, ~, ++, --, -, * (for pointers), & (casts), sizeof. Binary operators should have spaces on both sides: +, =, *, /, %, >>, <<, <, >, ==, !=, &, |, &&, ||, ?:, =, +=, and so on. ",+ Don't use lack of spaces to indicate precedence (4+ 3*2). *$+ Put a space after commas and semicolons, not before. !,& Parentheses should not have spaces on either side. )"& Keywords, such as if, should be set off by a space: if (a == b).!&/ The body of a comment should be set off from the // with a space. #// Place the pointer or reference indicator next to the type name, not the variable name: char* foo; int& theInt; !/. rather than char *foo; int &theInt; (+% Do not declare more than one variable on the same line. Identifier Names Here are some guidelines for working with identifiers. !+' Identifier names should be long enough to be descriptive. #+. Avoid cryptic abbreviations. )-" Take the time and energy to spell things out. +'% &.) Do not use Hungarian notation. C++ is strongly typed and there is no reason to put the type into the variable name. With user-defined types (classes), Hungarian notation quickly breaks down. The exceptions to this may be to use a prefix for pointers (p) and references (r), as well as for class member variables (its). Short names (i, p, x, and so on) should only be used where their brevity makes the code more readable and where the usage is so obvious that a descriptive name is not needed. #'" The length of a variable's name should be proportional to its scope. (.' Make sure identifiers look and sound different from one another to minimize confusion. '!. Function (or method) names are usually verbs or verb-noun phrases: Search(), Reset(), FindParagraph(), ShowCursor(). Variable names are usually abstract nouns, possibly with an additional noun: count, state, windSpeed, windowHeight. Boolean variables should be named appropriately: windowIconized, fileIsOpen. Spelling and Capitalization of NamesSpelling and capitalization should not be overlooked when creating your own style. Some tips for these areas include the following: -(- +!/ /(" Use all uppercase and underscore to separate the logical words of names, such as SOURCE_FILE_TEMPLATE. Note, however, that these are rare in C++. Consider using constants and templates in most cases. All other identifiers should use mixed case--no underscores. Function names, methods, class, typedef, and struct names should begin with a capitalized letter. Elements such as data members or locals should begin with a lowercase letter. Enumerated constants should begin with a few lowercase letters as an abbreviation for the enum. For example: enum TextStyle { tsPlain, tsBold, tsItalic, tsUnderscore, }; Comments Comments can make it much easier to understand a program. Sometimes you will not work on a program for several days or even months. In this time you can forget what certain code does or why it has been included. Problems in understanding code can also occur when someone else reads your code. Comments that are applied in a consistent, well thought out style can be well worth the effort. There are several tips to remember concerning comments: .*- $#* Wherever possible, use C++ // comments rather than the /* */ style. Higher-level comments are infinitely more important than process details. Add value; do not merely restate the code. n++; // n is incremented by one ,&( )#- This comment isn't worth the time it takes to type it in. Concentrate on the semantics of functions and blocks of code. Say what a function does. Indicate side effects, types of parameters, and return values. Describe all assumptions that are made (or not made), such as "assumes n is non-negative" or "will return -1 if x is invalid". Within complex logic, use comments to indicate the conditions that exist at that point in the code. Use complete English sentences with appropriate punctuation and capitalization. The extratyping is worth it. Don't be overly cryptic and don't abbreviate. What seems exceedingly clear to you as you write code will be amazingly obtuse in a few months. "&+ Use blank lines freely to help the reader understand what is going on. Separate statements into logical groups. Access The way you access portions of your program should also be consistent. Some tips for access include these: *-) )%" '") +%! *+( Always use public:, private:, and protected: labels; don't rely on the defaults. List the public members first, then protected, then private. List the data members in a group after the methods. Put the constructor(s) first in the appropriate section, followed by the destructor. List overloaded methods with the same name adjacent to each other. Group accessor functions together when possible. Consider alphabetizing the method names within each group and alphabetizing the member variables. Be sure to alphabetize the filenames in include statements. Even though the use of the virtual keyword is optional when overriding, use it anyway; it helps to remind you that it is virtual, and also keeps the declaration consistent. Class Definitions Try to keep the definitions of methods in the same order as the declarations. It makes things easier to find. When defining a function, place the return type and all other modifiers on a previous line so that the class name and function name begin on the left margin. This makes it much easier to find functions. include Files Try as hard as you can to keep from including files into header files. The ideal minimum is the header file for the class this one derives from. Other mandatory includes will be those for objects that are members of the class being declared. Classes that are merely pointed to or referenced only need forward references of the form. Don't leave out an include file in a header just because you assume that whatever CPP file includes this one will also have the needed include.TIP: All header files should use inclusion guards. assert() Use assert() freely. It helps find errors, but it also greatly helps a reader by making it clear what the assumptions are. It also helps to focus the writer's thoughts around what is valid and what isn't. const Use const wherever appropriate: for parameters, variables, and methods. Often there is a need for both a const and a non-const version of a method; don't use this as an excuse to leave one out. Be very careful when explicitly casting from const to non-const and vice versa (there are times when this is the only way to do something), but be certain that it makes sense, and include a comment. Next Steps You've spent three long, hard weeks working at C++, and you are now a competent C++ programmer, but you are by no means finished. There is much more to learn and many more places you can get valuable information as you move from novice C++ programmer to expert. The following sections recommend a number of specific sources of information, and these recommendations reflect only my personal experience and opinions. There are dozens of books on each of these topics, however, so be sure to get other opinions before purchasing. Where to Get Help and Advice The very first thing you will want to do as a C++ programmer will be to tap into one or another C++ conference on an online service. These groups supply immediate contact with hundreds or thousands of C++ programmers who can answer your questions, offer advice, and provide a sounding board for your ideas. I participate in the C++ Internet newsgroups (comp.lang.c++ and comp.lang.c++.moderated), and I recommend them as excellent sources of information and support. Also, you may want to look for local user groups. Many cities have C++ interest groups where you can meet other programmers and exchange ideas. Required Reading The very next book I'd run out and buy and read is Meyers, Scott. Effective C++ (ISBN: 0-201-56364-9). Addison-Wesley Publishing, 1993.This is by far the most useful book I've ever read, and I've read it three times. Magazines There is one more thing you can do to strengthen your skills: subscribe to a good magazine on C++ programming. The absolute best magazine of this kind, I believe, is C++ Report from SIGS Publications. Every issue is packed with useful articles. Save them; what you don't care about today will become critically important tomorrow. You can reach C++ Report at SIGS Publications, P.O. Box 2031, Langhorne, PA 19047-9700. I have no affiliation with the magazine (I work for two other publishers!), but their magazine is the best, bar none. Staying in Touch If you have comments, suggestions, or ideas about this book or other books, I'd love to hear them. Please write to me at jliberty@libertyassociates.com, or check out my Web site: www.libertyassociates.com. I look forward to hearing from you. DO look at other books. There's plenty to learn and no single book can teach you everything you need to know. DON'T just read code! The best way to learn C++ is to write C++ programs. DO subscribe to a good C++ magazine and join a good C++ user group. Summary Today you saw how some of the standard libraries shipped with your C++ compiler can be used to manage some routine tasks. Strcpy(), strlen(), and related functions can be used to manipulate null-terminated strings. Although these won't work with the string classes you create, you may find that they provide functionality essential to implementing your own classes. The time and date functions allow you to obtain and manipulate time structures. These can be used to provide access to the system time for your programs, or they can be used to manipulate time and date objects you create. You also learned how to set and test individual bits, and how to allocate a limited number of bits to class members. Finally, C++ style issues were addressed, and resources were provided for further study. Q&AQ. Why are the standard libraries included with C++ compilers, and when would you use them? A. They are included for backwards-compatibility with C. They are not type-safe, and they don't work well with user-created classes, so their use is limited. Over time, you might expect all of their functionality to be migrated into C++ specific libraries, at which time the standard C libraries would become obsolete. Q. When would you use bit structures rather than simply using integers? A. When the size of the object is crucial. If you are working with limited memory or with communications software, you may find that the savings offered by these structures is essential to the success of your product. Q. Why do style wars generate so much emotion? A. Programmers become very attached to their habits. If you are used to this indentation, if (SomeCondition){ // statements } // closing brace it is a difficult transition to give it up. New styles look wrong and create confusion. If you get bored, try logging onto a popular online service and asking which indentation style works best, which editor is best for C++, or which product is the best word processor. Then sit back and watch as ten thousand messages are generated, all contradicting one another. Q. What is the very next thing to read? A. Tough question. If you want to review the fundamentals, read one of the other primers. If you want to hone C++, run out and get Scott Meyers' Effective C++. Finally, if you want to write for Windows or the Mac, it might make sense to pick up a primer on the platform. Q. Is that it? A. Yes! You've learned C++, but...no. Ten years ago it was possible for one person to learn all there was to know about microcomputers, or at least to feel pretty confident that he was close. Today it is out of the question: You can't possibly catch up, and even as you try the industry is changing. Be sure to keep reading, and stay in touch with the resources that will keep you up with the latest changes: magazines and online services. Quiz 1. What is the difference between strcpy() and strncpy()?2. What does ctime() do? 3. What is the function to call to turn an ASCII string into a long? 4. What does the complement operator do? 5. What is the difference between OR and exclusive OR? 6. What is the difference between & and &&? 7. What is the difference between | and ||? Exercises 1. Write a program to safely copy the contents of a 20-byte string to a 10-byte string, truncating whatever won't fit. 2. Write a program that tells the current date in the form 7/28/94. 3. Write a program that creates 26 flags (labeled a-z). Prompt the user to enter a sentence, and then quickly report on which letters were used by setting and then reading the flags. 4. Write a program that adds two numbers without using the addition operator (+). Hint: use the bit operators!In Review The following program brings together many of the advanced techniques you've learned during the past three weeks of hard work. Week 3 in Review provides a template-based linked list with exception handling. Examine it in detail; if you understand it fully, you are a C++ programmer. WARNING: If your compiler does not support templates, or if your compiler does not support try and catch, you will not be able to compile or run this listing. Listing R3.1. Week 3 in Review listing. 0: // ************************************************** 1: // 2: // Title: Week 3 in Review 3: // 4: // File: Week3 5: // 6: // Description: Provide a template-based linked list 7: // demonstration program with exception handling 8: // 9: // Classes: PART - holds part numbers and potentially other 10: // information about parts. This will be the 11: // example class for the list to hold 12: // Note use of operator<< to print the 13: // information about a part based on its 14: // runtime type. 15: // 16: // Node - acts as a node in a List 17: // 18: // List - template-based list which provides the 19: // mechanisms for a linked list 20: // 21: // 22: // Author: Jesse Liberty (jl) 23: //24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: type 68: // // // // // // // Developed: Target: Pentium 200 Pro. 128MB RAM MVC 5.0 Platform independent Rev History: 9/94 - First release (jl) 4/97 - Updated (jl) ************************************************** #include // exception classes class Exception {}; class OutOfMemory : public Exception{}; class NullNode : public Exception{}; class EmptyList : public Exception {}; class BoundsError : public Exception {}; // **************** Part ************ // Abstract base class of parts class Part { public: Part():itsObjectNumber(1) {} Part(int ObjectNumber):itsObjectNumber(ObjectNumber){} virtual ~Part(){}; int GetObjectNumber() const { return itsObjectNumber; } virtual void Display() const =0; // must be overridden private: int itsObjectNumber; }; // implementation of pure virtual function so that // derived classes can chain up void Part::Display() const { cout << "\nPart Number: " << itsObjectNumber << endl; } // // // // this one operator<< will be called for all part objects. It need not be a friend as it does not access private data It calls Display() which uses the required polymorphism We'd like to be able to override this based on the real // of thePart, but C++ does not support contravariance69: ostream& operator<<( ostream& theStream,Part& thePart) 70: { 71: thePart.Display(); // virtual contravariance! 72: return theStream; 73: } 74: 75: // **************** Car Part ************ 76: class CarPart : public Part 77: { 78: public: 79: CarPart():itsModelYear(94){} 80: CarPart(int year, int partNumber); 81: int GetModelYear() const { return itsModelYear; } 82: virtual void Display() const; 83: private: 84: int itsModelYear; 85: }; 86: 87: CarPart::CarPart(int year, int partNumber): 88: itsModelYear(year), 89: Part(partNumber) 90: {} 91: 92: void CarPart::Display() const 93: { 94: Part::Display(); 95: cout << "Model Year: " << itsModelYear << endl; 96: } 97: 98: // **************** AirPlane Part ************ 99: class AirPlanePart : public Part 100: { 101: public: 102: AirPlanePart():itsEngineNumber(1){}; 103: AirPlanePart(int EngineNumber, int PartNumber); 104: virtual void Display() const; 105: int GetEngineNumber()const { return itsEngineNumber; } 106: private: 107: int itsEngineNumber; 108: }; 109: 110: AirPlanePart::AirPlanePart(int EngineNumber, int PartNumber): 111: itsEngineNumber(EngineNumber), 112: Part(PartNumber) 113: {}114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: void AirPlanePart::Display() const { Part::Display(); cout << "Engine No.: " << itsEngineNumber << endl; } // forward declaration of class List template class List; // **************** Node ************ // Generic node, can be added to a list // ************************************ template class Node { public: friend class List; Node (T*); ~Node(); void SetNext(Node * node) { itsNext = node; } Node * GetNext() const; T * GetObject() const; private: T* itsObject; Node * itsNext; }; // Node Implementations... template Node::Node(T* pOjbect): itsObject(pOjbect), itsNext(0) {} template Node::~Node() { delete itsObject; itsObject = 0; delete itsNext; itsNext = 0; }160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: // Returns NULL if no next Node template Node * Node::GetNext() const { return itsNext; } template T * Node::GetObject() const { if (itsObject) return itsObject; else throw NullNode(); } // **************** List ************ // Generic list template // Works with any numbered object // *********************************** template class List { public: List(); ~List(); T* Find(int & position, int ObjectNumber) T* GetFirst() const; void Insert(T *); T* operator[](int) const; int GetCount() const { return itsCount; } private: Node * pHead; int itsCount; }; // Implementations for Lists... template List::List(): pHead(0), itsCount(0) {} template const;206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: (id) 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: List::~List() { delete pHead; } template T* List::GetFirst() const { if (pHead) return pHead->itsObject; else throw EmptyList(); } template T * List::operator[](int offSet) const { Node* pNode = pHead; if (!pHead) throw EmptyList(); if (offSet > itsCount) throw BoundsError(); for (int i=0;iitsNext; return } pNode->itsObject; // find a given object in list based on its unique number template T* List::Find(int & position, int ObjectNumber) const { Node * pNode = 0; for (pNode = pHead, position = 0; pNode!=NULL; pNode = pNode->itsNext, position++) { if (pNode->itsObject->GetObjectNumber() == ObjectNumber) break; } if (pNode == NULL) return NULL;251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: else return pNode->itsObject; } // insert if the number of the object is unique template void List::Insert(T* pObject) { Node * pNode = new Node(pObject); Node * pCurrent = pHead; Node * pNext = 0; int New = pObject->GetObjectNumber(); int Next = 0; itsCount++; if (!pHead) { pHead = pNode; return; } // if this one is smaller than head // this one is the new head if (pHead->itsObject->GetObjectNumber() > New) { pNode->itsNext = pHead; pHead = pNode; return; } for (;;) { // if there is no next, append this new one if (!pCurrent->itsNext) { pCurrent->itsNext = pNode; return; } // if this goes after this one and before the next // then insert it here, otherwise get the next pNext = pCurrent->itsNext; Next = pNext->itsObject->GetObjectNumber(); if (Next > New) { pCurrent->itsNext = pNode; pNode->itsNext = pNext; return; } pCurrent = pNext; } } int main() { List theList; int choice; int ObjectNumber; int value; Part * pPart; while (1) { cout << "(0)Quit (1)Car (2)Plane: "; cin >> choice; if (!choice) break; cout << "New PartNumber?: "; cin >> ObjectNumber; if (choice == 1) { cout << "Model Year?: "; cin >> value; try { pPart = new CarPart(value,ObjectNumber); } catch (OutOfMemory) { cout << "Not enough memory; Exiting..." << endl; return 1; } } else { cout << "Engine Number?: "; cin >> value; try343: { 344: pPart = new AirPlanePart(value,ObjectNumber); 345: } 346: catch (OutOfMemory) 347: { 348: cout << "Not enough memory; Exiting..." << endl; 349: return 1; 350: } 351: } 352: try 353: { 354: theList.Insert(pPart); 355: } 356: catch (NullNode) 357: { 358: cout << "The list is broken, and the node is null!" << endl; 359: return 1; 360: } 361: catch (EmptyList) 362: { 363: cout << "The list is empty!" << endl; 364: return 1; 365: } 366: } 367: try 368: { 369: for (int i = 0; i < theList.GetCount(); i++ ) 370: cout << *(theList[i]); 371: } 372: catch (NullNode) 373: { 374: cout << "The list is broken, and the node is null!" << endl; 375: return 1; 376: } 377: catch (EmptyList) 378: { 379: cout << "The list is empty!" << endl; 380: return 1; 381: } 382: catch (BoundsError) 383: { 384: cout << "Tried to read beyond the end of the list!" << endl; 385: return 1;386: } 387: return 0; 388: } Output: (0)Quit (1)Car (2)Plane: 1 New PartNumber?: 2837 Model Year? 90 (0)Quit (1)Car (2)Plane: 2 New PartNumber?: 378 Engine Number?: 4938 (0)Quit (1)Car (2)Plane: 1 New PartNumber?: 4499 Model Year? 94 (0)Quit (1)Car (2)Plane: 1 New PartNumber?: 3000 Model Year? 93 (0)Quit (1)Car (2)Plane: 0 Part Number: 378 Engine No. 4938 Part Number: 2837 Model Year: 90 Part Number: 3000 Model Year: 93 Part Number 4499 Model Year: 94 Analysis: The Week 3 in Review listing modifies the program provided in Week 2 to add templates, ostream processing, and exception handling. The output is identical. On lines 35-39, a number of exception classes are declared. In the somewhat primitive exception handling provided by this program, no data or methods are required of these exceptions; they serve as flags to the catch statements, which print out a very simple warning and then exit. A more robust program might pass these exceptions by reference and then extract context or other data from the exception objects in an attempt to recover from the problem. On line 44, the abstract base class Part is declared exactly as it was in Week 2. The only interesting change here is in the non-class member operator<<(), which is declared on lines 69-73. Note that this is neither a member of Part nor a friend of part, it simply takes a Part reference as one of its arguments.You might want to have operator<< take a CarPart and an AirPlanePart in the hopes that the correct operator<< would be called, based on whether a car part or an airplane part is passed. Since the program passes a pointer to a part, however, and not a pointer to a car part or an airplane part, C++ would have to call the right function based on the real type of one of the arguments to the function. This is called contravariance and is not supported in C++. There are only two ways to achieve polymorphism in C++: function polymorphism and virtual functions. Function polymorphism won't work here because in every case you are matching the same signature: the one taking a reference to a Part. Virtual functions won't work here because operator<< is not a member function of Part. You can't make operator<< a member function of Part because you want to invoke cout << thePart and that means that the actual call would be to cout.operator<<(Part&), and cout does not have a version of operator<< that takes a Part reference! To get around this limitation, the Week 3 program uses just one operator<<, taking a reference to a Part. This then calls Display(), which is a virtual member function, and thus the right version is called. On lines 129-142, Node is defined as a template. It serves the same function as Node did in the Week 2 Review program, but this version of Node is not tied to a Part object. It can, in fact, be the node for any type of object. Note that if you try to get the object from Node, and there is no object, this is considered an exception, and the exception is thrown on line 174. On lines 181-197, a generic List class template is defined. This List class can hold nodes of any objects that have unique identification numbers, and it keeps them sorted in ascending order. Each of the list functions checks for exceptional circumstances and throws the appropriate exceptions as required. On lines 306-388, the driver program creates a list of two types of Part objects and then prints out the values of the objects in the list by using the standard streams mechanism.Appendix A Operator Precedence It is important to understand that operators have a precedence, but it is not essential to memorize the precedence. New Term: Precedence is the order in which a program performs the operations in a formula. If one operator has precedence over another operator, it is evaluated first. Higher precedence operators "bind tighter" than lower precedence operators; thus, higher precedence operators are evaluated first. The lower the rank in the following chart, the higher the precedence. Table A.1. Operator Precedence . Rank Name Operator 1 scope resolution :: 2 member selection, subscripting, . -> function calls, postfix increment () and decrement ++ -- 3 sizeof, prefix increment and decrement, ++ -- complement, and, not, unary minus and plus, ^ ! address of and dereference, new, new[], delete, - + delete[], casting, sizeof(), & * () member selection for pointer 4 .* ->* 5 multiply, divide, modulo * / % add, subtract 6 + - 7 shift << >> 8 inequality relational < <= > >= 9 equality, inequality == != 10 bitwise AND & 11 bitwise exclusive OR ^12 13 14 15 16 bitwise OR logical AND logical OR conditional assignment operators 17 18 throw operator comma | && || ?: = *= /= %= += -= <<= >>= &= |= ^= throw ,Appendix B C++ Keywords Keywords are reserved to the compiler for use by the language. You cannot define classes, variables, or functions that have these keywords as their names. The list is a bit arbitrary, as some of the keywords are specific to a given compiler. Your mileage may vary slightly: auto break case catch char class const continue default delete do double else enum extern float for friend goto if int long mutable new operator private protected public register return short signedsizeof static struct switch template this throw typedef union unsigned virtual void volatile whileAppendix C Binary and Hexadecimal You learned the fundamentals of arithmetic so long ago, it is hard to imagine what it would be like without that knowledge. When you look at the number 145 you instantly see "one hundred and forty-five" without much reflection. Understanding binary and hexadecimal requires that you re-examine the number 145 and see it not as a number, but as a code for a number. Start small: Examine the relationship between the number three and "3." The numeral 3 is a squiggle on a piece of paper; the number three is an idea. The numeral is used to represent the number. The distinction can be made clear by realizing that three, 3, |||, III, and *** all can be used to represent the same idea of three. In base 10 (decimal) math you use the numerals 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 to represent all numbers. How is the number ten represented? One can imagine that we would have evolved a strategy of using the letter A to represent ten; or we might have used IIIIIIIIII to represent that idea. The Romans used X. The Arabic system, which we use, makes use of position in conjunction with numerals to represent values. The first (right-most) column is used for "ones," and the next column is used for tens. Thus, the number fifteen is represented as 15 (read "one, five"); that is, 1 ten and 5 ones. Certain rules emerge, from which some generalizations can be made: 1. Base 10 uses the digits 0-9. 2. The columns are powers of ten: 1s, 10s, 100s, and so on. 3. If the third column is 100, the largest number you can make with two columns is 99. More generally, with n columns you can represent 0 to (10 n -1). Thus, with 3 columns you can represent 0 to (10 3 -1) or 0-999. Other Bases It is not a coincidence that we use base 10; we have 10 fingers. One can imagine a different base, however. Using the rules found in base 10, you can describe base 8: 1. The digits used in base 8 are 0-7. 2. The columns are powers of 8: 1s, 8s, 64, and so on. 3. With n columns you can represent 0 to 8 n -1. To distinguish numbers written in each base, write the base as a subscript next to the number. The number fifteen in base 10 would be written as 15 10 and read as "one, five, base ten." Thus, to represent the number 15 10 in base 8 you would write 17 8 . This is read "one, seven, base eight." Note that it can also be read "fifteen" as that is the number it continues to represent. Why 17? The 1 means 1 eight, and the 7 means 7 ones. One eight plus seven ones equals fifteen. Consider fifteen asterisks: ***** ***** ***** The natural tendency is to make two groups, a group of ten asterisks and another of five. This would be represented in decimal as 15 (1 ten and 5 ones). You can also group the asterisks as **** ******* **** That is, eight asterisks and seven. That would be represented in base eight as 17 8 . That is, one eight and seven ones. Around the BasesYou can represent the number fifteen in base ten as 15, in base nine as 16 9 , in base 8 as 17 8 , in base 7 as 21 7 . Why 21 7 ? In base 7 there is no numeral 8. In order to represent fifteen, you will need two sevens and one 1. How do you generalize the process? To convert a base ten number to base 7, think about the columns: in base 7 they are ones, sevens, forty-nines, three- hundred forty-threes, and so on. Why these columns? They represent 7 0 , 7 1 , 7 2 , 7 4 and so forth. Create a table for yourself: 4 3 2 1 7 3 7 2 7 1 7 0 343 49 7 1 The first row represents the column number. The second row represents the power of 7. The third row represents the decimal value of each number in that row. To convert from a decimal value to base 7, here is the procedure: Examine the number and decide which column to use first. If the number is 200, for example, you know that column 4 (343) is 0, and you don't have to worry about it. To find out how many 49s there are, divide 200 by 49. The answer is 4, so put 4 in column 3 and examine the remainder: 4. There are no 7s in 4, so put a zero in the sevens column. There are 4 ones in 4, so put a 4 in the 1s column. The answer is 404 7 . To convert the number 968 to base 6: 5 4 3 2 1 6 4 6 3 6 2 6 1 6 0 1296 216 36 6 1 There are no 1296s in 968, so column 5 has 0. Dividing 968 by 216 yields 4 with a remainder of 104. Column 4 is 4. Dividing 104 by 36 yields 2 with a remainder of 32. Column 3 is 2. Dividing 32 by 6 yields 5 with a remainder of 2. The answer therefore is 4252 6 . 5 4 3 2 1 6 4 6 3 6 2 6 1 1296 216 36 6 6 0 1 0 4 2 5 2 There is a shortcut when converting from one base to another base (such as 6) to base 10. You can multiply: 4 * 216 = 864 2 * 36 = 72 5 * 6 = 30 2 * 1 =2 968 Binary Base 2 is the ultimate extension of this idea. There are only two digits: 0 and 1. The columns are: Col: 8 7 6 5 4 3 2 1 Power: 2 7 2 6 2 5 2 4 2 3 2 2 2 1 2 0 Value: 128 64 32 16 8 4 2 1 To convert the number 88 to base 2, you follow the same procedure: There are no 128s, so column 8 is 0. There is one 64 in 88, so column 7 is 1 and 24 is the remainder. There are no 32s in 24 so column 6 is 0. There is one 16 in 24 so column 5 is 1. The remainder is 8. There is one 8 in 8, and so column 4 is 1. There is no remainder, so the rest of the columns are 0. 0 1 To test this answer, convert it back: 1 0 1 1 0 0 * 64 = * 32 = * 16 = * 8 = * 4 = * 2 = 64 0 16 8 0 0 0 1 1 0 0 00 * 1 = 0 88 Why Base 2? The power of base 2 is that it corresponds so cleanly to what a computer needs to represent. Computers do not really know anything at all about letters, numerals, instructions, or programs. At their core they are just circuitry, and at a given juncture there either is a lot of power or there is very little. To keep the logic clean, engineers do not treat this as a relative scale (a little power, some power, more power, lots of power, tons of power), but rather as a binary scale ("enough power" or "not enough power"). Rather than saying "enough" or "not enough," they simplify it to "yes" or "no." Yes or no, or true or false, can be represented as 1 or 0. By convention, 1 means true or Yes, but that is just a convention; it could just as easily have meant false or no. Once you make this great leap of intuition, the power of binary becomes clear: With 1s and 0s you can represent the fundamental truth of every circuit (there is power or there isn't). All a computer ever knows is, "Is you is, or is you ain't?" Is you is = 1; is you ain't = 0. Bits, Bytes, and Nybbles Once the decision is made to represent truth and falsehood with 1s and 0s, binary digits (or bits) become very important. Since early computers could send 8 bits at a time, it was natural to start writing code using 8-bit numbers--called bytes. NOTE: Half a byte (4 bits) is called a nybble! With 8 binary digits you can represent up to 256 different values. Why? Examine the columns: If all 8 bits are set (1), the value is 255. If none is set (all the bits are clear or zero) the value is 0. 0-255 is 256 possible states. Whats a KB? It turns out that 2 10 (1,024) is roughly equal to 10 3 (1,000). This coincidence was too good to miss, so computer scientists started referring to 2 10 bytes as 1KB or 1 kilobyte, based on the scientific prefix of kilo for thousand. Similarly, 1024 * 1024 (1,048,576) is close enough to one million to receive the designation 1MB or 1 megabyte, and 1,024 megabytes is called 1 gigabyte (giga implies thousand-million or billion). Binary Numbers Computers use patterns of 1s and 0s to encode everything they do. Machine instructions are encoded as a series of 1s and 0s and interpreted by the fundamental circuitry. Arbitrary sets of 1s and 0s can be translated back into numbers by computer scientists, but it would be a mistake to think that these numbers have intrinsic meaning. For example, the Intel 80x6 chip set interprets the bit pattern 1001 0101 as an instruction. You certainly can translate this into decimal (149), but that number per se has no meaning. Sometimes the numbers are instructions, sometimes they are values, and sometimes they are codes. One important standardized code set is ASCII. In ASCII every letter and punctuation is given a 7-digit binary representation. For example, the lowercase letter "a" is represented by 0110 0001. This is not a number, although you can translate it to the number 97 (64 + 32 + 1). It is in this sense that people say that the letter "a" is represented by 97 in ASCII; but the truth is that the binary representation of 97, 01100001, is the encoding of the letter "a," and the decimal value 97 is a human convenience. Hexadecimal Because binary numbers are difficult to read, a simpler way to represent the same values is sought. Translating from binary to base 10 involves a fair bit of manipulation of numbers; but it turns out that translating from base 2 to base 16 is very simple, because there is a very good shortcut. To understand this, you must first understand base 16, which is known as hexadecimal. In base 16 there are sixteen numerals: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, and F. The last six are arbitrary; the letters A-F were chosen because they are easy to represent on a keyboard. The columns in hexadecimal are 4 3 2 1 16 3 16 2 16 1 16 0 4096 256 16 1 To translate from hexadecimal to decimal, you can multiply. Thus, the number F8C represents: F * 256 = 15 * 256 = 3840 8 * 16 = 128 C * 1 = 12 * 1 = 12 3980 Translating the number FC to binary is best done by translating first to base 10, and then to binary:F * 16 = 15 * 16 = C * 1 = 12 * 1 = 252 240 12 Converting 252 10 to binary requires the chart: Col: Power: Value: 9 28 256 8 27 128 There are no 256s. 1 128 leaves 124 1 64 leaves 60 1 32 leaves 28 1 16 leaves 12 1 8 leaves 4 1 4 leaves 0 0 0 1 1 1 1 7 26 64 1 6 25 32 5 24 16 4 23 8 1 0 0 3 22 4 2 21 2 1 20 1 Thus, the answer in binary is 1111 1100. Now, it turns out that if you treat this binary number as two sets of 4 digits, you can do a magical transformation. The right set is 1100. In decimal that is 12, or in hexadecimal it is C. The left set is 1111, which in base 10 is 15, or in hex is F. Thus, you have: 1111 1100 F C Putting the two hex numbers together is FC, which is the real value of 1111 1100. This shortcut always works. You can take any binary number of any length, and reduce it to sets of 4, translate each set of four to hex, and put the hex numbers together to get the result in hex. Here's a much larger number: 1011 0001 1101 0111 The columns are 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, and 32768. 1 1 1 0 x x x x 1 = 2= 4 = 8 = 1 1 0 1 1 x x x x 16 = 32 = 64 = 128 = 16 0 64 128 1 0 0 0 x x x x 256 = 512 = 1024 = 2048 = 256 0 0 0 2 4 0 1 x 4096 = 4,096 1 x 8192 = 8,192 0 x 16384 = 0 1 x 32768 = 32,768 Total: 45,527 Converting this to hexadecimal requires a chart with the hexadecimal values. 65535 4096 256 16 1 There are no 65,536s in 45,527 so the first column is 4096. There are 11 4096s (45,056), with a remainder of 471. There is one 256 in 471 with a remainder of 215. There are 13 16s (208) in 215 with a remainder of 7. Thus, the hexadecimal number is B1D7. Checking the math:B (11) * 4096 = 45,056 1 * 256 = 256 D (13) * 16 = 208 7 * 1 = 7 Total 45,527 The shortcut version would be to take the original binary number, 1011000111010111, and break it into groups of 4: 1011 0001 1101 0111. Each of the four then is evaluated as a hexadecimal number: 1011 = 1 x 1 =1 1 x 2 =2 0 x 4 =0 1 x 8 =8 Total 11 Hex: B 0001 = 1 x 1 =1 0 x 2 =0 0 x 4 =0 0 * 8 =0 Total 1 Hex: 1 1101 = 1 x 1 =1 0 x 2 =0 1 x 4 =4 1 x 8 =8 Total 13 Hex = D 0111 = 1 x 1 =1 1 x 2 =2 1 x 4 =40 x 8 =0 Total 7 Hex: 7 Total Hex: B1D7Appendix D Answers Day 1 Quiz 1. What is the difference between interpreters and compilers? Interpreters read through source code and translate a program, turning the programmer's code, or program instructions, directly into actions. Compilers translate source code into an executable program that can be run at a later time. 2. How do you compile the source code with your compiler? Every compiler is different. Be sure to check the documentation that came with your compiler. 3. What does the linker do? The linker's job is to tie together your compiled code with the libraries supplied by your compiler vendor and other sources. The linker lets you build your program in pieces and then link together the pieces into one big program. 4. What are the steps in the development cycle? Edit source code, compile, link, test, repeat. Exercises 1. Initializes two integer variables and then prints out their sum and their product. 2. See your compiler manual. 3. You must put a # symbol before the word include on the first line. 4. This program prints the words Hello World to the screen, followed by a new line (carriage return). Day 2Quiz 1. What is the difference between the compiler and the preprocessor? Each time you run your compiler, the preprocessor runs first. It reads through your source code and includes the files you've asked for, and performs other housekeeping chores. The preprocessor is discussed in detail on Day 18, "Object-Oriented Analysis and Design." 2. Why is the function main() special? main() is called automatically, each time your program is executed. 3. What are the two types of comments, and how do they differ? C++-style comments are two slashes (//), and they comment out any text until the end of the line. C-style comments come in pairs (/* */), and everything between the matching pairs is commented out. You must be careful to ensure you have matched pairs. 4. Can comments be nested? Yes, C++-style comments can be nested within C-style comments. You can, in fact, nest C- style comments within C++-style comments, as long as you remember that the C++-style comments end at the end of the line. 5. Can comments be longer than one line? C-style comments can. If you want to extend C++-style comments to a second line, you must put another set of double slashes (//). Exercises 1. Write a program that writes I love C++ to the screen. 1: 2: 3: 4: 5: 6: 7: #include int main() { cout << "I love C++\n"; return 0; } 2. Write the smallest program that can be compiled, linked, and run. int main(){} 3. BUG BUSTERS: Enter this program and compile it. Why does it fail? How can you fix it?1: 2: 3: 4: 5: #include main() { cout << Is there a bug here?"; } Line 4 is missing an opening quote for the string. 4. Fix the bug in Exercise 3 and recompile, link, and run it. 1: 2: 3: 4: 5: #include main() { cout << "Is there a bug here?"; } Day 3 Quiz 1. What is the difference between an integral variable and a floating-point variable? Integer variables are whole numbers; floating-point variables are "reals" and have a "floating" decimal point. Floating-point numbers can be represented using a mantissa and an exponent. 2. What are the differences between an unsigned short int and a long int? The keyword unsigned means that the integer will hold only positive numbers. On most computers, short integers are 2 bytes and long integers are 4. 3. What are the advantages of using a symbolic constant rather than a literal? A symbolic constant explains itself; the name of the constant tells what it is for. Also, symbolic constants can be redefined at one location in the source code, rather than the programmer having to edit the code everywhere the literal is used. 4. What are the advantages of using the const keyword rather than #define? const variables are "typed;" thus the compiler can check for errors in how they are used. Also, they survive the preprocessor; thus the name is available in the debugger. 5. What makes for a good or bad variable name? A good variable name tells you what the variable is for; a bad variable name has no information. myAge and PeopleOnTheBus are good variable names, but xjk and prndl are probably less useful.6. Given this enum, what is the value of Blue? enum COLOR { WHITE, BLACK = 100, RED, BLUE, GREEN = 300 }; BLUE = 102 7. Which of the following variable names are good, which are bad, and which are invalid? a. Age Good b. !ex Not legal c. R79J Legal, but a bad choice d. TotalIncome Good e. __Invalid Legal, but a bad choice Exercises 1. What would be the correct variable type in which to store the following information? a. Your age. Unsigned short integer b. The area of your backyard. Unsigned long integer or unsigned float c. The number of stars in the galaxy. Unsigned double d. The average rainfall for the month of January. Unsigned short integer 2. Create good variable names for this information. a. myAge b. backYardArea c. StarsInGalaxy d. averageRainFall3. Declare a constant for pi as 3.14159. const float PI = 3.14159; 4. Declare a float variable and initialize it using your pi constant. float myPi = PI; Day 4 Quiz 1. What is an expression? Any statement that returns a value. 2. Is x = 5 + 7 an expression? What is its value? Yes. 12 3. What is the value of 201 / 4? 50 4. What is the value of 201 % 4? 1 5. If myAge, a, and b are all int variables, what are their values after: myAge = 39; a = myAge++; b = ++myAge; myAge: 41, a: 39, b: 41 6. What is the value of 8+2*3? 14 7. What is the difference between if(x = 3) and if(x == 3)? The first one assigns 3 to x and returns true. The second one tests whether x is equal to 3; it returns true if the value of x is equal to 3 and false if it is not.8. Do the following values evaluate to TRUE or FALSE? a. 0 FALSE b. 1 TRUE c. -1 TRUE d. x = 0 FALSE e. x == 0 // assume that x has the value of 0 TRUE Exercises 1. Write a single if statement that examines two integer variables and changes the larger to the smaller, using only one else clause. if (x > y) x = y; else y = x; // y > x || y == x 2. Examine the following program. Imagine entering three numbers, and write what output you expect. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14 15: 16: 17: 15: #include int main() { int a, b, c; cout << "Please enter three numbers\n"; cout << "a: "; cin >> a; cout << "\nb: "; cin >> b; cout << "\nc: "; cin >> c; if (c = (a-b)) { cout << "a: " << a << " minus b: "; cout << b << " _equals c: " << c; } else16: 17: 18: cout << "a-b does not equal c: "; return 0; } 3. Enter the program from Exercise 2; compile, link, and run it. Enter the numbers 20, 10, and 50. Did you get the output you expected? Why not? Enter 20, 10, 50. Get back a: 20 b: 30 c: 10. Line 13 is assigning, not testing for equality. 4. Examine this program and anticipate the output: 1: 2: 3: 4: 5: 6: 7: #include int main() { int a = 2, b = 2, c; if (c = (a-b)) cout << "The value of c is: " << c; return 0; 8: } 5. Enter, compile, link, and run the program from Exercise 4. What was the output? Why? Because line 5 is assigning the value of a-b to c, the value of the assignment is a (1) minus b (1), or 0. Because 0 is evaluated as FALSE, the if fails and nothing is printed. Day 5 Quiz 1. What are the differences between the function prototype and the function defi-nition? The function prototype declares the function; the definition defines it. The prototype ends with a semicolon; the definition need not. The declaration can include the keyword inline and default values for the parameters; the definition cannot. The declaration need not include names for the parameters; the definition must. 2. Do the names of parameters have to agree in the prototype, definition, and call to the function? No. All parameters are identified by position, not name.3. If a function doesn't return a value, how do you declare the function? Declare the function to return void. 4. If you don't declare a return value, what type of return value is assumed? Any function that does not explicitly declare a return type returns int. 5. What is a local variable? A local variable is a variable passed into or declared within a block, typically a function. It is visible only within the block. 6. What is scope? Scope refers to the visibility and lifetime of local and global variables. Scope is usually established by a set of braces. 7. What is recursion? Recursion generally refers to the ability of a function to call itself. 8. When should you use global variables? Global variables are typically used when many functions need access to the same data. Global variables are very rare in C++; once you know how to create static class variables, you will almost never create global variables. 9. What is function overloading? Function overloading is the ability to write more than one function with the same name, distinguished by the number or type of the parameters. 10. What is polymorphism? Polymorphism is the ability to treat many objects of differing but related types without regard to their differences. In C++, polymorphism is accomplished by using class derivation and virtual functions. Exercises 1. Write the prototype for a function named Perimeter, which returns an unsigned long int and which takes two parameters, both unsigned short ints. unsigned long int Perimeter(unsigned short int, unsigned short int); 2. Write the definition of the function Perimeter as described in Exercise 1. The two parameters represent the length and width of a rectangle and have the function return theperimeter (twice the length plus twice the width). unsigned long int Perimeter(unsigned short int length, unsigned short int width) { return 2*length + 2*width; } 3. BUG BUSTERS: What is wrong with the function? #include void myFunc(unsigned short int x); int main() { unsigned short int x, y; y = myFunc(int); cout << "x: " << x << " y: " << y << "\n"; return 0; } void myFunc(unsigned short int x) { return (4*x); } The function is declared to return void and it cannot return a value. 4. BUG BUSTERS: What is wrong with the function? #include int myFunc(unsigned short int x); int main() { unsigned short int x, y; y = myFunc(int); cout << "x: " << x << " y: " << y << "\n"; return 0; } int myFunc(unsigned short int x) { return (4*x); } This function would be fine, but there is a semicolon at the end of the function definition'sheader. 5. Write a function that takes two unsigned short int arguments and returns the result of dividing the first by the second. Do not do the division if the second number is 0, but do return -1. short int Divider(unsigned short int valOne, unsigned short int valTwo) { if (valTwo == 0) return -1; else return valOne / valTwo; } 6. Write a program that asks the user for two numbers and calls the function you wrote in Exercise 5. Print the answer, or print an error message if you get -1. #include typedef unsigned short int USHORT; typedef unsigned long int ULONG; short int Divider( unsigned short int valone, unsigned short int valtwo); int main() { USHORT one, two; short int answer; cout << "Enter two numbers.\n Number one: "; cin >> one; cout << "Number two: "; cin >> two; answer = Divider(one, two); if (answer > -1) cout << "Answer: " << answer; else cout << "Error, can't divide by zero!"; return 0; } 7. Write a program that asks for a number and a power. Write a recursive function that takes the number to the power. Thus, if the number is 2 and the power is 4, the function will return 16. #include typedef unsigned short USHORT;typedef unsigned long ULONG; ULONG GetPower(USHORT n, USHORT power); int main() { USHORT number, power; ULONG answer; cout << "Enter a number: "; cin >> number; cout << "To what power? "; cin >> power; answer = GetPower(number,power); cout << number << " to the " << power << "th power is " << answer << endl; return 0; } ULONG GetPower(USHORT n, USHORT power) { if(power == 1) return n; else return (n * GetPower(n,power-1)); } Day 6 Quiz 1. What is the dot operator, and what is it used for? The dot operator is the period (.). It is used to access the members of the class. 2. Which sets aside memory--declaration or definition? Definitions of variables set aside memory. Declarations of classes don't set aside memory. 3. Is the declaration of a class its interface or its implementation? The declaration of a class is its interface; it tells clients of the class how to interact with the class. The implementation of the class is the set of member functions stored--usually in a related CPP file. 4. What is the difference between public and private data members? Public data members can be accessed by clients of the class. Private data members can be accessed only by member functions of the class.5. Can member functions be private? Yes. Both member functions and member data can be private. 6. Can member data be public? Although member data can be public, it is good programming practice to make it private and to provide public accessor functions to the data. 7. If you declare two Cat objects, can they have different values in their itsAge member data? Yes. Each object of a class has its own data members. 8. Do class declarations end with a semicolon? Do class method definitions? Declarations end with a semicolon after the closing brace; function definitions do not. 9. What would the header for a Cat function, Meow, that takes no parameters and returns void look like? The header for a Cat function, Meow(), that takes no parameters and returns void looks like this: void Cat::Meow() 10. What function is called to initialize a class? The constructor is called to initialize a class. Exercises 1. Write the code that declares a class called Employee with these data members: age, yearsOfService, and Salary. class Employee { int Age; int YearsOfService; int Salary; }; 2. Rewrite the Employee class to make the data members private, and provide public accessor methods to get and set each of the data members. class Employee {public: int GetAge() const; void SetAge(int age); int GetYearsOfService()const; void SetYearsOfService(int years); int GetSalary()const; void SetSalary(int salary); private: int Age; int YearsOfService; int Salary; }; 3. Write a program with the Employee class that makes two Employees; sets their age, YearsOfService, and Salary; and prints their values. main() { Employee John; Employee Sally; John.SetAge(30); John.SetYearsOfService(5); John.SetSalary(50000); Sally.SetAge(32); Sally.SetYearsOfService(8); Sally.SetSalary(40000); cout << "At AcmeSexist company, John and Sally have the same job.\n"; cout << "John is " << John.GetAge() << " years old and he has been with"; cout << "the firm for " << John.GetYearsOfService << " years.\n"; cout << "John earns $" << John.GetSalary << " dollars per year.\n\n"; cout << "Sally, on the other hand is " << Sally.GetAge() << " years old and has"; cout << "been with the company " << Sally.GetYearsOfService; cout << " years. Yet Sally only makes $" << Sally.GetSalary(); cout << " dollars per year! Something here is unfair."; 4. Continuing from Exercise 3, provide a method of Employee that reports how many thousands of dollars the employee earns, rounded to the nearest 1,000.float Employee:GetRoundedThousands()const { return Salary / 1000; } 5. Change the Employee class so that you can initialize age, YearsOfService, and Salary when you create the employee. class Employee { public: Employee(int age, int yearsOfService, int salary); int GetAge()const; void SetAge(int age); int GetYearsOfService()const; void SetYearsOfService(int years); int GetSalary()const; void SetSalary(int salary); private: int Age; int YearsOfService; int Salary; }; 6. BUG BUSTERS: What is wrong with the following declaration? class Square { public: int Side; } Class declarations must end with a semicolon. 7. BUG BUSTERS: Why isn't the following class declaration very useful? class Cat { int GetAge()const; private: int itsAge; }; The accessor GetAge() is private. Remember: All class members are private unless you sayotherwise. 8. BUG BUSTERS: What three bugs in this code will the compiler find? class TV { public: void SetStation(int Station); int GetStation() const; private: int itsStation; }; main() { TV myTV; myTV.itsStation = 9; TV.SetStation(10); TV myOtherTv(2); } You can't access itsStation directly. It is private. You can't call SetStation() on the class. You can call SetStation() only on objects. You can't initialize itsStation because there is no matching constructor. Day 7 Quiz 1. How do I initialize more than one variable in a for loop? Separate the initializations with commas, such as for (x = 0, y = 10; x < 100; x++, y++) 2. Why is goto avoided? goto jumps in any direction to any arbitrary line of code. This makes for source code that is difficult to understand and therefore difficult to maintain. 3. Is it possible to write a for loop with a body that is never executed? Yes, if the condition is FALSE after the initialization, the body of the for loop will never execute. Here's an example: for (int x = 100; x < 100; x++)4. Is it possible to nest while loops within for loops? Yes. Any loop can be nested within any other loop. 5. Is it possible to create a loop that never ends? Give an example. Yes. Following are examples for both a for loop and a while loop: for(;;) { // This for loop never ends! } while(1) { // This while loop never ends! } 6. What happens if you create a loop that never ends? Your program hangs, and you usually must reboot the computer. Exercises 1. What is the value of x when the for loop completes? for (int x = 0; x < 100; x++) 100 2. Write a nested for loop that prints a 10x10 pattern of 0s. for (int i = 0; i< 10; i++) { for ( int j = 0; j< 10; j++) cout << "0"; cout << "\n"; } 3. Write a for statement to count from 100 to 200 by 2s. for (int x = 100; x<=200; x+=2) 4. Write a while loop to count from 100 to 200 by 2s.int x = 100; while (x <= 200) x+= 2; 5. Write a do...while loop to count from 100 to 200 by 2s. int x = 100; do { x+=2; } while (x <= 200); 6. BUG BUSTERS: What is wrong with this code? int counter = 0 while (counter < 10) { cout << "counter: " << counter; counter++; } counter is never incremented and the while loop will never terminate. 7. BUG BUSTERS: What is wrong with this code? for (int counter = 0; counter < 10; counter++); cout << counter << "\n"; There is a semicolon after the loop, and the loop does nothing. The programmer may have intended this, but if counter was supposed to print each value, it won't. 8. BUG BUSTERS: What is wrong with this code? int counter = 100; while (counter < 10) { cout << "counter now: " << counter; counter--; } counter is initialized to 100, but the test condition is that if it is less than 10, the test will fail and the body will never be executed. If line 1 were changed to int counter = 5;, the loop would not terminate until it had counted down past the smallest possible int. Because int is signed by default, this would not be what was intended.9. BUG BUSTERS: What is wrong with this code? cout << "Enter a number cin >> theNumber; switch (theNumber) { case 0: doZero(); case 1: case 2: case 3: case 4: case 5: doOneToFive(); break; default: doDefault(); break; } between 0 and 5: "; // // // // fall fall fall fall through through through through Case 0 probably needs a break statement. If it does not, it should be documented with a comment. Day 8 Quiz 1. What operator is used to determine the address of a variable? The address of operator (&) is used to determine the address of any variable. 2. What operator is used to find the value stored at an address held in a pointer? The dereference operator (*) is used to access the value at an address in a pointer. 3. What is a pointer? A pointer is a variable that holds the address of another variable. 4. What is the difference between the address stored in a pointer and the value at that address? The address stored in the pointer is the address of another variable. The value stored at that address is any value stored in any variable. The indirection operator (*) returns the value stored at the address, which itself is stored in the pointer.5. What is the difference between the indirection operator and the address of oper-ator? The indirection operator returns the value at the address stored in a pointer. The address of operator (&) returns the memory address of the variable. 6. What is the difference between const int * ptrOne and int * const ptrTwo? The const int * ptrOne declares that ptrOne is a pointer to a constant integer. The integer itself cannot be changed using this pointer. The int * const ptrTwo declares that ptrTwo is a constant pointer to an integer. Once it is initialized, this pointer cannot be reassigned. Exercises 1. What do these declarations do? a. int * pOne; b. int vTwo; c. int * pThree = &vTwo; a. int * pOne; declares a pointer to an integer. b. int vTwo; declares an integer variable. c. int * pThree = &vTwo; declares a pointer to an integer and initializes it with the address of another variable. 2. If you have an unsigned short variable named yourAge, how would you declare a pointer to manipulate yourAge? unsigned short *pAge = &yourAge; 3. Assign the value 50 to the variable yourAge by using the pointer that you declared in Exercise 2. *pAge = 50; 4. Write a small program that declares an integer and a pointer to integer. Assign the address of the integer to the pointer. Use the pointer to set a value in the integer variable. int theInteger; int *pInteger = &theInteger; *pInteger = 5; 5. BUG BUSTERS: What is wrong with this code? #include int main() { int *pInt; *pInt = 9; cout << "The value at pInt: " << *pInt; return 0; } pInt should have been initialized. More importantly, because it was not initialized and was not assigned the address of any memory, it points to a random place in memory. Assigning 9 to that random place is a dangerous bug. 6. BUG BUSTERS: What is wrong with this code? int main() { int SomeVariable = 5; cout << "SomeVariable: " << SomeVariable << "\n"; int *pVar = & SomeVariable; pVar = 9; cout << "SomeVariable: " << *pVar << "\n"; return 0; } Presumably, the programmer meant to assign 9 to the value at pVar. Unfortunately, 9 was assigned to be the value of pVar because the indirection operator (*) was left off. This will lead to disaster if pVar is used to assign a value. Day 9 Quiz 1. What is the difference between a reference and a pointer? A reference is an alias, and a pointer is a variable that holds an address. References cannot be null and cannot be assigned to. 2. When must you use a pointer rather than a reference? When you may need to reassign what is pointed to, or when the pointer may be null. 3. What does new return if there is insufficient memory to make your new object? A null pointer (0). 4. What is a constant reference?This is a shorthand way of saying "a reference to a constant object." 5. What is the difference between passing by reference and passing a reference? Passing by reference means not making a local copy. It can be accomplished by passing a reference or by passing a pointer. Exercises 1. Write a program that declares an int, a reference to an int, and a pointer to an int. Use the pointer and the reference to manipulate the value in the int. int main() { int varOne; int& rVar = varOne; int* pVar = &varOne; rVar = 5; *pVar = 7; return 0; } 2. Write a program that declares a constant pointer to a constant integer. Initialize the pointer to an integer variable, varOne. Assign 6 to varOne. Use the pointer to assign 7 to varOne. Create a second integer variable, varTwo. Reassign the pointer to varTwo. int main() { int varOne; const int * const pVar = &varOne; *pVar = 7; int varTwo; pVar = &varTwo; return 0; } 3. Compile the program in Exercise 2. What produces errors? What produces warnings? You can't assign a value to a constant object, and you can't reassign a constant pointer. 4. Write a program that produces a stray pointer. int main() { int * pVar; *pVar = 9;return 0; } 5. Fix the program from Exercise 4. int main() { int VarOne; int * pVar = &varOne; *pVar = 9; return 0; } 6. Write a program that produces a memory leak. int FuncOne(); int main() { int localVar = FunOne(); cout << "the value of localVar is: " << localVar; return 0; } int FuncOne() { int * pVar = new int (5); return *pVar; } 7. Fix the program from Exercise 6. void FuncOne(); int main() { FuncOne(); return 0; } void FuncOne() { int * pVar = new int (5); cout << "the value of *pVar is: " << *pVar ; } 8. BUG BUSTERS: What is wrong with this program?1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: #include class CAT { public: CAT(int age) { itsAge = age; } ~CAT(){} int GetAge() const { return itsAge;} private: int itsAge; }; CAT & MakeCat(int age); int main() { int age = 7; CAT Boots = MakeCat(age); cout << "Boots is " << Boots.GetAge() << " years old\n"; return 0; } CAT & MakeCat(int age) { CAT * pCat = new CAT(age); return *pCat; } MakeCat returns a reference to the CAT created on the free store. There is no way to free that memory, and this produces a memory leak. 9. Fix the program from Exercise 8. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: #include class CAT { public: CAT(int age) { itsAge = age; } ~CAT(){} int GetAge() const { return itsAge;} private: int itsAge; }; CAT * MakeCat(int age); int main()15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: { int age = 7; CAT * Boots = MakeCat(age); cout << "Boots is " << Boots->GetAge() << " years old\n"; delete Boots; return 0; } CAT * MakeCat(int age) { return new CAT(age); } Day 10 Quiz 1. When you overload member functions, in what ways must they differ? Overloaded member functions are functions in a class that share a name but differ in the number or type of their parameters. 2. What is the difference between a declaration and a definition? A definition sets aside memory, but a declaration does not. Almost all declarations are definitions; the major exceptions are class declarations, function prototypes, and typedef statements. 3. When is the copy constructor called? Whenever a temporary copy of an object is created. This happens every time an object is passed by value. 4. When is the destructor called? The destructor is called each time an object is destroyed, either because it goes out of scope or because you call delete on a pointer pointing to it. 5. How does the copy constructor differ from the assignment operator (=)? The assignment operator acts on an existing object; the copy constructor creates a new one. 6. What is the this pointer? The this pointer is a hidden parameter in every member function that points to the object itself. 7. How do you differentiate between overloading the prefix and postfix increments?The prefix operator takes no parameters. The postfix operator takes a single int parameter, which is used as a signal to the compiler that this is the postfix variant. 8. Can you overload the operator+ for short integers? No, you cannot overload any operator for built-in types. 9. Is it legal in C++ to overload operator++ so that it decrements a value in your class? It is legal, but it is a bad idea. Operators should be overloaded in a way that is likely to be readily understood by anyone reading your code. 10. What return value must conversion operators have in their declaration? None. Like constructors and destructors, they have no return values. Exercises 1. Write a SimpleCircle class declaration (only) with one member variable: itsRadius. Include a default constructor, a destructor, and accessor methods for itsRadius. class SimpleCircle { public: SimpleCircle(); ~SimpleCircle(); void SetRadius(int); int GetRadius(); private: int itsRadius; }; 2. Using the class you created in Exercise 1, write the implementation of the default constructor, initializing itsRadius with the value 5. SimpleCircle::SimpleCircle(): itsRadius(5) {} 3. Using the same class, add a second constructor that takes a value as its parameter and assigns that value to itsRadius. SimpleCircle::SimpleCircle(int radius): itsRadius(radius) {}4. Create a prefix and postfix increment operator for your SimpleCircle class that increments itsRadius. const SimpleCircle& SimpleCircle::operator++() { ++(itsRadius); return *this; } // Operator ++(int) postfix. // Fetch then increment const SimpleCircle SimpleCircle::operator++ (int) { // declare local SimpleCircle and initialize to value of *this SimpleCircle temp(*this); ++(itsRadius); return temp; } 5. Change SimpleCircle to store itsRadius on the free store, and fix the existing methods. class SimpleCircle { public: SimpleCircle(); SimpleCircle(int); ~SimpleCircle(); void SetRadius(int); int GetRadius(); const SimpleCircle& operator++(); const SimpleCircle operator++(int); private: int *itsRadius; }; SimpleCircle::SimpleCircle() {itsRadius = new int(5);} SimpleCircle::SimpleCircle(int radius) {itsRadius = new int(radius);} const SimpleCircle& SimpleCircle::operator++() { ++(itsRadius);return *this; } // Operator ++(int) postfix. // Fetch then increment const SimpleCircle SimpleCircle::operator++ (int) { // declare local SimpleCircle and initialize to value of *this SimpleCircle temp(*this); ++(itsRadius); return temp; } 6. Provide a copy constructor for SimpleCircle. SimpleCircle::SimpleCircle(const SimpleCircle & rhs) { int val = rhs.GetRadius(); itsRadius = new int(val); } 7. Provide an operator= for SimpleCircle. SimpleCircle& SimpleCircle::operator=(const SimpleCircle & rhs) { if (this == &rhs) return *this; delete itsRadius; itsRadius = new int; *itsRadius = rhs.GetRadius(); return *this; } 8. Write a program that creates two SimpleCircle objects. Use the default constructor on one and instantiate the other with the value 9. Call increment on each and then print their values. Finally, assign the second to the first and print its values. #include class SimpleCircle { public: // constructors SimpleCircle(); SimpleCircle(int); SimpleCircle(const SimpleCircle &);~SimpleCircle() {} // accessor functions void SetRadius(int); int GetRadius()const; // operators const SimpleCircle& operator++(); const SimpleCircle operator++(int); SimpleCircle& operator=(const SimpleCircle &); private: int *itsRadius; }; SimpleCircle::SimpleCircle() {itsRadius = new int(5);} SimpleCircle::SimpleCircle(int radius) {itsRadius = new int(radius);} SimpleCircle::SimpleCircle(const SimpleCircle & rhs) { int val = rhs.GetRadius(); itsRadius = new int(val); } SimpleCircle& SimpleCircle::operator=(const SimpleCircle & rhs) { if (this == &rhs) return *this; *itsRadius = rhs.GetRadius(); return *this; } const SimpleCircle& SimpleCircle::operator++() { ++(itsRadius); return *this; } // Operator ++(int) postfix. // Fetch then increment const SimpleCircle SimpleCircle::operator++ (int) { // declare local SimpleCircle and initialize to value of *this SimpleCircle temp(*this); ++(itsRadius);return temp; } int SimpleCircle::GetRadius() const { return *itsRadius; } int main() { SimpleCircle CircleOne, CircleTwo(9); CircleOne++; ++CircleTwo; cout << "CircleOne: " << CircleOne.GetRadius() cout << "CircleTwo: " << CircleTwo.GetRadius() CircleOne = CircleTwo; cout << "CircleOne: " << CircleOne.GetRadius() cout << "CircleTwo: " << CircleTwo.GetRadius() return 0; } << endl; << endl; << endl; << endl; 9. BUG BUSTERS: What is wrong with this implementation of the assignment operator? SQUARE SQUARE ::operator=(const SQUARE & rhs) { itsSide = new int; *itsSide = rhs.GetSide(); return *this; } You must check to see whether rhs equals this, or the call to a = a will crash your program. 10. BUG BUSTERS: What is wrong with this implementation of operator+? VeryShort VeryShort::operator+ (const VeryShort& rhs) { itsVal += rhs.GetItsVal(); return *this; } This operator+ is changing the value in one of the operands, rather than creating a new VeryShort object with the sum. The right way to do this is as follows: VeryShort VeryShort::operator+ (const VeryShort& rhs) { return VeryShort(itsVal + rhs.GetItsVal());} Day 11 Quiz 1. What are the first and last elements in SomeArray[25]? SomeArray[0], SomeArray[24] 2. How do you declare a multidimensional array? Write a set of subscripts for each dimension. For example, SomeArray[2][3][2] is a three-dimensional array. The first dimension has two elements, the second has three, and the third has two. 3. Initialize the members of the array in Question 2. SomeArray[2][3][2] = { { {1,2},{3,4},{5,6} } , { {7,8},{9,10},{11,12} } }; 4. How many elements are in the array SomeArray[10][5][20]? 10x5x20=1,000 5. What is the maximum number of elements that you can add to a linked list? There is no fixed maximum. It depends on how much memory you have available. 6. Can you use subscript notation on a linked list? You can use subscript notation on a linked list only by writing your own class to contain the linked list and overloading the subscript operator. 7. What is the last character in the string "Brad is a nice guy"? The null character. Exercises 1. Declare a two-dimensional array that represents a tic-tac-toe game board. int GameBoard[3][3]; 2. Write the code that initializes all the elements in the array you created in Exercise 1 to the value 0.int GameBoard[3][3] = { {0,0,0},{0,0,0},{0,0,0} } 3. Write the declaration for a Node class that holds unsigned short integers. class Node { public: Node (); Node (int); ~Node(); void SetNext(Node * node) { itsNext = node; } Node * GetNext() const { return itsNext; } int GetVal() const { return itsVal; } void Insert(Node *); void Display(); private: int itsVal; Node * itsNext; }; 4. BUG BUSTERS: What is wrong with this code fragment? unsigned short SomeArray[5][4]; for (int i = 0; i<4; i++) for (int j = 0; j<5; j++) SomeArray[i][j] = i+j; The array is 5 elements by 4 elements, but the code initializes 4x5. 5. BUG BUSTERS: What is wrong with this code fragment? unsigned short SomeArray[5][4]; for (int i = 0; i<=5; i++) for (int j = 0; j<=4; j++) SomeArray[i][j] = 0; You wanted to write i<5, but you wrote i<=5 instead. The code will run when i == 5 and j == 4, but there is no such element as SomeArray[5][4]. Day 12 Quiz 1. What is a v-table?A v-table, or virtual function table, is a common way for compilers to manage virtual functions in C++. The table keeps a list of the addresses of all the virtual functions and, depending on the runtime type of the object pointed to, invokes the right function. 2. What is a virtual destructor? A destructor of any class can be declared to be virtual. When the pointer is deleted, the runtime type of the object will be assessed and the correct derived destructor invoked. 3. How do you show the declaration of a virtual constructor? There are no virtual constructors. 4. How can you create a virtual copy constructor? By creating a virtual method in your class, which itself calls the copy constructor. 5. How do you invoke a base member function from a derived class in which you've overridden that function? Base::FunctionName(); 6. How do you invoke a base member function from a derived class in which you have not overridden that function? FunctionName(); 7. If a base class declares a function to be virtual, and a derived class does not use the term virtual when overriding that class, is it still virtual when inherited by a third- generation class? Yes, the virtuality is inherited and cannot be turned off. 8. What is the protected keyword used for? protected members are accessible to the member functions of derived objects. Exercises 1. Show the declaration of a virtual function taking an integer parameter and returning void. virtual void SomeFunction(int); 2. Show the declaration of a class Square, which derives from Rectangle, which in turn derives from Shape.class Square : public Rectangle {}; 3. If, in Exercise 2, Shape takes no parameters, Rectangle takes two (length and width), and Square takes only one (length), show the constructor initialization for Square. Square::Square(int length): Rectangle(length, length){} 4. Write a virtual copy constructor for the class Square (from the preceding question). class Square { public: // ... virtual Square * clone() const { return new Square(*this); } // ... }; 5. BUG BUSTERS: What is wrong with this code snippet? void SomeFunction (Shape); Shape * pRect = new Rectangle; SomeFunction(*pRect); Perhaps nothing. SomeFunction expects a Shape object. You've passed it a Rectangle "sliced" down to a Shape. As long as you don't need any of the Rectangle parts, this will be fine. If you do need the Rectangle parts, you'll need to change SomeFunction to take a pointer or a reference to a Shape. 6. BUG BUSTERS: What is wrong with this code snippet? class Shape() { public: Shape(); virtual ~Shape(); virtual Shape(const Shape&); }; You can't declare a copy constructor to be virtual. Day 13Quiz 1. What is a down cast? A down cast (also called "casting down") is a declaration that a pointer to a base class is to be treated as a pointer to a derived class. 2. What is the v-ptr? The v-ptr, or virtual-function pointer, is an implementation detail of virtual functions. Each object in a class with virtual functions has a v-ptr, which points to the virtual function table for that class. 3. If a round rectangle has straight edges and rounded corners, your RoundRect class inherits both from Rectangle and from Circle, and they in turn both inherit from Shape, how many Shapes are created when you create a RoundRect? If neither class inherits using the keyword virtual, two Shapes are created: one for Rectangle and one for Shape. If the keyword virtual is used for both classes, only one shared Shape is created. 4. If Horse and Bird inherit virtual public from Animal, do their constructors initialize the Animal constructor? If Pegasus inherits from both Horse and Bird, how does it initialize Animal's constructor? Both Horse and Bird initialize their base class, Animal, in their constructors. Pegasus does as well, and when a Pegasus is created, the Horse and Bird initializations of Animal are ignored. 5. Declare a class Vehicle and make it an abstract data type. class Vehicle { virtual void Move() = 0; } 6. If a base class is an ADT, and it has three pure virtual functions, how many of these functions must be overridden in its derived classes? None must be overridden unless you want to make the class non-abstract, in which case all three must be overridden. Exercises 1. Show the declaration for a class JetPlane, which inherits from Rocket and Airplane.class JetPlane : public Rocket, public Airplane 2. Show the declaration for 747, which inherits from the JetPlane class described in Exercise 1. class 747 : public JetPlane 3. Show the declarations for the classes Car and Bus, which each derive from the class Vehicle. Make Vehicle an ADT with two pure virtual functions. Make Car and Bus not be ADTs. class Vehicle { virtual void Move() = 0; virtual void Haul() = 0; }; class Car : public Vehicle { virtual void Move(); virtual void Haul(); }; class Bus : public Vehicle { virtual void Move(); virtual void Haul(); }; 4. Modify the program in Exercise 1 so that Car is an ADT, and derive SportsCar and Coupe from Car. In the Car class, provide an implementation for one of the pure virtual functions in Vehicle and make it non-pure. class Vehicle { virtual void Move() = 0; virtual void Haul() = 0; }; class Car : public Vehicle { virtual void Move(); }; class Bus : public Vehicle {virtual void Move(); virtual void Haul(); }; class SportsCar : public Car { virtual void Haul(); }; class Coupe : public Car { virtual void Haul(); }; Day 14 Quiz 1. Can static member variables be private? Yes. They are member variables, and their access can be controlled like any other. If they are private, they can be accessed only by using member functions or, more commonly, static member functions. 2. Show the declaration for a static member variable. static int itsStatic; 3. Show the declaration for a static function pointer. static int SomeFunction(); 4. Show the declaration for a pointer to function returning long and taking an integer parameter. long (* function)(int); 5. Modify the pointer in Exercise 4 to be a pointer to member function of class Car long ( Car::*function)(int); 6. Show the declaration for an array of 10 pointers as defined in Exercise 5. (long ( Car::*function)(int) theArray [10];Exercises 1. Write a short program declaring a class with one member variable and one static member variable. Have the constructor initialize the member variable and increment the static member variable. Have the destructor decrement the member variable. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: class myClass { public: myClass(); ~myClass(); private: int itsMember; static int itsStatic; }; myClass::myClass(): itsMember(1) { itsStatic++; } myClass::~myClass() { itsStatic--; } int myClass::itsStatic = 0; int main() {} 2. Using the program from Exercise 1, write a short driver program that makes three objects and then displays their member variables and the static member variable. Then destroy each object and show the effect on the static member variable. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: #include class myClass { public: myClass(); ~myClass(); void ShowMember(); void ShowStatic(); private:11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: endl; 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: int itsMember; static int itsStatic; }; myClass::myClass(): itsMember(1) { itsStatic++; } myClass::~myClass() { itsStatic--; cout << "In destructor. ItsStatic: " << itsStatic << } void myClass::ShowMember() { cout << "itsMember: " << itsMember << endl; } void myClass::ShowStatic() { cout << "itsStatic: " << itsStatic << endl; } int myClass::itsStatic = 0; int main() { myClass obj1; obj1.ShowMember(); obj1.ShowStatic(); myClass obj2; obj2.ShowMember(); obj2.ShowStatic(); myClass obj3; obj3.ShowMember(); obj3.ShowStatic(); return 0; } 3. Modify the program from Exercise 2 to use a static member function to access the static member variable. Make the static member variable private.1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: endl; 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: #include class myClass { public: myClass(); ~myClass(); void ShowMember(); static int GetStatic(); private: int itsMember; static int itsStatic; }; myClass::myClass(): itsMember(1) { itsStatic++; } myClass::~myClass() { itsStatic--; cout << "In destructor. ItsStatic: " << itsStatic << } void myClass::ShowMember() { cout << "itsMember: " << itsMember << endl; } int myClass::itsStatic = 0; void myClass::GetStatic() { return itsStatic; } int main() { myClass obj1; obj1.ShowMember(); cout << "Static: " << myClass::GetStatic() << endl;45: 46: 47: 48: 49: 50: 51: 52: 53: myClass obj2; obj2.ShowMember(); cout << "Static: " << myClass::GetStatic() << endl; myClass obj3; obj3.ShowMember(); cout << "Static: " << myClass::GetStatic() << endl; return 0; } 4. Write a pointer to a member function to access the non-static member data in the program in Exercise 3, and use that pointer to print the value of that data. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: endl; 25: 26: 27: 28: 29: 30: 31: 32: #include class myClass { public: myClass(); ~myClass(); void ShowMember(); static int GetStatic(); private: int itsMember; static int itsStatic; }; myClass::myClass(): itsMember(1) { itsStatic++; } myClass::~myClass() { itsStatic--; cout << "In destructor. ItsStatic: " << itsStatic << } void myClass::ShowMember() { cout << "itsMember: " << itsMember << endl; } int myClass::itsStatic = 0;33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: int myClass::GetStatic() { return itsStatic; } int main() { void (myClass::*PMF) (); PMF=myClass::ShowMember; myClass obj1; (obj1.*PMF)(); cout << "Static: " << myClass::GetStatic() << endl; myClass obj2; (obj2.*PMF)(); cout << "Static: " << myClass::GetStatic() << endl; myClass obj3; (obj3.*PMF)(); cout << "Static: " << myClass::GetStatic() << endl; return 0; } 5. Add two more member variables to the class from the previous questions. Add accessor functions that get the value of this data and give all the member functions the same return values and signatures. Use the pointer to the member function to access these functions. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: #include class myClass { public: myClass(); ~myClass(); void ShowMember(); void ShowSecond(); void ShowThird(); static int GetStatic(); private: int itsMember; int itsSecond; int itsThird; static int itsStatic;17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: endl; 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: }; myClass::myClass(): itsMember(1), itsSecond(2), itsThird(3) { itsStatic++; } myClass::~myClass() { itsStatic--; cout << "In destructor. ItsStatic: " << itsStatic << } void myClass::ShowMember() { cout << "itsMember: " << itsMember << endl; } void myClass::ShowSecond() { cout << "itsSecond: " << itsSecond << endl; } void myClass::ShowThird() { cout << "itsThird: " << itsThird << endl; } int myClass::itsStatic = 0; int myClass::GetStatic() { return itsStatic; } int main() { void (myClass::*PMF) (); myClass obj1; PMF=myClass::ShowMember; (obj1.*PMF)(); PMF=myClass::ShowSecond;62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: (obj1.*PMF)(); PMF=myClass::ShowThird; (obj1.*PMF)(); cout << "Static: " << myClass::GetStatic() << endl; myClass obj2; PMF=myClass::ShowMember; (obj2.*PMF)(); PMF=myClass::ShowSecond; (obj2.*PMF)(); PMF=myClass::ShowThird; (obj2.*PMF)(); cout << "Static: " << myClass::GetStatic() << endl; myClass obj3; PMF=myClass::ShowMember; (obj3.*PMF)(); PMF=myClass::ShowSecond; (obj3.*PMF)(); PMF=myClass::ShowThird; (obj3.*PMF)(); cout << "Static: " << myClass::GetStatic() << endl; return 0; } Day 15 Quiz 1. How do you establish an is-a relationship? With public inheritance. 2. How do you establish a has-a relationship? With containment; that is, one class has a member that is an object of another type. 3. What is the difference between containment and delegation? Containment describes the idea of one class having a data member that is an object of another type. Delegation expresses the idea that one class uses another class to accomplish a task or goal. Delegation is usually accomplished by containment. 4. What is the difference between delegation and implemented-in-terms-of? Delegation expresses the idea that one class uses another class to accomplish a task or goal. Implemented-in-terms-of expresses the idea of inheriting implementation from another class.5. What is a friend function? A friend function is a function declared to have access to the protected and private members of your class. 6. What is a friend class? A friend class is a class declared so that all its member functions are friend functions of your class. 7. If Dog is a friend of Boy, is Boy a friend of Dog? No, friendship is not commutative. 8. If Dog is a friend of Boy, and Terrier derives from Dog, is Terrier a friend of Boy? No, friendship is not inherited. 9. If Dog is a friend of Boy and Boy is a friend of House, is Dog a friend of House? No, friendship is not associative. 10. Where must the declaration of a friend function appear? Anywhere within the class declaration. It makes no difference whether you put the declaration within the public:, protected:, or private: access areas. Exercises 1. Show the declaration of a class Animal that contains a data member that is a String object. class Animal: { private: String itsName; }; 2. Show the declaration of a class BoundedArray that is an array. class boundedArray : public Array { //... }3. Show the declaration of a class Set that is declared in terms of an array. class Set : private Array { // ... } 4. Modify Listing 15.1 to provide the String class with an extraction operator (>>). 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: } 26: 27: 28: 29: 30: 31: 32: 33 34: 35: #include #include class String { public: // constructors String(); String(const char *const); String(const String &); ~String(); // overloaded operators char & operator[](int offset); char operator[](int offset) const; String operator+(const String&); void operator+=(const String&); String & operator= (const String &); friend ostream& operator<< ( ostream& _theStream,String& theString); friend istream& operator>> ( istream& _theStream,String& theString); // General accessors int GetLen()const { return itsLen; } const char * GetString() const { return itsString; // static int ConstructorCount; private: String (int); // private constructor char * itsString; unsigned short itsLen; }; ostream& operator<<( ostream& theStream,String&theString) 36: { 37: theStream << theString.GetString(); 38: return theStream; 39: } 40: 41: istream& operator>>( istream& theStream,String& theString) 42: { 43: theStream >> theString.GetString(); 44: return theStream; 45: } 46: 47: int main() 48: { 49: String theString("Hello world."); 50: cout << theString; 51: return 0; 52: } 5. BUG BUSTERS: What is wrong with this program? 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: #include class Animal; void setValue(Animal& , int); class Animal { public: int GetWeight()const { return itsWeight; } int GetAge() const { return itsAge; } private: int itsWeight; int itsAge; }; void setValue(Animal& theAnimal, int theWeight) { friend class Animal; theAnimal.itsWeight = theWeight; } int main()25: 26: 27: 28: 29: { Animal peppy; setValue(peppy,5); return 0; } You can't put the friend declaration into the function. You must declare the function to be a friend in the class. 6. Fix the listing in Exercise 5 so that it will compile. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: #include class Animal; void setValue(Animal& , int); class Animal { public: friend void setValue(Animal&, int); int GetWeight()const { return itsWeight; } int GetAge() const { return itsAge; } private: int itsWeight; int itsAge; }; void setValue(Animal& theAnimal, int theWeight) { theAnimal.itsWeight = theWeight; } int main() { Animal peppy; setValue(peppy,5); return 0; } 7. BUG BUSTERS: What is wrong with this code? 1: 2: 3: #include class Animal;4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: void setValue(Animal& , int); void setValue(Animal& ,int,int); class Animal { friend void setValue(Animal& ,int); // here's the change! private: int itsWeight; int itsAge; }; void setValue(Animal& theAnimal, int theWeight) { theAnimal.itsWeight = theWeight; } void setValue(Animal& theAnimal, int theWeight, int theAge) { theAnimal.itsWeight = theWeight; theAnimal.itsAge = theAge; } int main() { Animal peppy; setValue(peppy,5); setValue(peppy,7,9); return 0; } The function setValue(Animal&,int) was declared to be a friend, but the overloaded function setValue(Animal&,int,int) was not declared to be a friend. 8. Fix Exercise 7 so it compiles. 1: 2: 3: 4: 5: 6: 7: 8: 9: #include class Animal; void setValue(Animal& , int); void setValue(Animal& ,int,int); // here's the change! class Animal {10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: friend void setValue(Animal& ,int); friend void setValue(Animal& ,int,int); private: int itsWeight; int itsAge; }; void setValue(Animal& theAnimal, int theWeight) { theAnimal.itsWeight = theWeight; } void setValue(Animal& theAnimal, int theWeight, int theAge) { theAnimal.itsWeight = theWeight; theAnimal.itsAge = theAge; } int main() { Animal peppy; setValue(peppy,5); setValue(peppy,7,9); return 0; } Day 16 Quiz 1. What is the insertion operator and what does it do? The insertion operator (<<) is a member operator of the ostream object and is used for writing to the output device. 2. What is the extraction operator and what does it do? The extraction operator (>>) is a member operator of the istream object and is used for writing to your program's variables. 3. What are the three forms of cin.get() and what are their differences? The first form of get() is without parameters. This returns the value of the character found, and will return EOF (end of file) if the end of the file is reached. The second form of cin.get() takes a character reference as its parameter; that character isfilled with the next character in the input stream. The return value is an iostream object. The third form of cin.get() takes an array, a maximum number of characters to get, and a terminating character. This form of get() fills the array with up to one fewer characters than the maximum (appending null) unless it reads the terminating character, in which case it immediately writes a null and leaves the terminating character in the buffer. 4. What is the difference between cin.read() and cin.getline()? cin.read() is used for reading binary data structures. getline() is used to read from the istream's buffer. 5. What is the default width for ouputting a long integer using the insertion operator? Wide enough to display the entire number. 6. What is the return value of the insertion operator? A reference to an istream object. 7. What parameter does the constructor to an ofstream object take? The filename to be opened. 8. What does the ios::ate argument do? ios::ate places you at the end of the file, but you can write data anywhere in the file. Exercises 1. Write a program that writes to the four standard iostream objects: cin, cout, cerr, and clog. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: #include int main() { int x; cout << "Enter a number: "; cin >> x; cout << "You entered: " << x << endl; cerr << "Uh oh, this to cerr!" << endl; clog << "Uh oh, this to clog!" << endl; return 0; } 2. Write a program that prompts the user to enter her full name and then displays it on the screen.1: 2: 3: 4: 5: 6: 7: 8: 9: #include int main() { char name[80]; cout << "Enter your full name: "; cin.getline(name,80); cout << "\nYou entered: " << name << endl; return 0; } 3. Rewrite Listing 16.9 to do the same thing, but without using putback() or ignore(). 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: // Listing #include int main() { char ch; cout << "enter a phrase: "; while ( cin.get(ch) ) { switch (ch) { case `!': cout << `$'; break; case `#': break; default: cout << ch; break; } } return 0; } 4. Write a program that takes a filename as a parameter and opens the file for reading. Read every character of the file and display only the letters and punctuation to the screen. (Ignore all non-printing characters.) Then close the file and exit. 1: 2: 3: 4: 5: #include enum BOOL { FALSE, TRUE }; int main(int argc, char**argv) { // returns 1 on error6: 7: if (argc != 2) 8: { 9: cout << "Usage: argv[0] \n"; 10: return(1); 11: } 12: 13: // open the input stream 14: ifstream fin (argv[1],ios::binary); 15: if (!fin) 16: { 17: cout << "Unable to open " << argv[1] << " for reading.\n"; 18: return(1); 19: } 20: 21: char ch; 22: while ( fin.get(ch)) 23: if ((ch > 32 && ch < 127) || ch == `\n' || ch == `\t') 24: cout << ch; 25: fin.close(); 26: } 5. Write a program that displays its command-line arguments in reverse order and does not display the program name. 1: 2: 3: 4: 5: 6: 7: #include int main(int argc, char**argv) // returns 1 on error { for (int ctr = argc; ctr ; ctr--) cout << argv[ctr] << " "; } Day 17 Quiz 1. What is an inclusion guard? Inclusion guards are used to protect a header file from being included into a program more than once. 2. How do you instruct your compiler to print the contents of the intermediate file showing the effects of the preprocessor?This quiz question must be answered by you, depending on the compiler you are using. 3. What is the difference between #define debug 0 and #undef debug? #define debug 0 defines the term debug to equal 0 (zero). Everywhere the word debug is found, the character 0 will be substituted. #undef debug removes any definition of debug; when the word debug is found in the file, it will be left unchanged. 4. Name four predefined macros. __DATE__, __TIME__, __FILE__, __LINE__ 5. Why can't you call invariants() as the first line of your constructor? The job of your constructor is to create the object. The class invariants cannot and should not exist before the object is fully created, so any meaningful use of invariants() will return false until the constructor is finished. Exercises 1. Write the inclusion guard statements for the header file STRING.H. #ifndef STRING_H #define STRING_H ... #endif 2. Write an assert() macro that prints an error message and the file and line number if debug level is 2, prints just a message (without file and line number) if the level is 1, and does nothing if the level is 0. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: #include #ifndef DEBUG #define ASSERT(x) #elif DEBUG == 1 #define ASSERT(x) \ if (! (x)) \ { \ cout << "ERROR!! Assert " << #x << " failed\n"; \ } #elif DEBUG == 2 #define ASSERT(x) \ if (! (x) ) \ { \ cout << "ERROR!! Assert " << #x << " failed\n"; \16: 17: 18: 19: cout << " on line " << __LINE__ << "\n"; \ cout << " in file " << __FILE__ << "\n"; \ } #endif 3. Write a macro DPrint that tests whether debug is defined, and if it is, prints the value passed in as a parameter. #ifndef DEBUG #define DPRINT(string) #else #define DPRINT(STRING) cout << #STRING ; #endif 4. Write a function that prints an error message. The function should print the line number and filename where the error occurred. Note that the line number and filename are passed in to this function. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: #include void ErrorFunc( int LineNumber, const char * FileName) { cout << "An error occurred in file "; cout << FileName; cout << " at line " cout << LineNumber << endl; } 5. How would you call the preceding error function? 1: 2: 3: 4: 5: 6: 7: // driver program to exercise ErrorFunc int main() { cout << "An error occurs on next line!"; ErrorFunc(__LINE__, __FILE__); return 0; } Note that the __LINE__ and __FILE__ macros are used at the point of the error, and not in the error function. If you used them in the error function, they would report the line and file for the error function itself. 6. Write an assert() macro that uses the error function from Exercise 4, and write a driverprogram that calls that assert() macro. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: #include #define DEBUG // turn error handling on #ifndef DEBUG #define ASSERT(x) #else #define ASSERT(X) \ if (! (X)) \ { \ ErrorFunc(__LINE__, __FILE__); \ } #endif void ErrorFunc(int LineNumber, const char * FileName) { cout << "An error occurred in file "; cout << FileName; cout << " at line "; cout << LineNumber << endl; } // driver program to exercise ErrorFunc int main() { int x = 5; ASSERT(x >= 5); // no error x = 3; ASSERT(x >= 5); // error! return 0; } Note that in this case, the __LINE__ and __FILE__ macros can be called in the assert() macro and will still give the correct line (line 29). This is because the assert() macro is expanded in place, where it is called. Therefore, this program is evaluated exactly as if main() were written as 1: 2: 3: 4: 5: 6: 7: // driver program to exercise ErrorFunc int main() { int x = 5; if (! (x >= 5)) {ErrorFunc(__LINE__, __FILE__);} x = 3; if (! (x >= 5)) {ErrorFunc(__LINE__, __FILE__);}8: 9: return 0; } Day 18 Quiz 1. What is the difference between object-oriented programming and procedural programming? Procedural programming focuses on functions separate from data. Object-oriented programming ties data and functionality together into objects, and focuses on the interaction among the objects. 2. To what does "event-driven" refer? Event-driven programs are distinguished by the fact that action is taken only in response to some form of (usually external) simulation, such as a user's keyboard or mouse input. 3. What are the stages in the development cycle? Typically, the development cycle includes analysis, design, coding, testing, programming, and interaction and feedback among these stages. 4. What is a rooted hierarchy? A rooted hierarchy is one in which all the classes in the program derive directly or indirectly from a single base class. 5. What is a driver program? A driver program is simply a function that is designed to exercise whatever objects and functions you are currently programming. 6. What is encapsulation? Encapsulation refers to the (desirable) trait of bringing together in one class all the data and functionality of one discrete entity. Exercises 1. Suppose you had to simulate the intersection of Massachusetts Avenue and Vassar Street-- two typical two-lane roads with traffic lights and crosswalks. The purpose of the simulation is to determine whether the timing of the traffic signal allows for a smooth flow of traffic. What kinds of objects should be modeled in the simulation? What should be the classes defined for the simulation?Cars, motorcycles, trucks, bicycles, pedestrians, and emergency vehicles all use the intersection. In addition, there is a traffic signal with Walk/Don't Walk lights. Should the road surface be included in the simulation? Certainly, road quality can have an effect on the traffic, but for a first design, it may be simpler to leave this consideration aside. The first object is probably the intersection itself. Perhaps the intersection object maintains lists of cars waiting to pass through the signal in each direction, as well as lists of people waiting to cross at the crosswalks. It will need methods to choose which and how many cars and people go through the intersection. There will be only one intersection, so you may want to consider how you will ensure that only one object is instantiated (hint: think about static methods and protected access). People and cars are both clients of the intersection. They share a number of characteristics: they can appear at any time, there can be any number of them, and they both wait at the signal (although in different lines). This suggests that you will want to consider a common base class for pedestrians and cars. The classes would therefore include class Entity; // a client of the intersection // the root of all cars, trucks, bicycles and emergency vehicles. class Vehicle : Entity ...; // the root of all People class Pedestrian : Entity...; class class class class class Car : public Vehicle...; Truck : public Vehicle...; Motorcycle : public Vehicle...; Bicycle : public Vehicle...; Emergency_Vehicle : public Vehicle...; // contains lists of cars and people waiting to pass class Intersection; 2. Suppose the intersections from Exercise 1 were in a suburb of Boston, which has arguably the unfriendliest streets in the United States. At any time, there are three kinds of Boston drivers: Locals, who continue to drive through intersections after the light turns red Tourists, who drive slowly and cautiously (in a rental car, typically)Taxis, which have a wide variation of driving patterns, depending on the kinds of passengers in the cabs Also, Boston has two kinds of pedestrians: Locals, who cross the street whenever they feel like it and seldom use the crosswalk buttons Tourists, who always use the crosswalk buttons and only cross when the Walk/Don't Walk light permits. Finally, Boston has bicyclists who never pay attention to stoplights How do these considerations change the model? A reasonable start on this would be to create derived objects that model the refinements suggested by the problem: class Local_Car class class class Pedestrian...; class Pedestrian...; class : public Car...; Tourist_Car : public Car...; Taxi : public Car...; Local_Pedestrian : public Tourist_Pedestrian : public Boston_Bicycle : public Bicycle...; By using virtual methods, each class can modify the generic behavior to meet its own specifications. For example, the Boston driver can react to a red light differently than a tourist does, while still inheriting the generic behaviors that continue to apply. 3. You are asked to design a group scheduler. The software allows you to arrange meetings among individuals or groups, and to reserve a limited number of conference rooms. Identify the principal subsystems. Two discrete programs need to be written for this project: the client, which the users run; and the server, which would run on a separate machine. In addition, the client machine would have an administrative component to enable a system administrator to add new people and rooms. If you decide to implement this as a client/server model, the client would accept input from users and generate a request to the server. The server would service the request and send back the results to the client. With this model, many people can schedule meetings at the same time. On the client's side, there are two major subsystems in addition to the administrative module: the user interface and the communications subsystem. The server's side consists of three main subsystems: communications, scheduling, and a mail interface that would announce to the user when changes have occurred in the schedule.4. Design and show the interfaces to the classes in the room-reservation portion of the program discussed in Exercise 3. A meeting is defined as a group of people reserving a room for a certain amount of time. The person making the schedule might want a specific room, or a specified time; but the scheduler must always be told how long the meeting will last and who is required to attend. The objects will probably include the users of the system as well as the conference rooms. Don't forget to include classes for the calendar, and perhaps a Meeting class that encapsulates all that is known about a particular event. The prototypes for the classes might include: class Calendar_Class; // forward reference class Meeting; // forward reference class Configuration { public: Configuration(); ~Configuration(); Meeting Schedule( ListOfPerson&, Delta Time duration ); Meeting Schedule( ListOfPerson&, Delta Time duration, Time ); Meeting Schedule( ListOfPerson&, Delta Time duration, Room ); ListOfPerson& People(); // public accessors ListOfRoom& Rooms(); // public accessors protected: ListOfRoom rooms; ListOfPerson people; }; typedef long Room_ID; class Room { public: Room( String name, Room_ID id, int capacity, String directions = "", String description = "" ); ~Room(); Calendar_Class Calendar(); protected: Calendar_Class calendar; int capacity; Room_ID id; String name;String directions; // where is this room? String description; }; typedef long Person_ID; class Person { public: Person( String name, Person_ID id ); ~Person(); Calendar_Class Calendar(); // the access point to add meetings protected: Calendar_Class calendar; Person_ID id; String name; }; class Calendar_Class { public: Calendar_Class(); ~Calendar_Class(); void Add( const Meeting& ); meeting to the calendar void Delete( const Meeting& ); Meeting* Lookup( Time ); there is a meeting at the // add a // see if // given time Block( Time, Duration, String reason = "" ); // allocate time to yourself... protected: OrderedListOfMeeting meetings; }; class Meeting { public: Meeting( ListOfPerson&, Room room, Time when, Duration duration, String purpose = "" ); ~Meeting(); protected: ListOfPerson people; Room room; Time when;Duration String duration; purpose; }; Day 19 Quiz 1. What is the difference between a template and a macro? Templates are built into the C++ language and are type-safe. Macros are implemented by the preprocessor and are not type-safe. 2. What is the difference between the parameter to a template and the parameter to a function? The parameter to the template creates an instance of the template for each type. If you create six template instances, six different classes or functions are created. The parameters to the function change the behavior or data of the function, but only one function is created. 3. What is the difference between a type-specific template friend class and a general template friend class? The general template friend function creates one function for every type of the parameterized class; the type-specific function creates a type-specific instance for each instance of the parameterized class. 4. Is it possible to provide special behavior for one instance of a template but not for other instances? Yes, create a specialized function for the particular instance. In addition to creating Array::SomeFunction(), also create Array::SomeFunction() to change the behavior for integer arrays. 5. How many static variables are created if you put one static member into a template class definition? One for each instance of the class. Exercises 1. Create a template based on this List class: class List { private:public: List():head(0),tail(0),theCount(0) {} virtual ~List(); void insert( int value ); void append( int value ); int is_present( int value ) const; int is_empty() const { return head == 0; } int count() const { return theCount; } private: class ListCell { public: ListCell(int value, ListCell *cell = 0):val(value),next(cell){} int val; ListCell *next; }; ListCell *head; ListCell *tail; int theCount; }; Here is one way to implement this template: template class List { public: List():head(0),tail(0),theCount(0) { } virtual ~List(); void insert( Type value ); void append( Type value ); int is_present( Type value ) const; int is_empty() const { return head == 0; } int count() const { return theCount; } private: class ListCell { public: ListCell(Type value, ListCell *cell = 0):val(value),next(cell){} Type val; ListCell *next;}; ListCell *head; ListCell *tail; int theCount; }; 2. Write the implementation for the List class (non-template) version. void List::insert(int value) { ListCell *pt = new ListCell( value, head ); assert (pt != 0); // this line added to handle tail if ( head == 0 ) tail = pt; head = pt; theCount++; } void List::append( int value ) { ListCell *pt = new ListCell( value ); if ( head == 0 ) head = pt; else tail->next = pt; tail = pt; theCount++; } int List::is_present( int value ) const { if ( head == 0 ) return 0; if ( head->val == value || tail->val == value ) return 1; ListCell *pt = head->next; for (; pt != tail; pt = pt->next) if ( pt->val == value ) return 1; return 0; }3. Write the template version of the implementations. template List::~List() { ListCell *pt = head; while ( pt ) { ListCell *tmp = pt; pt = pt->next; delete tmp; } head = tail = 0; } template void List::insert(Type value) { ListCell *pt = new ListCell( value, head ); assert (pt != 0); // this line added to handle tail if ( head == 0 ) tail = pt; head = pt; theCount++; } template void List::append( Type value ) { ListCell *pt = new ListCell( value ); if ( head == 0 ) head = pt; else tail->next = pt; tail = pt; theCount++; } template int List::is_present( Type value ) const { if ( head == 0 ) return 0; if ( head->val == value || tail->val == value )return 1; ListCell *pt = head->next; for (; pt != tail; pt = pt->next) if ( pt->val == value ) return 1; return 0; } 4. Declare three List objects: a list of strings, a list of Cats and a list of ints. List string_list; List Cat_List; List int_List; 5. BUG BUSTERS: What is wrong with the following code? (Assume the List template is defined, and Cat is the class defined earlier in the book.) List Cat_List; Cat Felix; CatList.append( Felix ); cout << "Felix is " << ( Cat_List.is_present( Felix ) ) ? "" : "not " << "present\n"; Hint: (this is tough) What makes Cat different from int? Cat doesn't have operator == defined; all operations that compare the values in the List cells, such as is_present, will result in compiler errors. To reduce the chance of this, put copious comments before the template definition stating what operations must be defined for the instantiation to compile. 6. Declare friend operator == for List. friend int operator==( const Type& lhs, const Type& rhs ); 7. Implement friend operator == for List. template int List::operator==( const Type& lhs, const Type& rhs ) { // compare lengths first if ( lhs.theCount != rhs.theCount ) return 0; // lengths differ ListCell *lh = lhs.head;ListCell *rh = rhs.head; for(; lh != 0; lh = lh.next, rh = rh.next ) if ( lh.value != rh.value ) return 0; return 1; // if they don't differ, they must match } 8. Does operator== have the same problem as in Exercise 4? Yes, because comparing the array involves comparing the elements, operator!= must be defined for the elements as well. 9. Implement a template function for swap, which exchanges two variables. // template swap: // must have assignment and the copy constructor defined for the Type. template void swap( Type& lhs, Type& rhs) { Type temp( lhs ); lhs = rhs; rhs = temp; } Day 20 Quiz 1. What is an exception? An exception is an object that is created as a result of invoking the keyword throw. It is used to signal an exceptional condition, and is passed up the call stack to the first catch statement that handles its type. 2. What is a try block? A try block is a set of statements that might generate an exception. 3. What is a catch statement? A catch statement has a signature of the type of exception it handles. It follows a try block and acts as the receiver of exceptions raised within the try block.4. What information can an exception contain? An exception is an object and can contain any information that can be defined within a user- created class. 5. When are exception objects created? Exception objects are created when you invoke the keyword throw. 6. Should you pass exceptions by value or by reference? In general, exceptions should be passed by reference. If you don't intend to modify the contents of the exception object, you should pass a const reference. 7. Will a catch statement catch a derived exception if it is looking for the base class? Yes, if you pass the exception by reference. 8. If there are two catch statements, one for base and one for derived, which should come first? catch statements are examined in the order they appear in the source code. The first catch statement whose signature matches the exception is used. 9. What does catch(...) mean? catch(...) will catch any exception of any type. 10. What is a breakpoint? A breakpoint is a place in the code where the debugger will stop execution. Exercises 1. Create a try block, a catch statement, and a simple exception. #include class OutOfMemory {}; int main() { try { int *myInt = new int; if (myInt == 0) throw OutOfMemory(); } catch (OutOfMemory){ cout << "Unable to allocate memory!\n"; } return 0; } 2. Modify the answer from Exercise 1, put data into the exception along with an accessor function, and use it in the catch block. #include #include #include class OutOfMemory { public: OutOfMemory(char *); char* GetString() { return itsString; } private: char* itsString; }; OutOfMemory::OutOfMemory(char * theType) { itsString = new char[80]; char warning[] = "Out Of Memory! Can't allocate room for: "; strncpy(itsString,warning,60); strncat(itsString,theType,19); } int main() { try { int *myInt = new int; if (myInt == 0) throw OutOfMemory("int"); } catch (OutOfMemory& theException) { cout << theException.GetString(); } return 0; } 3. Modify the class from Exercise 2 to be a hierarchy of exceptions. Modify the catch blockto use the derived objects and the base objects. 1: #include 2: 3: // Abstract exception data type 4: class Exception 5: { 6: public: 7: Exception(){} 8: virtual ~Exception(){} 9: virtual void PrintError() = 0; 10: }; 11: 12: // Derived class to handle memory problems. 13: // Note no allocation of memory in this class! 14: class OutOfMemory : public Exception 15: { 16: public: 17: OutOfMemory(){} 18: ~OutOfMemory(){} 19: virtual void PrintError(); 20: private: 21: }; 22: 23: void OutOfMemory::PrintError() 24: { 25: cout << "Out of Memory!!\n"; 26: } 27: 28: // Derived class to handle bad numbers 29: class RangeError : public Exception 30: { 31: public: 32: RangeError(unsigned long number){badNumber = number;} 33: ~RangeError(){} 34: virtual void PrintError(); 35: virtual unsigned long GetNumber() { return badNumber; } 36: virtual void SetNumber(unsigned long number) {badNumber = $)number;} 37: private: 38: unsigned long badNumber; 39: }; 40: 41: void RangeError::PrintError() 42: { 43: cout << "Number out of range. You used " << GetNumber()<< 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: the 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: "!"!!\n"; } void MyFunction(); // func. prototype int main() { try { MyFunction(); } // Only one catch required, use virtual functions to do // right thing. catch (Exception& theException) { theException.PrintError(); } return 0; } void MyFunction() { unsigned int *myInt = new unsigned int; long testNumber; if (myInt == 0) throw OutOfMemory(); cout << "Enter an int: "; cin >> testNumber; // this weird test should be replaced by a series // of tests to complain about bad user input if (testNumber > 3768 || testNumber < 0) throw RangeError(testNumber); *myInt = testNumber; cout << "Ok. myInt: " << *myInt; delete myInt; } 4. Modify the program from Exercise 3 to have three levels of function calls. 1: 2: 3: 4: 5: #include // Abstract exception data type class Exception {6: public: 7: Exception(){} 8: virtual ~Exception(){} 9: virtual void PrintError() = 0; 10: }; 11: 12: // Derived class to handle memory problems. 13: // Note no allocation of memory in this class! 14: class OutOfMemory : public Exception 15: { 16: public: 17: OutOfMemory(){} 18: ~OutOfMemory(){} 19: virtual void PrintError(); 20: private: 21: }; 22: 23: void OutOfMemory::PrintError() 24: { 25: cout << "Out of Memory!!\n"; 26: } 27: 28: // Derived class to handle bad numbers 29: class RangeError : public Exception 30: { 31: public: 32: RangeError(unsigned long number){badNumber = number;} 33: ~RangeError(){} 34: virtual void PrintError(); 35: virtual unsigned long GetNumber() { return badNumber; } 36: virtual void SetNumber(unsigned long number) {badNumber = ,,number;} 37: private: 38: unsigned long badNumber; 39: }; 40: 41: void RangeError::PrintError() 42: { 43: cout << "Number out of range. You used " << GetNumber() << ")"!!\n"; 44: } 45: 46: // func. prototypes 47: void MyFunction(); 48: unsigned int * FunctionTwo(); 49: void FunctionThree(unsigned int *);50: 51: 52: 53: 54: 55: 56: 57: the 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: int main() { try { MyFunction(); } // Only one catch required, use virtual functions to do // right thing. catch (Exception& theException) { theException.PrintError(); } return 0; } unsigned int * FunctionTwo() { unsigned int *myInt = new unsigned int; if (myInt == 0) throw OutOfMemory(); return myInt; } void MyFunction() { unsigned int *myInt = FunctionTwo(); FunctionThree(myInt); cout << "Ok. myInt: " << *myInt; delete myInt; } void FunctionThree(unsigned int *ptr) { long testNumber; cout << "Enter an int: "; cin >> testNumber; // this weird test should be replaced by a series // of tests to complain about bad user input if (testNumber > 3768 || testNumber < 0) throw RangeError(testNumber); *ptr = testNumber; }5. BUG BUSTERS: What is wrong with the following code? #include "stringc.h" // our string class class xOutOfMemory { public: xOutOfMemory( const String& where ) : location( where ){} ~xOutOfMemory(){} virtual String where(){ return location }; private: String location; } main() { try { char *var = new char; if ( var == 0 ) throw xOutOfMemory(); } catch( xOutOfMemory& theException ) { cout << "Out of memory at " << theException.location() << "\n"; } } In the process of handling an "out of memory" condition, a string object is created by the constructor of xOutOfMemory. This exception can only be raised when the program is out of memory, so this allocation must fail. It is possible that trying to create this string will raise the same exception, creating an infinite loop until the program crashes. If this string is really required, you can allocate the space in a static buffer before beginning the program, and then use it as needed when the exception is thrown. Day 21 Quiz 1. What is the difference between strcpy() and strncpy()? strcpy(char* destination, char* source) copies source to destination, and puts a null at the end of destination. destination must be large enough to accommodate source, or strcpy() will simply write past the end of the array.strncpy(char* destination char* source, int howmany) will write howmany bytes of source to destination, but will not put a terminating null. 2. What does ctime() do? ctime() takes a time_t variable and returns an ASCII string with the current time. The time_t variable is typically filled by passing its address to time(). 3. What is the function to call to turn an ASCII string into a long? atol() 4. What does the complement operator do? It flips every bit in a number. 5. What is the difference between OR and exclusive OR? OR returns TRUE if either or both bits are set; exclusive OR returns TRUE only if one, but not both, is set. 6. What is the difference between & and &&? & is the bitwise AND operator, and && is the logical AND operator. 7. What is the difference between | and ||? | is the bitwise OR operator, and || is the logical OR operator. Exercises 1. Write a program to safely copy the contents of a 20-byte string into a 10-byte string, truncating whatever won't fit. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: #include #include int main() { char bigString[21] = "12345678901234567890"; char smallString[10]; strncpy(smallString,bigString,9); smallString[9]='\0'; cout << "BigString: " << bigString << endl; cout << "smallString: " << smallString << endl; return 0;13: } 2. Write a program that tells the current date in the form 7/28/94. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: #include #include int main() { time_t currentTime; struct tm *timeStruct; time (¤tTime); timeStruct = localtime(¤tTime); cout << timeStruct->tm_mon+1 << "/"; cout << timeStruct->tm_mday << "/"; cout << timeStruct->tm_year << " "; return 0; } 3. Write the definition of a class that uses bit fields to store whether the computer is monochrome or color, a PC or Macintosh, a laptop or a desktop, and whether it has a CD- ROM. #include enum Boolean { FALSE = 0, TRUE = 1 }; class Computer { public: // types enum Machine { Mac = 0, PC }; public: // methods Computer( Boolean color, Boolean laptop, Machine kind, Boolean cdrom ) : Color( color ), Laptop( laptop ), Kind( kind ), CDRom( cdrom ){} ~Computer(){} friend ostream& operator<<( ostream& os, const Computer& computer ); private: Boolean Color : 1; Boolean Laptop : 1; Machine Kind : 1;Boolean CDRom : 1; }; ostream& operator<<( ostream& os, const Computer& computer ) { os << "["; ( computer.Color ) ? os << "color" : os << "monochrome"; os << ", "; ( computer.Laptop ) ? os << "laptop" : os << "desktop"; os << ", "; ( computer.Kind ) ? os << "PC" : os << "Mac"; os << ", "; ( computer.CDRom ) ? os << "" : os << "no "; os << "CD-Rom"; os << "]"; return os; } int main() { Computer pc( TRUE, TRUE, Computer :: PC, TRUE ); cout << pc << `\n'; return 0; } 4. Write a program that creates a 26-bit mask. Prompt the user to enter a word, and then quickly report on which letters were used in setting and reading the bits (one bit per character). The program should treat upper- and lowercase letters as the same. #include #include #include class Bits { public: enum { BITS_PER_INT = 16 }; Bits( int cnt ); virtual ~Bits(); void clear(); void set( int position ); void reset( int position ); int is_set( int position ); private: unsigned int * bits;int count; int Ints_Needed; }; class AlphaBits : private Bits { public: AlphaBits() : Bits( 26 ){} ~AlphaBits(){} void clear() { Bits::clear(); } void set( char ); void reset( char ); int is_set( char ); }; Bits :: Bits( int cnt ) : count( cnt ) { Ints_Needed = count / BITS_PER_INT; // if there is a remainder, you need one more member in array if ( 0 != count % BITS_PER_INT ) Ints_Needed++; // create an array of ints to hold all the bits bits = new unsigned int[ Ints_Needed ]; clear(); } Bits :: ~Bits() { delete [] bits; } void Bits :: clear() { // clear the bits for ( int i = 0; i < Ints_Needed; i++ ) bits[ i ] = 0; } void Bits :: set( int position ) { // find the bit to set int Int_Number = position / BITS_PER_INT; int Bit_Number = position % BITS_PER_INT;// create mask with that one bit set unsigned int mask = 1 << Bit_Number; // set the bit bits[ Int_Number ] |= mask; } // clear the bit void Bits :: reset( int position ) { int Int_Number = position / BITS_PER_INT; int Bit_Number = position % BITS_PER_INT; unsigned int mask = ~( 1 << Bit_Number ); bits[ Int_Number ] &= mask; } int Bits :: is_set( int position ) { int Int_Number = position / BITS_PER_INT; int Bit_Number = position % BITS_PER_INT; unsigned int mask = 1 << Bit_Number; return ( 0 != ( bits[ Int_Number ] & mask ) ); } void AlphaBits :: set( char s ) { // make sure the requested character is an alphabetic character // if so, force it to lower case, then subtract the ascii value // of `a' to get its ordinal (where a = 0, b =1) and set that bit if ( isalpha( s ) ) Bits :: set( tolower( s ) - `a' ); } void AlphaBits :: reset( char s ) { if ( isalpha( s ) ) Bits :: reset( tolower( s ) - `a' ); }int AlphaBits :: is_set( char s ) { if ( isalpha( s ) ) return Bits :: is_set( tolower( s ) - `a' ); else return 0; } int main() { AlphaBits letters; char buffer[512]; for (;;) { cout << "\nPlease type a word (0 to quit): "; cin >> buffer; if (strcmp(buffer,"0") == 0) break; // set the bits for ( char *s = buffer; *s; s++ ) letters.set( *s ); // print the results cout << "The letters used were: "; for ( char c = `a'; c <= `z'; c++ ) if ( letters.is_set( c ) ) cout << c << ` `; cout << `\n'; // clear the bits letters.clear(); } return 0; 5. Write a program that sorts the command-line parameters. If the user enters SortFunc cat bird fish dog, the program prints bird cat dog fish. #include #include void swap ( char* &s, char* &t ) {char* temp = s; s = t; t = temp; } int main( int argc, char* argv[] ) { // Since argv[0] is the program name, //we don't want to sort or print it; // we start sorting at element 1 (not 0). // a "Bubble items. int i,j; for ( i = 1; for ( j if Sort" is used because of the small number of i < argc; i++ ) = i + 1; j < argc; j++ ) ( 0 < strcmp( argv[i], argv[j] ) ) swap( argv[i], argv[j] ); for ( i = 1; i < argc; i++ ) cout << argv[i] << ` `; cout << `\n'; return 0; } 6. Write a program that adds two numbers without using the addition operator (+), subtraction operator (-), increment (++), or decrement (--). Hint: Use the bit operators! If you take a look at the addition of two bits, you'll notice the answer will contain two bits: the result bit and the carry bit. Thus, adding 1 and 1 in binary results in 1 with a carry of 1. If we add 101 to 001, here are the results: 101 001 110 // 5 //1 //6 If you add two "set" bits (each is valued as one), the result is that the result bit is 0 but the carry bit is 1. If you add two clear bits, both the result and the carry are 0. If you add two bits with one set and the other clear, the result bit is 1, but the carry bit is 0. Here is a table that summarizes these rules: lhs rhs | carry result 0 0 | 0 0 0 1 | 0 11 1 0 1 | | 0 1 1 0 Examine the logic of the carry bit. If both bits to be added (lhs and rhs) are 0 or either side is 0, the answer is 0. Only if both bits are 1 is the answer 1. This is exactly the same as the AND operator (&). In the same way, the result is an XOR (^) operation: if either bit is 1 (but not both), the answer is 1; otherwise, the answer is 0. When you get a carry, it is added to the next most significant (leftmost) bit. This implies either iterating through each bit or recursion. #include unsigned int add( unsigned int lhs, unsigned int rhs ) { unsigned int result, carry; while ( 1 ) { result = lhs ^ rhs; carry = lhs & rhs; if ( carry == 0 ) break; lhs = carry << 1; rhs = result; }; return result; } int main() { unsigned long a, b; for (;;) { cout << "Enter two numbers. (0 0 to stop): "; cin >> a >> b; if (!a && !b) break; cout <> a >> b; if (!a && !b) break; cout <